- yarn: NPM client.
- Lerna: Multiple packages management tool.
Put each package under the packages directory.
.
├── README.md
├── lerna.json
├── package.json
├── packages
│ ├── x-cli
│ │ ├── lib
│ │ │ ├── main.d.ts
│ │ │ ├── main.js
│ │ │ └── main.js.map
│ │ ├── package.json
│ │ ├── src
│ │ │ └── main.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── x-core
│ ├── lib
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ └── index.js.map
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── tsconfig.base.json
├── tsconfig.json
└── yarn.lock
Using yarn workspace feature, configure the following files:
- /package.json
Append the workspaces key.
{
"private": true,
"workspaces": ["packages/*"]
}- lerna.json
Set npmClient "yarn" and turn useWorkspaces on.
{
"lerna": "2.2.0",
"packages": ["packages/*"],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "1.0.0"
}Exec yarn install(or lerna bootstrap). After successful running, all dependency packages 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.
yarn solve it. This command create sim-link of each packages into the top-level node_modules dir.
As mentioned above, Lerna resolves dependencies between packages. It's enough for "runtime". However considering writing TypeScript sources, it's not.
For example, the following code depends a module x-core located at other package.
/* packages/x-cli/src/main.ts */
import { awesomeFn } from '@quramy/x-core';
export function cli() {
awesomeFn();
return Promise.resolve(true);
}If you compile this code, TypeScript compiler emits a "Cannot find module" error until building x-core package and creating x-core/index.d.ts. And it's silly to compile dependent packages(e.g. x-core) in the same repository after each editing them.
TypeScript's path mapping is the best solution. Path mappings are declared such as:
/* tsconfig.json */
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"baseUrl": "./packages",
"paths": {
"@quramy/*": ["./*/src"]
}
}
}The above setting means import { awesomeFn } from "@quramy/x-core" is mapped to import { awesomeFn } from "../../x-core/src"(it's relative from "packages/x-cli/src/main.ts"). In other words, path mapping allows to treat developing packages' sources as published(compiled) modules.