๐Ÿ“ฆ colinhacks / live-typescript-monorepo

Strategies for live-updating TypeScript types in monorepos

โ˜… 119 stars โ‘‚ 11 forks ๐Ÿ‘ 119 watching
๐Ÿ“ฅ Clone https://github.com/colinhacks/live-typescript-monorepo.git
HTTPS git clone https://github.com/colinhacks/live-typescript-monorepo.git
SSH git clone git@github.com:colinhacks/live-typescript-monorepo.git
CLI gh repo clone colinhacks/live-typescript-monorepo
Colin McDonnell Colin McDonnell Simplify readme d3f5290 1 years ago ๐Ÿ“ History
๐Ÿ“‚ main View all commits โ†’
๐Ÿ“ custom-conditions
๐Ÿ“ project-references
๐Ÿ“ publishconfig
๐Ÿ“ tsconfig-paths
๐Ÿ“ tshy-dev
๐Ÿ“„ .gitignore
๐Ÿ“„ .prettierrc
๐Ÿ“„ README.md
๐Ÿ“„ README.md

Read the associated blog post for complete information.

Live types in TypeScript monorepos

In development, your TypeScript code should feel "alive". When you update your code in one file, the effects of that change should propogate to all files that import it.

This is true even for monorepos, where you may not be importing things from a file, but from a local package.

- import { Fish } from "../pkg-a/index";
+ import { Fish } from "pkg-a"

This repo contains a few strategies you can use to make your TypeScript monorepo feel more alive.

Approaches

Each subdirectory contains a pnpm monorepo (pnpm-workspace.yaml) with two packages, pkg-a and pkg-b. pkg-b has a dependency on pkg-a. Each package has a tsconfig.json that extends a tsconfig.base.json in the root of the monorepo.

The file structure looks something like this:

.
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ packages
โ”‚ย ย  โ”œโ”€โ”€ pkg-a
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ index.ts
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ package.json
โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ tsconfig.json
โ”‚ย ย  โ””โ”€โ”€ pkg-b
โ”‚ย ย      โ”œโ”€โ”€ index.ts
โ”‚ย ย      โ”œโ”€โ”€ package.json
โ”‚ย ย      โ””โ”€โ”€ tsconfig.json
โ”œโ”€โ”€ pnpm-lock.yaml
โ”œโ”€โ”€ pnpm-workspace.yaml
โ”œโ”€โ”€ tsconfig.base.json
โ””โ”€โ”€ tsconfig.json

Let's break down the approaches. (Refer to the associated blog post for complete breakdowns.)

1. project-references

Use TypeScript project references to link packages together. This has a lot of downsides and doesn't play nice with node or other tooling out of the box.

2. publishConfig

Use publishConfig in package.json to specify .ts file in development and .js file in production.

3. tsconfig-paths

Configure compilerOptions.paths in tsconfig.json to override resolution for local package names.

4. tshy-dev

Using tshy's liveDev mode to hard-link the .ts source code directly into ./dist/esm and ./dist/commonjs directories. This lets VS Code discover your live TypeScript source code without any additional package.json configuration.

5. custom-conditions

Recommended User-defined conditional export in package.json#exports plus customConditions in tsconfig.json

Usage

To play around with a particular approach, clone this repo and navigate to the appropriate subdirectory.

$ gh repo clone colinhacks/live-typescript-monorepo
$ cd live-typescript-monorepo
$ cd <subdir>  # tsconfig-paths, publishconfig, project-references, custom-conditions

Then run the following commands:

$ pnpm i                  # install
$ code .                  # open in VS Code

Once the project is open in VS Code, open packages/pkg-a/index.ts and packages/pkg-b/index.ts. When you change the definition of Fish in pkg-a, those changes should propagate immediately to pkg-b without a build step.

Each subdirectory also contains a "test" script that executes pkg-b/index.ts. (This won't work in project-references.) This should execute the latest .ts files for both pkg-a and pkg-b! Make some changes and see the effects.

pnpm test

Happy monorepo hacking!