@curvenote/cli
Version:
CLI Client library for Curvenote
114 lines (113 loc) • 5.7 kB
JavaScript
import chalk from 'chalk';
import { incrementOptions } from 'simple-validators';
import { validateCheck } from '@curvenote/check-implementations';
import { CheckStatus, } from '@curvenote/check-definitions';
export async function runChecks(session, checks, implementations) {
const opts = { property: 'checks', messages: {} };
const completedChecks = await Promise.all(checks.map(async (check, index) => {
var _a;
const validCheck = validateCheck(session, check, implementations, incrementOptions(`${index}`, opts));
if (!validCheck) {
// Validates the journal options against the check definition
// TODO! - send this as an error back anyways
session.log.error(`Check with ID: ${check.id} was not found or is invalid`);
session.log.info(opts.messages);
return undefined;
}
const { validate, ...def } = (_a = implementations.find(({ id }) => check.id === id)) !== null && _a !== void 0 ? _a : {};
if (!validate) {
// TODO! - send this as an error back anyways
session.log.error(`Check with ID: ${check.id} did not have a validate function`);
return undefined;
}
const result = await validate(session, validCheck);
if (!result)
return;
const allResults = Array.isArray(result) ? result : [result];
return allResults.map((res) => {
return {
...def,
...check,
...res, // The specific results adds "status" (pass, fail, error), a message/note/file/position, and may also add "optional"
};
});
}));
// TODO: clean the messages and make file links relative in the file, message, note fields.
return completedChecks.flat().filter((check) => !!check);
}
export function sortCheckResults(completedChecks) {
let finalStatus = CheckStatus.pass;
const checkCategories = {};
completedChecks.forEach((check) => {
const { tags, status, optional } = check;
const tag = tags[0];
if (!checkCategories[tag]) {
checkCategories[tag] = {
category: tag,
status,
checks: [check],
};
}
else {
checkCategories[tag].checks.push(check);
}
if (status !== CheckStatus.pass && optional !== true) {
checkCategories[tag].status = CheckStatus.fail;
finalStatus = CheckStatus.fail;
}
});
return { status: finalStatus, results: Object.values(checkCategories) };
}
export function logCheckReport(session, completedChecks, exitOnFailure = true) {
const report = sortCheckResults(completedChecks);
const checkFail = (msg, icon, prefix, optional) => {
return chalk[optional ? 'yellow' : 'red'](`${prefix !== null && prefix !== void 0 ? prefix : ''}${icon ? (optional ? '↓' : '×') : ''} ${msg}`);
};
const checkError = (msg, icon, prefix) => {
return chalk.yellow(`${prefix !== null && prefix !== void 0 ? prefix : ''}${icon ? '↓' : ''} ${msg}`);
};
const checkPass = (msg, icon, prefix) => {
return chalk.green(`${prefix !== null && prefix !== void 0 ? prefix : ''}${icon ? '✓' : ''} ${msg}`);
};
session.log.info(`\n\n${chalk.bold.bgBlueBright(' Curvenote Checks ')} ✓✓✓ 🚀\n`);
report.results.forEach((result) => {
const { status, category, checks } = result;
const numPassed = checks.filter((c) => c.status === CheckStatus.pass).length;
const numOptional = checks.filter((c) => c.status !== CheckStatus.pass && c.optional).length;
if (status === CheckStatus.pass && numPassed === checks.length) {
session.log.info(checkPass(`❯ ${chalk.bold(category)} (${numPassed}/${checks.length} tests passed)`, false));
}
else if (status === CheckStatus.pass) {
session.log.info(checkFail(`❯ ${chalk.bold(category)} (${numPassed}/${checks.length} tests passed, ${numOptional} optional)`, false, '', true));
}
else {
session.log.error(checkFail(`❯ ${chalk.bold(category)} (${numPassed}/${checks.length} tests passed${numOptional > 0 ? `, ${numOptional} optional` : ''})`, false));
}
checks.forEach((check) => {
const { message, file, position, optional } = check;
const line = file && (position === null || position === void 0 ? void 0 : position.start.line) ? `:${position.start.line}` : '';
const column = file && line && (position === null || position === void 0 ? void 0 : position.start.column) && (position === null || position === void 0 ? void 0 : position.start.column) > 1
? `:${position.start.column}`
: '';
const fileSuffix = file ? `(${file}${line}${column})` : '';
const messageWithTitle = [
`${chalk.bold(check.title)}${message ? ':' : ''}`,
message !== null && message !== void 0 ? message : '',
fileSuffix,
]
.filter((m) => !!m)
.join(' ');
if (check.status === CheckStatus.pass) {
session.log.info(checkPass(messageWithTitle, true, ' '));
}
else if (check.status === CheckStatus.error) {
session.log.error(checkError(messageWithTitle, true, ' '));
}
else {
session.log.error(checkFail(messageWithTitle, true, ' ', optional));
}
});
});
if (report.status !== CheckStatus.pass && exitOnFailure)
process.exit(1);
}