npm package: @reed239/test-project
Description: My first open source project!
Author: Rob Reed
Latest version: 1.1.0

# 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](https://www.npmjs.com/) - 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

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

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

```bash
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

```bash
yarn add --dev typescript
```

And add a `tsconfig.json` with these options

```json
{
  "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:

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

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

```bash
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

```bash
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`](https://www.npmjs.com/package/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

```bash
./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:

```js
{
    // ...
    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:

```ts
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:

```ts
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:

```bash
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

```json
{
  "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.

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

Add an `.eslintrc` file with this config:

```json
{
  "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_.

```txt
node_modules
lib
```

Now, add the `lint` command to `package.json`

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

Now verify everything works by running

```bash
yarn run lint
```

### Formatting

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

```bash
yarn add --dev prettier
```

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

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

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

```json
 "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`](https://docs.npmjs.com/cli/v9/commands/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`:

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

The full `scripts` section should look like this:

```json
  "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.

```json
{
    // ...
    "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](https://docs.npmjs.com/package-name-guidelines) 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`](https://www.npmjs.com/package/@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`](https://www.npmjs.com/package/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`.

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

Now, it's time to [create your NPM account](https://www.npmjs.com/signup) if you haven't already.

_NOTE: Your username should be what you scope `/test-project` under_.

Then login through your terminal using

```bash
yarn login
```

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

```bash
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](https://www.npmjs.com/) 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](https://docs.npmjs.com/about-package-readme-files). 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](https://docs.npmjs.com/about-package-readme-files) 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):

```markdown
# 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](https://shields.io/badges/npm-scoped) to create one quickly using this format

```markdown
![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.

```bash
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](https://semver.org/), or semver for short.

Here's what Semver 2.0.0 says:

```txt
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!