- 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.