Repository: NomicFoundation/hardhat-ignition (ts)

Issue:
Restructure ignition folder to have a modules subfolder
The current layout of the ./ignition/ directory is this:

```
<root>/
  ignition/
     mod1.js
     deployments/
        deployment-123/
              ...
```

We want to reorganise Ignition modules under their own folder:

```
<root>/
  ignition/
     modules/
        mod1.js
     deployments/
        deployment-123/
              ...
```

## TODO

- [ ] Change cli tasks to read modules from new folder
- [ ] Update docs to give instructions on setting up from the new modules folder (getting-started-guide)

Pull request:
feat/require modules folder (PR 10)
Ignition modules should be loaded from a `modules` folder under the `ignition` folder. Attempts to load an Ignition module from a different location will error.

The `getting-started-guide` has been updated to give instructions on creating the new folder layout.

Resolves #511.

Patch:
diff --git a/docs/getting-started-guide.md b/docs/getting-started-guide.md
index 56d15a88..1914536c 100644
--- a/docs/getting-started-guide.md
+++ b/docs/getting-started-guide.md
@@ -51,18 +51,19 @@ require("@nomicfoundation/hardhat-toolbox");
 require("@nomicfoundation/hardhat-ignition");
 ```
 
-Create an `./ignition` folder in your project root to contain your deployment modules.
+Create an `./ignition` folder in your project root, with a `modules` subfolder to contain your deployment modules.
 
 ```bash
 mkdir ./ignition
+mkdir ./ignition/modules
 ```
 
 ## Writing Your First Deployment Module
 
-Add a deployment module under the `./ignition` folder for the example `Lock.sol` contract:
+Add a deployment module under the `./ignition/modules` folder for the example `Lock.sol` contract:
 
 ```js
-// ./ignition/LockModule.js
+// ./ignition/modules/LockModule.js
 const { buildModule } = require("@nomicfoundation/hardhat-ignition");
 
 const currentTimestampInSeconds = Math.round(new Date(2023, 0, 1) / 1000);
@@ -95,7 +96,7 @@ A file containing module parameters, indexed by the `ModuleId` used in `buildMod
 the command line:
 
 ```json
