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
100 lines (98 loc) • 5.73 kB
JavaScript
;
var path = require("node:path"), node_worker_threads = require("node:worker_threads"), logSymbols = require("log-symbols"), readPkgUp = require("read-pkg-up"), node_tty = require("node:tty"), chalk = require("chalk");
function _interopDefaultCompat(e) {
return e && typeof e == "object" && "default" in e ? e : { default: e };
}
var path__default = /* @__PURE__ */ _interopDefaultCompat(path), logSymbols__default = /* @__PURE__ */ _interopDefaultCompat(logSymbols), readPkgUp__default = /* @__PURE__ */ _interopDefaultCompat(readPkgUp), chalk__default = /* @__PURE__ */ _interopDefaultCompat(chalk);
const isTty = node_tty.isatty(1), headers = {
error: isTty ? chalk__default.default.bold(chalk__default.default.bgRed(chalk__default.default.black(" ERROR "))) : chalk__default.default.red("[ERROR]"),
warning: isTty ? chalk__default.default.bold(chalk__default.default.bgYellow(chalk__default.default.black(" WARN "))) : chalk__default.default.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)).slice(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) => {
const [firstSegment] = next.path;
if (!firstSegment || firstSegment.kind !== "type") return acc;
const topLevelType = firstSegment.name || `<unnamed_${firstSegment.type}_type_${unnamedTopLevelTypeCount++}>`, problems = acc[topLevelType] ?? [];
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__default.default.bgWhite(chalk__default.default.black(` ${topLevelType} `)) : `[${topLevelType}]`, header = `${headers[getAggregatedSeverity(groups)]} ${formattedTopLevelType}`, body = groups.sort((a, b) => severityValues[getAggregatedSeverity(a)] - severityValues[getAggregatedSeverity(b)]).map((group) => {
const formattedPath = ` ${chalk__default.default.bold(formatPath(group.path) || "(root)")}`, formattedMessages = group.problems.sort((a, b) => severityValues[a.severity] - severityValues[b.severity]).map(({
severity,
message
}) => ` ${logSymbols__default.default[severity]} ${message}`).join(`
`);
return `${formattedPath}
${formattedMessages}`;
}).join(`
`);
return `${header}
${body}`;
}).join(`
`);
}
async function validateAction(args, {
workDir,
output
}) {
const flags = args.extOptions, rootPkgPath = readPkgUp__default.default.sync({
cwd: __dirname
})?.path;
if (!rootPkgPath)
throw new Error("Could not find root directory for `sanity` package");
const workerPath = path__default.default.join(path__default.default.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 node_worker_threads.Worker(workerPath, {
workerData: {
workDir,
level,
workspace: flags.workspace
},
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?.succeed("Validated schema"), output.print(`
Validation results:`), output.print(`${logSymbols__default.default.error} Errors: ${errorCount.toLocaleString("en-US")} error${errorCount === 1 ? "" : "s"}`), level !== "error" && output.print(`${logSymbols__default.default.warning} Warnings: ${warningCount.toLocaleString("en-US")} warning${warningCount === 1 ? "" : "s"}`), output.print(), output.print(formatSchemaValidation(validation));
}
process.exitCode = overallSeverity === "error" ? 1 : 0;
}
exports.default = validateAction;
//# sourceMappingURL=validateAction.js.map