Core Concepts

内部包

了解如何在 Turborepo 工作区中创建和使用内部包来共享代码,包括不同的编译策略。

内部包是源代码位于您的工作区内的库。您可以快速创建内部包来在 monorepo 中共享代码,如果以后需要,还可以选择将它们发布到 npm 注册表

内部包在您的仓库中的使用方式是在 package.json 中安装它们,类似于来自 npm 注册表的外部包。但是,您可以使用包管理器的工作区安装语法来引用包,而不是标记要安装的版本:

{
  "dependencies": {
    "@repo/ui": "workspace:*"  }
}

创建内部包指南中,您可以使用编译包策略从头开始构建内部包。在本页面上,我们将描述创建内部包的其他策略及其权衡,包括将包发布到 npm 注册表以创建外部包。

然后您可以像使用外部包一样将包导入到您的代码中:

import { Button } from '@repo/ui';
export default function Page() {
  return <Button>Submit</Button>;
}

编译策略

根据您对库的需求,您可以选择三种编译策略之一:

  • 即时编译包:通过允许应用程序打包器在使用包时编译包,为您的包创建最少的配置。
  • 编译包:通过适量的配置,使用构建工具如 tsc 或打包器编译您的包。
  • 可发布包:编译并准备包以发布到 npm 注册表。这种方法需要最多的配置。

即时编译包

即时编译包由使用它的应用程序编译。这意味着您可以直接使用 TypeScript(或未编译的 JavaScript)文件,需要的配置比本页面上的其他策略少得多。

这种策略在以下情况下最有用:

  • 您的应用程序使用现代打包器构建,如 Turbopack、webpack 或 Vite。
  • 您希望避免配置和设置步骤。
  • 您对应用程序的构建时间感到满意,即使无法命中包的缓存。

即时编译包的 package.json 可能如下所示:

{
  "name": "@repo/ui",
  "exports": {
    "./button": "./src/button.tsx",    "./card": "./src/card.tsx"  },
  "scripts": {
    "lint": "eslint . --max-warnings 0",    "check-types": "tsc --noEmit"  }
}

在这个 package.json 中有几个重要的注意事项:

  • 直接导出 TypeScriptexports 字段标记包的入口点,在这种情况下,您直接引用 TypeScript 文件。这是可能的,因为应用程序的打包器将在其构建过程中使用代码时编译代码。
  • 没有 build 脚本:因为这个包导出 TypeScript,它不需要转译包的构建步骤。这意味着您不必在此包中配置构建工具来使其在工作区中工作。

限制和权衡

  • 仅适用于消费者进行转译时:这种策略只能在包将在使用打包器或原生理解 TypeScript 的工具中使用时使用。消费者的打包器负责将 TypeScript 包转译为 JavaScript。如果您的构建或包的其他用法无法使用 TypeScript,您将需要转移到编译包策略。
  • 没有 TypeScript paths:由其消费者转译的库不能使用 compilerOptions.paths 配置,因为 TypeScript 假设源代码在编写它的包中被转译。如果您使用 TypeScript 5.4 或更高版本,我们建议使用 Node.js 子路径导入
  • Turborepo 无法缓存即时编译包的构建:因为包没有自己的 build 步骤,所以不能被 Turborepo 缓存。如果您希望将配置保持在最低限度并且对应用程序的构建时间感到满意,这种权衡可能对您有意义。
  • 将报告内部依赖项中的错误:当直接导出 TypeScript 时,如果内部依赖项中的代码有 TypeScript 错误,依赖包中的类型检查将失败。您可能会在某些情况下发现这令人困惑或有问题。

编译包

编译包是使用构建工具(如 tsc(TypeScript 编译器))处理自己编译的包。

{
  "name": "@repo/ui",
  "exports": {
    "./button": {
      "types": "./src/button.tsx",      "default": "./dist/button.js"    },
    "./card": {
      "types": "./src/card.tsx",      "default": "./dist/card.js"    }
  },
  "scripts": {
    "build": "tsc"  }
}

编译您的库会将编译的 JavaScript 输出生成到一个目录(distbuild 等)中,您将使用该目录作为包的入口点。一旦将构建输出添加到任务的 outputs中,构建输出将被 Turborepo 缓存,从而让您拥有更快的构建时间。

限制和权衡

  • 使用 TypeScript 编译器:大多数编译包应该使用 tsc。由于包很可能被使用打包器的应用程序使用,应用程序的打包器将为应用程序的最终包中的库包准备分发,处理 polyfilling、降级和其他问题。只有在您有特定用例需要时才应使用打包器,比如将静态资源打包到包的输出中。
  • 更多配置:编译包需要更深入的知识和配置来创建构建输出。TypeScript 编译器有许多配置可能难以管理和理解,以及进一步的配置来优化打包器,如 package.json 中的 sideEffects

可发布包

将包发布到 npm 注册表是本页面上包策略中要求最严格的。因为您不知道从注册表下载包的消费者将如何使用您的包,您可能会发现由于强大包所需的众多配置而感到困难。

此外,将包发布到 npm 注册表的过程需要专门的知识和工具。我们建议使用 changesets 来管理版本控制、变更日志和发布过程。

有关详细指南,请访问我们的发布包指南