Skip to content

Commit e5da44b

Browse files
Copiloticlanton
andcommitted
Fix CI: add zh-cn translation stubs for new Heft documentation pages
The heft.rushstack.io build was failing because the zh-cn locale could not resolve markdown links in three new pages that lacked corresponding translation stubs: - authoring-plugins.md - isolated-transpile.md - rspack.md Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
1 parent 686ea67 commit e5da44b

3 files changed

Lines changed: 688 additions & 0 deletions

File tree

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
---
2+
title: Authoring plugins
3+
---
4+
5+
import Tabs from '@theme/Tabs';
6+
import TabItem from '@theme/TabItem';
7+
8+
Heft is designed to be extensible. If the [official plugins](../plugins/package_index.md) don't cover your
9+
needs, you can create your own Heft plugin package. This page explains how to create a task plugin, which
10+
is the most common kind of plugin.
11+
12+
> For quick prototyping, you may want to start with the [Run script plugin](../plugins/run-script.md) instead.
13+
> That approach lets you run an arbitrary script as a Heft task without creating a full plugin package. However,
14+
> for production use, a proper plugin package is recommended because it provides TypeScript type safety, proper
15+
> ESLint validation, and a better developer experience.
16+
17+
## Plugin concepts
18+
19+
Before you begin, make sure you're familiar with the key concepts from the
20+
[Heft architecture](../intro/architecture.md) page:
21+
22+
- A **task plugin** implements `IHeftTaskPlugin` and provides its behavior via the task session hooks
23+
- A **lifecycle plugin** implements `IHeftLifecyclePlugin` and provides general functionality not specific to any task
24+
- A **plugin manifest** (`heft-plugin.json`) describes available plugins, their options, and CLI parameters
25+
- Plugin packages use the NPM naming pattern `heft-____-plugin` or `heft-____-plugins`
26+
27+
## Step 1: Create the package
28+
29+
Create a new NPM package for your plugin. The key requirements are:
30+
31+
- Add `@rushstack/heft` as a `peerDependency` (not a regular dependency)
32+
- Export a `heft-plugin.json` manifest file from the package root
33+
34+
**package.json**
35+
36+
```json
37+
{
38+
"name": "heft-my-plugin",
39+
"version": "1.0.0",
40+
"description": "A Heft plugin for ...",
41+
"main": "./lib-commonjs/index.js",
42+
"peerDependencies": {
43+
"@rushstack/heft": "^1.2.2"
44+
},
45+
"devDependencies": {
46+
"@rushstack/heft": "^1.2.2"
47+
},
48+
"exports": {
49+
"./lib/*": {
50+
"types": "./lib-dts/*.d.ts",
51+
"node": "./lib-commonjs/*.js",
52+
"import": "./lib-esm/*.js",
53+
"require": "./lib-commonjs/*.js"
54+
},
55+
"./heft-plugin.json": "./heft-plugin.json",
56+
"./package.json": "./package.json"
57+
}
58+
}
59+
```
60+
61+
> **Important:** It is essential that `"./heft-plugin.json": "./heft-plugin.json"` is included in
62+
> the `"exports"` field so that Heft can locate the plugin manifest.
63+
64+
## Step 2: Create the plugin manifest
65+
66+
Create a **heft-plugin.json** file in your package root. This manifest tells Heft about the plugins
67+
provided by your package. The file must conform to the
68+
[heft-plugin.schema.json](https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json) schema.
69+
70+
**heft-plugin.json**
71+
72+
```json
73+
{
74+
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json",
75+
76+
"taskPlugins": [
77+
{
78+
"pluginName": "my-plugin",
79+
"entryPoint": "./lib-commonjs/MyPlugin"
80+
}
81+
]
82+
}
83+
```
84+
85+
### Manifest fields
86+
87+
The plugin manifest supports these fields for each plugin:
88+
89+
| Field | Required | Description |
90+
| :--------------- | :------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
91+
| `pluginName` | yes | A unique kebab-case name for the plugin (e.g. `"my-plugin"`) |
92+
| `entryPoint` | yes | Path to the compiled JS module that exports the plugin class (resolved relative to the package folder) |
93+
| `optionsSchema` | no | Path to a JSON Schema file that validates `options` provided in **heft.json** |
94+
| `parameterScope` | no | A scope prefix for CLI parameters (defaults to the plugin name). If multiple plugins define a `--verbose` parameter, they can be disambiguated as `--my-scope:verbose`. |
95+
| `parameters` | no | An array of CLI parameter definitions (see [Defining CLI parameters](#defining-cli-parameters) below) |
96+
97+
## Step 3: Implement the plugin class
98+
99+
Create a TypeScript file that exports a class implementing `IHeftTaskPlugin`. The class must have an
100+
`apply()` method that receives the task session and Heft configuration.
101+
102+
**src/MyPlugin.ts**
103+
104+
```ts
105+
import type {
106+
HeftConfiguration,
107+
IHeftTaskSession,
108+
IHeftTaskPlugin,
109+
IHeftTaskRunHookOptions
110+
} from '@rushstack/heft';
111+
112+
const PLUGIN_NAME: 'my-plugin' = 'my-plugin';
113+
114+
export default class MyPlugin implements IHeftTaskPlugin {
115+
public apply(taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration): void {
116+
taskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => {
117+
const { logger } = taskSession;
118+
119+
logger.terminal.writeLine('My plugin is running!');
120+
121+
// Your plugin logic here...
122+
});
123+
}
124+
}
125+
```
126+
127+
The `apply()` method is called once when the plugin is loaded. Inside it, you "tap" into one or more
128+
hooks to register your callbacks.
129+
130+
### Available task hooks
131+
132+
The `taskSession.hooks` object provides these hooks:
133+
134+
| Hook | Description |
135+
| :----------------------- | :------------------------------------------------------------------------------------------------------- |
136+
| `run` | Called when the task executes during a normal build. Use `run.tapPromise(name, callback)` to register. |
137+
| `runIncremental` | Called instead of `run` during watch mode, if provided. Receives additional APIs for incremental builds. |
138+
| `registerFileOperations` | Called once before the first `run` or `runIncremental` to register dynamic file copy/delete operations. |
139+
140+
### Using the task session
141+
142+
The `IHeftTaskSession` provides access to several useful properties:
143+
144+
| Property | Description |
145+
| :------------------ | :------------------------------------------------------------- |
146+
| `taskName` | The name of the task as defined in **heft.json** |
147+
| `hooks` | The hooks available for the plugin to tap (described above) |
148+
| `parameters` | CLI parameters (including custom ones defined in the manifest) |
149+
| `parsedCommandLine` | The command line used to invoke Heft |
150+
| `tempFolderPath` | A unique temp folder for the task, cleaned with `--clean` |
151+
| `logger` | A scoped logger prefixed with `[<phaseName>:<taskName>]` |
152+
153+
### Watch mode support
154+
155+
To support Heft's watch mode, tap the `runIncremental` hook. This hook provides additional APIs
156+
for efficient incremental builds:
157+
158+
```ts
159+
taskSession.hooks.runIncremental.tapPromise(
160+
PLUGIN_NAME,
161+
async (options: IHeftTaskRunIncrementalHookOptions) => {
162+
// Watch for changes to specific files
163+
const changedFiles = await options.watchGlobAsync('src/**/*.json');
164+
165+
// Process only changed files
166+
for (const [filePath, changeInfo] of changedFiles) {
167+
logger.terminal.writeLine(`Processing changed file: ${filePath}`);
168+
}
169+
}
170+
);
171+
```
172+
173+
## Step 4: Use the plugin
174+
175+
Once your plugin is published (or linked locally), consumers can load it in their **heft.json** config file:
176+
177+
**config/heft.json**
178+
179+
```json
180+
{
181+
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json",
182+
183+
"phasesByName": {
184+
"build": {
185+
"tasksByName": {
186+
"my-task": {
187+
"taskPlugin": {
188+
"pluginPackage": "heft-my-plugin",
189+
"pluginName": "my-plugin"
190+
}
191+
}
192+
}
193+
}
194+
}
195+
}
196+
```
197+
198+
If the package only provides a single task plugin, the `"pluginName"` can be omitted.
199+
200+
## Accepting plugin options
201+
202+
Plugins can accept user-defined options via **heft.json**. To enable this:
203+
204+
1. Create a JSON Schema file for your options:
205+
206+
**src/schemas/my-plugin-options.schema.json**
207+
208+
```json
209+
{
210+
"$schema": "http://json-schema.org/draft-04/schema#",
211+
"type": "object",
212+
"additionalProperties": false,
213+
"properties": {
214+
"outputFolder": {
215+
"type": "string",
216+
"description": "The output folder for generated files."
217+
}
218+
},
219+
"required": ["outputFolder"]
220+
}
221+
```
222+
223+
2. Reference the schema from your plugin manifest:
224+
225+
**heft-plugin.json**
226+
227+
```json
228+
{
229+
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json",
230+
"taskPlugins": [
231+
{
232+
"pluginName": "my-plugin",
233+
"entryPoint": "./lib-commonjs/MyPlugin",
234+
"optionsSchema": "./lib-commonjs/schemas/my-plugin-options.schema.json"
235+
}
236+
]
237+
}
238+
```
239+
240+
3. Consumers specify the options in **heft.json**:
241+
242+
```json
243+
{
244+
"tasksByName": {
245+
"my-task": {
246+
"taskPlugin": {
247+
"pluginPackage": "heft-my-plugin",
248+
"options": {
249+
"outputFolder": "dist/generated"
250+
}
251+
}
252+
}
253+
}
254+
}
255+
```
256+
257+
The options are validated against your schema at load time, providing clear error messages for
258+
invalid configurations.
259+
260+
## Defining CLI parameters
261+
262+
Plugins can define CLI parameters by adding a `parameters` array to the plugin manifest.
263+
These parameters are automatically added to Heft's command line when the plugin is loaded.
264+
265+
**heft-plugin.json**
266+
267+
```json
268+
{
269+
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json",
270+
"taskPlugins": [
271+
{
272+
"pluginName": "my-plugin",
273+
"entryPoint": "./lib-commonjs/MyPlugin",
274+
"parameterScope": "my-plugin",
275+
"parameters": [
276+
{
277+
"longName": "--output-format",
278+
"parameterKind": "choice",
279+
"description": "Specifies the output format.",
280+
"alternatives": [
281+
{ "name": "json", "description": "Output as JSON" },
282+
{ "name": "yaml", "description": "Output as YAML" }
283+
],
284+
"defaultValue": "json"
285+
},
286+
{
287+
"longName": "--verbose",
288+
"parameterKind": "flag",
289+
"description": "Enable verbose logging."
290+
}
291+
]
292+
}
293+
]
294+
}
295+
```
296+
297+
The supported parameter kinds are: `flag`, `string`, `stringList`, `integer`, `integerList`,
298+
`choice`, and `choiceList`.
299+
300+
To read the parameter values in your plugin, use `taskSession.parameters`:
301+
302+
```ts
303+
public apply(taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration): void {
304+
taskSession.hooks.run.tapPromise(PLUGIN_NAME, async () => {
305+
const verbose = taskSession.parameters.getFlagParameter('--verbose');
306+
if (verbose.value) {
307+
taskSession.logger.terminal.writeLine('Verbose mode enabled');
308+
}
309+
});
310+
}
311+
```
312+
313+
## Interacting with other plugins
314+
315+
A plugin can request access to another plugin within the same phase using the
316+
`requestAccessToPluginByName()` API. This enables plugins to share data via custom accessor hooks.
317+
318+
```ts
319+
taskSession.requestAccessToPluginByName(
320+
'@rushstack/heft-typescript-plugin', // the package containing the plugin
321+
'typescript-plugin', // the plugin name
322+
(accessor) => {
323+
// Access hooks or data exposed by the TypeScript plugin
324+
}
325+
);
326+
```
327+
328+
## Reference examples
329+
330+
The best way to learn plugin development is to study the official plugin implementations:
331+
332+
- [heft-dev-cert-plugin](https://github.com/microsoft/rushstack/tree/main/heft-plugins/heft-dev-cert-plugin) — A simple task plugin example
333+
- [heft-jest-plugin](https://github.com/microsoft/rushstack/tree/main/heft-plugins/heft-jest-plugin) — A complex plugin with many CLI parameters
334+
- [heft-typescript-plugin](https://github.com/microsoft/rushstack/tree/main/heft-plugins/heft-typescript-plugin) — The TypeScript compiler plugin
335+
- [heft-rspack-plugin](https://github.com/microsoft/rushstack/tree/main/heft-plugins/heft-rspack-plugin) — A plugin with watch mode support
336+
337+
## See also
338+
339+
- [Heft architecture](../intro/architecture.md) — Key concepts and terminology
340+
- [heft.json](../configs/heft_json.md) — Config file reference for loading plugins
341+
- [Run script plugin](../plugins/run-script.md) — A simpler alternative for quick prototyping
342+
- [Plugin package index](../plugins/package_index.md) — List of official plugins
343+
- [API Reference](https://api.rushstack.io/pages/heft/) — Complete Heft API documentation

0 commit comments

Comments
 (0)