创建内部包

内部软件包 是你工作区的基石,为你提供了一种在代码库中共享代码和功能的强大方法。Turborepo 会使用 package.json 中的依赖自动理解内部包之间的关系,并在后台创建 软件包图表 以优化代码库的工作流程。

¥Internal Packages are the building blocks of your workspace, giving you a powerful way to share code and functionality across your repo. Turborepo automatically understands the relationships between Internal Packages using the dependencies in package.json, creating a Package Graph under the hood to optimize your repository's workflows.

Visual representation of a Package Graph in a Turborepo.

让我们使用 软件包结构 部分中的指导和 已编译的软件包 模式,创建你的第一个内部包,以便在你的代码库中共享数学实用程序。在以下步骤中,我们假设你已经拥有 使用以下方式创建新仓库 create-turbo 或正在使用类似结构的代码库。

¥Let's create your first Internal Package to share math utilities in your repo using the guidance in the Anatomy of a package section and the Compiled Packages pattern. In the steps below, we assume you've created a new repository using create-turbo or are using a similarly structured repository.

创建一个空目录

¥Create an empty directory

你需要一个用于放置包的目录。让我们在 ./packages/math 上创建一个。

¥You'll need a directory to put the package in. Let's create one at ./packages/math.

package.json
turbo.json

添加 package.json 文件

¥Add a package.json

接下来,为包创建 package.json。通过添加此文件,你将满足 内部包的两个要求 的要求,使其可以被 Turborepo 和你工作区的其他部分发现:

¥Next, create the package.json for the package. By adding this file, you'll fulfill the two requirements for an Internal Package, making it discoverable to Turborepo and the rest of your Workspace:

./packages/math/package.json
{
  "name": "@repo/math",
  "type": "module",
  "scripts": {
    "dev": "tsc --watch",
    "build": "tsc"
  },
  "exports": {
    "./add": {
      "types": "./src/add.ts",
      "default": "./dist/add.js"
    },
    "./subtract": {
      "types": "./src/subtract.ts",
      "default": "./dist/subtract.js"
    }
  },
  "devDependencies": {
    "@repo/typescript-config": "workspace:*",
    "typescript": "latest"
  }
}

让我们逐一分解这个 package.json

¥Let's break down this package.json piece-by-piece:

  • scriptsdevbuild 脚本使用 TypeScript 编译器 编译软件包。dev 脚本将监视源代码的更改并自动重新编译软件包。

    ¥scripts: The dev and build script compile the package using the TypeScript compiler. The dev script will watch for changes to source code and automatically recompile the package.

  • devDependenciestypescript@repo/typescript-config 都是 devDependencies,因此你可以在 @repo/math 包中使用这些包。在实际软件包中,你可能会有更多 devDependenciesdependencies。 - 但我们现在可以保持简单。

    ¥devDependencies: typescript and @repo/typescript-config are devDependencies so you can use those packages in the @repo/math package. In a real-world package, you will likely have more devDependencies and dependencies - but we can keep it simple for now.

  • exports:为包定义多个入口点,以便可以在其他包(import { add } from '@repo/math')中使用。

    ¥exports: Defines multiple entrypoints for the package so it can be used in other packages (import { add } from '@repo/math').

需要注意的是,此 package.json 将内部包 @repo/typescript-config 声明为依赖。Turborepo 会将 @repo/math 识别为 @repo/typescript-config 的依赖,以便对任务进行排序。

¥Notably, this package.json declares an Internal Package, @repo/typescript-config, as a dependency. Turborepo will recognize @repo/math as a dependent of @repo/typescript-config for ordering your tasks.

添加 tsconfig.json

¥Add a tsconfig.json

通过将 tsconfig.json 文件添加到包的根目录来指定此包的 TypeScript 配置。TypeScript 具有 extends,允许你在整个代码库中使用基本配置,并根据需要使用不同的选项进行覆盖。

¥Specify the TypeScript configuration for this package by adding a tsconfig.json file to the root of the package. TypeScript has an extends key, allowing you to use a base configuration throughout your repository and overwrite with different options as needed.

