From 2e049fa19899f03b80aa76cbc5574cbe4a024c90 Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Fri, 9 Aug 2024 18:51:54 +0200 Subject: [PATCH 01/12] feat: add a built-in console task --- .../internal/builtin-plugins/console/index.ts | 19 +++++ .../builtin-plugins/console/task-action.ts | 46 ++++++++++++ .../src/internal/builtin-plugins/index.ts | 9 ++- .../builtin-plugins/console/task-action.ts | 74 +++++++++++++++++++ v-next/hardhat/test/internal/cli/main.ts | 1 + 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 v-next/hardhat/src/internal/builtin-plugins/console/index.ts create mode 100644 v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts create mode 100644 v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts new file mode 100644 index 0000000000..8ac05d402e --- /dev/null +++ b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts @@ -0,0 +1,19 @@ +import type { HardhatPlugin } from "@ignored/hardhat-vnext-core/types/plugins"; + +import { task } from "@ignored/hardhat-vnext-core/config"; + +const hardhatPlugin: HardhatPlugin = { + id: "console", + tasks: [ + task("console", "Opens a hardhat console") + .setAction(import.meta.resolve("./task-action.js")) + .addVariadicArgument({ + name: "commands", + description: "Commands to run in the console", + defaultValue: [".help"], + }) + .build(), + ], +}; + +export default hardhatPlugin; diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts new file mode 100644 index 0000000000..18f7fb910e --- /dev/null +++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts @@ -0,0 +1,46 @@ +import type { NewTaskActionFunction } from "@ignored/hardhat-vnext-core/types/tasks"; +import type { REPLServer } from "node:repl"; + +import path from "node:path"; +import repl from "node:repl"; + +import { getCacheDir } from "@ignored/hardhat-vnext-core/global-dir"; +import debug from "debug"; + +const log = debug("hardhat:core:tasks:console"); + +interface ConsoleActionArguments { + commands: string[]; +} + +const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( + { commands }, + _hre, +) => { + const globalCacheDir = await getCacheDir(); + + return new Promise<REPLServer>((resolve) => { + // Start a new REPL server with the default options + const replServer = repl.start(); + + // Set up the REPL history file in the global cache directory + const historyPath = path.join(globalCacheDir, "console-history.txt"); + replServer.setupHistory(historyPath, (err: Error | null) => { + if (err !== null) { + log(`Failed to setup REPL history: ${err.message}`); + } + }); + + // Execute each command in the REPL server + for (const command of commands) { + replServer.write(`${command}\n`); + } + + // Resolve the task action promise when the REPL server exits + replServer.on("exit", () => { + resolve(replServer); + }); + }); +}; + +export default consoleAction; diff --git a/v-next/hardhat/src/internal/builtin-plugins/index.ts b/v-next/hardhat/src/internal/builtin-plugins/index.ts index d8a7773f4b..00b74e4c6c 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/index.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/index.ts @@ -1,13 +1,20 @@ import type { HardhatPlugin } from "@ignored/hardhat-vnext-core/types/plugins"; import clean from "./clean/index.js"; +import console from "./console/index.js"; import hardhatFoo from "./hardhat-foo/index.js"; import run from "./run/index.js"; // Note: When importing a plugin, you have to export its types, so that its // type extensions, if any, also get loaded. export type * from "./clean/index.js"; +export type * from "./console/index.js"; export type * from "./hardhat-foo/index.js"; export type * from "./run/index.js"; -export const builtinPlugins: HardhatPlugin[] = [clean, hardhatFoo, run]; +export const builtinPlugins: HardhatPlugin[] = [ + clean, + console, + hardhatFoo, + run, +]; diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts new file mode 100644 index 0000000000..7b06ff7c7f --- /dev/null +++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts @@ -0,0 +1,74 @@ +import type { HardhatRuntimeEnvironment } from "@ignored/hardhat-vnext-core/types/hre"; + +import assert from "node:assert/strict"; +import { before, describe, it } from "node:test"; + +import { ensureError } from "@ignored/hardhat-vnext-utils/error"; + +import { createHardhatRuntimeEnvironment } from "../../../../src/hre.js"; +import consoleAction from "../../../../src/internal/builtin-plugins/console/task-action.js"; +import { useFixtureProject } from "../../../helpers/project.js"; + +describe("console/task-action", function () { + let hre: HardhatRuntimeEnvironment; + + before(async function () { + hre = await createHardhatRuntimeEnvironment({}); + }); + + describe("javascript", function () { + useFixtureProject("run-js-script"); + + it("should throw inside the console if script does not exist", async function () { + const replServer = await consoleAction( + { commands: ['await import("./scripts/non-existent.js");', ".exit"] }, + hre, + ); + ensureError(replServer.lastError); + }); + + it("should run a script inside the console successfully", async function () { + const replServer = await consoleAction( + { commands: ['await import("./scripts/success.js");', ".exit"] }, + hre, + ); + assert.equal(replServer.lastError, undefined); + }); + + it("should throw inside the console if the script throws", async function () { + const replServer = await consoleAction( + { commands: ['await import("./scripts/throws.js");', ".exit"] }, + hre, + ); + ensureError(replServer.lastError); + }); + }); + + describe("typescript", function () { + useFixtureProject("run-ts-script"); + + it("should throw inside the console if script does not exist", async function () { + const replServer = await consoleAction( + { commands: ['await import("./scripts/non-existent.ts");', ".exit"] }, + hre, + ); + ensureError(replServer.lastError); + }); + + it("should run a script inside the console successfully", async function () { + const replServer = await consoleAction( + { commands: ['await import("./scripts/success.ts");', ".exit"] }, + hre, + ); + assert.equal(replServer.lastError, undefined); + }); + + it("should throw inside the console if the script throws", async function () { + const replServer = await consoleAction( + { commands: ['await import("./scripts/throws.ts");', ".exit"] }, + hre, + ); + ensureError(replServer.lastError); + }); + }); +}); diff --git a/v-next/hardhat/test/internal/cli/main.ts b/v-next/hardhat/test/internal/cli/main.ts index 64036921ad..8800aa764d 100644 --- a/v-next/hardhat/test/internal/cli/main.ts +++ b/v-next/hardhat/test/internal/cli/main.ts @@ -223,6 +223,7 @@ Usage: hardhat [GLOBAL OPTIONS] <TASK> [SUBTASK] [TASK OPTIONS] [--] [TASK ARGUM AVAILABLE TASKS: clean Clears the cache and deletes all artifacts + console Opens a hardhat console example Example task run Runs a user-defined script after compiling the project task A task that uses arg1 From 1fde8fe4c91075bf632ac6174b7b5c19c3159365 Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Sat, 10 Aug 2024 11:54:34 +0200 Subject: [PATCH 02/12] fix: do not use stdio nor global cache dir during console tests --- .../internal/builtin-plugins/console/index.ts | 5 ++ .../builtin-plugins/console/task-action.ts | 42 +++++---- .../builtin-plugins/console/task-action.ts | 90 +++++++++++++++++-- 3 files changed, 115 insertions(+), 22 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts index 8ac05d402e..225f7441b0 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts @@ -7,6 +7,11 @@ const hardhatPlugin: HardhatPlugin = { tasks: [ task("console", "Opens a hardhat console") .setAction(import.meta.resolve("./task-action.js")) + .addOption({ + name: "history", + description: "Path to a history file", + defaultValue: "console-history.txt", + }) .addVariadicArgument({ name: "commands", description: "Commands to run in the console", diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts index 18f7fb910e..4928b8d800 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts @@ -11,35 +11,47 @@ const log = debug("hardhat:core:tasks:console"); interface ConsoleActionArguments { commands: string[]; + history: string; + // We accept ReplOptions as an argument to allow tests overriding the IO streams + options?: repl.ReplOptions; } const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( - { commands }, + { commands, history, options }, _hre, ) => { - const globalCacheDir = await getCacheDir(); + // Resolve the history path if it is not empty + let historyPath: string | undefined; + if (history !== "") { + const globalCacheDir = await getCacheDir(); + historyPath = path.isAbsolute(history) + ? history + : path.resolve(globalCacheDir, history); + } return new Promise<REPLServer>((resolve) => { // Start a new REPL server with the default options - const replServer = repl.start(); - - // Set up the REPL history file in the global cache directory - const historyPath = path.join(globalCacheDir, "console-history.txt"); - replServer.setupHistory(historyPath, (err: Error | null) => { - if (err !== null) { - log(`Failed to setup REPL history: ${err.message}`); - } + const replServer = repl.start(options); + + // Resolve the task action promise only when the REPL server exits + replServer.on("exit", () => { + resolve(replServer); }); + // Set up the REPL history file if the historyPath has been set + if (historyPath !== undefined) { + replServer.setupHistory(historyPath, (err: Error | null) => { + // Fail silently if the history file cannot be set up + if (err !== null) { + log(`Failed to setup REPL history: ${err.message}`); + } + }); + } + // Execute each command in the REPL server for (const command of commands) { replServer.write(`${command}\n`); } - - // Resolve the task action promise when the REPL server exits - replServer.on("exit", () => { - resolve(replServer); - }); }); }; diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts index 7b06ff7c7f..b6f523b395 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts @@ -1,7 +1,12 @@ import type { HardhatRuntimeEnvironment } from "@ignored/hardhat-vnext-core/types/hre"; +import type repl from "node:repl"; import assert from "node:assert/strict"; -import { before, describe, it } from "node:test"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { PassThrough } from "node:stream"; +import { afterEach, before, beforeEach, describe, it } from "node:test"; import { ensureError } from "@ignored/hardhat-vnext-utils/error"; @@ -11,17 +16,32 @@ import { useFixtureProject } from "../../../helpers/project.js"; describe("console/task-action", function () { let hre: HardhatRuntimeEnvironment; + let options: repl.ReplOptions; before(async function () { hre = await createHardhatRuntimeEnvironment({}); }); + beforeEach(function () { + const input = new PassThrough(); + const output = new PassThrough(); + output.pipe(process.stdout); + options = { + input, + output, + }; + }); + describe("javascript", function () { useFixtureProject("run-js-script"); it("should throw inside the console if script does not exist", async function () { const replServer = await consoleAction( - { commands: ['await import("./scripts/non-existent.js");', ".exit"] }, + { + commands: ['await import("./scripts/non-existent.js");', ".exit"], + history: "", + options, + }, hre, ); ensureError(replServer.lastError); @@ -29,7 +49,11 @@ describe("console/task-action", function () { it("should run a script inside the console successfully", async function () { const replServer = await consoleAction( - { commands: ['await import("./scripts/success.js");', ".exit"] }, + { + commands: [".help", 'await import("./scripts/success.js");', ".exit"], + history: "", + options, + }, hre, ); assert.equal(replServer.lastError, undefined); @@ -37,7 +61,11 @@ describe("console/task-action", function () { it("should throw inside the console if the script throws", async function () { const replServer = await consoleAction( - { commands: ['await import("./scripts/throws.js");', ".exit"] }, + { + commands: ['await import("./scripts/throws.js");', ".exit"], + history: "", + options, + }, hre, ); ensureError(replServer.lastError); @@ -49,7 +77,11 @@ describe("console/task-action", function () { it("should throw inside the console if script does not exist", async function () { const replServer = await consoleAction( - { commands: ['await import("./scripts/non-existent.ts");', ".exit"] }, + { + commands: ['await import("./scripts/non-existent.ts");', ".exit"], + history: "", + options, + }, hre, ); ensureError(replServer.lastError); @@ -57,7 +89,11 @@ describe("console/task-action", function () { it("should run a script inside the console successfully", async function () { const replServer = await consoleAction( - { commands: ['await import("./scripts/success.ts");', ".exit"] }, + { + commands: ['await import("./scripts/success.ts");', ".exit"], + history: "", + options, + }, hre, ); assert.equal(replServer.lastError, undefined); @@ -65,10 +101,50 @@ describe("console/task-action", function () { it("should throw inside the console if the script throws", async function () { const replServer = await consoleAction( - { commands: ['await import("./scripts/throws.ts");', ".exit"] }, + { + commands: ['await import("./scripts/throws.ts");', ".exit"], + history: "", + options, + }, hre, ); ensureError(replServer.lastError); }); }); + + describe("history", function () { + let cacheDir: string; + let history: string; + + beforeEach(function () { + cacheDir = fs.mkdtempSync( + path.resolve(os.tmpdir(), "console-action-test-"), + ); + history = path.resolve(cacheDir, "console-history.txt"); + }); + + afterEach(function () { + fs.rmSync(cacheDir, { recursive: true }); + }); + + it("should create a history file", async function () { + assert.ok( + !fs.existsSync(history), + "History file exists before running the console", + ); + const replServer = await consoleAction( + { + commands: [".help", ".exit"], + history, + options, + }, + hre, + ); + assert.equal(replServer.lastError, undefined); + assert.ok( + fs.existsSync(history), + "History file does not exist after running the console", + ); + }); + }); }); From f73612f6d7b3b71ffb5aa9b47e22362389dac5d2 Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Sat, 10 Aug 2024 11:55:08 +0200 Subject: [PATCH 03/12] feat: expose hre to the hardhat console's context --- .../builtin-plugins/console/task-action.ts | 5 ++++- .../builtin-plugins/console/task-action.ts | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts index 4928b8d800..d228b01b29 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts @@ -18,7 +18,7 @@ interface ConsoleActionArguments { const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( { commands, history, options }, - _hre, + hre, ) => { // Resolve the history path if it is not empty let historyPath: string | undefined; @@ -38,6 +38,9 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( resolve(replServer); }); + // Add the Hardhat Runtime Environment to the REPL context + replServer.context.hre = hre; + // Set up the REPL history file if the historyPath has been set if (historyPath !== undefined) { replServer.setupHistory(historyPath, (err: Error | null) => { diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts index b6f523b395..5fffa4ac46 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts @@ -112,6 +112,20 @@ describe("console/task-action", function () { }); }); + describe("context", function () { + it("should expose the Hardhat Runtime Environment", async function () { + const replServer = await consoleAction( + { + commands: ["console.log(hre);", ".exit"], + history: "", + options, + }, + hre, + ); + assert.equal(replServer.lastError, undefined); + }); + }); + describe("history", function () { let cacheDir: string; let history: string; From a0ecee1b76621d4e2aa86c5cc1bdadee788f1c14 Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Sat, 10 Aug 2024 14:43:42 +0200 Subject: [PATCH 04/12] fix: await history setup --- .../builtin-plugins/console/task-action.ts | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts index d228b01b29..00bb4f890f 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts @@ -20,16 +20,16 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( { commands, history, options }, hre, ) => { - // Resolve the history path if it is not empty - let historyPath: string | undefined; - if (history !== "") { - const globalCacheDir = await getCacheDir(); - historyPath = path.isAbsolute(history) - ? history - : path.resolve(globalCacheDir, history); - } - - return new Promise<REPLServer>((resolve) => { + return new Promise<REPLServer>(async (resolve) => { + // Resolve the history path if it is not empty + let historyPath: string | undefined; + if (history !== "") { + const globalCacheDir = await getCacheDir(); + historyPath = path.isAbsolute(history) + ? history + : path.resolve(globalCacheDir, history); + } + // Start a new REPL server with the default options const replServer = repl.start(options); @@ -43,11 +43,14 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( // Set up the REPL history file if the historyPath has been set if (historyPath !== undefined) { - replServer.setupHistory(historyPath, (err: Error | null) => { - // Fail silently if the history file cannot be set up - if (err !== null) { - log(`Failed to setup REPL history: ${err.message}`); - } + await new Promise<void>((resolveSetupHistory) => { + replServer.setupHistory(historyPath, (err: Error | null) => { + // Fail silently if the history file cannot be set up + if (err !== null) { + log(`Failed to setup REPL history: ${err.message}`); + } + resolveSetupHistory(); + }); }); } From 35336949e88ac1d55fc2c03a4d7d813accc90fee Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Sat, 10 Aug 2024 14:47:47 +0200 Subject: [PATCH 05/12] fix: force remove temporary cache directory --- .../test/internal/builtin-plugins/console/task-action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts index 5fffa4ac46..dc3a532bd6 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts @@ -138,7 +138,7 @@ describe("console/task-action", function () { }); afterEach(function () { - fs.rmSync(cacheDir, { recursive: true }); + fs.rmSync(cacheDir, { recursive: true, force: true }); }); it("should create a history file", async function () { From b02caf90e18c6dc9ccac2dc4a117877985480da0 Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Sat, 10 Aug 2024 15:04:24 +0200 Subject: [PATCH 06/12] feat: add noCompile flag to the console task --- .../internal/builtin-plugins/console/index.ts | 4 +++ .../builtin-plugins/console/task-action.ts | 26 ++++++++++++------- .../builtin-plugins/console/task-action.ts | 8 ++++++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts index 225f7441b0..4f3290915e 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts @@ -12,6 +12,10 @@ const hardhatPlugin: HardhatPlugin = { description: "Path to a history file", defaultValue: "console-history.txt", }) + .addFlag({ + name: "noCompile", + description: "Don't compile before running this task", + }) .addVariadicArgument({ name: "commands", description: "Commands to run in the console", diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts index 00bb4f890f..3897c236e4 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts @@ -12,24 +12,30 @@ const log = debug("hardhat:core:tasks:console"); interface ConsoleActionArguments { commands: string[]; history: string; + noCompile: boolean; // We accept ReplOptions as an argument to allow tests overriding the IO streams options?: repl.ReplOptions; } const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( - { commands, history, options }, + { commands, history, noCompile, options }, hre, ) => { - return new Promise<REPLServer>(async (resolve) => { - // Resolve the history path if it is not empty - let historyPath: string | undefined; - if (history !== "") { - const globalCacheDir = await getCacheDir(); - historyPath = path.isAbsolute(history) - ? history - : path.resolve(globalCacheDir, history); - } + // Resolve the history path if it is not empty + let historyPath: string | undefined; + if (history !== "") { + const globalCacheDir = await getCacheDir(); + historyPath = path.isAbsolute(history) + ? history + : path.resolve(globalCacheDir, history); + } + // If noCompile is false, run the compile task first + if (!noCompile) { + // todo: run compile task + } + + return new Promise<REPLServer>(async (resolve) => { // Start a new REPL server with the default options const replServer = repl.start(options); diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts index dc3a532bd6..c0f9f38f7f 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts @@ -40,6 +40,7 @@ describe("console/task-action", function () { { commands: ['await import("./scripts/non-existent.js");', ".exit"], history: "", + noCompile: false, options, }, hre, @@ -52,6 +53,7 @@ describe("console/task-action", function () { { commands: [".help", 'await import("./scripts/success.js");', ".exit"], history: "", + noCompile: false, options, }, hre, @@ -64,6 +66,7 @@ describe("console/task-action", function () { { commands: ['await import("./scripts/throws.js");', ".exit"], history: "", + noCompile: false, options, }, hre, @@ -80,6 +83,7 @@ describe("console/task-action", function () { { commands: ['await import("./scripts/non-existent.ts");', ".exit"], history: "", + noCompile: false, options, }, hre, @@ -92,6 +96,7 @@ describe("console/task-action", function () { { commands: ['await import("./scripts/success.ts");', ".exit"], history: "", + noCompile: false, options, }, hre, @@ -104,6 +109,7 @@ describe("console/task-action", function () { { commands: ['await import("./scripts/throws.ts");', ".exit"], history: "", + noCompile: false, options, }, hre, @@ -118,6 +124,7 @@ describe("console/task-action", function () { { commands: ["console.log(hre);", ".exit"], history: "", + noCompile: false, options, }, hre, @@ -150,6 +157,7 @@ describe("console/task-action", function () { { commands: [".help", ".exit"], history, + noCompile: false, options, }, hre, From b0b1badeae1166ceb51241a28f67df28a796b942 Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Sat, 10 Aug 2024 15:25:56 +0200 Subject: [PATCH 07/12] fix: us fs utils for file operations --- .../builtin-plugins/console/task-action.ts | 2 +- .../builtin-plugins/console/task-action.ts | 24 +++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts index 3897c236e4..89fae266e5 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts @@ -53,7 +53,7 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( replServer.setupHistory(historyPath, (err: Error | null) => { // Fail silently if the history file cannot be set up if (err !== null) { - log(`Failed to setup REPL history: ${err.message}`); + log("Failed to setup REPL history", err); } resolveSetupHistory(); }); diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts index c0f9f38f7f..aac04b91d0 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts @@ -2,18 +2,22 @@ import type { HardhatRuntimeEnvironment } from "@ignored/hardhat-vnext-core/type import type repl from "node:repl"; import assert from "node:assert/strict"; -import fs from "node:fs"; +import fsPromises from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { PassThrough } from "node:stream"; import { afterEach, before, beforeEach, describe, it } from "node:test"; import { ensureError } from "@ignored/hardhat-vnext-utils/error"; +import { exists, remove } from "@ignored/hardhat-vnext-utils/fs"; +import debug from "debug"; import { createHardhatRuntimeEnvironment } from "../../../../src/hre.js"; import consoleAction from "../../../../src/internal/builtin-plugins/console/task-action.js"; import { useFixtureProject } from "../../../helpers/project.js"; +const log = debug("hardhat:test:console:task-action"); + describe("console/task-action", function () { let hre: HardhatRuntimeEnvironment; let options: repl.ReplOptions; @@ -137,20 +141,25 @@ describe("console/task-action", function () { let cacheDir: string; let history: string; - beforeEach(function () { - cacheDir = fs.mkdtempSync( + beforeEach(async function () { + cacheDir = await fsPromises.mkdtemp( path.resolve(os.tmpdir(), "console-action-test-"), ); history = path.resolve(cacheDir, "console-history.txt"); }); - afterEach(function () { - fs.rmSync(cacheDir, { recursive: true, force: true }); + afterEach(async function () { + try { + await remove(cacheDir); + } catch (error) { + log("Failed to remove temporary cache dir", error); + } }); it("should create a history file", async function () { + let historyExists = await exists(history); assert.ok( - !fs.existsSync(history), + !historyExists, "History file exists before running the console", ); const replServer = await consoleAction( @@ -163,8 +172,9 @@ describe("console/task-action", function () { hre, ); assert.equal(replServer.lastError, undefined); + historyExists = await exists(history); assert.ok( - fs.existsSync(history), + historyExists, "History file does not exist after running the console", ); }); From 511a3bb518e656765f99968d38d29ee47577a126 Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Mon, 12 Aug 2024 10:30:16 +0200 Subject: [PATCH 08/12] chore: add comments about testing choices --- .../test/internal/builtin-plugins/console/task-action.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts index aac04b91d0..bcbc17cb94 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts @@ -27,6 +27,10 @@ describe("console/task-action", function () { }); beforeEach(function () { + // Using process.stdin for the input during tests is not reliable as it + // causes the test runner to hang indefinitely. We use a PassThrough stream + // instead. This, in turn, prevents us from using process.stdout for output. + // Hence, we use a PassThrough stream for output as well. const input = new PassThrough(); const output = new PassThrough(); output.pipe(process.stdout); @@ -142,6 +146,8 @@ describe("console/task-action", function () { let history: string; beforeEach(async function () { + // We use a temporary cache dir to avoid conflicts with other tests + // and global user settings. cacheDir = await fsPromises.mkdtemp( path.resolve(os.tmpdir(), "console-action-test-"), ); @@ -149,6 +155,9 @@ describe("console/task-action", function () { }); afterEach(async function () { + // We try to remove the temporary cache dir after each test, but we don't + // fail the test if it fails. For example, we have observed that in GHA + // on Windows, the temp dir cannot be removed due to permission issues. try { await remove(cacheDir); } catch (error) { From c23b974279680c14ff0b26a4d2f0fe50d310c07c Mon Sep 17 00:00:00 2001 From: Piotr Galar <piotr.galar@gmail.com> Date: Wed, 14 Aug 2024 10:31:14 +0200 Subject: [PATCH 09/12] feat: expose hre fields in the console context --- .../src/internal/builtin-plugins/console/task-action.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts index 89fae266e5..45b3f05e23 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts @@ -46,6 +46,11 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( // Add the Hardhat Runtime Environment to the REPL context replServer.context.hre = hre; + replServer.context.config = hre.config; + replServer.context.tasks = hre.tasks; + replServer.context.globalOptions = hre.globalOptions; + replServer.context.hooks = hre.hooks; + replServer.context.interruptions = hre.interruptions; // Set up the REPL history file if the historyPath has been set if (historyPath !== undefined) { From 57a4811ebf4d738f1b35fc1dfb1e920485a3c38c Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Wed, 14 Aug 2024 10:47:10 +0200 Subject: [PATCH 10/12] chore: add a note to replace usage of global cache directory --- .../src/internal/builtin-plugins/console/task-action.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts index 45b3f05e23..ab407d808a 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts @@ -24,10 +24,11 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( // Resolve the history path if it is not empty let historyPath: string | undefined; if (history !== "") { - const globalCacheDir = await getCacheDir(); + // TODO(#5599): Replace with hre.config.paths.cache once it is available + const cacheDir = await getCacheDir(); historyPath = path.isAbsolute(history) ? history - : path.resolve(globalCacheDir, history); + : path.resolve(cacheDir, history); } // If noCompile is false, run the compile task first From eccb397b50046abc394fcd1d656796cf9e10fa41 Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Wed, 14 Aug 2024 10:52:58 +0200 Subject: [PATCH 11/12] chore: add an issue number to the compile related todos --- .../hardhat/src/internal/builtin-plugins/console/task-action.ts | 2 +- v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts index ab407d808a..818b130ab5 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts @@ -33,7 +33,7 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async ( // If noCompile is false, run the compile task first if (!noCompile) { - // todo: run compile task + // TODO(#5600): run compile task } return new Promise<REPLServer>(async (resolve) => { diff --git a/v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts index e5a7c7f741..134101af64 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts @@ -27,7 +27,7 @@ const runScriptWithHardhat: NewTaskActionFunction<RunActionArguments> = async ( } if (!noCompile) { - // todo: run compile task + // TODO(#5600): run compile task } try { From 62fa7d28fff2f3940856cd692d8122219585f5cc Mon Sep 17 00:00:00 2001 From: galargh <piotr.galar@gmail.com> Date: Wed, 14 Aug 2024 10:59:19 +0200 Subject: [PATCH 12/12] chore: add a note to remove direct fs use when replacement is available --- .../hardhat/test/internal/builtin-plugins/console/task-action.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts index bcbc17cb94..9a4d88f5b1 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts @@ -146,6 +146,7 @@ describe("console/task-action", function () { let history: string; beforeEach(async function () { + // TODO(#5601): Use the mkdtemp from hardhat-utils once it's available // We use a temporary cache dir to avoid conflicts with other tests // and global user settings. cacheDir = await fsPromises.mkdtemp(