Skip to content

Commit 8e22440

Browse files
authored
Merge pull request #4786 from NomicFoundation/slow-imports-fix
Fix slow imports eslint rule
2 parents 69d1bb3 + 5f62bf9 commit 8e22440

File tree

13 files changed

+62
-19
lines changed

13 files changed

+62
-19
lines changed

.changeset/curvy-cherries-beg.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@nomicfoundation/hardhat-chai-matchers": patch
3+
"hardhat": patch
4+
"@nomicfoundation/hardhat-viem": patch
5+
---
6+
7+
Improved loading performance

.github/workflows/test-slow-imports-rule.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
- name: Install
3333
run: pnpm install --frozen-lockfile --prefer-offline
3434
- name: Add slow import
35-
run: echo 'import "lodash";' >> src/internal/constants.ts
35+
run: echo 'import "lodash";' >> src/internal/core/config/config-resolution.ts
3636
- name: Build
3737
run: pnpm build
3838
- name: Run eslint

config/eslint/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module.exports.slowImportsCommonIgnoredModules = [
77
"source-map-support/register",
88
"@nomicfoundation/hardhat-ethers",
99
"hardhat/common",
10+
"hardhat/common/bigInt",
1011
"hardhat/config",
1112
"hardhat/plugins",
1213
"hardhat/types",

packages/eslint-plugin-slow-imports/lib/rules/no-top-level-external-import.js

+19-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"use strict";
66
const fs = require("node:fs");
77
const { isBuiltin } = require("node:module");
8-
const resolve = require("eslint-module-utils/resolve").default;
8+
const { relative } = require("eslint-module-utils/resolve");
99
const parse = require("eslint-module-utils/parse").default;
1010
const visit = require("eslint-module-utils/visit").default;
1111
const { visitModules } = require("../helpers/module-visitor");
@@ -45,6 +45,8 @@ module.exports = {
4545
messages: {
4646
ENFORCE_DYNAMIC_IMPORT:
4747
"This import transitively imports the slow dependency '{{dependency}}' in file '{{filename}}' at line {{line}}",
48+
CANNOT_RESOLVE_MODULE:
49+
"Unable to resolve the absolute module path. This is likely an error in the rule configuration or the file paths.",
4850
},
4951
},
5052

@@ -55,7 +57,7 @@ module.exports = {
5557
function visitor(filename, originalNode, node) {
5658
const modulePath = node.value;
5759
if (ignoreModules.has(modulePath) || isBuiltin(modulePath)) {
58-
return {};
60+
return;
5961
}
6062

6163
function detectTopLevelExternalDependency(path) {
@@ -80,10 +82,22 @@ module.exports = {
8082
}
8183

8284
if (!isExternalModule(modulePath)) {
83-
const absoluteModulePath = resolve(modulePath, context);
85+
const absoluteModulePath = relative(
86+
modulePath,
87+
filename,
88+
context.settings
89+
);
90+
91+
if (!absoluteModulePath) {
92+
context.report({
93+
node: originalNode,
94+
messageId: "CANNOT_RESOLVE_MODULE",
95+
});
96+
return;
97+
}
8498

85-
if (!absoluteModulePath || traversed.has(absoluteModulePath)) {
86-
return {};
99+
if (traversed.has(absoluteModulePath)) {
100+
return;
87101
}
88102

89103
traversed.add(absoluteModulePath);

packages/hardhat-chai-matchers/src/internal/addressable.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isAddress, isAddressable } from "ethers";
1+
import type EthersT from "ethers";
22

33
export function supportAddressable(
44
Assertion: Chai.AssertionStatic,
@@ -26,6 +26,7 @@ function override(
2626
// so we are looking for a sync way of getting the address. If an address was recovered, it is returned as a string,
2727
// otherwise undefined is returned.
2828
function tryGetAddressSync(value: any): string | undefined {
29+
const { isAddress, isAddressable } = require("ethers") as typeof EthersT;
2930
if (isAddress(value)) {
3031
return value;
3132
} else if (isAddressable(value)) {

packages/hardhat-chai-matchers/src/internal/changeEtherBalances.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type EthersT from "ethers";
22
import type { Addressable, BigNumberish, TransactionResponse } from "ethers";
33
import type { BalanceChangeOptions } from "./misc/balance";
4-
5-
import ordinal from "ordinal";
4+
import type OrdinalT from "ordinal";
65

76
import { buildAssert } from "../utils";
87
import { getAddressOf } from "./misc/account";
@@ -23,6 +22,7 @@ export function supportChangeEtherBalances(
2322
options?: BalanceChangeOptions
2423
) {
2524
const { toBigInt } = require("ethers") as typeof EthersT;
25+
const ordinal = require("ordinal") as typeof OrdinalT;
2626
// capture negated flag before async code executes; see buildAssert's jsdoc
2727
const negated = this.__flags.negate;
2828

packages/hardhat-chai-matchers/src/internal/emit.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type EthersT from "ethers";
22
import type { Contract, Transaction } from "ethers";
33
import type { AssertWithSsfi, Ssfi } from "../utils";
4+
import type OrdinalT from "ordinal";
45

56
import { AssertionError } from "chai";
67
import util from "util";
7-
import ordinal from "ordinal";
88

99
import { buildAssert } from "../utils";
1010
import { ASSERTION_ABORTED, EMIT_MATCHER } from "./constants";
@@ -156,6 +156,7 @@ function assertArgsArraysEqual(
156156
ssfi: Ssfi
157157
) {
158158
const ethers = require("ethers") as typeof EthersT;
159+
const ordinal = require("ordinal") as typeof OrdinalT;
159160
const parsedLog = (
160161
chaiUtils.flag(context, "contract").interface as Interface
161162
).parseLog(log);

packages/hardhat-chai-matchers/src/internal/reverted/revertedWithCustomError.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type EthersT from "ethers";
2+
import type OrdinalT from "ordinal";
23

34
import { AssertionError } from "chai";
4-
import ordinal from "ordinal";
55

66
import {
77
ASSERTION_ABORTED,
@@ -191,6 +191,7 @@ export async function revertedWithCustomErrorWithArgs(
191191
expectedArgs: any[],
192192
ssfi: Ssfi
193193
) {
194+
const ordinal = require("ordinal") as typeof OrdinalT;
194195
const negated = false; // .withArgs cannot be negated
195196
const assert = buildAssert(negated, ssfi);
196197

packages/hardhat-core/src/internal/core/config/config-loading.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { getUserConfigPath } from "../project-structure";
2020

2121
import { SUPPORTED_SOLIDITY_VERSION_RANGE } from "../../hardhat-network/stack-traces/constants";
2222
import { resolveConfig } from "./config-resolution";
23-
import { validateConfig, validateResolvedConfig } from "./config-validation";
2423
import { DEFAULT_SOLC_VERSION } from "./default-config";
2524

2625
const log = debug("hardhat:core:config");
@@ -68,6 +67,8 @@ export function loadConfigAndTasks(
6867
showSolidityConfigWarnings: false,
6968
}
7069
): { resolvedConfig: HardhatConfig; userConfig: HardhatUserConfig } {
70+
const { validateConfig, validateResolvedConfig } =
71+
require("./config-validation") as typeof import("./config-validation");
7172
let configPath =
7273
hardhatArguments !== undefined ? hardhatArguments.config : undefined;
7374

packages/hardhat-core/src/internal/core/config/config-resolution.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import cloneDeep from "lodash/cloneDeep";
1+
import type { LoDashStatic } from "lodash";
2+
23
import path from "path";
34
import semver from "semver";
45

@@ -62,6 +63,7 @@ export function resolveConfig(
6263
userConfigPath: string,
6364
userConfig: HardhatUserConfig
6465
): HardhatConfig {
66+
const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
6567
userConfig = cloneDeep(userConfig);
6668

6769
return {
@@ -77,6 +79,7 @@ export function resolveConfig(
7779
function resolveNetworksConfig(
7880
networksConfig: NetworksUserConfig = {}
7981
): NetworksConfig {
82+
const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
8083
const hardhatNetworkConfig = networksConfig[HARDHAT_NETWORK_NAME];
8184

8285
const localhostNetworkConfig =
@@ -128,6 +131,7 @@ function normalizeHexString(str: string): string {
128131
function resolveHardhatNetworkConfig(
129132
hardhatNetworkConfig: HardhatNetworkUserConfig = {}
130133
): HardhatNetworkConfig {
134+
const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
131135
const clonedDefaultHardhatNetworkParams = cloneDeep(
132136
defaultHardhatNetworkParams
133137
);
@@ -246,6 +250,7 @@ function isHdAccountsConfig(
246250
function resolveHttpNetworkConfig(
247251
networkConfig: HttpNetworkUserConfig
248252
): HttpNetworkConfig {
253+
const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
249254
const accounts: HttpNetworkAccountsConfig =
250255
networkConfig.accounts === undefined
251256
? defaultHttpNetworkParams.accounts
@@ -427,6 +432,7 @@ function resolveCompiler(compiler: SolcUserConfig): SolcConfig {
427432
}
428433

429434
function resolveMochaConfig(userConfig: HardhatUserConfig): Mocha.MochaOptions {
435+
const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
430436
return {
431437
...cloneDeep(defaultMochaOptions),
432438
...userConfig.mocha,

packages/hardhat-core/src/internal/core/config/config-validation.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import type { HardhatConfig as HardhatConfigT } from "../../../types";
2+
import type {
3+
Context,
4+
ValidationError,
5+
getFunctionName as getFunctionNameT,
6+
} from "io-ts/lib";
7+
import type { Reporter } from "io-ts/lib/Reporter";
28

39
import * as t from "io-ts";
4-
import { Context, getFunctionName, ValidationError } from "io-ts/lib";
5-
import { Reporter } from "io-ts/lib/Reporter";
610

711
import {
812
HARDHAT_MEMPOOL_SUPPORTED_ORDERS,
@@ -19,6 +23,9 @@ import { defaultHardhatNetworkParams } from "./default-config";
1923

2024
function stringify(v: any): string {
2125
if (typeof v === "function") {
26+
const { getFunctionName } = require("io-ts/lib") as {
27+
getFunctionName: typeof getFunctionNameT;
28+
};
2229
return getFunctionName(v);
2330
}
2431
if (typeof v === "number" && !isFinite(v)) {

packages/hardhat-core/src/internal/sentry/transport.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Event, Response } from "@sentry/node";
1+
import type { Event, Response } from "@sentry/node";
22
import { spawn } from "child_process";
33
import * as path from "path";
44

packages/hardhat-viem/src/internal/clients.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ import type {
1313
WalletClient,
1414
} from "../types";
1515

16-
import { getChain, getMode, isDevelopmentNetwork } from "./chains";
17-
import { getAccounts } from "./accounts";
18-
1916
/**
2017
* Get a PublicClient instance. This is a read-only client that can be used to
2118
* query the blockchain.
@@ -28,6 +25,7 @@ export async function getPublicClient(
2825
provider: EthereumProvider,
2926
publicClientConfig?: Partial<PublicClientConfig>
3027
): Promise<PublicClient> {
28+
const { getChain } = await import("./chains");
3129
const chain = publicClientConfig?.chain ?? (await getChain(provider));
3230
return innerGetPublicClient(provider, chain, publicClientConfig);
3331
}
@@ -38,6 +36,7 @@ export async function innerGetPublicClient(
3836
publicClientConfig?: Partial<PublicClientConfig>
3937
): Promise<PublicClient> {
4038
const viem = await import("viem");
39+
const { isDevelopmentNetwork } = await import("./chains");
4140
const defaultParameters = isDevelopmentNetwork(chain.id)
4241
? { pollingInterval: 50, cacheTime: 0 }
4342
: {};
@@ -65,6 +64,8 @@ export async function getWalletClients(
6564
provider: EthereumProvider,
6665
walletClientConfig?: Partial<WalletClientConfig>
6766
): Promise<WalletClient[]> {
67+
const { getAccounts } = await import("./accounts");
68+
const { getChain } = await import("./chains");
6869
const chain = walletClientConfig?.chain ?? (await getChain(provider));
6970
const accounts = await getAccounts(provider);
7071
return innerGetWalletClients(provider, chain, accounts, walletClientConfig);
@@ -77,6 +78,7 @@ export async function innerGetWalletClients(
7778
walletClientConfig?: Partial<WalletClientConfig>
7879
): Promise<WalletClient[]> {
7980
const viem = await import("viem");
81+
const { isDevelopmentNetwork } = await import("./chains");
8082
const defaultParameters = isDevelopmentNetwork(chain.id)
8183
? { pollingInterval: 50, cacheTime: 0 }
8284
: {};
@@ -108,6 +110,7 @@ export async function getWalletClient(
108110
address: Address,
109111
walletClientConfig?: Partial<WalletClientConfig>
110112
): Promise<WalletClient> {
113+
const { getChain } = await import("./chains");
111114
const chain = walletClientConfig?.chain ?? (await getChain(provider));
112115
return (
113116
await innerGetWalletClients(provider, chain, [address], walletClientConfig)
@@ -126,6 +129,7 @@ export async function getTestClient(
126129
provider: EthereumProvider,
127130
testClientConfig?: Partial<TestClientConfig>
128131
): Promise<TestClient> {
132+
const { getChain, getMode } = await import("./chains");
129133
const chain = testClientConfig?.chain ?? (await getChain(provider));
130134
const mode = await getMode(provider);
131135
return innerGetTestClient(provider, chain, mode, testClientConfig);

0 commit comments

Comments
 (0)