./packages/math/tsconfig.json
{
  "extends": "@repo/typescript-config/base.json",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

你在这里完成了四件重要的事情:

¥You've done four important things here:

  • ./packages/typescript-config 中的 @repo/typescript-config/base.json 配置包含你需要的所有配置,因此你可以从中扩展。

    ¥The @repo/typescript-config/base.json configuration that lives in ./packages/typescript-config has all the configuration you need so you extend from it.

  • compilerOptions 中的 outDir 会告诉 TypeScript 将编译输出放在哪里。它与 package.jsonexports 指定的目录匹配。

    ¥The outDir key in compilerOptions tells TypeScript where to put the compiled output. It matches the directory specified in your exports in package.json.

  • compilerOptions 中的 rootDir 确保 outDir 中的输出使用与 src 目录相同的结构。

    ¥The rootDir key in compilerOptions ensures that the output in outDir uses the same structure as the src directory.

  • includeexclude 键并非从基本配置 根据 TypeScript 规范 继承,因此你在此处添加了它们。

    ¥The include and exclude keys are not inherited from the base configuration, according to the TypeScript specification, so you've included them here.

关于 TypeScript 配置还有很多需要学习,但目前这是一个很好的起点。如果你想了解更多信息,请访问 TypeScript 官方文档我们的 TypeScript 指南

¥There's a lot more to learn about TypeScript configuration, but this is a good place to start for now. If you'd like to learn more, visit the official TypeScript documentation or our TypeScript guide.

添加包含源代码的 src 目录

¥Add a src directory with source code

你现在可以为你的包编写一些代码。在 src 目录中创建两个文件:

¥You can now write some code for your package. Create two files inside a src directory:

./packages/math/src/add.ts
export const add = (a: number, b: number) => a + b;

这些文件映射到你稍后运行 turbo buildtsc 将创建的输出。

¥These files map to the outputs that will be created by tsc when you run turbo build in a moment.

将包添加到应用

¥Add the package to an application

你已准备好在应用中使用新的包。让我们将它添加到 web 应用中。

¥You're ready to use your new package in an application. Let's add it to the web application.

apps/web/package.json
  "dependencies": {
+   "@repo/math": "workspace:*",
    "next": "latest",
    "react": "latest",
    "react-dom": "latest"
  },

你只需更改代码库中的依赖即可。确保运行包管理器的安装命令来更新锁文件。

¥You just changed the dependencies in your repo. Make sure to run your package manager's installation command to update your lockfile.

@repo/math 现在可在 web 应用中使用,你可以在代码中使用它:

¥@repo/math is now available in the web application, you can use it in your code:

apps/web/src/app/page.tsx
import { add } from '@repo/math/add';
 
function Page() {
  return <div>{add(1, 2)}</div>;
}
 
export default Page;

Edit turbo.json

将新 @repo/math 库的工件添加到 turbo.jsonbuild 任务的 outputs 中。这确保了 Turborepo 会缓存其构建输出,因此当你开始运行构建时,它们可以立即恢复。

¥Add the artifacts for the new @repo/math library to the outputs for the build task in turbo.json. This ensures that its build outputs will be cached by Turborepo, so they can be restored instantly when you start running builds.

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    }
  }
}

运行 turbo build

¥Run turbo build

如果你已经安装了 全局安装 turbo,请在工作区根目录的终端中运行 turbo build。你还可以使用包管理器从 package.json 运行 build 脚本,这将使用 turbo run build

¥If you've installed turbo globally, run turbo build in your terminal at the root of your Workspace. You can also run the build script from package.json with your package manager, which will use turbo run build.

@repo/math 包在 web 应用之前构建,以便 ./packages/math/dist 中的运行时代码在 web 应用打包时可供使用。

¥The @repo/math package built before the web application built so that the runtime code in ./packages/math/dist is available to the web application when it bundles.

你可以再次运行 turbo build,以毫秒为单位查看你的 web 应用的重建情况。我们将在 缓存指南 中详细讨论这一点。

¥You can run turbo build again to see your web application rebuild in milliseconds. We'll discuss this at length in the Caching guide.

内部软件包的最佳实践

¥Best practices for Internal Packages

每个包一个 "purpose" 文件

¥One "purpose" per package

创建内部包时,建议创建包含单个 "purpose" 的包。这并非严格的科学或规则,而是一项最佳实践,取决于你的仓库、规模、组织、团队需求等等。此策略有几个优点:

¥When you're creating Internal Packages, it's recommended to create packages that have a single "purpose". This isn't a strict science or rule, but a best practice depending on your repository, your scale, your organization, what your teams need, and more. This strategy has several advantages:

  • 更易于理解:随着仓库规模的扩大,在仓库中工作的开发者将更容易找到他们需要的代码。

    ¥Easier to understand: As a repository scales, developers working in the repository will more easily be able to find the code they need.

  • 减少每个包的依赖:每个包使用更少的依赖,使 Turborepo 能够更有效地使用 修剪软件包图的依赖

    ¥Reducing dependencies per package: Using fewer dependencies per package makes it so Turborepo can more effectively prune the dependencies of your package graph.

一些示例如下:

¥Some examples include:

  • @repo/ui:包含所有共享 UI 组件的包

    ¥@repo/ui: A package containing all of your shared UI components

  • @repo/tool-specific-config:用于管理特定工具配置的包

    ¥@repo/tool-specific-config: A package for managing configuration of a specific tool

  • @repo/graphs:用于创建和操作图形数据的字段特定库

    ¥@repo/graphs: A domain-specific library for creating and manipulating graphical data

应用包不包含共享代码

¥Application Packages do not contain shared code

创建 应用包 时,最好避免在这些包中放置共享代码。相反,你应该为共享代码创建一个单独的包,并让应用包依赖于该包。

¥When you're creating Application Packages, it's best to avoid putting shared code in those packages. Instead, you should create a separate package for the shared code and have the application packages depend on that package.

此外,应用包不应安装到其他包中。相反,它们应该被视为 软件包图表 的入口点。

¥Additionally, Application Packages are not meant to be installed into other packages. Instead, they should be thought of as an entrypoint to your Package Graph.

Good to know: 

此规则包含 罕见例外

¥There are rare exceptions to this rule.

后续步骤

¥Next steps

新的内部软件包到位后,你可以启动 配置任务

¥With a new Internal Package in place, you can start configuring tasks.