zod-opts
Version:
node.js CLI option parser / validator using Zod
272 lines (271 loc) • 8.87 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDefaultValue = getDefaultValue;
exports.toInternalType = toInternalType;
exports.toInternalTypeForZodUnion = toInternalTypeForZodUnion;
exports.optionToInternal = optionToInternal;
exports.positionalArgumentToInternal = positionalArgumentToInternal;
const compat_1 = require("./compat");
const type_1 = require("./type");
const util_1 = require("./util");
const TYPE_NAME_MAP = {
ZodString: "string",
ZodNumber: "number",
ZodBoolean: "boolean",
ZodArray: "array",
ZodObject: "object",
ZodUnion: "union",
ZodOptional: "optional",
ZodDefault: "default",
ZodEnum: "enum",
ZodEffects: "effects",
ZodPipeline: "pipe",
ZodNullable: "nullable",
ZodBranded: "branded",
ZodPromise: "promise",
ZodReadonly: "readonly",
};
function normalizeTypeName(typeName) {
var _a;
return (_a = TYPE_NAME_MAP[typeName]) !== null && _a !== void 0 ? _a : typeName;
}
function getTypeName(def) {
const raw = typeof def.typeName === "string" ? def.typeName : def.type;
return normalizeTypeName(typeof raw === "string" ? raw : "");
}
function getWrappedSchema(def) {
if ((0, compat_1.isZodSchema)(def.innerType)) {
return def.innerType;
}
if ((0, compat_1.isZodSchema)(def.schema)) {
return def.schema;
}
if ((0, compat_1.isZodSchema)(def.inner)) {
return def.inner;
}
if ((0, compat_1.isZodSchema)(def.in)) {
return def.in;
}
return undefined;
}
function getWrappedDef(def) {
const wrapped = getWrappedSchema(def);
if (wrapped == null) {
return undefined;
}
return (0, compat_1.getDef)(wrapped);
}
function getDefaultValue(def, allowOptionalDefault = false) {
const resolveDefault = (currentDef, optionalSeen) => {
if (currentDef == null || typeof currentDef !== "object") {
return undefined;
}
const typedDef = currentDef;
const typeName = getTypeName(typedDef);
const nextOptionalSeen = optionalSeen || typeName === "optional";
if ("defaultValue" in typedDef) {
if (nextOptionalSeen && !allowOptionalDefault) {
return undefined;
}
const value = typedDef.defaultValue;
if (typeof value === "function") {
return value();
}
return value;
}
const next = getWrappedDef(typedDef);
if (next == null) {
return undefined;
}
return resolveDefault(next, nextOptionalSeen);
};
const defaultValue = resolveDefault(def, false);
const isPrimitive = [...type_1.BASE_TYPES, "undefined"].includes(typeof defaultValue);
if (!isPrimitive && !Array.isArray(defaultValue)) {
throw new Error(`Unsupported default value: ${JSON.stringify(defaultValue)}`);
}
if (Array.isArray(defaultValue)) {
if (defaultValue.length === 0) {
return defaultValue;
}
const firstType = typeof defaultValue[0];
if (!["string", "number"].includes(firstType)) {
throw new Error(`Unsupported default value: ${JSON.stringify(defaultValue)}`);
}
if ((0, util_1.uniq)(defaultValue.map((value) => typeof value)).length > 1) {
throw new Error(`Unsupported default value: ${JSON.stringify(defaultValue)}`);
}
return defaultValue;
}
return defaultValue;
}
function isZodOptional(def) {
let current = def;
while (current != null) {
if (getTypeName(current) === "optional") {
return true;
}
current = getWrappedDef(current);
}
return false;
}
function isRequired(def) {
if (isZodOptional(def)) {
return false;
}
if (getDefaultValue(def) !== undefined) {
return false;
}
if (getTypeName(def) === "union") {
const options = Array.isArray(def.options) ? def.options : [];
return !options.some((option) => {
if (!(0, compat_1.isZodSchema)(option)) {
return false;
}
return !isRequired((0, compat_1.getDef)(option));
});
}
return true;
}
function resolveInnerType(def) {
let current = def;
while (true) {
const typeName = getTypeName(current);
if (["optional", "default"].includes(typeName)) {
const next = getWrappedDef(current);
if (next == null) {
return current;
}
current = next;
continue;
}
if ([
"effects",
"pipe",
"branded",
"nullable",
"promise",
"readonly",
].includes(typeName)) {
const next = getWrappedDef(current);
if (next == null) {
return current;
}
current = next;
continue;
}
return current;
}
}
function toInternalType(def, isPositional = false) {
const solvedDef = resolveInnerType(def);
const typeName = getTypeName(solvedDef);
switch (typeName) {
case "number":
return "number";
case "string":
return "string";
case "boolean":
if (isPositional) {
throw new Error(`Unsupported zod type (positional argument): ${typeName}`);
}
return "boolean";
case "enum":
return "string";
case "union":
return toInternalTypeForZodUnion(solvedDef);
case "array":
return toInternalTypeForZodArray(solvedDef);
default:
throw new Error(`Unsupported zod type: ${typeName}`);
}
}
function toInternalTypeForZodUnion(def) {
const options = Array.isArray(def.options) ? def.options : [];
const types = (0, util_1.uniq)(options.flatMap((option) => {
if (!(0, compat_1.isZodSchema)(option)) {
return [];
}
return [toInternalType((0, compat_1.getDef)(option))];
}));
if (types.length !== 1) {
throw new Error("Union types are not same");
}
return types[0];
}
function toInternalTypeForZodArray(def) {
const elementSchema = (0, compat_1.isZodSchema)(def.element)
? def.element
: (0, compat_1.isZodSchema)(def.type)
? def.type
: undefined;
if (elementSchema == null) {
throw new Error("Array element type not found");
}
const elementDef = (0, compat_1.getDef)(elementSchema);
const typeName = getTypeName(elementDef);
switch (typeName) {
case "string":
return "string";
case "number":
return "number";
default:
throw new Error(`Unsupported zod type: Array of ${typeName}`);
}
}
function getEnumValues(def) {
const solvedDef = resolveInnerType(def);
if (getTypeName(solvedDef) !== "enum") {
return undefined;
}
if (Array.isArray(solvedDef.values) &&
solvedDef.values.every((value) => typeof value === "string")) {
return solvedDef.values;
}
if ((0, compat_1.isRecord)(solvedDef.entries)) {
return Object.keys(solvedDef.entries);
}
return undefined;
}
function optionToInternal(option, name) {
var _a;
const zodType = option.type;
const def = (0, compat_1.getDef)(zodType);
const defaultValue = getDefaultValue(def, (0, compat_1.isZodV4)(zodType));
const internalType = toInternalType(def);
const resolvedDef = resolveInnerType(def);
const resolvedTypeName = getTypeName(resolvedDef);
const description = (_a = option.description) !== null && _a !== void 0 ? _a : (0, compat_1.getDescription)(zodType);
const enumValues = getEnumValues(def);
return {
type: internalType,
name,
alias: option.alias,
argumentName: option.argumentName,
description,
required: isRequired(def),
isArray: resolvedTypeName === "array",
defaultValue,
enumValues,
};
}
function positionalArgumentToInternal(option) {
var _a;
const zodType = option.type;
const def = (0, compat_1.getDef)(zodType);
const defaultValue = getDefaultValue(def, (0, compat_1.isZodV4)(zodType));
const internalType = toInternalType(def, true);
const resolvedDef = resolveInnerType(def);
const resolvedTypeName = getTypeName(resolvedDef);
const description = (_a = option.description) !== null && _a !== void 0 ? _a : (0, compat_1.getDescription)(zodType);
const enumValues = getEnumValues(def);
return {
type: internalType,
name: option.name,
description,
required: isRequired(def),
isArray: resolvedTypeName === "array",
defaultValue,
enumValues,
};
}