claude-code/components/ValidationErrorsList.tsx

148 lines
19 KiB
TypeScript
Raw Permalink Normal View History

import { c as _c } from "react/compiler-runtime";
import setWith from 'lodash-es/setWith.js';
import * as React from 'react';
import { Box, Text, useTheme } from '../ink.js';
import type { ValidationError } from '../utils/settings/validation.js';
import { type TreeNode, treeify } from '../utils/treeify.js';
/**
* Builds a nested tree structure from dot-notation paths
* Uses lodash setWith to avoid automatic array creation
*/
function buildNestedTree(errors: ValidationError[]): TreeNode {
const tree: TreeNode = {};
errors.forEach(error => {
if (!error.path) {
// Root level error - use empty string as key
tree[''] = error.message;
return;
}
// Try to enhance the path with meaningful values
const pathParts = error.path.split('.');
let modifiedPath = error.path;
// If we have an invalid value, try to make the path more readable
if (error.invalidValue !== null && error.invalidValue !== undefined && pathParts.length > 0) {
const newPathParts: string[] = [];
for (let i = 0; i < pathParts.length; i++) {
const part = pathParts[i];
if (!part) continue;
const numericPart = parseInt(part, 10);
// If this is a numeric index and it's the last part where we have the invalid value
if (!isNaN(numericPart) && i === pathParts.length - 1) {
// Format the value for display
let displayValue: string;
if (typeof error.invalidValue === 'string') {
displayValue = `"${error.invalidValue}"`;
} else if (error.invalidValue === null) {
displayValue = 'null';
} else if (error.invalidValue === undefined) {
displayValue = 'undefined';
} else {
displayValue = String(error.invalidValue);
}
newPathParts.push(displayValue);
} else {
// Keep other parts as-is
newPathParts.push(part);
}
}
modifiedPath = newPathParts.join('.');
}
setWith(tree, modifiedPath, error.message, Object);
});
return tree;
}
/**
* Groups and displays validation errors using treeify with deduplication
*/
export function ValidationErrorsList(t0) {
const $ = _c(9);
const {
errors
} = t0;
const [themeName] = useTheme();
if (errors.length === 0) {
return null;
}
let T0;
let t1;
let t2;
if ($[0] !== errors || $[1] !== themeName) {
const errorsByFile = errors.reduce(_temp, {});
const sortedFiles = Object.keys(errorsByFile).sort();
T0 = Box;
t1 = "column";
t2 = sortedFiles.map(file_0 => {
const fileErrors = errorsByFile[file_0] || [];
fileErrors.sort(_temp2);
const errorTree = buildNestedTree(fileErrors);
const suggestionPairs = new Map();
fileErrors.forEach(error_0 => {
if (error_0.suggestion || error_0.docLink) {
const key = `${error_0.suggestion || ""}|${error_0.docLink || ""}`;
if (!suggestionPairs.has(key)) {
suggestionPairs.set(key, {
suggestion: error_0.suggestion,
docLink: error_0.docLink
});
}
}
});
const treeOutput = treeify(errorTree, {
showValues: true,
themeName,
treeCharColors: {
treeChar: "inactive",
key: "text",
value: "inactive"
}
});
return <Box key={file_0} flexDirection="column"><Text>{file_0}</Text><Box marginLeft={1}><Text dimColor={true}>{treeOutput}</Text></Box>{suggestionPairs.size > 0 && <Box flexDirection="column" marginTop={1}>{Array.from(suggestionPairs.values()).map(_temp3)}</Box>}</Box>;
});
$[0] = errors;
$[1] = themeName;
$[2] = T0;
$[3] = t1;
$[4] = t2;
} else {
T0 = $[2];
t1 = $[3];
t2 = $[4];
}
let t3;
if ($[5] !== T0 || $[6] !== t1 || $[7] !== t2) {
t3 = <T0 flexDirection={t1}>{t2}</T0>;
$[5] = T0;
$[6] = t1;
$[7] = t2;
$[8] = t3;
} else {
t3 = $[8];
}
return t3;
}
function _temp3(pair, index) {
return <Box key={`suggestion-pair-${index}`} flexDirection="column" marginBottom={1}>{pair.suggestion && <Text dimColor={true} wrap="wrap">{pair.suggestion}</Text>}{pair.docLink && <Text dimColor={true} wrap="wrap">Learn more: {pair.docLink}</Text>}</Box>;
}
function _temp2(a, b) {
if (!a.path && b.path) {
return -1;
}
if (a.path && !b.path) {
return 1;
}
return (a.path || "").localeCompare(b.path || "");
}
function _temp(acc, error) {
const file = error.file || "(file not specified)";
if (!acc[file]) {
acc[file] = [];
}
acc[file].push(error);
return acc;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJzZXRXaXRoIiwiUmVhY3QiLCJCb3giLCJUZXh0IiwidXNlVGhlbWUiLCJWYWxpZGF0aW9uRXJyb3IiLCJUcmVlTm9kZSIsInRyZWVpZnkiLCJidWlsZE5lc3RlZFRyZWUiLCJlcnJvcnMiLCJ0cmVlIiwiZm9yRWFjaCIsImVycm9yIiwicGF0aCIsIm1lc3NhZ2UiLCJwYXRoUGFydHMiLCJzcGxpdCIsIm1vZGlmaWVkUGF0aCIsImludmFsaWRWYWx1ZSIsInVuZGVmaW5lZCIsImxlbmd0aCIsIm5ld1BhdGhQYXJ0cyIsImkiLCJwYXJ0IiwibnVtZXJpY1BhcnQiLCJwYXJzZUludCIsImlzTmFOIiwiZGlzcGxheVZhbHVlIiwiU3RyaW5nIiwicHVzaCIsImpvaW4iLCJPYmplY3QiLCJWYWxpZGF0aW9uRXJyb3JzTGlzdCIsInQwIiwiJCIsIl9jIiwidGhlbWVOYW1lIiwiVDAiLCJ0MSIsInQyIiwiZXJyb3JzQnlGaWxlIiwicmVkdWNlIiwiX3RlbXAiLCJzb3J0ZWRGaWxlcyIsImtleXMiLCJzb3J0IiwibWFwIiwiZmlsZV8wIiwiZmlsZUVycm9ycyIsImZpbGUiLCJfdGVtcDIiLCJlcnJvclRyZWUiLCJzdWdnZXN0aW9uUGFpcnMiLCJNYXAiLCJlcnJvcl8wIiwic3VnZ2VzdGlvbiIsImRvY0xpbmsiLCJrZXkiLCJoYXMiLCJzZXQiLCJ0cmVlT3V0cHV0Iiwic2hvd1ZhbHVlcyIsInRyZWVDaGFyQ29sb3JzIiwidHJlZUNoYXIiLCJ2YWx1ZSIsInNpemUiLCJBcnJheSIsImZyb20iLCJ2YWx1ZXMiLCJfdGVtcDMiLCJ0MyIsInBhaXIiLCJpbmRleCIsImEiLCJiIiwibG9jYWxlQ29tcGFyZSIsImFjYyJdLCJzb3VyY2VzIjpbIlZhbGlkYXRpb25FcnJvcnNMaXN0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgc2V0V2l0aCBmcm9tICdsb2Rhc2gtZXMvc2V0V2l0aC5qcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0LCB1c2VUaGVtZSB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgVmFsaWRhdGlvbkVycm9yIH0gZnJvbSAnLi4vdXRpbHMvc2V0dGluZ3MvdmFsaWRhdGlvbi5qcydcbmltcG9ydCB7IHR5cGUgVHJlZU5vZGUsIHRyZWVpZnkgfSBmcm9tICcuLi91dGlscy90cmVlaWZ5LmpzJ1xuXG4vKipcbiAqIEJ1aWxkcyBhIG5lc3RlZCB0cmVlIHN0cnVjdHVyZSBmcm9tIGRvdC1ub3RhdGlvbiBwYXRoc1xuICogVXNlcyBsb2Rhc2ggc2V0V2l0aCB0byBhdm9pZCBhdXRvbWF0aWMgYXJyYXkgY3JlYXRpb25cbiAqL1xuZnVuY3Rpb24gYnVpbGROZXN0ZWRUcmVlKGVycm9yczogVmFsaWRhdGlvbkVycm9yW10pOiBUcmVlTm9kZSB7XG4gIGNvbnN0IHRyZWU6IFRyZWVOb2RlID0ge31cblxuICBlcnJvcnMuZm9yRWFjaChlcnJvciA9PiB7XG4gICAgaWYgKCFlcnJvci5wYXRoKSB7XG4gICAgICAvLyBSb290IGxldmVsIGVycm9yIC0gdXNlIGVtcHR5IHN0cmluZyBhcyBrZXlcbiAgICAgIHRyZWVbJyddID0gZXJyb3IubWVzc2FnZVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gVHJ5IHRvIGVuaGFuY2UgdGhlIHBhdGggd2l0aCBtZWFuaW5nZnVsIHZhbHVlc1xuICAgIGNvbnN0IHBhdGhQYXJ0cyA9IGVycm9yLnBhdGguc3BsaXQoJy4nKVxuICAgIGxldCBtb2RpZmllZFBhdGggPSBlcnJvci5wYXRoXG5cbiAgICAvLyBJZiB3ZSBoYXZlIGFuIGludmFsaWQgdmFsdWUsIHRyeSB0byBtYWtlIHRoZSBwYXRoIG1vcmUgcmVhZGFibGVcbiAgICBpZiAoXG4gICAgICBlcnJvci5pbnZhbGlkVmFsdWUgIT09IG51bGwgJiZcbiAgICAgIGVycm9yLmludmFsaWRWYWx1ZSAhPT0gdW5kZWZpbmVkICYmXG4gICAgICBwYXRoUGFydHMubGVuZ3RoID4gMFxuICAgICkge1xuICAgICAgY29uc3QgbmV3UGF0aFBhcnRzOiBzdHJpbmdbXSA9IFtdXG5cbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGF0aFBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IHBhcnQgPSBwYXRoUGFydHNbaV1cbiAgICAgICAgaWYgKCFwYXJ0KSBjb250aW51ZVxuXG4gICAgICAgIGNvbnN0IG51bWVyaWNQYXJ0ID0gcGFyc2VJbnQocGFydCwgMTApXG5cbiAgICAgICAgLy8gSWYgdGhpcyBpcyBhIG51bWVyaWMgaW5kZXggYW5kIGl0J3MgdGhlIGxhc3QgcGFydCB3aGVyZSB3ZSBoYXZlIHRoZSBpbnZhbGlkIHZhbHVlXG4gICAgICAgIGlmICghaXNOYU4obnVtZXJpY1BhcnQpICYmIGkgPT09IHBhdGhQYXJ0cy5sZW5ndGggLSAxKSB7XG4gICAgICAgICAgLy8gRm9ybWF0IHRoZSB2YWx1ZSBmb3IgZGlzcGxheVxuICAgICAgICAgIGxldCBkaXNwbGF5VmFsdWU6IHN0cmluZ1xuICAgICAgICAgIGlmICh0eXBlb2YgZXJyb3IuaW52YWxpZFZhbHVlID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgZGlzcGxheVZhbHVlID0gYFwiJHtlcnJvci5pbnZhbGlkVmFsdWV9XCJgXG4gICAgICAgICAgfSBlbHNlIGlmIChlcnJvci5pbnZhbGlkVmFsdWUgPT09IG51bGwpIHtcbiAgICAgICAgICAgIGRpc3BsYXlWYWx1ZSA9ICdudWxsJ1xuICAgICAgICAgIH0gZWxzZSBpZiAoZXJyb3IuaW52YWxpZFZhbHVlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGRpc3BsYXlWYWx1ZSA9ICd1bmRlZmluZWQnXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRpc3BsYXlWYWx1ZSA9IFN0cmluZyhlcnJvci5pbnZhbGlkVmFsdWUpXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgbmV3UGF0aFBhcnRzLnB1c2goZGlzcGxheVZhbHVlKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIEtlZXAgb3RoZXIgcGFydHMgYXMtaXNcbiAgICAgICAgICBuZXdQYXRoUGFydHMucHVzaChwYXJ0KVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIG1vZGlmaWVkUGF0aCA9IG5ld1BhdGhQYXJ0cy5qb2luKCcuJylcbiAgICB9XG5cbiAgICBzZXRXaXRoKHRyZWUsIG1vZGlmaWVkUGF0aCwgZXJyb3IubWVzc2FnZSwgT2JqZWN0KVxuICB9KVxuXG4gIHJldHVybiB0cmVlXG59XG5cbi8qKlxuICogR3JvdXBzIGFuZCBkaXNwbGF5cyB2YWxpZGF0aW9uIGVycm9ycyB1c2luZyB0cmVlaWZ5IHdpdGggZGVkdXBsaWNhdGlvblxuICovXG5leHBvcnQgZnV