mo-fouad / esbuild-typescript-turborepo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ESBuild TypeScript Turborepo Monorepo starter/example

Update December 2022:

  • The way of exporting/sharing packages has changed. Check the updated section.

Update November 2022:

  • Added support for React with an example app in apps/react.

Details:

This is an example monorepository using ESBuild for it's near-instantaneous build times and Turborepo for it's caching capabilities. It's pre-configured for TypeScript (2 different configurations for browser and for node) and ESLint for linting.

Additionally it's using NPM Workspaces, most examples I could find online were using YARN.

Installation:

git clone https://github.com/barca-reddit/typescript-vscode-esbuild.git

cd typescript-vscode-esbuild

npm run watch

Tech stack:

Exporting/sharing packages:

NB: I don't know if this is the best or the accepted way to do this, neither I consider myself an expert, so PR/issues/feedback of any kind is welcome.

Previously we were making use of typeVersions in package.json to share code within the monorepository, but that caused some issues. Now, we're making use of "moduleResolution": "NodeNext" in tsconfig.json, so that makes things easier.

To create a shared package and import it somewhere else in your monorepo, edit the contents of package.json of the package you want to export and add the following fields:

"exports": {
    ".": {
        "types": "./src/main.ts",
        "import": "./out/main.js"
    }
}

The first part of the export object is the path you want to import (details below).

The types key should point out to an index file where all your exports live. For example:

// src/main.ts
export const foo = "foo";
export const bar = "foo";

The import key should point out to an index (main) file in your compiled out directory and it's there to server plain javascript imports.

All of this allows you to do the following:

// inside some other package
import { foo, bar } from "@repo/shared";

Don't forget to add the package you're exporting as a dependency to the package you're importing it to:

// package.json
{
    // ...
    "dependencies": { "@repo/shared": "*" }
}

You can also have multiple import paths.

"exports": {
    ".": {
        "types": "./src/main.ts",
        "import": "./out/main.js"
    },
    "./server": {
        "types": "./src/server/index.ts",
        "import": "./out/server/index.js"
    },
    "./web": {
        "types": "./src/web/index.ts",
        "import": "./out/web/index.js"
    }
}
// inside some other package
import { foo } from "@repo/shared/server";
import { bar } from "@repo/shared/web";

It is also possible to have wildcard exports like this:

"exports": {
    "./*": {
        "types": "./src/*.ts",
        "import": "./out/*.js"
    }
}

But unfortunately TypeScript is unable to find type declarations this way. If you have a solution or tips about this, issues and PRs are welcome!

Notes:

Turborepo

For Turborepo caching to work, it's essential that all .cache directories it creates are git-ignored.

If build order isn't important for your setup, add the --parallel flag to the npm build script to speed up compiling. You can probably get away with this if you don't bundle any code via bundle: true setting passed to esbuild.

TSC

The TypeScript compiler is used only for type checking, everything else is handled by ESBuild.

Typescript/Eslint

TypeScript and ESLint configurations are matter of personal preference and can easily be adjusted to one's requirements. The same applies for ESBuild, you can also pass additional parameters to buildBrowser or buildNode which will override the default ones.

VSCode

If the .cache directories become annoying, you can just hide them in VSCode, create/edit this file under .vscode/settings.json.

{
    "files.exclude": {
        "cache/": true,
        "**/.turbo": true
    }
}

Version mismatches

You can quickly check whether your package dependencies are in sync, e.g, @repo/a and @repo/b are different versions of the same library.

// package.json (repo a)
{
    "name": "repo/a",
    "dependencies": {
        "foo": "^1.0.0"
    }
}
// package.json (repo b)
{
    "name": "repo/b",
    "dependencies": {
        "foo": "^2.0.0"
    }
}
npm run mismatch

Error: Found version mismatch for the following package:

foo - versions: ^1.0.0, ^2.0.0
- apps/package-a/package.json (@repo/a) - ^1.0.0
- apps/package-b/package.json (@repo/b) - ^2.0.0

This is just a quick and dirty solution that will only report mismatches but won't fix them for you. For more advanced solutions, check out syncpack.

Useful resources:

About


Languages

Language:JavaScript 78.0%Language:TypeScript 15.7%Language:HTML 6.3%