Frontline: Preparing Grounds
Tags: frontend, typescript, yarn
Published: 2022-08-14 05:14:41

Hello everyone! A long time after the first post, it's time to roll up the sleeves and start coding.

Before anything else, I like to prepare the project structure and build. The logical first step is installing Yarn and Typescript and get it to build _anything_.

**In this article** | [Repository](https://github.com/hossomi/tamiyo) | [Changes](https://github.com/hossomi/tamiyo/compare/0e32e22...833dcd9) | [Final commit](https://github.com/hossomi/tamiyo/tree/833dcd989529e5b8baca85055348160c479c9168)

## Setting up Yarn

To install Yarn, it is recommended to use [Corepack](https://nodejs.org/api/corepack.html), bundled with Node starting in 16.10. It seems to be an all-in-one package manager, and it already includes Yarn out of the box. All you need to do is run `corepack enable` (since it is experimental and disabled by default) and then `yarn init -2` to initialize a new project.

> ℹ `-2` ensures that it uses [Yarn 2+ (modern)](https://yarnpkg.com), required to work with zero-installs. More on it later!

As I mentioned, I want to use Yarn workspaces to organize modules. Each module needs its own `package.json` file, so let's start with the following structure:

```
📦tamiyo
 ┣ 📂client
 ┃ ┗ 📜package.json
 ┣ 📂server
 ┃ ┗ 📜package.json
 ┣ 📂shared
 ┃ ┗ 📜package.json
 ┗ 📜package.json
```

The `package.json` content is mostly the usual, the difference being that the parent file contains a `workspaces` field specifying where the workspaces are:
 
 ```json
 {
  "name": "tamiyo",
  "version": "1.0.0",
  "packageManager": "yarn@3.2.0",
  "workspaces": [
    "server",
    "client",
    "shared"
  ]
}
```

For the modules, we are prefixing their package names with `@tamiyo` for consistency. The client file for example is like this:

```json
{
  "name": "@tamiyo/client",
  "version": "1.0.0",
  "packageManager": "yarn@3.2.0"
}
```

Running `yarn install` makes sure we've setup correctly, and you'll notice it creates some files. The `yarn.lock` is well known, and the others are related to Yarn's [zero-installs](https://yarnpkg.com/features/zero-installs): `.yarnrc.yml`, `.pnp.cjs` and the `.yarn` directory. With all these checked into the repository, you no longer need to run this command unless dependencies change (hence zero installs)! Read the documentation for more advantages of this mechanism.

## Setting up Typescript

Let's get started with Typescript! The first thing to do is to install it with `yarn add -D typescript`. It will install the `tsc` CLI tool that we can run with `yarn tsc` to compile Typescript code into Javascript, making it runnable in broswers, node and the like.

Typescript is organized in projects, indicated by a [`tsconfig.json`](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) file along with various project settings. It can also be divided into smaller subprojects, which pairs well with the Yarn workspaces we are using! We just have to place a `tsconfig.json` file in each workspace as well.

```
📦tamiyo
 ┣ 📂client
 ┃ ┣ 📜package.json
 ┃ ┗ 📜tsconfig.json
 ┣ 📂server
 ┃ ┣ 📜package.json
 ┃ ┗ 📜tsconfig.json
 ┣ 📂shared
 ┃ ┣ 📜package.json
 ┃ ┗ 📜tsconfig.json
 ┣ 📜package.json
 ┗ 📜tsconfig.json
```

The root `tsconfig.json` is special: it has a [`references`](https://www.typescriptlang.org/docs/handbook/project-references.html) property specifying the subprojects, which in our case are the workspaces. We also specify `"include": []` so that the root project doesn't build anything, since it's not a module itself.

```json
{
  "references": [
    { "path": "./shared" },
    { "path": "./client" },
    { "path": "./server" }
  ],
  "include": []
}
```

The workspaces are where actual code resides, so their `tsconfig.json` contains compiler options:

```json
{
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "build",

    "target": "es6",
    "strict": true,
    "noFallthroughCasesInSwitch": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "composite": true,
    "esModuleInterop": true,
    "isolatedModules": false,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true
  }
}
```

You can see their meaning in the [TSConfig reference](https://www.typescriptlang.org/tsconfig). It's important to note that `composite: true` is required when using Typescript subprojects. To avoid repeating all those properties in each workspace, we can extract them to a common `tsconfig.common.json` and make the workspaces extend them:

```yaml
# ./tsconfig.common.json
{
  "compilerOptions": {
    "target": "es6",
    "strict": true,
    "noFallthroughCasesInSwitch": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "composite": true,
    "esModuleInterop": true,
    "isolatedModules": false,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true
  }
}

# {module}/tsconfig.json
{
  "extends": "../tsconfig.common.json",
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "build"
  }
}
```

With that, we should be able to build from the root directory. Let's put a simple file under the `src` directory of each module to test it out:

```typescript
// {module}/src/index.ts
console.log('Hello!')
```

Running `yarn tsc --build --verbose` should compile all the Typescript files and generate `.d.ts` and `.js` files under `{module}/build` directories, as we instructed in the configuration. These files are what will be actually run by either Node, browser and the like.

## Next step

Our project can at least be compiled! Next, it is time to get something up and running. As I mentioned, I will use Create React App to build our client, providing us a quick lift. We'll also see how we can implement and integrate our server with it.