This repository explains how to create monorepos project using npm and TypeScript.
- Tools
 - Directory Structure
 - Workspaces
 - Dependencies across packages
 - Resolve dependencies as TypeScript projects
 - How to execute scripts for each package ?
 - License
 
- npm cli(v7 or later)
 - TypeScript
 
Put each package under the packages directory.
.
├── node_modules/
├── README.md
├── package-lock.json
├── package.json
├── packages
│   ├── x-cli
│   │   ├── lib
│   │   │   ├── cli.d.ts
│   │   │   ├── cli.js
│   │   │   ├── cli.js.map
│   │   │   ├── main.d.ts
│   │   │   ├── main.js
│   │   │   └── main.js.map
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── cli.ts
│   │   │   └── main.ts
│   │   └── tsconfig.json
│   └── x-core
│       ├── lib
│       │   ├── index.d.ts
│       │   ├── index.js
│       │   └── index.js.map
│       ├── package.json
│       ├── src
│       │   └── index.ts
│       └── tsconfig.json
├── tsconfig.build.json
└── tsconfig.json
Using npm workspaces feature, configure the following files:
Open package.json and append the workspaces key.
/* package.json */
{
  "name": "npm-ts-workspaces-example",
  "private": true,
  ...
  "workspaces": ["packages/*"]
}Exec npm install. After successful running, all dependencies included from each package are downloaded under the repository root node_modules directory.
In this example, the x-cli package depends on another package, x-core. So to execute (or test) x-cli, x-core packages should be installed.
But in development the x-core package is not published so you can't install it.
For example, packages/x-cli/src/main.spec.ts is a test code for main.ts, which depends on packages/x-core/src/index.ts .
/* packages/x-cli/src/main.ts.*/
import { awesomeFn } from "@quramy/x-core";
export async function main() {
  // dependencies across child packages
  const out = await awesomeFn();
  return out;
}So we need to link x-core package from x-cli to execute the x-cli 's test.
Workspaces feature of npm also solves this problem. npm i creates sim-links of each package into the top-level node_modules dir.
As mentioned above, npm cli resolves dependencies across packages. It's enough for "runtime". However considering TypeScript sources, in other words "static", it's not.
We need to tell "x-cli package depends on x-core" to TypeScript compiler. TypeScript provides much useful feature to do this, "Project References".
First, you add composite: true to project-root tsconfig.json to use project references feature.
/* tsconfig.json */
{
  "compilerOptions": {
    ...
    "composite": true
  }
}Second, configure each package's tsconfig and configure dependencies across packages.
/* packages/x-cli/tsconfig.json */
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "lib"
  },
  "references": [{ "path": "../x-core" }]
}And create a project which depends on all packages:
/* tsconfig.build.json */
{
  "files": [],
  "references": [{ "path": "packages/x-core" }, { "path": "packages/x-cli" }]
}Let's exec npx tsc --build tsconfig.build.json. The .ts files included in all packages are build at once!
We can use --workspaces option.
# Excecute npm test in all workspaces
$ npm test --workspaces# Bump up packages in all workspaces
$ npm version --workspaces patch
# Publish packages in all workspaces
$ npm publish --workspacesThe MIT License (MIT)