text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.lookupUnmatched = exports.convertSmartDefaultsIntoNamedParams = exports.warnDeprecations = exports.combineOptionsForGenerator = exports.combineOptionsForExecutor = exports.applyVerbosity = exports.setDefaults = exports.validateObject = exports.validateOptsAgainstSchema = exports.SchemaError = exports.convertAliases = exports.coerceTypesInOptions = exports.convertToCamelCase = exports.handleErrors = void 0;
  const tslib_1 = require("tslib");
  const logger_1 = require("./logger");
  function handleErrors(isVerbose, fn) {
      return tslib_1.__awaiter(this, void 0, void 0, function* () {
          try {
              return yield fn();
          }
          catch (err) {
              if (err.constructor.name === 'UnsuccessfulWorkflowExecution') {
                  logger_1.logger.error('The generator workflow failed. See above.');
              }
              else if (err.message) {
                  logger_1.logger.error(err.message);
              }
              else {
                  logger_1.logger.error(err);
              }
              if (isVerbose && err.stack) {
                  logger_1.logger.info(err.stack);
              }
              return 1;
          }
      });
  }
  exports.handleErrors = handleErrors;
  function camelCase(input) {
      if (input.indexOf('-') > 1) {
          return input
              .toLowerCase()
              .replace(/-(.)/g, (match, group1) => group1.toUpperCase());
      }
      else {
          return input;
      }
  }
  function convertToCamelCase(parsed) {
      return Object.keys(parsed).reduce((m, c) => (Object.assign(Object.assign({}, m), { [camelCase(c)]: parsed[c] })), {});
  }
  exports.convertToCamelCase = convertToCamelCase;
  /**
   * Coerces (and replaces) options identified as 'boolean' or 'number' in the Schema
   *
   * @param opts The options to check
   * @param schema The schema definition with types to check against
   *
   */
  function coerceTypesInOptions(opts, schema) {
      Object.keys(opts).forEach((k) => {
          const prop = findSchemaForProperty(k, schema);
          opts[k] = coerceType(prop === null || prop === void 0 ? void 0 : prop.description, opts[k]);
      });
      return opts;
  }
  exports.coerceTypesInOptions = coerceTypesInOptions;
  function coerceType(prop, value) {
      if (!prop)
          return value;
      if (typeof value !== 'string' && value !== undefined)
          return value;
      if (prop.oneOf) {
          for (let i = 0; i < prop.oneOf.length; ++i) {
              const coerced = coerceType(prop.oneOf[i], value);
              if (coerced !== value) {
                  return coerced;
              }
          }
          return value;
      }
      else if (normalizedPrimitiveType(prop.type) == 'boolean' &&
          isConvertibleToBoolean(value)) {
          return value === true || value == 'true';
      }
      else if (normalizedPrimitiveType(prop.type) == 'number' &&
          isConvertibleToNumber(value)) {
          return Number(value);
      }
      else if (prop.type == 'array') {
          return value.split(',').map((v) => coerceType(prop.items, v));
      }
      else {
          return value;
      }
  }
  /**
   * Converts any options passed in with short aliases to their full names if found
   * Unmatched options are added to opts['--']
   *
   * @param opts The options passed in by the user
   * @param schema The schema definition to check against
   */
  function convertAliases(opts, schema, excludeUnmatched) {
      return Object.keys(opts).reduce((acc, k) => {
          const prop = findSchemaForProperty(k, schema);
          if (prop) {
              acc[prop.name] = opts[k];
          }
          else if (excludeUnmatched) {
              if (!acc['--']) {
                  acc['--'] = [];
              }
              acc['--'].push({
                  name: k,
                  possible: [],
              });
          }
          else {
              acc[k] = opts[k];
          }
          return acc;
      }, {});
  }
  exports.convertAliases = convertAliases;
  class SchemaError {
      constructor(message) {
          this.message = message;
      }
  }
  exports.SchemaError = SchemaError;
  function validateOptsAgainstSchema(opts, schema) {
      validateObject(opts, schema.properties || {}, schema.required || [], schema.additionalProperties, schema.definitions || {});
  }
  exports.validateOptsAgainstSchema = validateOptsAgainstSchema;
  function validateObject(opts, properties, required, additionalProperties, definitions) {
      required.forEach((p) => {
          if (opts[p] === undefined) {
              throw new SchemaError(`Required property '${p}' is missing`);
          }
      });
      if (additionalProperties === false) {
          Object.keys(opts).find((p) => {
              if (Object.keys(properties).indexOf(p) === -1) {
                  throw new SchemaError(`'${p}' is not found in schema`);
              }
          });
      }
      Object.keys(opts).forEach((p) => {
          validateProperty(p, opts[p], properties[p], definitions);
      });
  }
  exports.validateObject = validateObject;
  function validateProperty(propName, value, schema, definitions) {
      if (!schema)
          return;
      if (schema.$ref) {
          schema = resolveDefinition(schema.$ref, definitions);
      }
      if (schema.oneOf) {
          if (!Array.isArray(schema.oneOf))
              throw new Error(`Invalid schema file. oneOf must be an array.`);
          const passes = schema.oneOf.filter((r) => {
              try {
                  const rule = Object.assign({ type: schema.type }, r);
                  validateProperty(propName, value, rule, definitions);
                  return true;
              }
              catch (e) {
                  return false;
              }
          }).length === 1;
          if (!passes)
              throwInvalidSchema(propName, schema);
          return;
      }
      if (schema.anyOf) {
          if (!Array.isArray(schema.anyOf))
              throw new Error(`Invalid schema file. anyOf must be an array.`);
          let passes = false;
          schema.anyOf.forEach((r) => {
              try {
                  const rule = Object.assign({ type: schema.type }, r);
                  validateProperty(propName, value, rule, definitions);
                  passes = true;
              }
              catch (e) { }
          });
          if (!passes)
              throwInvalidSchema(propName, schema);
          return;
      }
      if (schema.allOf) {
          if (!Array.isArray(schema.allOf))
              throw new Error(`Invalid schema file. anyOf must be an array.`);
          if (!schema.allOf.every((r) => {
              try {
                  const rule = Object.assign({ type: schema.type }, r);
                  validateProperty(propName, value, rule, definitions);
                  return true;
              }
              catch (e) {
                  return false;
              }
          })) {
              throwInvalidSchema(propName, schema);
          }
          return;
      }
      const isPrimitive = typeof value !== 'object';
      if (isPrimitive) {
          if (schema.type && typeof value !== normalizedPrimitiveType(schema.type)) {
              throw new SchemaError(`Property '${propName}' does not match the schema. '${value}' should be a '${schema.type}'.`);
          }
          if (schema.enum && !schema.enum.includes(value)) {
              throw new SchemaError(`Property '${propName}' does not match the schema. '${value}' should be one of ${schema.enum.join(',')}.`);
          }
          if (schema.type === 'number') {
              if (typeof schema.multipleOf === 'number' &&
                  value % schema.multipleOf !== 0) {
                  throw new SchemaError(`Property '${propName}' does not match the schema. ${value} should be a multiple of ${schema.multipleOf}.`);
              }
              if (typeof schema.minimum === 'number' && value < schema.minimum) {
                  throw new SchemaError(`Property '${propName}' does not match the schema. ${value} should be at least ${schema.minimum}`);
              }
              if (typeof schema.exclusiveMinimum === 'number' &&
                  value <= schema.exclusiveMinimum) {
                  throw new SchemaError(`Property '${propName}' does not match the schema. ${value} should be greater than ${schema.exclusiveMinimum}`);
              }
              if (typeof schema.maximum === 'number' && value > schema.maximum) {
                  throw new SchemaError(`Property '${propName}' does not match the schema. ${value} should be at most ${schema.maximum}`);
              }
              if (typeof schema.exclusiveMaximum === 'number' &&
                  value >= schema.exclusiveMaximum) {
                  throw new SchemaError(`Property '${propName}' does not match the schema. ${value} should be less than ${schema.exclusiveMaximum}`);
              }
          }
          if (schema.type === 'string') {
              if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
                  throw new SchemaError(`Property '${propName}' does not match the schema. '${value}' should match the pattern '${schema.pattern}'.`);
              }
              if (typeof schema.minLength === 'number' &&
                  value.length < schema.minLength) {
                  throw new SchemaError(`Property '${propName}' does not match the schema. '${value}' (${value.length} character(s)) should have at least ${schema.minLength} character(s).`);
              }
              if (typeof schema.maxLength === 'number' &&
                  value.length > schema.maxLength) {
                  throw new SchemaError(`Property '${propName}' does not match the schema. '${value}' (${value.length} character(s)) should have at most ${schema.maxLength} character(s).`);
              }
          }
      }
      else if (Array.isArray(value)) {
          if (schema.type !== 'array')
              throwInvalidSchema(propName, schema);
          value.forEach((valueInArray) => validateProperty(propName, valueInArray, schema.items || {}, definitions));
      }
      else {
          if (schema.type !== 'object')
              throwInvalidSchema(propName, schema);
          validateObject(value, schema.properties || {}, schema.required || [], schema.additionalProperties, definitions);
      }
  }
  /**
   * Unfortunately, due to use supporting Angular Devkit, we have to do the following
   * conversions.
   */
  function normalizedPrimitiveType(type) {
      if (type === 'integer')
          return 'number';
      return type;
  }
  function throwInvalidSchema(propName, schema) {
      throw new SchemaError(`Property '${propName}' does not match the schema.\n${JSON.stringify(schema, null, 2)}'`);
  }
  function setDefaults(opts, schema) {
      setDefaultsInObject(opts, schema.properties || {}, schema.definitions || {});
      return opts;
  }
  exports.setDefaults = setDefaults;
  function setDefaultsInObject(opts, properties, definitions) {
      Object.keys(properties).forEach((p) => {
          setPropertyDefault(opts, p, properties[p], definitions);
      });
  }
  function setPropertyDefault(opts, propName, schema, definitions) {
      if (schema.$ref) {
          schema = resolveDefinition(schema.$ref, definitions);
      }
      if (schema.type !== 'object' && schema.type !== 'array') {
          if (opts[propName] === undefined && schema.default !== undefined) {
              opts[propName] = schema.default;
          }
      }
      else if (schema.type === 'array') {
          const items = schema.items || {};
          if (opts[propName] &&
              Array.isArray(opts[propName]) &&
              items.type === 'object') {
              opts[propName].forEach((valueInArray) => setDefaultsInObject(valueInArray, items.properties || {}, definitions));
          }
          else if (!opts[propName] && schema.default) {
              opts[propName] = schema.default;
          }
      }
      else {
          const wasUndefined = opts[propName] === undefined;
          if (wasUndefined) {
              // We need an object to set values onto
              opts[propName] = {};
          }
          setDefaultsInObject(opts[propName], schema.properties || {}, definitions);
          // If the property was initially undefined but no properties were added, we remove it again instead of having an {}
          if (wasUndefined && Object.keys(opts[propName]).length === 0) {
              delete opts[propName];
          }
      }
  }
  function resolveDefinition(ref, definitions) {
      if (!ref.startsWith('#/definitions/')) {
          throw new Error(`$ref should start with "#/definitions/"`);
      }
      const definition = ref.split('#/definitions/')[1];
      if (!definitions[definition]) {
          throw new Error(`Cannot resolve ${ref}`);
      }
      return definitions[definition];
  }
  function applyVerbosity(options, schema, isVerbose) {
      if ((schema.additionalProperties || 'verbose' in schema.properties) &&
          isVerbose) {
          options['verbose'] = true;
      }
  }
  exports.applyVerbosity = applyVerbosity;
  function combineOptionsForExecutor(commandLineOpts, config, target, schema, defaultProjectName, relativeCwd, isVerbose = false) {
      const r = convertAliases(coerceTypesInOptions(commandLineOpts, schema), schema, false);
      let combined = target.options || {};
      if (config && target.configurations && target.configurations[config]) {
          Object.assign(combined, target.configurations[config]);
      }
      combined = convertAliases(combined, schema, false);
      Object.assign(combined, r);
      convertSmartDefaultsIntoNamedParams(combined, schema, commandLineOpts['_'] || [], defaultProjectName, relativeCwd);
      warnDeprecations(combined, schema);
      setDefaults(combined, schema);
      validateOptsAgainstSchema(combined, schema);
      applyVerbosity(combined, schema, isVerbose);
      return combined;
  }
  exports.combineOptionsForExecutor = combineOptionsForExecutor;
  function combineOptionsForGenerator(commandLineOpts, collectionName, generatorName, wc, schema, isInteractive, defaultProjectName, relativeCwd, isVerbose = false) {
      return tslib_1.__awaiter(this, void 0, void 0, function* () {
          const generatorDefaults = wc
              ? getGeneratorDefaults(defaultProjectName, wc, collectionName, generatorName)
              : {};
          let combined = convertAliases(coerceTypesInOptions(Object.assign(Object.assign({}, generatorDefaults), commandLineOpts), schema), schema, false);
          convertSmartDefaultsIntoNamedParams(combined, schema, commandLineOpts['_'] || [], defaultProjectName, relativeCwd);
          if (isInteractive && isTTY()) {
              combined = yield promptForValues(combined, schema);
          }
          warnDeprecations(combined, schema);
          setDefaults(combined, schema);
          validateOptsAgainstSchema(combined, schema);
          applyVerbosity(combined, schema, isVerbose);
          return combined;
      });
  }
  exports.combineOptionsForGenerator = combineOptionsForGenerator;
  function warnDeprecations(opts, schema) {
      Object.keys(opts).forEach((option) => {
          var _a;
          const deprecated = (_a = schema.properties[option]) === null || _a === void 0 ? void 0 : _a['x-deprecated'];
          if (deprecated) {
              logger_1.logger.warn(`Option "${option}" is deprecated${typeof deprecated == 'string' ? ': ' + deprecated : '.'}`);
          }
      });
  }
  exports.warnDeprecations = warnDeprecations;
  function convertSmartDefaultsIntoNamedParams(opts, schema, argv, defaultProjectName, relativeCwd) {
      Object.entries(schema.properties).forEach(([k, v]) => {
          if (opts[k] === undefined &&
              v.$default !== undefined &&
              v.$default.$source === 'argv' &&
              argv[v.$default.index]) {
              opts[k] = coerceType(v, argv[v.$default.index]);
          }
          else if (opts[k] === undefined &&
              v.$default !== undefined &&
              v.$default.$source === 'projectName' &&
              defaultProjectName) {
              opts[k] = defaultProjectName;
          }
          else if (opts[k] === undefined &&
              v.format === 'path' &&
              v.visible === false &&
              relativeCwd) {
              opts[k] = relativeCwd.replace(/\\/g, '/');
          }
      });
      delete opts['_'];
  }
  exports.convertSmartDefaultsIntoNamedParams = convertSmartDefaultsIntoNamedParams;
  function getGeneratorDefaults(projectName, wc, collectionName, generatorName) {
      let defaults = {};
      if (wc === null || wc === void 0 ? void 0 : wc.generators) {
          if (wc.generators[collectionName] &&
              wc.generators[collectionName][generatorName]) {
              defaults = Object.assign(Object.assign({}, defaults), wc.generators[collectionName][generatorName]);
          }
          if (wc.generators[`${collectionName}:${generatorName}`]) {
              defaults = Object.assign(Object.assign({}, defaults), wc.generators[`${collectionName}:${generatorName}`]);
          }
      }
      if (projectName &&
          wc &&
          wc.projects[projectName] &&
          wc.projects[projectName].generators) {
          const g = wc.projects[projectName].generators;
          if (g[collectionName] && g[collectionName][generatorName]) {
              defaults = Object.assign(Object.assign({}, defaults), g[collectionName][generatorName]);
          }
          if (g[`${collectionName}:${generatorName}`]) {
              defaults = Object.assign(Object.assign({}, defaults), g[`${collectionName}:${generatorName}`]);
          }
      }
      return defaults;
  }
  function promptForValues(opts, schema) {
      return tslib_1.__awaiter(this, void 0, void 0, function* () {
          const prompts = [];
          Object.entries(schema.properties).forEach(([k, v]) => {
              if (v['x-prompt'] && opts[k] === undefined) {
                  const question = {
                      name: k,
                  };
                  if (v.default) {
                      question.initial = v.default;
                  }
                  if (typeof v['x-prompt'] === 'string') {
                      question.message = v['x-prompt'];
                      if (v.type === 'string' && v.enum && Array.isArray(v.enum)) {
                          question.type = 'select';
                          question.choices = [...v.enum];
                      }
                      else {
                          question.type = v.type === 'boolean' ? 'confirm' : 'input';
                      }
                  }
                  else if (v['x-prompt'].type == 'number') {
                      question.message = v['x-prompt'].message;
                      question.type = 'numeral';
                  }
                  else if (v['x-prompt'].type == 'confirmation' ||
                      v['x-prompt'].type == 'confirm') {
                      question.message = v['x-prompt'].message;
                      question.type = 'confirm';
                  }
                  else {
                      question.message = v['x-prompt'].message;
                      question.type = v['x-prompt'].multiselect ? 'multiselect' : 'select';
                      question.choices =
                          v['x-prompt'].items &&
                              v['x-prompt'].items.map((item) => {
                                  if (typeof item == 'string') {
                                      return item;
                                  }
                                  else {
                                      return {
                                          message: item.label,
                                          name: item.value,
                                      };
                                  }
                              });
                  }
                  prompts.push(question);
              }
          });
          return yield (yield Promise.resolve().then(() => require('enquirer')))
              .prompt(prompts)
              .then((values) => (Object.assign(Object.assign({}, opts), values)))
              .catch((e) => {
              console.error(e);
              process.exit(0);
          });
      });
  }
  /**
   * Tries to find what the user meant by unmatched commands
   *
   * @param opts The options passed in by the user
   * @param schema The schema definition to check against
   *
   */
  function lookupUnmatched(opts, schema) {
      if (opts['--']) {
          const props = Object.keys(schema.properties);
          opts['--'].forEach((unmatched) => {
              unmatched.possible = props.filter((p) => levenshtein(p, unmatched.name) < 3);
          });
      }
      return opts;
  }
  exports.lookupUnmatched = lookupUnmatched;
  function findSchemaForProperty(propName, schema) {
      if (propName in schema.properties) {
          return {
              name: propName,
              description: schema.properties[propName],
          };
      }
      const found = Object.entries(schema.properties).find(([_, d]) => d.alias === propName ||
          (Array.isArray(d.aliases) && d.aliases.includes(propName)));
      if (found) {
          const [name, description] = found;
          return { name, description };
      }
      return null;
  }
  function levenshtein(a, b) {
      if (a.length == 0) {
          return b.length;
      }
      if (b.length == 0) {
          return a.length;
      }
      const matrix = [];
      for (let i = 0; i <= b.length; i++) {
          matrix[i] = [i];
      }
      for (let j = 0; j <= a.length; j++) {
          matrix[0][j] = j;
      }
      for (let i = 1; i <= b.length; i++) {
          for (let j = 1; j <= a.length; j++) {
              if (b.charAt(i - 1) == a.charAt(j - 1)) {
                  matrix[i][j] = matrix[i - 1][j - 1];
              }
              else {
                  matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
              }
          }
      }
      return matrix[b.length][a.length];
  }
  function isTTY() {
      return !!process.stdout.isTTY && process.env['CI'] !== 'true';
  }
  /**
   * Verifies whether the given value can be converted to a boolean
   * @param value
   */
  function isConvertibleToBoolean(value) {
      if ('boolean' === typeof value) {
          return true;
      }
      if ('string' === typeof value && /true|false/.test(value)) {
          return true;
      }
      return false;
  }
  /**
   * Verifies whether the given value can be converted to a number
   * @param value
   */
  function isConvertibleToNumber(value) {
      // exclude booleans explicitly
      if ('boolean' === typeof value) {
          return false;
      }
      return !isNaN(+value);
  }
  //# sourceMappingURL=params.js.map