UNPKG

zod-opts

Version:

node.js CLI option parser / validator using Zod

210 lines (209 loc) 8.13 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.validateCandidateValue = validateCandidateValue; exports.validatePositionalCandidateValue = validatePositionalCandidateValue; exports.validateMultipleCommands = validateMultipleCommands; exports.validate = validate; const error_1 = require("./error"); const internal_parser_1 = require("./internal_parser"); const logger_1 = require("./logger"); const util = __importStar(require("./util")); function validateCandidateValue(option, value, isNegative) { if (option.isArray) { if (value === undefined || !Array.isArray(value)) { return undefined; } switch (option.type) { case "string": // !isNegative is always true if (value.length === 0) { return undefined; } return { value }; case "number": // !isNegative is always true if (value === undefined || value.some((item) => !(0, internal_parser_1.isNumericValue)(item))) { return undefined; } return { value: value.map((item) => parseFloat(item)) }; } } if (Array.isArray(value)) { return undefined; } switch (option.type) { case "string": // !isNegative is always true if (value === undefined) { return undefined; } return { value }; case "number": // !isNegative is always true if (value === undefined || !(0, internal_parser_1.isNumericValue)(value)) { return undefined; } return { value: parseFloat(value) }; case "boolean": if (value !== undefined) { // --flag=10 is invalid return undefined; } if (isNegative) return { value: false }; return { value: true }; } } function validatePositionalCandidateValue(option, value) { if (option.isArray) { if (!Array.isArray(value)) { return undefined; } switch (option.type) { case "string": return { value }; case "number": if (value.some((v) => !(0, internal_parser_1.isNumericValue)(v))) { return undefined; } return { value: value.map((v) => parseFloat(v)), }; } } switch (option.type) { case "string": return { value }; case "number": if (!(0, internal_parser_1.isNumericValue)(value)) { return undefined; } return { value: parseFloat(value) }; } } function validateMultipleCommands(parsed, options, positionalArgs, commandName) { try { return validate(parsed, options, positionalArgs); } catch (e) { if (e instanceof error_1.ParseError) { e.commandName = commandName; } throw e; } } function validateOptions(candidates, options) { const optionMap = new Map(options.map((option) => [option.name, option])); const validValues = candidates.map((candidate) => { const option = optionMap.get(candidate.name); if (option === undefined) { throw new error_1.ParseError(`Unknown option: ${candidate.name}`); } const validated = validateCandidateValue(option, candidate.value, candidate.isNegative); if (validated === undefined) { throw new error_1.ParseError(`Invalid option value. ${option.type} is expected: ${candidate.name}`); } return [candidate.name, validated.value]; }); (0, logger_1.debugLog)("validateOptions", { validValues }); const arrayTypeMerged = options.flatMap((opt) => { const nameValues = validValues.filter(([name]) => name === opt.name); if (!opt.isArray) { return nameValues; } const values = nameValues.map(([, value]) => value); if (values.length === 0) { return []; } return [[opt.name, values.flat()]]; }); const duplicateOptionNames = util.findDuplicateValues(arrayTypeMerged.map(([name]) => name)); if (duplicateOptionNames.length !== 0) { throw new error_1.ParseError(`Duplicated option: ${duplicateOptionNames.join(", ")}`); } const validValueSet = new Map(arrayTypeMerged); return options.map((opt) => { if (!validValueSet.has(opt.name)) { if (!opt.required) { return { name: opt.name, value: undefined }; } throw new error_1.ParseError(`Required option is missing: ${opt.name}`); } return { name: opt.name, value: validValueSet.get(opt.name) }; }); } function validatePositionalArguments(candidates, positionalArgs) { const positionalArgMap = new Map(positionalArgs.map((option) => [option.name, option])); const validValues = candidates.map((candidate) => { const name = candidate.name; const positionalOption = positionalArgMap.get(name); if (positionalOption === undefined) { throw new error_1.ParseError(`Unknown positional argument: ${name}`); } const validated = validatePositionalCandidateValue(positionalOption, candidate.value); if (validated === undefined) { throw new error_1.ParseError(`Invalid positional argument value: ${name}`); } return [candidate.name, validated.value]; }); (0, logger_1.debugLog)("validatePositionalArguments", { validValues }); const duplicatedPositionalArgNames = util.findDuplicateValues(validValues.map(([name]) => name)); if (duplicatedPositionalArgNames.length !== 0) { throw new error_1.ParseError(`Duplicated positional argument: ${duplicatedPositionalArgNames.join(", ")}`); } const validValueSet = new Map(validValues); return positionalArgs.map((opt) => { if (!validValueSet.has(opt.name)) { if (!opt.required) { return { name: opt.name, value: undefined }; } } if (!validValueSet.has(opt.name)) { throw new error_1.ParseError(`Required argument is missing: ${opt.name}`); } return { name: opt.name, value: validValueSet.get(opt.name), }; }); } function validate(parsed, options, positionalArgs) { return { options: validateOptions(parsed.candidates, options), positionalArgs: validatePositionalArguments(parsed.positionalCandidates, positionalArgs), }; }