Set up our pnpm monorepo
Tags: monorepo, pnpm
Published: 2022-05-29 19:47:59

In this post, we'll implement the basic monorepo structure we mentioned at the end of [High level technical overview](https://dev.to/with_heart/high-level-technical-overview-5hc0).

As a quick refresher, we'll have three packages:

- `app`
- `bot`
- `db`

We'll only generate a basic package structure for each. Here's what the end structure will look like (with some files/directories excluded to focus on the changes we'll be making):

```sh
.
├── app
│  ├── package.json
│  ├── pages
│  ├── public
│  ├── README.md
│  └── tsconfig.json
├── bot
│  ├── package.json
│  ├── src
│  │  └── index.ts
│  └── tsconfig.json
├── db
│  ├── package.json
│  ├── src
│  │  └── index.ts
│  └── tsconfig.json
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── README.md
└── tsconfig.json
```

Additional monorepo setup like using the packages as dependencies of each other, build tooling, etc. will come later.

## Create `pnpm` workspace

Since we're using `pnpm`, monorepo setup doesn't require much setup on our part.

Essentially, we just need to create a `pnpm-workspace.yaml` file that defines the package directories in our monorepo:

```yaml
packages:
  - 'app'
  - 'bot'
  - 'db'
```

With this change, `pnpm` knows that the `app`, `bot`, and `db` directories are packages in our monorepo.

Now that we have this file in place, we can set up our package directories.

## Create the `app` package

Since `app` is a Next.js app, we'll use `create-next-app` to generate that package: `pnpm create next-app --ts app`.

The setup runs as expected and dependencies are added to the root `pnpm-lock.yaml` file.

We can start our app to test that it works: `pnpm --filter app run dev`

--- 

I just really want to note how cool `pnpm` is. The `--filter` flag allows us to zoom in on specific packages to execute our commands. In the above command, we use `--filter app` to target `pnpm run dev` to the `app` directory.

--- 

Before we move on, we need to fix an `eslint` error in the `app` directory: `Parsing error: Cannot find module 'next/babel'`. 

To resolve that, we need to update `app/.eslintrc.json` to extend from `next/babel`:

```json
{
  "extends": ["next/babel", "next/core-web-vitals"]
}
```

## Create the `bot` and `db` packages

### Prepare `typescript`

Since `bot` and `db` will both be libs and not React apps, we can configure `typescript` at the root and have both packages extend their configuration from that.

To get started, we'll install a few packages: `pnpm i typescript @types/node -w @tsconfig/node16-recommended`

The `-w` flag tells `pnpm` that we want to install the dependencies at the root.

The `@tsconfig/node16-recommended` package contains a `tsconfig.json` file with recommended TypeScript configuration. We'll use that in our root `tsconfig.json` file:

```json
{
  "extends": "@tsconfig/node16-strictest/tsconfig.json",
  "exclude": ["app"]
}
```

We also exclude the `app` directory because Next.js uses its own `tsconfig.json` file.

### Create the `bot` package

```sh
bot
├── package.json
├── src
│  └── index.ts
└── tsconfig.json
```

We'll create the `bot` folder and a `package.json` inside of it:

```json
{
  "name": "bot",
  "version": "0.0.0",
  "private": true
}
```

We'll also create `bot/tsconfig.json`:

```json
{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "dist"
  },
  "include": ["src"]
}
```

This configuration includes `src` in the files TypeScript will compile and the `extends` option means we reuse the configuration specified in our root `tsconfig.json`.

### Create the `db` package

```sh
db
├── package.json
├── src
│  └── index.ts
└── tsconfig.json
```

To create this package, we'll do everything we did to create the `bot` package except we replace the word `bot` with `db`.

---

Awesome! We now have a monorepo! This gives us a lot of flexibility and clarity in how we configure and use our packages.

To check out the PR for this post, look here: https://github.com/Developer-DAO/discord-server-info/pull/10