How to use yarn workspaces with typescript and out folders? How to use yarn workspaces with typescript and out folders? typescript typescript

How to use yarn workspaces with typescript and out folders?


I created a Github Repository to make it easier to follow the following code description:


Code Description

TypeScript Project References make it possible to compile a TypeScript project that consist of multiple smaller TypeScript projects, each project having a tsconfig.json file. (Source: Project References Documentation)


TypeScript Setup

We have a root tsconfig.json file that only manages its sub-projects. The references property specifies the directories that each contain a valid tsconfig.json file. If we now build the project with the --build option (tsc --build tsconfig.json) then we specified the projects which should be compiled, but we didn't specified the build order in which the projects should be compiled.

{  "references": [    { "path": "./client" },    { "path": "./cmd" }  ],  "files": [],  "include": [],  "exclude": ["**/node_modules"]}

To correctly specify the build order we need to add a references property to the cmd/tsconfig.json file. This tells the compiler that it first needs to compile client/ before we compile cmd/:

cmd/tsconfig.json:

{  "extends": "../tsconfig.packages.json",  "compilerOptions": {    "rootDir": "src",    "outDir": "dist"  },  "references": [    {      "path": "../client"    }  ]}

Build order

client/  ^  |  | cmd/

Node Setup

Best practice is that each sub-project has its own package.json file with the main property and the name set. In our example both packages (cmd/ and client/) have a main property pointing to the index.js file in the TypeScript outDir directory (cmd/dist/index.js and client/dist/index.js).

Project structure:

tsconfig.jsoncmd/    tsconfig.json    package.json    src/        index.ts    dist/  #artifacts        index.jsclient/    tsconfig.json    package.json    src/        index.ts    dist/  #artifacts        index.js

client/packages.json

{  "name": "client",  "version": "1.0.0",  "main": "dist/index",  ...}

It is important that we add the client/ as dependency to the cmd/packages.json so the module resolution algorithm can find the client/dist/index.js when we import it in our TypeScript code import Foo from 'client';:

cmd/packages.json

{  "name": "cmd",  "version": "1.0.0",  "main": "dist/index",  "dependencies": {    "client": "1.0.0" // important  }}

cmd/src/index.ts

import Foo from 'client';console.log(Foo())

Yarn Setup

The yarn setup is easy. Yarn adds all packages under node_modules instead of:

  • cmd/node_modules
  • client/node_modules

To enable yarn workspaces add the workspaces property and the private: true property to the <root>/package.json file.

<root>/package.json

{  "private": true,  "workspaces": [    "cmd",    "client"  ],  "name": "yarn_workplace",  "version": "1.0.0"  ...}

The cmd/ and client/ packages are symlinked under the <root>/node_modules/ directory:

yarn node_modules


Notes

  • To enable code navigation one has to first build the project
  • Every package lives on its own. The cmd/ package uses the definition file client/dist/index.d.ts for type information instead of using the the TypeScript files directly.


I've set up a monorepo I with some configurations I've always use in my projects using Yarn Workspaces and Typescript in this repository.

I don't know if this set up solves your problem, but you don't need to specify your packages at Typescript config. When you use Yarn Workspaces, it links all your packages in root node_modules that you defined in your workspace package property in your root package.json:

"workspaces": {    "packages": [        "packages/**/*"    ],    "nohoist": []}

After yarn install, the root node_modules has client and cmd as linked folder.

enter image description here

With this configuration, you can simply import any package in any package inside the Workspace. For instance:

// cmd/src/index.tsimport { name } from 'client';const otherName = 'cmd' + name;console.log(otherName);