hardhat-output-validator
Version:
Zero-config Hardhat plugin to check the build output of Solidity contracts
134 lines • 8.22 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFullBuildInfo = exports.getDefaultSeverity = exports.getConfig = void 0;
/* eslint-disable guard-for-in, max-len, no-await-in-loop, no-restricted-syntax */
const config_1 = require("hardhat/config");
const task_names_1 = require("hardhat/builtin-tasks/task-names");
const chalk_1 = __importDefault(require("chalk"));
require("./type-extensions");
const checks_1 = require("./checks");
const _1compWarnings_1 = require("./checks/1compWarnings");
(0, config_1.extendConfig)((config, userConfig) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
const errorMode = ((_a = userConfig.outputValidator) === null || _a === void 0 ? void 0 : _a.errorMode) || false;
const defaultValue = errorMode ? 'error' : 'warning';
config.outputValidator = {
errorMode,
strict: ((_b = userConfig.outputValidator) === null || _b === void 0 ? void 0 : _b.strict) || false,
checks: Object.assign(Object.assign({ title: defaultValue, details: defaultValue, compilationWarnings: defaultValue, missingUserDoc: defaultValue, missingDevDoc: defaultValue, events: false, functions: defaultValue, variables: false, ctor: false, params: defaultValue, returns: defaultValue }, (((_c = userConfig.outputValidator) === null || _c === void 0 ? void 0 : _c.checks) || {})), { userDoc: Object.assign({ events: ((_e = (_d = userConfig.outputValidator) === null || _d === void 0 ? void 0 : _d.checks) === null || _e === void 0 ? void 0 : _e.events) || defaultValue, functions: ((_g = (_f = userConfig.outputValidator) === null || _f === void 0 ? void 0 : _f.checks) === null || _g === void 0 ? void 0 : _g.functions) || defaultValue, variables: ((_j = (_h = userConfig.outputValidator) === null || _h === void 0 ? void 0 : _h.checks) === null || _j === void 0 ? void 0 : _j.variables) || defaultValue, ctor: ((_l = (_k = userConfig.outputValidator) === null || _k === void 0 ? void 0 : _k.checks) === null || _l === void 0 ? void 0 : _l.ctor) || defaultValue }, (((_o = (_m = userConfig.outputValidator) === null || _m === void 0 ? void 0 : _m.checks) === null || _o === void 0 ? void 0 : _o.userDoc) || {})), devDoc: Object.assign({ events: ((_q = (_p = userConfig.outputValidator) === null || _p === void 0 ? void 0 : _p.checks) === null || _q === void 0 ? void 0 : _q.events) || defaultValue, functions: ((_s = (_r = userConfig.outputValidator) === null || _r === void 0 ? void 0 : _r.checks) === null || _s === void 0 ? void 0 : _s.functions) || defaultValue, variables: ((_u = (_t = userConfig.outputValidator) === null || _t === void 0 ? void 0 : _t.checks) === null || _u === void 0 ? void 0 : _u.variables) || defaultValue, ctor: ((_w = (_v = userConfig.outputValidator) === null || _v === void 0 ? void 0 : _v.checks) === null || _w === void 0 ? void 0 : _w.ctor) || defaultValue }, (((_y = (_x = userConfig.outputValidator) === null || _x === void 0 ? void 0 : _x.checks) === null || _y === void 0 ? void 0 : _y.devDoc) || {})) }),
include: ((_z = userConfig.outputValidator) === null || _z === void 0 ? void 0 : _z.include) || [],
exclude: ((_0 = userConfig.outputValidator) === null || _0 === void 0 ? void 0 : _0.exclude) || [],
runOnCompile: ((_1 = userConfig.outputValidator) === null || _1 === void 0 ? void 0 : _1.runOnCompile) || false,
};
});
(0, config_1.task)(task_names_1.TASK_COMPILE, async (args, hre, runSuper) => {
const config = hre.config.outputValidator;
// Updates the compiler settings
for (const compiler of hre.config.solidity.compilers) {
compiler.settings.outputSelection['*']['*'].push('devdoc');
compiler.settings.outputSelection['*']['*'].push('userdoc');
}
// Calls the actual COMPILE task
await runSuper();
if (!config.runOnCompile) {
return;
}
await run(hre);
});
(0, config_1.task)('validateOutput', async (args, hre) => {
console.log('<<< Validating Output for Natspec >>>');
await run(hre);
});
let getConfig = () => ({});
exports.getConfig = getConfig;
let getDefaultSeverity = () => 'warning';
exports.getDefaultSeverity = getDefaultSeverity;
let getFullBuildInfo = () => [];
exports.getFullBuildInfo = getFullBuildInfo;
const run = async (hre) => {
const config = hre.config.outputValidator;
exports.getConfig = () => config;
exports.getDefaultSeverity = () => (config.errorMode ? 'error' : 'warning');
const getBuildInfo = async (qualifiedName) => hre.artifacts.getBuildInfo(qualifiedName);
// Loops through all the qualified names to get all the compiled contracts
const getContractBuildInfo = async (qualifiedName) => {
const [source, name] = qualifiedName.split(':');
const build = await getBuildInfo(qualifiedName);
const info = build === null || build === void 0 ? void 0 : build.output.contracts[source][name];
return Object.assign(Object.assign({}, info), { filePath: source, fileName: name });
};
// ====== Start ======
console.log('<<< Starting Output Checks >>> ');
// Apply filters
const allContracts = await hre.artifacts.getAllFullyQualifiedNames();
const qualifiedNames = allContracts
.filter((str) => str.startsWith('contracts'))
.filter((path) => {
// Checks if this contact is included
const includesPath = config.include.some((str) => path.includes(str));
const excludesPath = config.exclude.some((str) => path.includes(str));
return (config.include.length === 0 || includesPath) && !excludesPath;
});
console.log('qualifiedNames', qualifiedNames);
// ============ 1.Setup ============
// (*0) Full Build Info
const buildInfo = (await Promise.all(qualifiedNames.map(getBuildInfo))).filter((inf) => inf !== undefined);
exports.getFullBuildInfo = () => buildInfo;
// (*1) Subset - build.output.contracts[name]
const contractBuildInfo = (await Promise.all(qualifiedNames.map(getContractBuildInfo))).filter((inf) => inf !== undefined);
// ============ 2.Checks ============
// (*0) checks
console.log('<<< Checking for Errors >>> ');
const errors = contractBuildInfo.reduce((foundErrors, info) => {
const docErrors = (0, checks_1.checkForErrors)(info);
if (docErrors && docErrors.length > 0) {
const key = info.filePath + ':' + info.fileName;
foundErrors[key] = docErrors;
}
return foundErrors;
}, {});
// (*1) checks
// compilationWarnings
if (config.checks.compilationWarnings) {
const compErros = (0, _1compWarnings_1.checkForCompilationWarnings)(buildInfo, config.checks.compilationWarnings);
Object.keys(compErros).forEach((fi) => {
if (!errors[fi]) {
errors[fi] = [];
}
errors[fi].push(...compErros[fi]);
});
}
// ============ 3.Act ============
const printErrors = () => {
let printedErrors = false;
Object.keys(errors).forEach((file) => {
const errorsInfo = errors[file];
if (errorsInfo && errorsInfo.length > 0) {
errorsInfo.forEach((erIn) => {
if (erIn.severityLevel === 'error') {
console.error(chalk_1.default.red(erIn.text));
if (!printedErrors) {
printedErrors = true;
}
}
else {
console.warn(chalk_1.default.yellow(erIn.text));
}
});
}
});
return printedErrors;
};
if (Object.keys(errors).length > 0) {
const printedErrors = printErrors();
if (printedErrors) {
throw new Error('Missing Natspec Comments - Found Errors.');
}
}
console.log('✅ All Contracts have been checked for missing Natspec comments');
// ====== END ======
};
//# sourceMappingURL=index.js.map