Turborepo logo

内部包

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

内部包在你的仓库中使用,方式类似于安装来自 npm 注册表的外部包,需要在 package.json 中进行安装。但是,你无需标记要安装的版本,而是可以使用包管理器的工作空间安装语法来引用包

./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>;
}

编译策略

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

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

即时编译包

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

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

  • 你的应用程序是使用现代 bundler 构建的,例如 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 文件。这是可能的,因为应用程序的 bundler 将在构建过程中使用代码时对其进行编译。
  • 没有 build 脚本:由于此包正在导出 TypeScript,因此它不需要构建步骤来转译包。这意味着你无需在此包中配置构建工具即可使其在你的工作空间中工作。

局限性和权衡

  • 仅在消费者进行转译时适用:此策略只能在包将用于使用 bundler 或原生理解 TypeScript 的工具时使用。消费者的 bundler 负责将 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。由于该包很可能被使用 bundler 的应用程序使用,因此应用程序的 bundler 将准备库包以在应用程序的最终 bundle 中分发,处理 polyfilling、降级和其他问题。只有在你有需要它的特定用例时才应使用 bundler,例如将静态资源捆绑到包的输出中。
  • 更多配置:已编译包需要更深入的知识和配置才能创建构建输出。TypeScript 编译器有很多配置,可能难以管理和理解,并且还需要进一步配置以优化 bundler,例如 package.json 中的 sideEffects。你可以在我们专门的 TypeScript 指南中找到我们的一些建议。

可发布包

将包发布到 npm 注册表对本页的打包策略提出了最严格的要求。因为你不知道从注册表下载包的消费者将如何使用你的包,所以你可能会发现由于健壮的包需要大量配置而感到困难。

此外,将包发布到 npm 注册表的过程需要专业的知识和工具。我们推荐 changesets 用于管理版本控制、更新日志和发布过程。

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

hours

总计算时间节省
开始使用
远程缓存 →

本页内容