Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic support for ESM (rebased) #3550

Merged
merged 20 commits into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/empty-suns-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomiclabs/hardhat-vyper": patch
---

Keep the parent exception when throwing because the compiler list download failed.
8 changes: 8 additions & 0 deletions .changeset/slimy-plums-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@nomicfoundation/hardhat-toolbox": patch
"hardhat": minor
---

Added support for writing scripts and tests as ES modules.

To learn how to start using ESM with Hardhat read [this guide](https://hardhat.org/hardhat-runner/docs/advanced/using-esm).
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ order:
- /flattening
- href: /vscode-tests
title: Running tests in VS Code
- /using-esm
132 changes: 132 additions & 0 deletions docs/src/content/hardhat-runner/docs/advanced/using-esm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Using ES modules

Node.js projects can use one of two module systems: CommonJS and ES Modules (ESM). Hardhat was designed mainly with CommonJS in mind, but in the last years adoption of ESM has been growing.

This guide explains where you can use ESM in your Hardhat project and how to do it.

## Hardhat support for ES modules

You can write your scripts and tests as both CommonJS and ES modules. However, your Hardhat config, and any file imported by it, **must** be CommonJS modules.

If your package uses ESM by default (that is, you have [`"type": "module"`](https://nodejs.org/api/packages.html#type) in your `package.json`), then your Hardhat config file must be named `hardhat.config.cjs`.

Hardhat doesn't support [ESM in TypeScript projects](#esm-and-typescript-projects).

## Using ES Modules in Hardhat

The following sections explain how to use ES modules in new or existing Hardhat projects.

### Starting an ESM-first Hardhat project

If you want to start a Hardhat project that uses ES modules by default, first you have to initialize a Node.js project:

::::tabsgroup{options="npm 7+,npm 6,yarn"}

:::tab{value="npm 7+"}

```
npm init -y
```

:::

:::tab{value="npm 6"}

```
npm init -y
```

:::

:::tab{value="yarn"}

```
yarn init -y
```

:::

::::

Open the `package.json` that was created and add a `"type": "module"` entry. This will make the project use ESM by default.

After that, install Hardhat:

::::tabsgroup{options="npm 7+,npm 6,yarn"}

:::tab{value="npm 7+"}

```
npm install --save-dev hardhat
```

:::

:::tab{value="npm 6"}

```
npm install --save-dev hardhat
```

:::

:::tab{value="yarn"}

```
yarn add --dev hardhat
```

:::

::::

and run `npx hardhat` to create a Hardhat project:

```
888 888 888 888 888
888 888 888 888 888
888 888 888 888 888
8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
888 888 "88b 888P" d88" 888 888 "88b "88b 888
888 888 .d888888 888 888 888 888 888 .d888888 888
888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888

Welcome to Hardhat v2.13.0

? What do you want to do? …
▸ Create a JavaScript project
Create a TypeScript project (not available for ESM projects)
Create an empty hardhat.config.cjs
Quit
```

Select the `Create a JavaScript project` option. This will initialize a Hardhat project where the scripts and tests are ES modules, and where the configuration has a `.cjs` extension.

### Migrating a project to ESM

If you have an existing Hardhat project and you want to convert it into an ESM project, follow these steps:

1. Edit your `package.json` and add a `"type": "module"` entry.
2. Rename your `hardhat.config.js` file to `hardhat.config.cjs`.
3. Migrate all your scripts and tests from CommonJS to ESM. Alternatively, you can rename them to have a `.cjs` extension instead of `.js`.

### Adding ESM files to an existing Hardhat project

It's also possible to write ESM scripts and tests without making your whole project ESM by default. To do this, just create your scripts and tests with an `.mjs` extension.

## ESM and TypeScript projects

At the moment, it's not possible to use ESM in TypeScript projects.

Hardhat uses [`ts-node`](https://typestrong.org/ts-node/) to run TypeScript projects, which in turn relies on Node's loader hooks. This is all experimental and the current functionality is not enough for Hardhat's needs.

If you need this feature, please let us know in [this issue](https://github.com/NomicFoundation/hardhat/issues/3385).

## Learn more

To learn more about ES modules in general, check these resources:

- [Node.js docs](https://nodejs.org/api/packages.html)
- [ES modules: A cartoon deep-dive](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)
- The [Modules chapter](https://exploringjs.com/impatient-js/ch_modules.html) of "JavaScript for impatient programmers"
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"@changesets/cli": "^2.16.0",
"prettier": "2.4.1",
"shelljs": "^0.8.5",
"typescript": "~4.5.2",
"typescript": "~4.7.4",
"wsrun": "^5.2.2"
},
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions packages/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"prettier": "2.4.1",
"rimraf": "^3.0.2",
"shelljs": "^0.8.5",
"ts-node": "^8.1.0",
"typescript": "~4.5.2"
"ts-node": "^10.8.0",
"typescript": "~4.7.4"
}
}
4 changes: 2 additions & 2 deletions packages/hardhat-chai-matchers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@
"mocha": "^10.0.0",
"prettier": "2.4.1",
"rimraf": "^3.0.2",
"ts-node": "^8.1.0",
"typescript": "~4.5.2"
"ts-node": "^10.8.0",
"typescript": "~4.7.4"
},
"peerDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/hardhat-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@
"rimraf": "^3.0.2",
"sinon": "^9.0.0",
"time-require": "^0.1.2",
"ts-node": "^8.1.0",
"typescript": "~4.5.2"
"ts-node": "^10.8.0",
"typescript": "~4.7.4"
},
"dependencies": {
"@ethersproject/abi": "^5.1.2",
Expand Down
11 changes: 11 additions & 0 deletions packages/hardhat-core/sample-projects/javascript-esm/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# License

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to [https://unlicense.org](https://unlicense.org)
13 changes: 13 additions & 0 deletions packages/hardhat-core/sample-projects/javascript-esm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Sample Hardhat Project

This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a script that deploys that contract.

Try running some of the following tasks:

```shell
npx hardhat help
npx hardhat test
REPORT_GAS=true npx hardhat test
npx hardhat node
npx hardhat run scripts/deploy.js
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

// Uncomment this line to use console.log
// import "hardhat/console.sol";

contract Lock {
uint public unlockTime;
address payable public owner;

event Withdrawal(uint amount, uint when);

constructor(uint _unlockTime) payable {
require(
block.timestamp < _unlockTime,
"Unlock time should be in the future"
);

unlockTime = _unlockTime;
owner = payable(msg.sender);
}

function withdraw() public {
// Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
// console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);

require(block.timestamp >= unlockTime, "You can't withdraw yet");
require(msg.sender == owner, "You aren't the owner");

emit Withdrawal(address(this).balance, block.timestamp);

owner.transfer(address(this).balance);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.17",
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat
// will compile your contracts, add the Hardhat Runtime Environment's members to the
// global scope, and execute the script.
import hre from "hardhat";

const currentTimestampInSeconds = Math.round(Date.now() / 1000);
const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
const unlockTime = currentTimestampInSeconds + ONE_YEAR_IN_SECS;

const lockedAmount = hre.ethers.utils.parseEther("1");

const Lock = await hre.ethers.getContractFactory("Lock");
const lock = await Lock.deploy(unlockTime, { value: lockedAmount });

await lock.deployed();

console.log(
`Lock with 1 ETH and unlock timestamp ${unlockTime} deployed to ${lock.address}`
);
Loading