Monorepo + Typescript

278 阅读2分钟

什么是monorepo?

单仓库是一种软件开发方法,其中一个仓库包含多个项目。这些项目可以是任何东西,从单个应用程序到可重用的组件或者实用程序函数。在monorepo中,这些包通常被称为本地包。

monorepo有哪些好处

  1. 更容易跨团队标准化代码
  2. 支持更好的可见性和跨团队合作
  3. 简化了文件组织和代码依赖关系管理

如何使用npm构建Typescript Monorepo

可以通过以下命令获取github仓库中示例代码:

git clone https://github.com/vvangwenkun/monorepo.git

准备

为了使用npm构建Typescript Monorepo,你需要安装以下工具:

  • Node.js >= 14
  • NPM >= 7
  • Typescript

初始化目录结构

你的monorepo目录结构应如下所示:

directory structure.png

创建根目录package.json文件

{
  "name": "monorepo",
  "version": "1.0.0",
  "description": "a monorepo in typescript",
  "main": "index.js",
  "scripts": {
    "build": "npx tsc --build --verbose"
  },
  "author": "aric.vvang@gmail.com",
  "license": "ISC",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "devDependencies": {
    "typescript": "^5.5.3"
  }
}

其中,workspaces属性必须设置,npm才允许你在根目录中定义多个包。

添加一个本地包

packages目录中创建util文件夹:

  • 使用以下命令初始化util包的package.json文件
npm init --scope @monorepo --workspace ./packages/util -y

确保package.json包含以下内容:

{
  "name": "@monorepo/util",
  "version": "1.0.0",
  "description": "",
  "main": "dist/index.js",
  "scripts": {
    "build": "npx tsc --build --verbose"
  },
  "author": "aric.vvang@gmail.com",
  "license": "ISC",
  "dependencies": {
    "dayjs": "^1.11.12"
  }
}
  • 初始化tsconfig.json文件
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "CommonJS",
    "moduleResolution": "Node",
    "declaration": true,
    "strict": true,
    "incremental": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "rootDir": "src",
    "outDir": "dist",
    "composite": true
  }
}

必须将composite选项设置为true,你才能在monorepo的其他部分引用这个项目。

验证本地包是否正常工作

npm run build --workspace ./packages/util

编译成功后,你的目录应该如下所示:

Screenshot 2024-07-20 at 11.54.59.png

初始化根目录tsconfig.json文件

{
  "compilerOptions": {
    "incremental": true,
    "target": "ESNext",
    "module": "CommonJS",
    "declaration": true,
    "strict": true,
    "moduleResolution": "Node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "rootDir": "src",
    "outDir": "dist"
  },
  "references": [
    {
      "path": "./packages/util"
    }
  ]
}

references属性中列出本地包目录。如果你需要新增一个名为ui本地包,则references应该如下所示:

{
  "references": [
    {
      "path": "./packages/util"
    },
    {
      "path": "./packages/ui"
    }
  ]
}

在根目录下执行npm run build

编译成功后,你的目录应该如下所示:

Screenshot 2024-07-20 at 12.02.20.png

在本地包中使用其他本地包

@monorepo/ui 包中引用 @monorepo/util 包,你需要在根目录中执行:

npm i @monorepo/util --workspace ./packages/ui

该命令会将 @monorepo/util 包设置为 @monorepo/ui 包的依赖:

{
  "name": "@monorepo/ui",
  "dependencies": {
    "@monorepo/util": "^1.0.0"
  }
}

添加外部npm包到本地包

比如在 @monorepo/util 包中引用dayjs包:

npm i dayjs --workspace ./packages/util

在根目录中添加所有本地依赖包

npm i @monorepo/ui @monorepo/util

注意

在本例中,因为ui包依赖于util包,所以在配置根目录中tsconfig.json中的references属性时,需要注意包的列出顺序,先列出被依赖包(util),如:

{
  "references": [
    {
      "path": "./packages/util"
    },
    {
      "path": "./packages/ui"
    }
  ]
}

否则,编译时会出现error:

Screenshot 2024-07-20 at 12.24.34.png