-// ignition/LockModule.config.json
+// ignition/modules/LockModule.config.json
 {
   "LockModule": {
     "unlockTime": 4102491600
@@ -104,7 +105,7 @@ the command line:
 ```
 
 ```bash
-npx hardhat deploy --parameters ignition/LockModule.config.json LockModule
+npx hardhat deploy --parameters ignition/modules/LockModule.config.json LockModule
 ```
 
 To deploy against a specific network pass it on the command line, for instance to deploy against
@@ -117,21 +118,21 @@ npx hardhat deploy LockModule --network localhost
 ```
 
 Running against a non-ephemeral network will generate a `deployment` stored under `./ignition/deployments`. The
-deployment identified by a `deploymentId` that can be passed at the command line:
+deployment identified by an `id` that can be passed at the command line:
 
 ```bash
 npx hardhat deploy LockModule --network localhost --id dev-deploy
 ```
 
-If no `deploymentId` is provided a default is generated based on the `chainId` e.g. _network-31337_ for the localhost
+If no `id` is provided a default is generated based on the `chainId` e.g. _network-31337_ for the localhost
 network.
 
 ### Getting Info About Previous Deployments
 
-Run the `ignition-info` task to display info about your successfully deployed contracts within a deployment:
+Run the `status` task to display info about your successfully deployed contracts within a deployment:
 
 ```bash
-npx hardhat ignition-info --deployment-id dev-deploy
+npx hardhat status --id dev-deploy
 # Deployed Addresses
 # ==================
 
@@ -146,7 +147,7 @@ the `./test/Lock.js` test file can be leverage **Ignition** by updating the `dep
 ```js
 ...
 const { expect } = require("chai");
-const LockModule = require("../ignition/LockModule");
+const LockModule = require("../ignition/modules/LockModule");
 
 ...
 
diff --git a/examples/complete/ignition/CompleteModule.js b/examples/complete/ignition/modules/CompleteModule.js
similarity index 87%
rename from examples/complete/ignition/CompleteModule.js
rename to examples/complete/ignition/modules/CompleteModule.js
index 3c6a3b66..668aaa29 100644
--- a/examples/complete/ignition/CompleteModule.js
+++ b/examples/complete/ignition/modules/CompleteModule.js
@@ -1,8 +1,8 @@
 // ./ignition/CompleteModule.js
 const { buildModule } = require("@nomicfoundation/hardhat-ignition");
 
-const withLibArtifact = require("../libArtifacts/ContractWithLibrary.json");
-const libArtifact = require("../libArtifacts/BasicLibrary.json");
+const withLibArtifact = require("../../libArtifacts/ContractWithLibrary.json");
+const libArtifact = require("../../libArtifacts/BasicLibrary.json");
 
 module.exports = buildModule("CompleteModule", (m) => {
   const basic = m.contract("BasicContract");
diff --git a/examples/complete/package.json b/examples/complete/package.json
index f33290ec..4d6ab525 100644
--- a/examples/complete/package.json
+++ b/examples/complete/package.json
@@ -7,7 +7,7 @@
     "lint": "npm run prettier -- --check && npm run eslint",
     "lint:fix": "npm run prettier -- --write && npm run eslint -- --fix",
     "eslint": "eslint \"ignition/**/*.{js,jsx}\" \"test/**/*.{js,jsx}\"",
-    "prettier": "prettier \"*.{js,md,json}\" \"ignition/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
+    "prettier": "prettier \"*.{js,md,json}\" \"ignition/modules/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
   },
   "devDependencies": {
     "@nomicfoundation/hardhat-ignition": "^0.4.0",
diff --git a/examples/ens/ignition/ENS.js b/examples/ens/ignition/modules/ENS.js
similarity index 100%
rename from examples/ens/ignition/ENS.js
rename to examples/ens/ignition/modules/ENS.js
diff --git a/examples/ens/ignition/test-registrar.js b/examples/ens/ignition/modules/test-registrar.js
similarity index 100%
rename from examples/ens/ignition/test-registrar.js
rename to examples/ens/ignition/modules/test-registrar.js
diff --git a/examples/ens/package.json b/examples/ens/package.json
index 3c21a7f2..2455304b 100644
--- a/examples/ens/package.json
+++ b/examples/ens/package.json
@@ -7,7 +7,7 @@
     "lint": "npm run prettier -- --check && npm run eslint",
     "lint:fix": "npm run prettier -- --write && npm run eslint -- --fix",
     "eslint": "eslint \"ignition/**/*.{js,jsx}\" \"test/**/*.{js,jsx}\"",
-    "prettier": "prettier \"*.{js,md,json}\" \"ignition/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
+    "prettier": "prettier \"*.{js,md,json}\" \"ignition/modules/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
   },
   "devDependencies": {
     "@nomicfoundation/hardhat-ignition": "^0.4.0",
diff --git a/examples/sample/ignition/LockModule.js b/examples/sample/ignition/modules/LockModule.js
similarity index 100%
rename from examples/sample/ignition/LockModule.js
rename to examples/sample/ignition/modules/LockModule.js
diff --git a/examples/sample/package.json b/examples/sample/package.json
index 22d64492..f10a96d3 100644
--- a/examples/sample/package.json
+++ b/examples/sample/package.json
@@ -7,7 +7,7 @@
     "lint": "npm run prettier -- --check && npm run eslint",
     "lint:fix": "npm run prettier -- --write && npm run eslint -- --fix",
     "eslint": "eslint \"ignition/**/*.{js,jsx}\" \"test/**/*.{js,jsx}\"",
-    "prettier": "prettier \"*.{js,md,json}\" \"ignition/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
+    "prettier": "prettier \"*.{js,md,json}\" \"ignition/modules/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
   },
   "devDependencies": {
     "@nomicfoundation/hardhat-ignition": "^0.4.0",
diff --git a/examples/ts-sample/ignition/LockModule.ts b/examples/ts-sample/ignition/modules/LockModule.ts
similarity index 100%
rename from examples/ts-sample/ignition/LockModule.ts
rename to examples/ts-sample/ignition/modules/LockModule.ts
diff --git a/examples/ts-sample/package.json b/examples/ts-sample/package.json
index 417c3ac7..8a9afe9a 100644
--- a/examples/ts-sample/package.json
+++ b/examples/ts-sample/package.json
@@ -7,7 +7,7 @@
     "lint": "npm run prettier -- --check && npm run eslint",
     "lint:fix": "npm run prettier -- --write && npm run eslint -- --fix",
     "eslint": "eslint \"ignition/**/*.{js,jsx,ts,tsx}\" \"test/**/*.{js,jsx,ts,tsx}\"",
-    "prettier": "prettier \"*.{js,ts,md,json}\" \"ignition/*.{js,ts,md,json}\" \"test/*.{js,ts,md,json}\" \"contracts/**/*.sol\""
+    "prettier": "prettier \"*.{js,ts,md,json}\" \"ignition/modules/*.{js,ts,md,json}\" \"test/*.{js,ts,md,json}\" \"contracts/**/*.sol\""
   },
   "devDependencies": {
     "@nomicfoundation/hardhat-ignition": "^0.4.0",
diff --git a/examples/uniswap/ignition/Uniswap.js b/examples/uniswap/ignition/modules/Uniswap.js
similarity index 100%
rename from examples/uniswap/ignition/Uniswap.js
rename to examples/uniswap/ignition/modules/Uniswap.js
diff --git a/examples/uniswap/package.json b/examples/uniswap/package.json
index cbadd0a1..e6025a29 100644
--- a/examples/uniswap/package.json
+++ b/examples/uniswap/package.json
@@ -7,7 +7,7 @@
     "lint": "npm run prettier -- --check && npm run eslint",
     "lint:fix": "npm run prettier -- --write && npm run eslint -- --fix",
     "eslint": "eslint \"ignition/**/*.{js,jsx}\" \"test/**/*.{js,jsx}\"",
-    "prettier": "prettier \"*.{js,md,json}\" \"ignition/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
+    "prettier": "prettier \"*.{js,md,json}\" \"ignition/modules/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
   },
   "devDependencies": {
     "@nomicfoundation/hardhat-ignition": "^0.4.0",
diff --git a/packages/hardhat-plugin/src/load-module.ts b/packages/hardhat-plugin/src/load-module.ts
index 8fc190a2..74772636 100644
--- a/packages/hardhat-plugin/src/load-module.ts
+++ b/packages/hardhat-plugin/src/load-module.ts
@@ -6,21 +6,40 @@ import path from "path";
 
 const debug = setupDebug("hardhat-ignition:modules");
 
+const MODULES_FOLDER = "modules";
+
 export function loadModule(
-  modulesDirectory: string,
+  ignitionDirectory: string,
   moduleNameOrPath: string
 ): IgnitionModule | undefined {
-  debug(`Loading user modules from '${modulesDirectory}'`);
+  const fullModulesDirectoryName = path.resolve(
+    ignitionDirectory,
+    MODULES_FOLDER
+  );
 
-  if (!existsSync(modulesDirectory)) {
+  const shortModulesDirectoryName = path.join(
+    ignitionDirectory,
+    MODULES_FOLDER
+  );
+
+  if (!existsSync(ignitionDirectory)) {
     throw new HardhatPluginError(
       "hardhat-ignition",
-      `Directory ${modulesDirectory} not found.`
+      `Ignition directory ${ignitionDirectory} not found.`
     );
   }
 
+  if (!existsSync(fullModulesDirectoryName)) {
+    throw new HardhatPluginError(
+      "hardhat-ignition",
+      `Ignition modules directory ${shortModulesDirectoryName} not found.`
+    );
+  }
+
+  debug(`Loading user modules from '${fullModulesDirectoryName}'`);
+
   const fullpathToModule = resolveFullPathToModule(
-    modulesDirectory,
+    fullModulesDirectoryName,
     moduleNameOrPath
   );
 
@@ -31,10 +50,10 @@ export function loadModule(
     );
   }
 
-  if (!isInModuleDirectory(modulesDirectory, fullpathToModule)) {
+  if (!isInModuleDirectory(fullModulesDirectoryName, fullpathToModule)) {
     throw new HardhatPluginError(
       "hardhat-ignition",
-      `The referenced module ${moduleNameOrPath} is outside the module directory ${modulesDirectory}`
+      `The referenced module ${moduleNameOrPath} is outside the module directory ${shortModulesDirectoryName}`
     );
   }
 
@@ -71,6 +90,7 @@ function resolveFullPathToModule(
     modulesDirectory,
     `${moduleNameOrPath}.ts`
   );
+
   if (pathExistsSync(relativeToModulesWithTsExtension)) {
     return relativeToModulesWithTsExtension;
   }


Test patch:
diff --git a/examples/complete/test/Complete.js b/examples/complete/test/Complete.js
index caf9eeec..3ccf6ed7 100644
--- a/examples/complete/test/Complete.js
+++ b/examples/complete/test/Complete.js
@@ -2,7 +2,7 @@ const {
   loadFixture,
 } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
 const { expect } = require("chai");
-const CompleteModule = require("../ignition/CompleteModule");
+const CompleteModule = require("../ignition/modules/CompleteModule");
 
 describe("Complete", function () {
   // We define a fixture to reuse the same setup in every test.
diff --git a/examples/ens/test/sample-test.js b/examples/ens/test/sample-test.js
index 992a3bef..acf9dc66 100644
--- a/examples/ens/test/sample-test.js
+++ b/examples/ens/test/sample-test.js
@@ -1,5 +1,5 @@
 const { expect } = require("chai");
-const ENSModule = require("../ignition/test-registrar");
+const ENSModule = require("../ignition/modules/test-registrar");
 const namehash = require("eth-ens-namehash");
 const labelhash = (label) =>
   hre.ethers.keccak256(hre.ethers.toUtf8Bytes(label));
diff --git a/examples/sample/test/Lock.js b/examples/sample/test/Lock.js
index adeda0ad..67d31ee8 100644
--- a/examples/sample/test/Lock.js
+++ b/examples/sample/test/Lock.js
@@ -4,7 +4,7 @@ const {
 } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
 const { anyValue } = require("@nomicfoundation/hardhat-chai-matchers/withArgs");
 const { expect } = require("chai");
-const LockModule = require("../ignition/LockModule");
+const LockModule = require("../ignition/modules/LockModule");
 
 describe("Lock", function () {
   // We define a fixture to reuse the same setup in every test.
diff --git a/examples/ts-sample/test/Lock.ts b/examples/ts-sample/test/Lock.ts
index e47e6331..584b914a 100644
--- a/examples/ts-sample/test/Lock.ts
+++ b/examples/ts-sample/test/Lock.ts
@@ -5,7 +5,7 @@ import {
 } from "@nomicfoundation/hardhat-toolbox/network-helpers";
 import { expect } from "chai";
 import { ethers, ignition } from "hardhat";
-import LockModule from "../ignition/LockModule";
+import LockModule from "../ignition/modules/LockModule";
 
 describe("Lock", function () {
   // We define a fixture to reuse the same setup in every test.
diff --git a/examples/uniswap/test/simplified-test.js b/examples/uniswap/test/simplified-test.js
index 4af03049..6979f373 100644
--- a/examples/uniswap/test/simplified-test.js
+++ b/examples/uniswap/test/simplified-test.js
@@ -1,5 +1,5 @@
 const { assert } = require("chai");
-const UniswapModule = require("../ignition/Uniswap");
+const UniswapModule = require("../ignition/modules/Uniswap");
 
 describe("uniswap deploy", () => {
   let nonfungibleTokenPositionManager, uniswapV3Factory, swapRouter02;
diff --git a/examples/uniswap/test/uniswap-test.js b/examples/uniswap/test/uniswap-test.js
index eef41244..471bbf5a 100644
--- a/examples/uniswap/test/uniswap-test.js
+++ b/examples/uniswap/test/uniswap-test.js
@@ -2,7 +2,7 @@ const { assert } = require("chai");
 const { Token } = require("@uniswap/sdk-core");
 const { Pool, Position, nearestUsableTick } = require("@uniswap/v3-sdk");
 const UniswapV3Pool = require("@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json");
-const UniswapModule = require("../ignition/Uniswap");
+const UniswapModule = require("../ignition/modules/Uniswap");
 const {
   encodePriceSqrt,
   getPoolImmutables,
diff --git a/packages/core/test-integrations/fixture-projects/default-with-new-chain-id/ignition/LockModule.js b/packages/core/test-integrations/fixture-projects/default-with-new-chain-id/ignition/modules/LockModule.js
similarity index 100%
rename from packages/core/test-integrations/fixture-projects/default-with-new-chain-id/ignition/LockModule.js
rename to packages/core/test-integrations/fixture-projects/default-with-new-chain-id/ignition/modules/LockModule.js
diff --git a/packages/core/test-integrations/new-api/internal/reconciliation/chainId.ts b/packages/core/test-integrations/new-api/internal/reconciliation/chainId.ts
index 909d3e7c..58825210 100644
--- a/packages/core/test-integrations/new-api/internal/reconciliation/chainId.ts
+++ b/packages/core/test-integrations/new-api/internal/reconciliation/chainId.ts
@@ -11,7 +11,9 @@ describe("chainId reconciliation", function () {
     this.hre.network.name = "something-else";
 
     await assert.isRejected(
-      this.hre.run("deploy", { moduleNameOrPath: "./ignition/LockModule.js" }),
+      this.hre.run("deploy", {
+        moduleNameOrPath: "./ignition/modules/LockModule.js",
+      }),
       /Previous chain id: 123\. Current chain id: 31337/
     );
   });
diff --git a/packages/hardhat-plugin/test/fixture-projects/minimal/ignition/MyModule.js b/packages/hardhat-plugin/test/fixture-projects/minimal/ignition/modules/MyModule.js
similarity index 100%
rename from packages/hardhat-plugin/test/fixture-projects/minimal/ignition/MyModule.js
rename to packages/hardhat-plugin/test/fixture-projects/minimal/ignition/modules/MyModule.js
diff --git a/packages/hardhat-plugin/test/fixture-projects/user-modules/ignition/TestModule.js b/packages/hardhat-plugin/test/fixture-projects/user-modules/ignition/modules/TestModule.js
similarity index 100%
rename from packages/hardhat-plugin/test/fixture-projects/user-modules/ignition/TestModule.js
rename to packages/hardhat-plugin/test/fixture-projects/user-modules/ignition/modules/TestModule.js
diff --git a/packages/hardhat-plugin/test/fixture-projects/with-config/ignition/MyModule.js b/packages/hardhat-plugin/test/fixture-projects/with-config/ignition/modules/MyModule.js
similarity index 100%
rename from packages/hardhat-plugin/test/fixture-projects/with-config/ignition/MyModule.js
rename to packages/hardhat-plugin/test/fixture-projects/with-config/ignition/modules/MyModule.js
diff --git a/packages/hardhat-plugin/test/fixture-projects/with-invalid-config/ignition/MyModule.js b/packages/hardhat-plugin/test/fixture-projects/with-invalid-config/ignition/modules/MyModule.js
similarity index 100%
rename from packages/hardhat-plugin/test/fixture-projects/with-invalid-config/ignition/MyModule.js
rename to packages/hardhat-plugin/test/fixture-projects/with-invalid-config/ignition/modules/MyModule.js
diff --git a/packages/hardhat-plugin/test/load-module.ts b/packages/hardhat-plugin/test/load-module.ts
index f79fcd98..b80f3782 100644
--- a/packages/hardhat-plugin/test/load-module.ts
+++ b/packages/hardhat-plugin/test/load-module.ts
@@ -41,16 +41,23 @@ describe("loadModule", function () {
   });
 
   it("should throw if the full path to the module is outside the module directory", () => {
+    const unixErrorMessage = `The referenced module ./hardhat.config.js is outside the module directory ignition/modules`;
+
+    const expectedErrorMessage =
+      process.platform === "win32"
+        ? unixErrorMessage.replace("ignition/modules", "ignition\\modules")
+        : unixErrorMessage;
+
     assert.throws(
-      () => loadModule("contracts", "./ignition/TestModule.js"),
-      `The referenced module ./ignition/TestModule.js is outside the module directory contracts`
+      () => loadModule("ignition", "./hardhat.config.js"),
+      expectedErrorMessage
     );
   });
 
   it("should throw if given a user module directory that does not exist", async () => {
     assert.throws(
       () => loadModule("/fake", "AFile.js"),
-      `Directory /fake not found.`
+      `Ignition directory /fake not found.`
     );
   });
 });