Rush Stack商店博客活动
跳至主要内容

你好世界

本演练将通过从头开始创建一个基本的 Node.js 控制台项目,逐步添加每个任务步骤,帮助您开始使用 Heft。在实际操作中,**您可能希望使用现成的 rig**。本教程的目标是阐明 Heft 的基本概念和架构。有了这个基础,您就可以更容易地理解复杂的配置并解决出现的任何问题。

"给我看代码!"

如果您时间紧迫,请查看 heft-node-basic-tutorialheft-webpack-basic-tutorial 文件夹,它们展示了一个使用 Heft 构建的简单项目的完整示例。

heft-node-rig-tutorialheft-web-rig-app-tutorial 文件夹展示了如何通过使用 Rush Stack rigs(而不是手动 Heft 配置)来实现相同的结果。Rigs 允许许多项目共享标准配置,这大大降低了升级的维护成本。

我们将从创建一个简单的独立项目(没有 Rush)开始。(稍后,与 Rush 交互 教程将检查在 monorepo 中使用 Heft 时有何不同。)

  1. 我们将使用 PNPM 包管理器 来演示此示例。它的命令行与 NPM 非常相似,因此您可以在这些步骤中用 npm 替换 pnpm。有 多种方法 可以安装 PNPM,但最简单的方法是像这样

    npm install --global pnpm
  2. 创建一个新的文件夹 **my-app**,其中包含一个用于我们项目的 **package.json** 文件,如下所示

    my-app/package.json

    {
    "name": "my-app",
    "version": "1.0.0",
    "description": "A Heft tutorial project",
    "license": "MIT",
    "main": "lib/start.js",
    "typings": "lib/start.d.ts",
    "scripts": {
    "start": "node lib/start.js"
    }
    }
  3. 创建一个我们将编译的 TypeScript 源文件。

    my-app/src/start.ts

    console.log("Hello, world!");
  4. @rushstack/heft@rushstack/heft-typescript-plugintypescript 安装为项目的 devDependenices

    cd my-app
    pnpm install --save-dev @rushstack/heft
    pnpm install --save-dev @rushstack/heft-typescript-plugin
    pnpm install --save-dev typescript

    # Since this project will use the console.log() API, we also need to add the TypeScript
    # typings for Node.js. Typings should always use "--save-exact" version specifiers.
    pnpm install --save-dev --save-exact @types/node
  5. 接下来,我们需要创建 TypeScript tsconfig.json 文件。该文件的存在会导致 Heft 调用 TypeScript 编译器。现在,我们将创建一个简单的独立 **tsconfig.json** 文件;稍后我们将演示如何在多个项目之间共享可重用的配置。

    my-app/tsconfig.json

    {
    "$schema": "http://json.schemastore.org/tsconfig",

    "compilerOptions": {
    "outDir": "lib",
    "rootDirs": ["src/"],

    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "sourceMap": true,
    "declarationMap": true,
    "inlineSources": true,
    "experimentalDecorators": true,
    "strict": true,
    "useUnknownInCatchVariables": false,
    "esModuleInterop": true,
    "noEmitOnError": false,
    "allowUnreachableCode": false,

    "types": ["node"],
    "module": "commonjs",
    "target": "es2017",
    "lib": ["es2017"]
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules", "lib"]
    }

    请注意,"types": ["node"] 引用了我们上面安装的 @types/node 包。这是必需的,因为 Node.js 是一个全局环境,因此其类型必须在全局范围内加载。大多数其他 @types 包可以通过在您的源代码中使用 import 语句来加载。

    有关使用 Heft 配置 TypeScript 的更多背景信息,请参阅 TypeScript 插件 文档。

  6. 您可以使用 ./node_modules/.bin/heft 调用 Heft,但更方便的做法是将其全局安装,以便它始终在您的 shell PATH 中可用

    # Install the Heft tool globally
    npm install --global @rushstack/heft

    如果全局安装的 heft 二进制文件是错误的版本怎么办?

    与 Rush 一样,Heft 实施了“版本选择器”功能,该功能将自动发现您本地的 node_modules 文件夹并调用 ./node_modules/.bin/heft,确保使用正确的版本。

  7. Heft 是配置驱动的,这意味着它在项目文件夹中的行为由数据(配置文件)而不是代码(任意脚本)定义。如果您需要使用程序逻辑扩展构建过程,我们强烈建议将该代码移到 Heft 插件包中,该插件包应该使用 TypeScript、ESLint 和代码审查作为专业软件开发。这需要更多工作,但随着您的 monorepo 规模的增长,它会大大简化维护。

    Heft 的主要配置文件是 config/heft.json。让我们从最简单的文件开始

    config/heft.json

    {
    "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json",
    }

    如果您运行 heft --help,您应该看到类似这样的输出

    usage: heft [-h] [--debug] [--unmanaged] <command> ...

    Heft is a pluggable build system designed for web projects.

    Positional arguments:
    <command>
    clean Clean the project, removing temporary task folders and
    specified clean paths.
    run Run a provided selection of Heft phases.
    run-watch Run a provided selection of Heft phases in watch mode..

    Optional arguments:
    -h, --help Show this help message and exit.
  8. 现在,让我们通过添加一个名为 "build" 的简单阶段来扩展我们的配置,该阶段调用一个名为 "typescript" 的任务来编译您的代码。(有关这些术语的定义,请参阅 架构 说明。)

    config/heft.json

    {
    "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json",

    "phasesByName": {
    // Define a phase whose name is "build"
    "build": {
    "phaseDescription": "This phase compiles the project source code.",

    // Before invoking the compiler, delete the "dist" and "lib" folders
    "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }],

    "tasksByName": {
    // Define a task whose name is "typescript"
    "typescript": {
    "taskPlugin": {
    // This task will invoke the TypeScript plugin
    "pluginPackage": "@rushstack/heft-typescript-plugin"
    }
    }
    }
    }
    }
    }

    有关这些设置的完整描述,请参阅 heft.json 模板。

    如果您运行 heft --help,您现在将看到已在您的命令行中添加了 buildbuild-watch 操作,因为我们的阶段名为 "build"

    usage: heft [-h] [--debug] [--unmanaged] <command> ...

    Heft is a pluggable build system designed for web projects.

    Positional arguments:
    <command>
    clean Clean the project, removing temporary task folders and
    specified clean paths.
    run Run a provided selection of Heft phases.
    build Runs to the build phase, including all transitive dependencies.
    run-watch Run a provided selection of Heft phases in watch mode..
    build-watch
    Runs to the build phase, including all transitive dependencies,
    in watch mode.

    Optional arguments:
    -h, --help Show this help message and exit.

    如果您运行 heft build --help,则会打印 "phaseDescription"

  9. 让我们尝试调用 Heft 的 命令行 来构建我们的项目。

    # Make sure we're in your project folder
    cd my-app

    # View the command line help
    heft --help
    heft build --help

    # Build the project
    heft build

    # To see more detail about what Heft is doing, add you can the "--verbose" flag
    heft build --verbose

    调用 heft build --verbose 应该生成类似这样的控制台输出

    Project: my-app@1.0.0
    Project build folder: C:\my-app
    Heft version: 0.56.2
    Node version: v16.15.1

    Executing a maximum of 4 simultaneous tasks...
    ---- lifecycle started ----
    [lifecycle:start] Applying lifecycle plugins
    ---- build started ----
    [build] Applying task plugins
    [build:typescript] Loaded plugin from "C:\my-app\node_modules\...\@rushstack\heft-typescript-plugin\lib\TypeScriptPlugin"
    [build:typescript] Starting task execution
    [build:typescript] Looking for tsconfig at C:/my-app/tsconfig.json
    [build:typescript] Resolved "typescript" as a direct devDependency of the project.
    [build:typescript] Using TypeScript version 5.1.6
    [build:typescript] Configure: 17.34340000152588ms
    [build:typescript] I/O Read: 16.67810034751892ms (98 files)
    [build:typescript] Parse: 491.7621006965637ms (98 files)
    [build:typescript] Program (includes Read + Parse): 581.9542999267578ms
    [build:typescript] Analyze: 1135.4448999166489ms
    [build:typescript] Bind: 189.5981993675232ms
    [build:typescript] Check: 929.5596989393234ms
    [build:typescript] Transform: 4.200200080871582ms (2 files)
    [build:typescript] Print: 12.058799982070923ms (1 files) (Includes Transform)
    [build:typescript] Emit: 12.5ms (Includes Print)
    [build:typescript] I/O Write: 0ms (0 files)
    [build:typescript] Finished task execution (1964.6486999988556ms)
    ---- build finished (2.014s) ----
    ---- lifecycle finished (2.018s) ----
    -------------------- Finished (2.02s) --------------------

    注意:在报告诊断消息(如编译错误)时,Heft 打印相对于项目文件夹的路径。可以使用 RUSHSTACK_FILE_ERROR_BASE_FOLDER 环境变量来自定义此行为。

    构建完成后,确认它在您的 lib 文件夹中生成了几个输出文件

    • **start.js** - 编译后的 JavaScript 代码
    • **start.d.ts** - TypeScript 类型定义,用于可能导入此模块的外部库
    • **start.js.map** 和 **start.d.ts.map** - 源映射文件,使调试器等工具能够找到与输出文件中的给定项相对应的源代码文件/行
  10. 如果您还记得,我们的 **package.json** 文件有一个 "scripts" 部分,其中指定了 "start": "node lib/start.js"。让我们尝试使用 pnpm run 运行编译后的代码。

    # Invoke the "start" script from package.json
    pnpm run start

    # If you have Rush installed, you can also use this slightly shorter equivalent
    rushx start

    您应该看到类似这样的输出

    > my-app@1.0.0 start C:\my-app
    > node lib/start.js

    Hello, world!
  11. 我们还可以将 "build" 脚本添加到我们的 **package.json** 文件中

    my-app/package.json

    {
    . . .
    "scripts": {
    "build": "heft build --clean",
    "start": "node lib/start.js"
    },
    . . .
    }

    通过此更改,您还可以通过调用 pnpm run build(或 rushx build)来构建。此工具链无关的约定使新手更容易猜测如何构建您的项目。它在稍后与 Rush 集成时也将很有用。