src/main/options-normalizer.js/optionInfoToSchema
function optionInfoToSchema(
  optionInfo,
  { isCLI, optionInfos, colorsModule, levenshteinDistance }
) {
  const { name } = optionInfo;

  if (name === "plugin-search-dir" || name === "pluginSearchDirs") {
    return vnopts.AnySchema.create({
      // @ts-expect-error
      name,
      preprocess(value) {
        if (value === false) {
          return value;
        }
        value = Array.isArray(value) ? value : [value];
        return value;
      },
      /**
       * @param {Array<unknown> | false} value
       */
      validate(value) {
        if (value === false) {
          return true;
        }
        return value.every((dir) => typeof dir === "string");
      },
      expected() {
        return "false or paths to plugin search dir";
      },
    });
  }

  const parameters = { name };
  let SchemaConstructor;
  const handlers = {};

  switch (optionInfo.type) {
    case "int":
      SchemaConstructor = vnopts.IntegerSchema;
      if (isCLI) {
        parameters.preprocess = Number;
      }
      break;
    case "string":
      SchemaConstructor = vnopts.StringSchema;
      break;
    case "choice":
      SchemaConstructor = vnopts.ChoiceSchema;
      parameters.choices = optionInfo.choices.map((choiceInfo) =>
        typeof choiceInfo === "object" && choiceInfo.redirect
          ? {
              ...choiceInfo,
              redirect: {
                to: { key: optionInfo.name, value: choiceInfo.redirect },
              },
            }
          : choiceInfo
      );
      break;
    case "boolean":
      SchemaConstructor = vnopts.BooleanSchema;
      break;
    case "flag":
      SchemaConstructor = getFlagSchema({ colorsModule, levenshteinDistance });
      parameters.flags = optionInfos.flatMap((optionInfo) =>
        [
          optionInfo.alias,
          optionInfo.description && optionInfo.name,
          optionInfo.oppositeDescription && `no-${optionInfo.name}`,
        ].filter(Boolean)
      );
      break;
    case "path":
      SchemaConstructor = vnopts.StringSchema;
      break;
    default:
      /* istanbul ignore next */
      throw new Error(`Unexpected type ${optionInfo.type}`);
  }

  if (optionInfo.exception) {
    parameters.validate = (value, schema, utils) =>
      optionInfo.exception(value) || schema.validate(value, utils);
  } else {
    parameters.validate = (value, schema, utils) =>
      value === undefined || schema.validate(value, utils);
  }

  /* istanbul ignore next */
  if (optionInfo.redirect) {
    handlers.redirect = (value) =>
      !value
        ? undefined
        : {
            to: {
              key: optionInfo.redirect.option,
              value: optionInfo.redirect.value,
            },
          };
  }

  /* istanbul ignore next */
  if (optionInfo.deprecated) {
    handlers.deprecated = true;
  }

  // allow CLI overriding, e.g., prettier package.json --tab-width 1 --tab-width 2
  if (isCLI && !optionInfo.array) {
    const originalPreprocess = parameters.preprocess || ((x) => x);
    parameters.preprocess = (value, schema, utils) =>
      schema.preprocess(
        originalPreprocess(Array.isArray(value) ? getLast(value) : value),
        utils
      );
  }

  return optionInfo.array
    ? vnopts.ArraySchema.create({
        ...(isCLI ? { preprocess: (v) => (Array.isArray(v) ? v : [v]) } : {}),
        ...handlers,
        // @ts-expect-error
        valueSchema: SchemaConstructor.create(parameters),
      })
    : SchemaConstructor.create({ ...parameters, ...handlers });
}