diff --git a/src/content/docs/docs/guides/writing-tasks.mdx b/src/content/docs/docs/guides/writing-tasks.mdx index a1ee51062..9487ffad4 100644 --- a/src/content/docs/docs/guides/writing-tasks.mdx +++ b/src/content/docs/docs/guides/writing-tasks.mdx @@ -7,7 +7,7 @@ import Run from "@hh/Run.astro"; At its core, Hardhat is a task runner that lets you automate your development workflow. It comes with built-in tasks like `compile` and `test`, but you can also add your own custom tasks. -This guide will show you how to extend Hardhat's functionality using tasks. It assumes you've initialized a sample project. If you haven't, please read the [getting started guide](/docs/getting-started) first. +In this guide, we'll explore how to extend Hardhat's functionality using tasks. It assumes you've initialized a sample project. If you haven't, read the [getting started guide](/docs/getting-started) first. ## Writing a task @@ -61,8 +61,60 @@ We're using the `task` function to define our new task. Its first argument is th The `task` function returns a task builder object that lets you further configure the task. In this case, we use the `setAction` method to define the task's behavior by providing a function that lazy loads another module. -That module exports the action function itself. You can do anything you want in this function. In this case, we send a request to the network provider to get all the configured accounts and print their addresses. +That module exports the action function itself, which implements your custom logic. In this case, we send a request to the network provider to get all the configured accounts and print their addresses. -You can add parameters to your tasks, and Hardhat will handle their parsing and validation for you. You can also override existing tasks, which lets you customize how different parts of Hardhat work. +Add parameters to your tasks, and Hardhat handles their parsing and validation for you. Override existing tasks to customize how different parts of Hardhat work. + +## Using inline actions + +As an alternative to defining task actions in separate files, define them directly inline using `setInlineAction`. This is convenient for simple tasks where a separate file would be overkill. + +Here's how the accounts task would look using an inline action: + +```ts +// hardhat.config.ts +import { defineConfig, task } from "hardhat/config"; + +const printAccounts = task("accounts", "Print the accounts") + .setInlineAction(async (taskArguments, hre) => { + const { provider } = await hre.network.connect(); + console.log(await provider.request({ method: "eth_accounts" })); + }) + .build(); + +export default defineConfig({ + // ... rest of the config + tasks: [printAccounts], +}); +``` + +With `setInlineAction`, the task logic is defined directly as a function parameter, eliminating the need for a separate file. + +### Choosing between `setAction` and `setInlineAction` + +Use `setAction` when you're building a plugin or a complex task with a lot of code, or if it needs to import dependencies. This keeps the code organized, improves Hardhat's load time (as they're loaded on demand), and makes your setup or plugin more resilient to installation errors. + +On the other hand, if you're building a simple task that only uses the Hardhat Runtime Environment, use `setInlineAction` to define the task's behavior inline, without the boilerplate of a separate file. + +Here's a comparison of the two approaches: + +| | `setAction()` (Lazy-loaded) | `setInlineAction()` | +| -------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------- | +| **Use cases** | Complex tasks
Plugin tasks
Tasks that import dependencies | Simple user tasks | +| **Available for plugins** | ✅ Yes (required) | ❌ No | +| **Available for users** | ✅ Yes | ✅ Yes | +| **Performance** | Lazy-loaded on demand | Loaded every time you run Hardhat | +| **File organization** | Separate action files | Defined inline in your config | +| **Can use `import { ... } from 'hardhat'`?** | ✅ Yes, because they are loaded after Hardhat's initialization | ❌ No, because they are evaluated during Hardhat's initialization | + +Each task must define exactly one action: call either `setAction()` or `setInlineAction()`, but not both. + +You can also use `setInlineAction` with `overrideTask` to customize existing tasks directly in your config file. + +:::caution[Plugin developers] +If you're developing a plugin, you must use `setAction()` with lazy-loaded modules. The `setInlineAction()` method is only available for user-defined tasks in configuration files. +::: + +To learn more about how task actions are loaded, see the [Task Actions' lifecycle](/docs/plugin-development/explanations/lifecycle#task-actions-lifecycle) documentation. {/* To learn more about tasks, please read [this guide](../advanced/create-task). */} diff --git a/src/content/docs/docs/migrate-from-hardhat2/index.mdx b/src/content/docs/docs/migrate-from-hardhat2/index.mdx index 50d21c7e6..9f582141f 100644 --- a/src/content/docs/docs/migrate-from-hardhat2/index.mdx +++ b/src/content/docs/docs/migrate-from-hardhat2/index.mdx @@ -161,7 +161,40 @@ This section assumes that your Hardhat 2 project uses Mocha as its tests runner, -{/* A section was removed here until we have setInlineAction. Take a look at the git history to restore it. */} +## Migrating custom tasks + +In Hardhat 2, tasks were defined imperatively in the config file, and the action function was passed directly: + +```ts +// Hardhat 2 +task("accounts", "Prints the accounts", async (taskArgs, hre) => { + const accounts = await hre.ethers.getSigners(); + for (const account of accounts) { + console.log(account.address); + } +}); +``` + +In Hardhat 3, the equivalent uses `setInlineAction` and requires calling `.build()` and adding the task to the `tasks` array in your config: + +```ts ins={4-5,8-9,13} +// hardhat.config.ts +import { defineConfig, task } from "hardhat/config"; + +const printAccounts = task("accounts", "Print the accounts") + .setInlineAction(async (taskArguments, hre) => { + const { provider } = await hre.network.connect(); + console.log(await provider.request({ method: "eth_accounts" })); + }) + .build(); + +export default defineConfig({ + // ... rest of the config + tasks: [printAccounts], +}); +``` + +For more complex tasks, you can use `setAction()` with a separate file for lazy loading. To learn more, see [Writing Hardhat tasks](/docs/guides/writing-tasks). ## Migrating `extendConfig` and `extendEnvironment` diff --git a/src/content/docs/docs/plugin-development/explanations/lifecycle.mdx b/src/content/docs/docs/plugin-development/explanations/lifecycle.mdx index 47c14b4fa..2a7709493 100644 --- a/src/content/docs/docs/plugin-development/explanations/lifecycle.mdx +++ b/src/content/docs/docs/plugin-development/explanations/lifecycle.mdx @@ -195,7 +195,14 @@ The lifecycle of a Dynamic Hook Handler is simpler, as it's manually registered ## Task Actions' lifecycle -You define Task Actions using the `setAction` method of the `TaskDefinitionBuilder`s APIs. +Task actions define the behavior that executes when a task runs. There are two ways to define task actions in Hardhat 3: + +1. **Lazy-loaded actions** using `setAction()` - Required for plugins +2. **Inline actions** using `setInlineAction()` - Only for user tasks + +### Lazy-loaded actions (plugins must use this) + +Plugins must always use the `setAction` method with lazy-loaded modules. This method is also available for user-defined tasks in configuration files. It looks like this: @@ -226,6 +233,32 @@ export default async function ( } ``` +**Why plugins must use lazy-loaded actions:** + +- **Performance**: Lazy loading ensures plugin code is only loaded when needed, keeping Hardhat initialization fast +- **Robustness**: Lazy loading also makes Hardhat more robust to plugin errors (e.g. installation issues), as a failure in one part of your plugin won't affect the rest of the system + +### Inline actions (user tasks only) + +User-defined tasks in configuration files can optionally use `setInlineAction()` to define task behavior directly inline. This is convenient for simple tasks but is **not available for plugins**. + +Example of an inline action: + +```ts +task("accounts", "Print the accounts") + .setInlineAction(async (taskArguments, hre) => { + const { provider } = await hre.network.connect(); + console.log(await provider.request({ method: "eth_accounts" })); + }) + .build(); +``` + +Inline actions are loaded immediately when the configuration file is evaluated, so they don't benefit from lazy loading. For a practical guide on choosing between action types, see [Writing Hardhat tasks](/docs/guides/writing-tasks#choosing-between-setaction-and-setinlineaction). + +:::caution[Plugin restriction] +Plugins cannot use `setInlineAction()`. Attempting to do so will result in an error. Plugins must always use `setAction()` with lazy-loaded modules. +::: + ## Configuration Variables' lifecycle There are two things that plugins can do with Configuration Variables: diff --git a/src/content/docs/docs/plugin-development/reference/hardhat-plugin-object.mdx b/src/content/docs/docs/plugin-development/reference/hardhat-plugin-object.mdx index 14c8e99cc..7c5d10e42 100644 --- a/src/content/docs/docs/plugin-development/reference/hardhat-plugin-object.mdx +++ b/src/content/docs/docs/plugin-development/reference/hardhat-plugin-object.mdx @@ -88,3 +88,5 @@ An array of Global Option definitions. You can create them using the `globalOpti ### `tasks` An array of Task definitions. Define them using the `task` and `overrideTask` functions exported by `hardhat/config`. + +**Important:** Plugin tasks must use `setAction()` with lazy-loaded modules. The `setInlineAction()` method is not available for plugin tasks. To learn more about task action lifecycle and the differences between these methods, see the [Task Actions' lifecycle](/docs/plugin-development/explanations/lifecycle#task-actions-lifecycle) documentation. diff --git a/src/content/docs/docs/plugin-development/tutorial/task.mdx b/src/content/docs/docs/plugin-development/tutorial/task.mdx index b8141f448..8e2249cad 100644 --- a/src/content/docs/docs/plugin-development/tutorial/task.mdx +++ b/src/content/docs/docs/plugin-development/tutorial/task.mdx @@ -41,6 +41,12 @@ task("my-account", "Prints your account.") You'll get an error in the highlighted line because a file is missing. We'll create it in the next section. +:::caution[Plugin tasks must use setAction] +Plugins must always use `setAction()` with lazy-loaded modules. The `setInlineAction()` method is only available for user-defined tasks in configuration files. + +To learn more, see the [Task Actions' lifecycle](/docs/plugin-development/explanations/lifecycle#task-actions-lifecycle) documentation. +::: + ## Creating a task action file The task action is defined in a separate file that's only imported when the task runs.