sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
114 lines (112 loc) • 5.22 kB
JavaScript
import logSymbols from "log-symbols";
import path from "path";
import readPkgUp from "read-pkg-up";
import { Worker } from "worker_threads";
import chalk from "chalk";
import { isatty } from "tty";
const isTty = isatty(1), headers = {
error: isTty ? chalk.bold(chalk.bgRed(chalk.black(" ERROR "))) : chalk.red("[ERROR]"),
warning: isTty ? chalk.bold(chalk.bgYellow(chalk.black(" WARN "))) : chalk.yellow("[WARN]")
}, severityValues = { error: 0, warning: 1 };
function formatPath(pathSegments) {
const format = ([curr, ...next], mode = "object") => {
if (!curr)
return "";
if (curr.kind === "property")
return format(next, curr.name === "of" ? "array" : "object");
const name = curr.name ? curr.name : `<anonymous_${curr.type}>`;
return `${mode === "array" ? `[${name}]` : `.${name}`}${format(next)}`;
};
return format(pathSegments.slice(1)).substring(1);
}
function getAggregatedSeverity(groupOrGroups) {
return (Array.isArray(groupOrGroups) ? groupOrGroups : [groupOrGroups]).flatMap((group) => group.problems.map((problem) => problem.severity)).find((severity) => severity === "error") ? "error" : "warning";
}
function formatSchemaValidation(validation) {
let unnamedTopLevelTypeCount = 0;
return Object.entries(
validation.reduce((acc, next) => {
var _a;
const [firstSegment] = next.path;
if (!firstSegment || firstSegment.kind !== "type")
return acc;
const topLevelType = firstSegment.name || `<unnamed_${firstSegment.type}_type_${unnamedTopLevelTypeCount++}>`, problems = (_a = acc[topLevelType]) != null ? _a : [];
return problems.push(next), acc[topLevelType] = problems, acc;
}, {})
).sort((a, b) => {
const [aType, aGroups] = a, [bType, bGroups] = b, aValue = severityValues[getAggregatedSeverity(aGroups)], bValue = severityValues[getAggregatedSeverity(bGroups)];
return aValue === bValue ? aType.localeCompare(bType, "en-US") : aValue - bValue;
}).map(([topLevelType, groups]) => {
const formattedTopLevelType = isTty ? chalk.bgWhite(chalk.black(` ${topLevelType} `)) : `[${topLevelType}]`, header = `${headers[getAggregatedSeverity(groups)]} ${formattedTopLevelType}`, body = groups.sort(
(a, b) => severityValues[getAggregatedSeverity(a)] - severityValues[getAggregatedSeverity(b)]
).map((group) => {
const formattedPath = ` ${chalk.bold(formatPath(group.path) || "(root)")}`, formattedMessages = group.problems.sort((a, b) => severityValues[a.severity] - severityValues[b.severity]).map(({ severity, message }) => ` ${logSymbols[severity]} ${message}`).join(`
`);
return `${formattedPath}
${formattedMessages}`;
}).join(`
`);
return `${header}
${body}`;
}).join(`
`);
}
async function validateAction(args, { workDir, output }) {
var _a;
const flags = args.extOptions, rootPkgPath = (_a = readPkgUp.sync({ cwd: __dirname })) == null ? void 0 : _a.path;
if (!rootPkgPath)
throw new Error("Could not find root directory for `sanity` package");
const workerPath = path.join(
path.dirname(rootPkgPath),
"lib",
"_internal",
"cli",
"threads",
"validateSchema.js"
), level = flags.level || "warning";
if (level !== "error" && level !== "warning")
throw new Error("Invalid level. Available levels are 'error' and 'warning'.");
const format = flags.format || "pretty";
if (!["pretty", "ndjson", "json"].includes(format))
throw new Error(
`Did not recognize format '${flags.format}'. Available formats are 'pretty', 'ndjson', and 'json'.`
);
let spinner;
format === "pretty" && (spinner = output.spinner(
flags.workspace ? `Validating schema from workspace '${flags.workspace}'\u2026` : "Validating schema\u2026"
).start());
const worker = new Worker(workerPath, {
workerData: {
workDir,
level,
workspace: flags.workspace
},
// eslint-disable-next-line no-process-env
env: process.env
}), { validation } = await new Promise((resolve, reject) => {
worker.addListener("message", resolve), worker.addListener("error", reject);
}), problems = validation.flatMap((group) => group.problems), errorCount = problems.filter((problem) => problem.severity === "error").length, warningCount = problems.filter((problem) => problem.severity === "warning").length, overallSeverity = getAggregatedSeverity(validation);
switch (format) {
case "ndjson": {
for (const group of validation)
output.print(JSON.stringify(group));
break;
}
case "json": {
output.print(JSON.stringify(validation));
break;
}
default:
spinner == null || spinner.succeed("Validated schema"), output.print(`
Validation results:`), output.print(
`${logSymbols.error} Errors: ${errorCount.toLocaleString("en-US")} error${errorCount === 1 ? "" : "s"}`
), level !== "error" && output.print(
`${logSymbols.warning} Warnings: ${warningCount.toLocaleString("en-US")} warning${warningCount === 1 ? "" : "s"}`
), output.print(), output.print(formatSchemaValidation(validation));
}
process.exitCode = overallSeverity === "error" ? 1 : 0;
}
export {
validateAction as default
};
//# sourceMappingURL=validateAction.js.map