Comparing version 2.19.0 to 2.19.2
@@ -336,4 +336,5 @@ "use strict"; | ||
const downloader = downloader_1.CompilerDownloader.getConcurrencySafeDownloader(compilerPlatform, compilersCache); | ||
const isCompilerDownloaded = await downloader.isCompilerDownloaded(solcVersion); | ||
if (!isCompilerDownloaded) { | ||
await downloader.downloadCompiler(solcVersion, | ||
// callback called before compiler download | ||
async (isCompilerDownloaded) => { | ||
await run(task_names_1.TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_START, { | ||
@@ -344,3 +345,5 @@ solcVersion, | ||
}); | ||
await downloader.downloadCompiler(solcVersion); | ||
}, | ||
// callback called after compiler download | ||
async (isCompilerDownloaded) => { | ||
await run(task_names_1.TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_END, { | ||
@@ -351,3 +354,3 @@ solcVersion, | ||
}); | ||
} | ||
}); | ||
const compiler = await downloader.getCompiler(solcVersion); | ||
@@ -359,4 +362,4 @@ if (compiler !== undefined) { | ||
const wasmDownloader = downloader_1.CompilerDownloader.getConcurrencySafeDownloader(downloader_1.CompilerPlatform.WASM, compilersCache); | ||
const isWasmCompilerDownloader = await wasmDownloader.isCompilerDownloaded(solcVersion); | ||
if (!isWasmCompilerDownloader) { | ||
await wasmDownloader.downloadCompiler(solcVersion, async (isCompilerDownloaded) => { | ||
// callback called before compiler download | ||
await run(task_names_1.TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_START, { | ||
@@ -367,3 +370,5 @@ solcVersion, | ||
}); | ||
await wasmDownloader.downloadCompiler(solcVersion); | ||
}, | ||
// callback called after compiler download | ||
async (isCompilerDownloaded) => { | ||
await run(task_names_1.TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_END, { | ||
@@ -374,3 +379,3 @@ solcVersion, | ||
}); | ||
} | ||
}); | ||
const wasmCompiler = await wasmDownloader.getCompiler(solcVersion); | ||
@@ -377,0 +382,0 @@ (0, errors_1.assertHardhatInvariant)(wasmCompiler !== undefined, `WASM build of solc ${solcVersion} isn't working`); |
@@ -49,7 +49,7 @@ "use strict"; | ||
} | ||
const jsFiles = await (0, fs_utils_1.getAllFilesMatching)(config.paths.tests, (f) => f.endsWith(".js") || f.endsWith(".cjs") || f.endsWith(".mjs")); | ||
const jsFiles = await (0, fs_utils_1.getAllFilesMatching)(config.paths.tests, typescript_support_1.isJavascriptFile); | ||
if (!(0, typescript_support_1.isRunningWithTypescript)(config)) { | ||
return jsFiles; | ||
} | ||
const tsFiles = await (0, fs_utils_1.getAllFilesMatching)(config.paths.tests, (f) => f.endsWith(".ts")); | ||
const tsFiles = await (0, fs_utils_1.getAllFilesMatching)(config.paths.tests, typescript_support_1.isTypescriptFile); | ||
return [...jsFiles, ...tsFiles]; | ||
@@ -56,0 +56,0 @@ }); |
@@ -141,2 +141,3 @@ import { Artifact, Artifacts as IArtifacts, BuildInfo, CompilerInput, CompilerOutput } from "../types"; | ||
private _getBuildInfoFromDebugFileSync; | ||
private _isArtifactPath; | ||
} | ||
@@ -143,0 +144,0 @@ /** |
@@ -113,6 +113,3 @@ "use strict"; | ||
} | ||
const buildInfosDir = path.join(this._artifactsPath, constants_1.BUILD_INFO_DIR_NAME); | ||
const paths = await (0, fs_utils_1.getAllFilesMatching)(this._artifactsPath, (f) => f.endsWith(".json") && | ||
!f.startsWith(buildInfosDir) && | ||
!f.endsWith(".dbg.json")); | ||
const paths = await (0, fs_utils_1.getAllFilesMatching)(this._artifactsPath, (f) => this._isArtifactPath(f)); | ||
const result = paths.sort(); | ||
@@ -385,6 +382,3 @@ if (this._cache !== undefined) { | ||
} | ||
const buildInfosDir = path.join(this._artifactsPath, constants_1.BUILD_INFO_DIR_NAME); | ||
const paths = (0, fs_utils_1.getAllFilesMatchingSync)(this._artifactsPath, (f) => f.endsWith(".json") && | ||
!f.startsWith(buildInfosDir) && | ||
!f.endsWith(".dbg.json")); | ||
const paths = (0, fs_utils_1.getAllFilesMatchingSync)(this._artifactsPath, (f) => this._isArtifactPath(f)); | ||
const result = paths.sort(); | ||
@@ -649,2 +643,8 @@ if (this._cache !== undefined) { | ||
} | ||
_isArtifactPath(file) { | ||
return (file.endsWith(".json") && | ||
file !== path.join(this._artifactsPath, "package.json") && | ||
!file.startsWith(path.join(this._artifactsPath, constants_1.BUILD_INFO_DIR_NAME)) && | ||
!file.endsWith(".dbg.json")); | ||
} | ||
} | ||
@@ -651,0 +651,0 @@ exports.Artifacts = Artifacts; |
@@ -45,6 +45,6 @@ "use strict"; | ||
} | ||
const { networks, tasks } = completionData; | ||
const { networks, tasks, scopes } = completionData; | ||
const words = line.split(/\s+/).filter((x) => x.length > 0); | ||
const wordsBeforeCursor = line.slice(0, point).split(/\s+/); | ||
// examples: | ||
// 'prev' and 'last' variables examples: | ||
// `hh compile --network|` => prev: "compile" last: "--network" | ||
@@ -61,26 +61,56 @@ // `hh compile --network |` => prev: "--network" last: "" | ||
.filter((x) => !words.includes(x.name)); | ||
// check if the user entered a task | ||
let task; | ||
// Get the task or scope if the user has entered one | ||
let taskName; | ||
let scopeName; | ||
let index = 1; | ||
while (index < words.length) { | ||
if (isGlobalFlag(words[index])) { | ||
const word = words[index]; | ||
if (isGlobalFlag(word)) { | ||
index += 1; | ||
} | ||
else if (isGlobalParam(words[index])) { | ||
else if (isGlobalParam(word)) { | ||
index += 2; | ||
} | ||
else if (words[index].startsWith("--")) { | ||
else if (word.startsWith("--")) { | ||
index += 1; | ||
} | ||
else { | ||
task = words[index]; | ||
break; | ||
// Possible scenarios: | ||
// - no task or scope: `hh ` | ||
// - only a task: `hh task ` | ||
// - only a scope: `hh scope ` | ||
// - both a scope and a task (the task always follow the scope): `hh scope task ` | ||
// Between a scope and a task there could be other words, e.g.: `hh scope --flag task ` | ||
if (scopeName === undefined) { | ||
if (tasks[word] !== undefined) { | ||
taskName = word; | ||
break; | ||
} | ||
else if (scopes[word] !== undefined) { | ||
scopeName = word; | ||
} | ||
} | ||
else { | ||
taskName = word; | ||
break; | ||
} | ||
index += 1; | ||
} | ||
} | ||
// if a task was found but it's equal to the last word, it means | ||
// that the cursor is after the task, we ignore the task in this | ||
// case because if you have a task `foo` and `foobar` and the | ||
// line is: `hh foo|`, we want tasks to be suggested | ||
if (task === last) { | ||
task = undefined; | ||
// If a task or a scope is found and it is equal to the last word, | ||
// this indicates that the cursor is positioned after the task or scope. | ||
// In this case, we ignore the task or scope. For instance, if you have a task or a scope named 'foo' and 'foobar', | ||
// and the line is 'hh foo|', we want to suggest the value for 'foo' and 'foobar'. | ||
// Possible scenarios: | ||
// - no task or scope: `hh ` -> task and scope already undefined | ||
// - only a task: `hh task ` -> task set to undefined, scope already undefined | ||
// - only a scope: `hh scope ` -> scope set to undefined, task already undefined | ||
// - both a scope and a task (the task always follow the scope): `hh scope task ` -> task set to undefined, scope stays defined | ||
if (taskName === last || scopeName === last) { | ||
if (taskName !== undefined && scopeName !== undefined) { | ||
[taskName, scopeName] = [undefined, scopeName]; | ||
} | ||
else { | ||
[taskName, scopeName] = [undefined, undefined]; | ||
} | ||
} | ||
@@ -93,2 +123,8 @@ if (prev === "--network") { | ||
} | ||
const scopeDefinition = scopeName === undefined ? undefined : scopes[scopeName]; | ||
const taskDefinition = taskName === undefined | ||
? undefined | ||
: scopeDefinition === undefined | ||
? tasks[taskName] | ||
: scopeDefinition.tasks[taskName]; | ||
// if the previous word is a param, then a value is expected | ||
@@ -102,4 +138,3 @@ // we don't complete anything here | ||
} | ||
const isTaskParam = task !== undefined && | ||
tasks[task]?.paramDefinitions[paramName]?.isFlag === false; | ||
const isTaskParam = taskDefinition?.paramDefinitions[paramName]?.isFlag === false; | ||
if (isTaskParam) { | ||
@@ -109,4 +144,7 @@ return exports.HARDHAT_COMPLETE_FILES; | ||
} | ||
// if there's no task, we complete either tasks or params | ||
if (task === undefined || tasks[task] === undefined) { | ||
// If there's no task or scope, we complete either tasks and scopes or params | ||
if (taskDefinition === undefined && scopeDefinition === undefined) { | ||
if (last.startsWith("-")) { | ||
return coreParams.filter((param) => startsWithLast(param.name)); | ||
} | ||
const taskSuggestions = Object.values(tasks) | ||
@@ -118,17 +156,31 @@ .filter((x) => !x.isSubtask) | ||
})); | ||
if (last.startsWith("-")) { | ||
return coreParams.filter((param) => startsWithLast(param.name)); | ||
} | ||
return taskSuggestions.filter((x) => startsWithLast(x.name)); | ||
const scopeSuggestions = Object.values(scopes).map((x) => ({ | ||
name: x.name, | ||
description: x.description, | ||
})); | ||
return taskSuggestions | ||
.concat(scopeSuggestions) | ||
.filter((x) => startsWithLast(x.name)); | ||
} | ||
// If there's a scope but not a task, we complete with the scopes'tasks | ||
if (taskDefinition === undefined && scopeDefinition !== undefined) { | ||
return Object.values(scopes[scopeName].tasks) | ||
.filter((x) => !x.isSubtask) | ||
.map((x) => ({ | ||
name: x.name, | ||
description: x.description, | ||
})) | ||
.filter((x) => startsWithLast(x.name)); | ||
} | ||
if (!last.startsWith("-")) { | ||
return exports.HARDHAT_COMPLETE_FILES; | ||
} | ||
// if there's a task and the last word starts with -, we complete its params and the global params | ||
const taskParams = Object.values(tasks[task].paramDefinitions) | ||
.map((param) => ({ | ||
name: ArgumentsParser_1.ArgumentsParser.paramNameToCLA(param.name), | ||
description: param.description, | ||
})) | ||
.filter((x) => !words.includes(x.name)); | ||
const taskParams = taskDefinition === undefined | ||
? [] | ||
: Object.values(taskDefinition.paramDefinitions) | ||
.map((param) => ({ | ||
name: ArgumentsParser_1.ArgumentsParser.paramNameToCLA(param.name), | ||
description: param.description, | ||
})) | ||
.filter((x) => !words.includes(x.name)); | ||
return [...taskParams, ...coreParams].filter((suggestion) => startsWithLast(suggestion.name)); | ||
@@ -163,11 +215,7 @@ } | ||
// is serializable and to avoid saving unnecessary things from the HRE | ||
const tasks = (0, lang_1.mapValues)(hre.tasks, (task) => ({ | ||
name: task.name, | ||
description: task.description ?? "", | ||
isSubtask: task.isSubtask, | ||
paramDefinitions: (0, lang_1.mapValues)(task.paramDefinitions, (paramDefinition) => ({ | ||
name: paramDefinition.name, | ||
description: paramDefinition.description ?? "", | ||
isFlag: paramDefinition.isFlag, | ||
})), | ||
const tasks = (0, lang_1.mapValues)(hre.tasks, (task) => getTaskFromTaskDefinition(task)); | ||
const scopes = (0, lang_1.mapValues)(hre.scopes, (scope) => ({ | ||
name: scope.name, | ||
description: scope.description ?? "", | ||
tasks: (0, lang_1.mapValues)(scope.tasks, (task) => getTaskFromTaskDefinition(task)), | ||
})); | ||
@@ -177,2 +225,3 @@ const completionData = { | ||
tasks, | ||
scopes, | ||
}; | ||
@@ -182,2 +231,14 @@ await saveCachedCompletionData(projectId, completionData, mtimes); | ||
} | ||
function getTaskFromTaskDefinition(taskDef) { | ||
return { | ||
name: taskDef.name, | ||
description: taskDef.description ?? "", | ||
isSubtask: taskDef.isSubtask, | ||
paramDefinitions: (0, lang_1.mapValues)(taskDef.paramDefinitions, (paramDefinition) => ({ | ||
name: paramDefinition.name, | ||
description: paramDefinition.description ?? "", | ||
isFlag: paramDefinition.isFlag, | ||
})), | ||
}; | ||
} | ||
function getProjectId() { | ||
@@ -184,0 +245,0 @@ const packageJsonPath = find_up_1.default.sync("package.json"); |
@@ -56,6 +56,6 @@ "use strict"; | ||
const ETHERS_PROJECT_DEPENDENCIES = { | ||
"@nomicfoundation/hardhat-toolbox": "^3.0.0", | ||
"@nomicfoundation/hardhat-toolbox": "^4.0.0", | ||
}; | ||
const VIEM_PROJECT_DEPENDENCIES = { | ||
"@nomicfoundation/hardhat-toolbox-viem": "^1.0.0", | ||
"@nomicfoundation/hardhat-toolbox-viem": "^2.0.0", | ||
}; | ||
@@ -65,3 +65,3 @@ const PEER_DEPENDENCIES = { | ||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0", | ||
"@nomicfoundation/hardhat-verify": "^1.0.0", | ||
"@nomicfoundation/hardhat-verify": "^2.0.0", | ||
chai: "^4.2.0", | ||
@@ -75,5 +75,5 @@ "hardhat-gas-reporter": "^1.0.8", | ||
ethers: "^6.4.0", | ||
"@typechain/hardhat": "^8.0.0", | ||
typechain: "^8.1.0", | ||
"@typechain/ethers-v6": "^0.4.0", | ||
"@typechain/hardhat": "^9.0.0", | ||
typechain: "^8.3.0", | ||
"@typechain/ethers-v6": "^0.5.0", | ||
}; | ||
@@ -80,0 +80,0 @@ const VIEM_PEER_DEPENDENCIES = { |
@@ -162,3 +162,3 @@ "use strict"; | ||
for (const [peerDependency, version] of Object.entries(peerDependencies)) { | ||
const peerDependencyPackageJson = readPackageJson(peerDependency); | ||
const peerDependencyPackageJson = readPackageJson(peerDependency, configPath); | ||
if (peerDependencyPackageJson === undefined) { | ||
@@ -180,5 +180,8 @@ missingPeerDependencies[peerDependency] = version; | ||
exports.analyzeModuleNotFoundError = analyzeModuleNotFoundError; | ||
function readPackageJson(packageName) { | ||
function readPackageJson(packageName, configPath) { | ||
const resolve = require("resolve"); | ||
try { | ||
const packageJsonPath = require.resolve(path_1.default.join(packageName, "package.json")); | ||
const packageJsonPath = resolve.sync(path_1.default.join(packageName, "package.json"), { | ||
basedir: path_1.default.dirname(configPath), | ||
}); | ||
return require(packageJsonPath); | ||
@@ -185,0 +188,0 @@ } |
@@ -16,4 +16,6 @@ "use strict"; | ||
const TS_CONFIG_FILENAME = "hardhat.config.ts"; | ||
const CTS_CONFIG_FILENAME = "hardhat.config.cts"; | ||
function isCwdInsideProject() { | ||
return (find_up_1.default.sync(TS_CONFIG_FILENAME) !== null || | ||
find_up_1.default.sync(CTS_CONFIG_FILENAME) !== null || | ||
find_up_1.default.sync(CJS_CONFIG_FILENAME) !== null || | ||
@@ -28,2 +30,6 @@ find_up_1.default.sync(JS_CONFIG_FILENAME) !== null); | ||
} | ||
const ctsConfigPath = find_up_1.default.sync(CTS_CONFIG_FILENAME); | ||
if (ctsConfigPath !== null) { | ||
return ctsConfigPath; | ||
} | ||
const cjsConfigPath = find_up_1.default.sync(CJS_CONFIG_FILENAME); | ||
@@ -30,0 +36,0 @@ if (cjsConfigPath !== null) { |
@@ -181,2 +181,16 @@ "use strict"; | ||
})); | ||
let maxPriorityFeePerGas = (0, base_types_1.rpcQuantityToBigInt)(response.reward[0][0]); | ||
if (maxPriorityFeePerGas === 0n) { | ||
try { | ||
const suggestedMaxPriorityFeePerGas = (await this._wrappedProvider.request({ | ||
method: "eth_maxPriorityFeePerGas", | ||
params: [], | ||
})); | ||
maxPriorityFeePerGas = (0, base_types_1.rpcQuantityToBigInt)(suggestedMaxPriorityFeePerGas); | ||
} | ||
catch { | ||
// if eth_maxPriorityFeePerGas does not exist, use 1 wei | ||
maxPriorityFeePerGas = 1n; | ||
} | ||
} | ||
return { | ||
@@ -193,3 +207,3 @@ // Each block increases the base fee by 1/8 at most, when full. | ||
1n), | ||
maxPriorityFeePerGas: (0, base_types_1.rpcQuantityToBigInt)(response.reward[0][0]), | ||
maxPriorityFeePerGas, | ||
}; | ||
@@ -196,0 +210,0 @@ } |
@@ -13,2 +13,4 @@ import { HardhatConfig } from "../../types"; | ||
export declare function loadTsNode(tsConfigPath?: string, shouldTypecheck?: boolean): void; | ||
export declare function isTypescriptFile(path: string): boolean; | ||
export declare function isJavascriptFile(path: string): boolean; | ||
//# sourceMappingURL=typescript-support.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.loadTsNode = exports.isTypescriptSupported = exports.isRunningWithTypescript = exports.willRunWithTypescript = void 0; | ||
exports.isJavascriptFile = exports.isTypescriptFile = exports.loadTsNode = exports.isTypescriptSupported = exports.isRunningWithTypescript = exports.willRunWithTypescript = void 0; | ||
const config_loading_1 = require("./config/config-loading"); | ||
@@ -15,3 +15,3 @@ const errors_1 = require("./errors"); | ||
const config = (0, config_loading_1.resolveConfigPath)(configPath); | ||
return isTypescriptFile(config); | ||
return isNonEsmTypescriptFile(config); | ||
} | ||
@@ -23,3 +23,3 @@ exports.willRunWithTypescript = willRunWithTypescript; | ||
function isRunningWithTypescript(config) { | ||
return isTypescriptFile(config.paths.configFile); | ||
return isNonEsmTypescriptFile(config.paths.configFile); | ||
} | ||
@@ -76,5 +76,13 @@ exports.isRunningWithTypescript = isRunningWithTypescript; | ||
exports.loadTsNode = loadTsNode; | ||
function isNonEsmTypescriptFile(path) { | ||
return /\.(ts|cts)$/i.test(path); | ||
} | ||
function isTypescriptFile(path) { | ||
return path.endsWith(".ts"); | ||
return /\.(ts|cts|mts)$/i.test(path); | ||
} | ||
exports.isTypescriptFile = isTypescriptFile; | ||
function isJavascriptFile(path) { | ||
return /\.(js|cjs|mjs)$/i.test(path); | ||
} | ||
exports.isJavascriptFile = isJavascriptFile; | ||
//# sourceMappingURL=typescript-support.js.map |
@@ -29,3 +29,3 @@ import { download } from "../../util/download"; | ||
*/ | ||
downloadCompiler(version: string): Promise<void>; | ||
downloadCompiler(version: string, downloadStartedCb: (isCompilerDownloaded: boolean) => Promise<any>, downloadEndedCb: (isCompilerDownloaded: boolean) => Promise<any>): Promise<void>; | ||
/** | ||
@@ -65,3 +65,3 @@ * Returns the compiler, which MUST be downloaded before calling this function. | ||
isCompilerDownloaded(version: string): Promise<boolean>; | ||
downloadCompiler(version: string): Promise<void>; | ||
downloadCompiler(version: string, downloadStartedCb: (isCompilerDownloaded: boolean) => Promise<any>, downloadEndedCb: (isCompilerDownloaded: boolean) => Promise<any>): Promise<void>; | ||
getCompiler(version: string): Promise<Compiler | undefined>; | ||
@@ -68,0 +68,0 @@ private _getCompilerBuild; |
@@ -103,4 +103,13 @@ "use strict"; | ||
} | ||
async downloadCompiler(version) { | ||
async downloadCompiler(version, downloadStartedCb, downloadEndedCb) { | ||
// Since only one process at a time can acquire the mutex, we avoid the risk of downloading the same compiler multiple times. | ||
// This is because the mutex blocks access until a compiler has been fully downloaded, preventing any new process | ||
// from checking whether that version of the compiler exists. Without mutex it might incorrectly | ||
// return false, indicating that the compiler isn't present, even though it is currently being downloaded. | ||
await this._mutex.use(async () => { | ||
const isCompilerDownloaded = await this.isCompilerDownloaded(version); | ||
if (isCompilerDownloaded === true) { | ||
return; | ||
} | ||
await downloadStartedCb(isCompilerDownloaded); | ||
let build = await this._getCompilerBuild(version); | ||
@@ -135,2 +144,3 @@ if (build === undefined && (await this._shouldDownloadCompilerList())) { | ||
await this._postProcessCompilerDownload(build, downloadPath); | ||
await downloadEndedCb(isCompilerDownloaded); | ||
}); | ||
@@ -137,0 +147,0 @@ } |
{ | ||
"name": "hardhat", | ||
"version": "2.19.0", | ||
"version": "2.19.2", | ||
"author": "Nomic Labs LLC", | ||
@@ -25,16 +25,2 @@ "license": "MIT", | ||
}, | ||
"scripts": { | ||
"lint": "yarn prettier --check && yarn eslint", | ||
"lint:fix": "yarn prettier --write && yarn eslint --fix", | ||
"eslint": "eslint 'src/**/*.ts' 'test/**/*.ts'", | ||
"prettier": "prettier \"**/*.{js,md,json}\"", | ||
"pretest": "cd ../.. && yarn build", | ||
"test": "mocha --recursive \"test/**/*.ts\"", | ||
"test:except-tracing": "mocha --recursive \"test/**/*.ts\" --invert --grep \"Stack traces\"", | ||
"test:tracing": "mocha --recursive \"test/internal/hardhat-network/{helpers,stack-traces}/**/*.ts\"", | ||
"test:forking": "mocha --recursive \"test/internal/hardhat-network/{helpers,jsonrpc,provider}/**/*.ts\"", | ||
"build": "tsc --build .", | ||
"prepublishOnly": "yarn build", | ||
"clean": "rimraf builtin-tasks internal types utils *.d.ts *.map *.js build-test tsconfig.tsbuildinfo test/internal/hardhat-network/provider/.hardhat_node_test_cache test/internal/hardhat-network/stack-traces/test-files/artifacts" | ||
}, | ||
"files": [ | ||
@@ -58,4 +44,2 @@ "builtin-tasks/", | ||
"devDependencies": { | ||
"@nomicfoundation/eslint-plugin-hardhat-internal-rules": "^1.0.0", | ||
"@nomicfoundation/eslint-plugin-slow-imports": "^1.0.0", | ||
"@types/async-eventemitter": "^0.2.1", | ||
@@ -98,3 +82,5 @@ "@types/bn.js": "^5.1.0", | ||
"ts-node": "^10.8.0", | ||
"typescript": "~5.0.0" | ||
"typescript": "~5.0.0", | ||
"@nomicfoundation/eslint-plugin-hardhat-internal-rules": "^1.0.2", | ||
"@nomicfoundation/eslint-plugin-slow-imports": "^1.0.0" | ||
}, | ||
@@ -185,3 +171,16 @@ "dependencies": { | ||
"all": true | ||
}, | ||
"scripts": { | ||
"lint": "pnpm prettier --check && pnpm eslint", | ||
"lint:fix": "pnpm prettier --write && pnpm eslint --fix", | ||
"eslint": "eslint 'src/**/*.ts' 'test/**/*.ts'", | ||
"prettier": "prettier \"**/*.{js,md,json}\"", | ||
"pretest": "cd ../.. && pnpm build", | ||
"test": "mocha --recursive \"test/**/*.ts\"", | ||
"test:except-tracing": "mocha --recursive \"test/**/*.ts\" --invert --grep \"Stack traces\"", | ||
"test:tracing": "mocha --recursive \"test/internal/hardhat-network/{helpers,stack-traces}/**/*.ts\"", | ||
"test:forking": "mocha --recursive \"test/internal/hardhat-network/{helpers,jsonrpc,provider}/**/*.ts\"", | ||
"build": "tsc --build .", | ||
"clean": "rimraf builtin-tasks internal types utils *.d.ts *.map *.js build-test tsconfig.tsbuildinfo test/internal/hardhat-network/provider/.hardhat_node_test_cache test/internal/hardhat-network/stack-traces/test-files/artifacts" | ||
} | ||
} | ||
} |
node_modules | ||
.env | ||
coverage | ||
coverage.json | ||
typechain | ||
typechain-types | ||
# Hardhat files | ||
cache | ||
artifacts | ||
/cache | ||
/artifacts | ||
# TypeChain files | ||
/typechain | ||
/typechain-types | ||
# solidity-coverage files | ||
/coverage | ||
/coverage.json |
@@ -583,22 +583,22 @@ import os from "os"; | ||
const isCompilerDownloaded = await downloader.isCompilerDownloaded( | ||
solcVersion | ||
await downloader.downloadCompiler( | ||
solcVersion, | ||
// callback called before compiler download | ||
async (isCompilerDownloaded: boolean) => { | ||
await run(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_START, { | ||
solcVersion, | ||
isCompilerDownloaded, | ||
quiet, | ||
}); | ||
}, | ||
// callback called after compiler download | ||
async (isCompilerDownloaded: boolean) => { | ||
await run(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_END, { | ||
solcVersion, | ||
isCompilerDownloaded, | ||
quiet, | ||
}); | ||
} | ||
); | ||
if (!isCompilerDownloaded) { | ||
await run(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_START, { | ||
solcVersion, | ||
isCompilerDownloaded, | ||
quiet, | ||
}); | ||
await downloader.downloadCompiler(solcVersion); | ||
await run(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_END, { | ||
solcVersion, | ||
isCompilerDownloaded, | ||
quiet, | ||
}); | ||
} | ||
const compiler = await downloader.getCompiler(solcVersion); | ||
@@ -619,21 +619,22 @@ | ||
const isWasmCompilerDownloader = | ||
await wasmDownloader.isCompilerDownloaded(solcVersion); | ||
await wasmDownloader.downloadCompiler( | ||
solcVersion, | ||
async (isCompilerDownloaded: boolean) => { | ||
// callback called before compiler download | ||
await run(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_START, { | ||
solcVersion, | ||
isCompilerDownloaded, | ||
quiet, | ||
}); | ||
}, | ||
// callback called after compiler download | ||
async (isCompilerDownloaded: boolean) => { | ||
await run(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_END, { | ||
solcVersion, | ||
isCompilerDownloaded, | ||
quiet, | ||
}); | ||
} | ||
); | ||
if (!isWasmCompilerDownloader) { | ||
await run(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_START, { | ||
solcVersion, | ||
isCompilerDownloaded, | ||
quiet, | ||
}); | ||
await wasmDownloader.downloadCompiler(solcVersion); | ||
await run(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_END, { | ||
solcVersion, | ||
isCompilerDownloaded, | ||
quiet, | ||
}); | ||
} | ||
const wasmCompiler = await wasmDownloader.getCompiler(solcVersion); | ||
@@ -640,0 +641,0 @@ |
@@ -10,3 +10,7 @@ import type { MochaOptions } from "mocha"; | ||
import { ERRORS } from "../internal/core/errors-list"; | ||
import { isRunningWithTypescript } from "../internal/core/typescript-support"; | ||
import { | ||
isJavascriptFile, | ||
isRunningWithTypescript, | ||
isTypescriptFile, | ||
} from "../internal/core/typescript-support"; | ||
import { getForkCacheDirPath } from "../internal/hardhat-network/provider/utils/disk-cache"; | ||
@@ -44,3 +48,3 @@ import { showForkRecommendationsBannerIfNecessary } from "../internal/hardhat-network/provider/utils/fork-recomendations-banner"; | ||
config.paths.tests, | ||
(f) => f.endsWith(".js") || f.endsWith(".cjs") || f.endsWith(".mjs") | ||
isJavascriptFile | ||
); | ||
@@ -52,4 +56,5 @@ | ||
const tsFiles = await getAllFilesMatching(config.paths.tests, (f) => | ||
f.endsWith(".ts") | ||
const tsFiles = await getAllFilesMatching( | ||
config.paths.tests, | ||
isTypescriptFile | ||
); | ||
@@ -56,0 +61,0 @@ |
@@ -157,10 +157,4 @@ import debug from "debug"; | ||
const buildInfosDir = path.join(this._artifactsPath, BUILD_INFO_DIR_NAME); | ||
const paths = await getAllFilesMatching( | ||
this._artifactsPath, | ||
(f) => | ||
f.endsWith(".json") && | ||
!f.startsWith(buildInfosDir) && | ||
!f.endsWith(".dbg.json") | ||
const paths = await getAllFilesMatching(this._artifactsPath, (f) => | ||
this._isArtifactPath(f) | ||
); | ||
@@ -567,10 +561,4 @@ | ||
const buildInfosDir = path.join(this._artifactsPath, BUILD_INFO_DIR_NAME); | ||
const paths = getAllFilesMatchingSync( | ||
this._artifactsPath, | ||
(f) => | ||
f.endsWith(".json") && | ||
!f.startsWith(buildInfosDir) && | ||
!f.endsWith(".dbg.json") | ||
const paths = getAllFilesMatchingSync(this._artifactsPath, (f) => | ||
this._isArtifactPath(f) | ||
); | ||
@@ -939,2 +927,11 @@ | ||
} | ||
private _isArtifactPath(file: string) { | ||
return ( | ||
file.endsWith(".json") && | ||
file !== path.join(this._artifactsPath, "package.json") && | ||
!file.startsWith(path.join(this._artifactsPath, BUILD_INFO_DIR_NAME)) && | ||
!file.endsWith(".dbg.json") | ||
); | ||
} | ||
} | ||
@@ -941,0 +938,0 @@ |
@@ -5,3 +5,3 @@ import findup from "find-up"; | ||
import { HardhatRuntimeEnvironment } from "../../types"; | ||
import { HardhatRuntimeEnvironment, TaskDefinition } from "../../types"; | ||
import { HARDHAT_PARAM_DEFINITIONS } from "../core/params/hardhat-params"; | ||
@@ -26,15 +26,26 @@ import { getCacheDir } from "../util/global-dir"; | ||
interface Task { | ||
name: string; | ||
description: string; | ||
isSubtask: boolean; | ||
paramDefinitions: { | ||
[paramName: string]: { | ||
name: string; | ||
description: string; | ||
isFlag: boolean; | ||
}; | ||
}; | ||
} | ||
interface CompletionData { | ||
networks: string[]; | ||
tasks: { | ||
[taskName: string]: { | ||
[taskName: string]: Task; | ||
}; | ||
scopes: { | ||
[scopeName: string]: { | ||
name: string; | ||
description: string; | ||
isSubtask: boolean; | ||
paramDefinitions: { | ||
[paramName: string]: { | ||
name: string; | ||
description: string; | ||
isFlag: boolean; | ||
}; | ||
tasks: { | ||
[taskName: string]: Task; | ||
}; | ||
@@ -68,3 +79,3 @@ }; | ||
const { networks, tasks } = completionData; | ||
const { networks, tasks, scopes } = completionData; | ||
@@ -74,3 +85,3 @@ const words = line.split(/\s+/).filter((x) => x.length > 0); | ||
const wordsBeforeCursor = line.slice(0, point).split(/\s+/); | ||
// examples: | ||
// 'prev' and 'last' variables examples: | ||
// `hh compile --network|` => prev: "compile" last: "--network" | ||
@@ -90,24 +101,54 @@ // `hh compile --network |` => prev: "--network" last: "" | ||
// check if the user entered a task | ||
let task: string | undefined; | ||
// Get the task or scope if the user has entered one | ||
let taskName: string | undefined; | ||
let scopeName: string | undefined; | ||
let index = 1; | ||
while (index < words.length) { | ||
if (isGlobalFlag(words[index])) { | ||
const word = words[index]; | ||
if (isGlobalFlag(word)) { | ||
index += 1; | ||
} else if (isGlobalParam(words[index])) { | ||
} else if (isGlobalParam(word)) { | ||
index += 2; | ||
} else if (words[index].startsWith("--")) { | ||
} else if (word.startsWith("--")) { | ||
index += 1; | ||
} else { | ||
task = words[index]; | ||
break; | ||
// Possible scenarios: | ||
// - no task or scope: `hh ` | ||
// - only a task: `hh task ` | ||
// - only a scope: `hh scope ` | ||
// - both a scope and a task (the task always follow the scope): `hh scope task ` | ||
// Between a scope and a task there could be other words, e.g.: `hh scope --flag task ` | ||
if (scopeName === undefined) { | ||
if (tasks[word] !== undefined) { | ||
taskName = word; | ||
break; | ||
} else if (scopes[word] !== undefined) { | ||
scopeName = word; | ||
} | ||
} else { | ||
taskName = word; | ||
break; | ||
} | ||
index += 1; | ||
} | ||
} | ||
// if a task was found but it's equal to the last word, it means | ||
// that the cursor is after the task, we ignore the task in this | ||
// case because if you have a task `foo` and `foobar` and the | ||
// line is: `hh foo|`, we want tasks to be suggested | ||
if (task === last) { | ||
task = undefined; | ||
// If a task or a scope is found and it is equal to the last word, | ||
// this indicates that the cursor is positioned after the task or scope. | ||
// In this case, we ignore the task or scope. For instance, if you have a task or a scope named 'foo' and 'foobar', | ||
// and the line is 'hh foo|', we want to suggest the value for 'foo' and 'foobar'. | ||
// Possible scenarios: | ||
// - no task or scope: `hh ` -> task and scope already undefined | ||
// - only a task: `hh task ` -> task set to undefined, scope already undefined | ||
// - only a scope: `hh scope ` -> scope set to undefined, task already undefined | ||
// - both a scope and a task (the task always follow the scope): `hh scope task ` -> task set to undefined, scope stays defined | ||
if (taskName === last || scopeName === last) { | ||
if (taskName !== undefined && scopeName !== undefined) { | ||
[taskName, scopeName] = [undefined, scopeName]; | ||
} else { | ||
[taskName, scopeName] = [undefined, undefined]; | ||
} | ||
} | ||
@@ -122,2 +163,12 @@ | ||
const scopeDefinition = | ||
scopeName === undefined ? undefined : scopes[scopeName]; | ||
const taskDefinition = | ||
taskName === undefined | ||
? undefined | ||
: scopeDefinition === undefined | ||
? tasks[taskName] | ||
: scopeDefinition.tasks[taskName]; | ||
// if the previous word is a param, then a value is expected | ||
@@ -134,4 +185,3 @@ // we don't complete anything here | ||
const isTaskParam = | ||
task !== undefined && | ||
tasks[task]?.paramDefinitions[paramName]?.isFlag === false; | ||
taskDefinition?.paramDefinitions[paramName]?.isFlag === false; | ||
@@ -143,4 +193,8 @@ if (isTaskParam) { | ||
// if there's no task, we complete either tasks or params | ||
if (task === undefined || tasks[task] === undefined) { | ||
// If there's no task or scope, we complete either tasks and scopes or params | ||
if (taskDefinition === undefined && scopeDefinition === undefined) { | ||
if (last.startsWith("-")) { | ||
return coreParams.filter((param) => startsWithLast(param.name)); | ||
} | ||
const taskSuggestions = Object.values(tasks) | ||
@@ -152,8 +206,24 @@ .filter((x) => !x.isSubtask) | ||
})); | ||
if (last.startsWith("-")) { | ||
return coreParams.filter((param) => startsWithLast(param.name)); | ||
} | ||
return taskSuggestions.filter((x) => startsWithLast(x.name)); | ||
const scopeSuggestions = Object.values(scopes).map((x) => ({ | ||
name: x.name, | ||
description: x.description, | ||
})); | ||
return taskSuggestions | ||
.concat(scopeSuggestions) | ||
.filter((x) => startsWithLast(x.name)); | ||
} | ||
// If there's a scope but not a task, we complete with the scopes'tasks | ||
if (taskDefinition === undefined && scopeDefinition !== undefined) { | ||
return Object.values(scopes[scopeName!].tasks) | ||
.filter((x) => !x.isSubtask) | ||
.map((x) => ({ | ||
name: x.name, | ||
description: x.description, | ||
})) | ||
.filter((x) => startsWithLast(x.name)); | ||
} | ||
if (!last.startsWith("-")) { | ||
@@ -163,9 +233,11 @@ return HARDHAT_COMPLETE_FILES; | ||
// if there's a task and the last word starts with -, we complete its params and the global params | ||
const taskParams = Object.values(tasks[task].paramDefinitions) | ||
.map((param) => ({ | ||
name: ArgumentsParser.paramNameToCLA(param.name), | ||
description: param.description, | ||
})) | ||
.filter((x) => !words.includes(x.name)); | ||
const taskParams = | ||
taskDefinition === undefined | ||
? [] | ||
: Object.values(taskDefinition.paramDefinitions) | ||
.map((param) => ({ | ||
name: ArgumentsParser.paramNameToCLA(param.name), | ||
description: param.description, | ||
})) | ||
.filter((x) => !words.includes(x.name)); | ||
@@ -208,11 +280,10 @@ return [...taskParams, ...coreParams].filter((suggestion) => | ||
// is serializable and to avoid saving unnecessary things from the HRE | ||
const tasks: CompletionData["tasks"] = mapValues(hre.tasks, (task) => ({ | ||
name: task.name, | ||
description: task.description ?? "", | ||
isSubtask: task.isSubtask, | ||
paramDefinitions: mapValues(task.paramDefinitions, (paramDefinition) => ({ | ||
name: paramDefinition.name, | ||
description: paramDefinition.description ?? "", | ||
isFlag: paramDefinition.isFlag, | ||
})), | ||
const tasks: CompletionData["tasks"] = mapValues(hre.tasks, (task) => | ||
getTaskFromTaskDefinition(task) | ||
); | ||
const scopes: CompletionData["scopes"] = mapValues(hre.scopes, (scope) => ({ | ||
name: scope.name, | ||
description: scope.description ?? "", | ||
tasks: mapValues(scope.tasks, (task) => getTaskFromTaskDefinition(task)), | ||
})); | ||
@@ -223,2 +294,3 @@ | ||
tasks, | ||
scopes, | ||
}; | ||
@@ -231,2 +303,18 @@ | ||
function getTaskFromTaskDefinition(taskDef: TaskDefinition): Task { | ||
return { | ||
name: taskDef.name, | ||
description: taskDef.description ?? "", | ||
isSubtask: taskDef.isSubtask, | ||
paramDefinitions: mapValues( | ||
taskDef.paramDefinitions, | ||
(paramDefinition) => ({ | ||
name: paramDefinition.name, | ||
description: paramDefinition.description ?? "", | ||
isFlag: paramDefinition.isFlag, | ||
}) | ||
), | ||
}; | ||
} | ||
function getProjectId(): string | undefined { | ||
@@ -233,0 +321,0 @@ const packageJsonPath = findup.sync("package.json"); |
@@ -48,7 +48,7 @@ import chalk from "chalk"; | ||
const ETHERS_PROJECT_DEPENDENCIES: Dependencies = { | ||
"@nomicfoundation/hardhat-toolbox": "^3.0.0", | ||
"@nomicfoundation/hardhat-toolbox": "^4.0.0", | ||
}; | ||
const VIEM_PROJECT_DEPENDENCIES: Dependencies = { | ||
"@nomicfoundation/hardhat-toolbox-viem": "^1.0.0", | ||
"@nomicfoundation/hardhat-toolbox-viem": "^2.0.0", | ||
}; | ||
@@ -59,3 +59,3 @@ | ||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0", | ||
"@nomicfoundation/hardhat-verify": "^1.0.0", | ||
"@nomicfoundation/hardhat-verify": "^2.0.0", | ||
chai: "^4.2.0", | ||
@@ -70,5 +70,5 @@ "hardhat-gas-reporter": "^1.0.8", | ||
ethers: "^6.4.0", | ||
"@typechain/hardhat": "^8.0.0", | ||
typechain: "^8.1.0", | ||
"@typechain/ethers-v6": "^0.4.0", | ||
"@typechain/hardhat": "^9.0.0", | ||
typechain: "^8.3.0", | ||
"@typechain/ethers-v6": "^0.5.0", | ||
}; | ||
@@ -75,0 +75,0 @@ |
@@ -221,3 +221,6 @@ import type StackTraceParserT from "stacktrace-parser"; | ||
for (const [peerDependency, version] of Object.entries(peerDependencies)) { | ||
const peerDependencyPackageJson = readPackageJson(peerDependency); | ||
const peerDependencyPackageJson = readPackageJson( | ||
peerDependency, | ||
configPath | ||
); | ||
if (peerDependencyPackageJson === undefined) { | ||
@@ -248,6 +251,14 @@ missingPeerDependencies[peerDependency] = version; | ||
function readPackageJson(packageName: string): PackageJson | undefined { | ||
function readPackageJson( | ||
packageName: string, | ||
configPath: string | ||
): PackageJson | undefined { | ||
const resolve = require("resolve") as typeof import("resolve"); | ||
try { | ||
const packageJsonPath = require.resolve( | ||
path.join(packageName, "package.json") | ||
const packageJsonPath = resolve.sync( | ||
path.join(packageName, "package.json"), | ||
{ | ||
basedir: path.dirname(configPath), | ||
} | ||
); | ||
@@ -254,0 +265,0 @@ |
@@ -13,2 +13,3 @@ import findUp from "find-up"; | ||
const TS_CONFIG_FILENAME = "hardhat.config.ts"; | ||
const CTS_CONFIG_FILENAME = "hardhat.config.cts"; | ||
@@ -18,2 +19,3 @@ export function isCwdInsideProject() { | ||
findUp.sync(TS_CONFIG_FILENAME) !== null || | ||
findUp.sync(CTS_CONFIG_FILENAME) !== null || | ||
findUp.sync(CJS_CONFIG_FILENAME) !== null || | ||
@@ -30,2 +32,7 @@ findUp.sync(JS_CONFIG_FILENAME) !== null | ||
const ctsConfigPath = findUp.sync(CTS_CONFIG_FILENAME); | ||
if (ctsConfigPath !== null) { | ||
return ctsConfigPath; | ||
} | ||
const cjsConfigPath = findUp.sync(CJS_CONFIG_FILENAME); | ||
@@ -32,0 +39,0 @@ if (cjsConfigPath !== null) { |
@@ -257,2 +257,21 @@ import { EIP1193Provider, RequestArguments } from "../../../types"; | ||
let maxPriorityFeePerGas = rpcQuantityToBigInt(response.reward[0][0]); | ||
if (maxPriorityFeePerGas === 0n) { | ||
try { | ||
const suggestedMaxPriorityFeePerGas = | ||
(await this._wrappedProvider.request({ | ||
method: "eth_maxPriorityFeePerGas", | ||
params: [], | ||
})) as string; | ||
maxPriorityFeePerGas = rpcQuantityToBigInt( | ||
suggestedMaxPriorityFeePerGas | ||
); | ||
} catch { | ||
// if eth_maxPriorityFeePerGas does not exist, use 1 wei | ||
maxPriorityFeePerGas = 1n; | ||
} | ||
} | ||
return { | ||
@@ -272,3 +291,3 @@ // Each block increases the base fee by 1/8 at most, when full. | ||
maxPriorityFeePerGas: rpcQuantityToBigInt(response.reward[0][0]), | ||
maxPriorityFeePerGas, | ||
}; | ||
@@ -275,0 +294,0 @@ } catch { |
@@ -16,3 +16,3 @@ import { HardhatConfig } from "../../types"; | ||
const config = resolveConfigPath(configPath); | ||
return isTypescriptFile(config); | ||
return isNonEsmTypescriptFile(config); | ||
} | ||
@@ -24,3 +24,3 @@ | ||
export function isRunningWithTypescript(config: HardhatConfig): boolean { | ||
return isTypescriptFile(config.paths.configFile); | ||
return isNonEsmTypescriptFile(config.paths.configFile); | ||
} | ||
@@ -85,4 +85,12 @@ | ||
function isTypescriptFile(path: string): boolean { | ||
return path.endsWith(".ts"); | ||
function isNonEsmTypescriptFile(path: string): boolean { | ||
return /\.(ts|cts)$/i.test(path); | ||
} | ||
export function isTypescriptFile(path: string): boolean { | ||
return /\.(ts|cts|mts)$/i.test(path); | ||
} | ||
export function isJavascriptFile(path: string): boolean { | ||
return /\.(js|cjs|mjs)$/i.test(path); | ||
} |
@@ -63,3 +63,7 @@ import path from "path"; | ||
*/ | ||
downloadCompiler(version: string): Promise<void>; | ||
downloadCompiler( | ||
version: string, | ||
downloadStartedCb: (isCompilerDownloaded: boolean) => Promise<any>, | ||
downloadEndedCb: (isCompilerDownloaded: boolean) => Promise<any> | ||
): Promise<void>; | ||
@@ -150,4 +154,20 @@ /** | ||
public async downloadCompiler(version: string): Promise<void> { | ||
public async downloadCompiler( | ||
version: string, | ||
downloadStartedCb: (isCompilerDownloaded: boolean) => Promise<any>, | ||
downloadEndedCb: (isCompilerDownloaded: boolean) => Promise<any> | ||
): Promise<void> { | ||
// Since only one process at a time can acquire the mutex, we avoid the risk of downloading the same compiler multiple times. | ||
// This is because the mutex blocks access until a compiler has been fully downloaded, preventing any new process | ||
// from checking whether that version of the compiler exists. Without mutex it might incorrectly | ||
// return false, indicating that the compiler isn't present, even though it is currently being downloaded. | ||
await this._mutex.use(async () => { | ||
const isCompilerDownloaded = await this.isCompilerDownloaded(version); | ||
if (isCompilerDownloaded === true) { | ||
return; | ||
} | ||
await downloadStartedCb(isCompilerDownloaded); | ||
let build = await this._getCompilerBuild(version); | ||
@@ -194,2 +214,4 @@ | ||
await this._postProcessCompilerDownload(build, downloadPath); | ||
await downloadEndedCb(isCompilerDownloaded); | ||
}); | ||
@@ -196,0 +218,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
3996163
72937
73