Turborepo

内部包

内部包是指源代码位于您的工作区内的库。您可以快速创建内部包,以便在您的单体仓库中共享代码,并且如果稍后需要,可以选择将其发布到 npm 注册表

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

./apps/web/package.json
{
  "dependencies": {
    "@repo/ui": "*"
  }
}

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

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

./apps/web/app/page.tsx
import { Button } from '@repo/ui'; 
 
export default function Page() {
  return <Button>Submit</Button>;
}

编译策略

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

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

即时包

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

此策略在以下情况下最有用:

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

一个即时包的 package.json 可能如下所示:

./packages/ui/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 子路径导入。要了解如何操作,请访问我们的 TypeScript 页面
  • Turborepo 无法缓存即时包的构建:由于包没有自己的 build 步骤,因此 Turborepo 无法缓存它。如果您想将配置保持在最低限度并且可以接受应用程序的构建时间,那么这种权衡可能对您来说是有意义的。
  • 将报告内部依赖项中的错误:直接导出 TypeScript 时,如果内部依赖项中的代码存在 TypeScript 错误,则依赖包中的类型检查将失败。在某些情况下,您可能会发现这令人困惑或有问题。

编译包

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

./packages/ui/package.json
{
  "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。由于该包很可能被使用打包器的应用程序使用,因此应用程序的打包器将准备该库包,以便在应用程序的最终捆绑包中分发,从而处理 polyfill、降级和其他问题。仅当您有需要它的特定用例时,才应使用打包器,例如将静态资源捆绑到包的输出中。
  • 更多配置:编译包需要更深入的知识和配置才能创建构建输出。 TypeScript 编译器有很多配置,这些配置可能难以管理和理解,并且需要进一步配置以针对打包器进行优化,例如 package.json 中的 sideEffects。您可以在我们专门的 TypeScript 指南中找到我们的一些建议。

可发布包

将包发布到 npm 注册表对本页的打包策略提出了最严格的要求。因为您不了解从注册表下载该包的使用者将如何使用您的包,所以由于可靠的包所需的众多配置,您可能会发现它很困难。

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

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

小时

节省的总计算量
开始使用
远程缓存 →

本页内容