Skip to content

Commit 57f9e87

Browse files
authored
Merge pull request #5776 from NomicFoundation/hook-context-fix
Define the `HookContext` as a partial view of the `HRE`
2 parents c41488d + 46d675d commit 57f9e87

File tree

3 files changed

+85
-17
lines changed

3 files changed

+85
-17
lines changed

v-next/hardhat/src/internal/core/hre.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,6 @@ export class HardhatRuntimeEnvironmentImplementation
118118

119119
const interruptions = new UserInterruptionManagerImplementation(hooks);
120120

121-
const hookContext: HookContext = {
122-
hooks,
123-
config,
124-
globalOptions,
125-
interruptions,
126-
};
127-
128-
hooks.setContext(hookContext);
129-
130121
const hre = new HardhatRuntimeEnvironmentImplementation(
131122
extendedUserConfig,
132123
config,
@@ -136,6 +127,19 @@ export class HardhatRuntimeEnvironmentImplementation
136127
globalOptionDefinitions,
137128
);
138129

130+
// We create an object with the HRE as its prototype, and overwrite the
131+
// tasks property with undefined, so that hooks don't have access to the
132+
// task runner.
133+
//
134+
// The reason we do this with a prototype instead of a shallow copy is that
135+
// the handlers hooked into hre/created may assign new properties to the
136+
// HRE and we want those to be accessible to all the handlers.
137+
const hookContext: HookContext = Object.create(hre, {
138+
tasks: { value: undefined },
139+
});
140+
141+
hooks.setContext(hookContext);
142+
139143
await hooks.runSequentialHandlers("hre", "created", [hre]);
140144

141145
return hre;

v-next/hardhat/src/types/hooks.ts

+1-8
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import type {
44
HardhatUserConfig,
55
ResolvedConfigurationVariable,
66
} from "./config.js";
7-
import type { GlobalOptions } from "./global-options.js";
87
import type { HardhatRuntimeEnvironment } from "./hre.js";
9-
import type { UserInterruptionManager } from "./user-interruptions.js";
108
import type {
119
LastParameter,
1210
ParametersExceptFirst,
@@ -32,12 +30,7 @@ declare module "./hre.js" {
3230
* The `HookContext` offers a subset of the functionality that the
3331
* `HardhatRuntimeEnvironment` does.
3432
*/
35-
export interface HookContext {
36-
readonly config: HardhatConfig;
37-
readonly globalOptions: GlobalOptions;
38-
readonly interruptions: UserInterruptionManager;
39-
readonly hooks: HookManager;
40-
}
33+
export type HookContext = Omit<HardhatRuntimeEnvironment, "tasks">;
4134

4235
/**
4336
* The different hooks that a plugin can define handlers for.

v-next/hardhat/test/internal/core/hook-manager.ts

+71
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,77 @@ describe("HookManager", () => {
482482
);
483483
});
484484
});
485+
486+
describe("HookContext and HRE extensions interactions", () => {
487+
it("Should not make the task manager available to the handlers", async () => {
488+
const assertionPlugin: HardhatPlugin = {
489+
id: "assertion",
490+
hookHandlers: {
491+
hre: async () => ({
492+
created: async (context, _): Promise<void> => {
493+
assert.equal((context as any).tasks, undefined);
494+
},
495+
}),
496+
},
497+
};
498+
499+
await HardhatRuntimeEnvironmentImplementation.create(
500+
{ plugins: [assertionPlugin] },
501+
{},
502+
);
503+
});
504+
505+
it("Should make the core fields of the HRE available to the handlers", async () => {
506+
const assertionPlugin: HardhatPlugin = {
507+
id: "assertion",
508+
hookHandlers: {
509+
hre: async () => ({
510+
created: async (context, hre): Promise<void> => {
511+
for (const field in Object.keys(hre)) {
512+
if (field !== "tasks") {
513+
assert.equal((context as any)[field], (hre as any)[field]);
514+
}
515+
}
516+
},
517+
}),
518+
},
519+
};
520+
521+
await HardhatRuntimeEnvironmentImplementation.create(
522+
{ plugins: [assertionPlugin] },
523+
{},
524+
);
525+
});
526+
527+
it("should make any extention to the HRE available to all the handlers as part of the HookContext", async () => {
528+
const extensionPlugin: HardhatPlugin = {
529+
id: "extension",
530+
hookHandlers: {
531+
hre: async () => ({
532+
created: async (_, hre): Promise<void> => {
533+
(hre as any).myExtension = "myExtension";
534+
},
535+
}),
536+
},
537+
};
538+
539+
const assertionPlugin: HardhatPlugin = {
540+
id: "assertion",
541+
hookHandlers: {
542+
hre: async () => ({
543+
created: async (context, _): Promise<void> => {
544+
assert.equal((context as any).myExtension, "myExtension");
545+
},
546+
}),
547+
},
548+
};
549+
550+
await HardhatRuntimeEnvironmentImplementation.create(
551+
{ plugins: [extensionPlugin, assertionPlugin] },
552+
{},
553+
);
554+
});
555+
});
485556
});
486557

487558
describe("dynamic hooks", () => {

0 commit comments

Comments
 (0)