使用 Lerna 创建单一仓库的 TypeScript 多包项目

Lerna 可以将多个 javascript 项目管理在一个 git 仓库里。

以下分创建过程和使用两部分来说。

本文代码见:https://github.com/MarshalW/monorepo-lerna/tree/v1.0.1

创建

执行命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 安装 lerna
npm install -g lerna

# 创建项目目录
mkdir monorepo-lerna && cd monorepo-lerna

# 创建 git repo
git init
# 创建git忽略配置,见:https://docs.gitignore.io/install/command-line
gi node > .gitignore

# 初始化 node.js 项目
npm init -y

# 初始化 typescript, 生成 tsconfig.json
tsc --init

# 初始化 lerna, independent 各个子项目版本是独立的
lerna init --independent

# 创建各个子项目
lerna create simple-nlp
lerna create simple-service

# 给所有子项目增加 typescript 库,类似于: npm i typescript -D
lerna add typescript --dev

# 给指定的子项目添加依赖库
lerna add natural packages/simple-nlp
lerna add @types/natural packages/simple-nlp

# 将指定子项目设置为另一个子项目的依赖库
lerna add mw-simple-nlp --scope=mw-simple-service

# 给各个子项目安装各自的依赖库,包括它们之间的依赖
lerna bootstrap

根目录下的 tsconfig.json,是包括所有 typescript 子项目的公共配置:

ROOT/tsconfig.json
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"compilerOptions": {
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"module": "ESNext" /* Specify what module code is generated. */,
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
"declarationMap": true /* Create sourcemaps for d.ts files. */,
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
"strict": true /* Enable all strict type-checking options. */,
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

typescript 子项目的 tsconfig.json

1
2
3
4
5
6
7
8
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./lib"
},
"include": ["./src"],
"exclude": ["node_modules", "**/__tests__/*"]
}

子项目的 package.json

simple-nlp/package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"name": "mw-simple-nlp",
"version": "0.1.10",
"description": "",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"type": "module",
"scripts": {
"test": "echo test OK",
"tsc": "tsc"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"typescript": "^4.6.2"
},
"dependencies": {
"@types/natural": "^5.1.0",
"natural": "^5.1.13"
},
"files": ["lib/**/*"]
}

剩下的就是写代码了,这里略了。

使用

在此项目中有 2 个 package:

1
2
3
4
5
6
$ lerna list
lerna notice cli v4.0.0
lerna info versioning independent
mw-simple-nlp
mw-simple-service
lerna success found 2 packages

安装各个 package 的依赖包,以及它们之间的依赖:

1
2
3
4
5
6
7
$ lerna bootstrap
lerna notice cli v4.0.0
lerna info versioning independent
lerna info Bootstrapping 2 packages
lerna info Installing external dependencies
lerna info Symlinking packages and binaries
lerna success Bootstrapped 2 packages

修改某个 package 后,需要 git 提交代码,否则无法执行发布流程。

发布:

1
$ npm run publish

将会交互方式设置 package 新版本,然后发布到 npm 上。

列出当前测试环境信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ lerna info
lerna notice cli v4.0.0
lerna info versioning independent

Environment info:

System:
OS: macOS 12.0.1
CPU: (8) x64 Intel(R) Core(TM) i5-8259U CPU @ 2.30GHz
Binaries:
Node: 16.14.0 - ~/.nvm/versions/node/v16.14.0/bin/node
Yarn: 1.22.17 - ~/.nvm/versions/node/v16.14.0/bin/yarn
npm: 8.3.1 - ~/.nvm/versions/node/v16.14.0/bin/npm
Utilities:
Git: 2.30.0 - /usr/local/bin/git

使用:

1
2
3
4
5
6
7
8
9
10
# 安装 mw-simple-service
$ npm i mw-simple-service --global

# 启动
$ mw-simple-service

# 测试服务是否正常使用
$ curl -d '{"content":"Last week I went to the theatre. I had a very good seat. The play was very interesting. I did not enjoy it. A young man and a young woman were sitting behind me. "}' -H "Content-Type: application/json" -X POST http://localhost:3000

{"lines":["Last week I went to the theatre.","I had a very good seat.","The play was very interesting.","I did not enjoy it.","A young man and a young woman were sitting behind me."]}