<commit_before>import * as t from './types';
import _ from 'lodash';

export interface GenerateOptions {
  /**
   * Enable/disable the default sorting (ascending) or provide your own sort function
   * @default true
   */
  sortProptypes?: boolean | ((a: t.PropTypeNode, b: t.PropTypeNode) => 0 | -1 | 1);

  /**
   * The name used when importing prop-types
   * @default 'PropTypes'
   */
  importedName?: string;

  /**
   * Enable/disable including JSDoc comments
   * @default true
   */
  includeJSDoc?: boolean;

  /**
   * Control which PropTypes are included in the final result
   * @param proptype The current PropType about to be converted to text
   */
  shouldInclude?(proptype: t.PropTypeNode): boolean;
}

/**
 * Generates code from the given node
 * @param node The node to convert to code
 * @param options The options used to control the way the code gets generated
 */
export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptions = {}): string {
  const {
    sortProptypes = true,
    importedName = 'PropTypes',
    includeJSDoc = true,
    shouldInclude,
  } = options;

  function jsDoc(node: t.PropTypeNode | t.LiteralNode) {
    if (!includeJSDoc || !node.jsDoc) {
      return '';
    }
    return `/**\n* ${node.jsDoc}\n*/\n`;
  }

  if (Array.isArray(node)) {
    let propTypes = node;

    if (typeof sortProptypes === 'function') {
      propTypes = propTypes.sort(sortProptypes);
    } else if (sortProptypes === true) {
      propTypes = propTypes.sort((a, b) => a.name.localeCompare(b.name));
    }

    let filteredNodes = node;
    if (shouldInclude) {
      filteredNodes = filteredNodes.filter(x => shouldInclude(x));
    }

    return filteredNodes
      .map(prop => generate(prop, options))
      .reduce((prev, curr) => `${prev}\n${curr}`);
  }

  if (t.isProgramNode(node)) {
    return node.body
      .map(prop => generate(prop, options))
      .reduce((prev, curr) => `${prev}\n${curr}`);
  }

  if (t.isComponentNode(node)) {
    return `${node.name}.propTypes = {\n${generate(node.types, options)}\n}`;
  }

  if (t.isPropTypeNode(node)) {
    let isOptional = false;
    let propType = { ...node.propType };

    if (t.isUnionNode(propType) && propType.types.some(t.isUndefinedNode)) {
      isOptional = true;
      propType.types = propType.types.filter(
        prop => !t.isUndefinedNode(prop) && !(t.isLiteralNode(prop) && prop.value === 'null'),
      );
      if (propType.types.length === 1 && t.isLiteralNode(propType.types[0]) === false) {
        propType = propType.types[0];
      }
    }

    return `${jsDoc(node)}${node.name}: ${generate(propType, options)}${
      isOptional ? '' : '.isRequired'
    },`;
  }

  if (t.isInterfaceNode(node)) {
    return `${importedName}.shape({\n${generate(node.types, {
      ...options,
      shouldInclude: undefined,
    })}\n})`;
  }

  if (t.isFunctionNode(node)) {
    return `${importedName}.func`;
  }

  if (t.isStringNode(node)) {
    return `${importedName}.string`;
  }

  if (t.isBooleanNode(node)) {
    return `${importedName}.bool`;
  }

  if (t.isNumericNode(node)) {
    return `${importedName}.number`;
  }

  if (t.isLiteralNode(node)) {
    return `${importedName}.oneOf([${jsDoc(node)}${node.value}])`;
  }

  if (t.isObjectNode(node)) {
    return `${importedName}.object`;
  }

  if (t.isAnyNode(node)) {
    return `${importedName}.any`;
  }

  if (t.isElementNode(node)) {
    return `${importedName}.${node.elementType}`;
  }

  if (t.isArrayNode(node)) {
    if (t.isAnyNode(node.arrayType)) {
      return `${importedName}.array`;
    }

    return `${importedName}.arrayOf(${generate(node.arrayType, options)})`;
  }

  if (t.isUnionNode(node)) {
    let [literals, rest] = _.partition(node.types, t.isLiteralNode);
    literals = _.uniqBy(literals, x => x.value);
    rest = _.uniqBy(rest, x => x.type);

    literals = literals.sort((a, b) => a.value.localeCompare(b.value));
    rest = rest.sort((a, b) => a.type.localeCompare(b.type));

    if (literals.find(x => x.value === 'true') && literals.find(x => x.value === 'false')) {
      rest.push(t.booleanNode());
      literals = literals.filter(x => x.value !== 'true' && x.value !== 'false');
    }

    const literalProps =
      literals.length !== 0
        ? `${importedName}.oneOf([${literals
            .map(x => `${jsDoc(x)}${x.value}`)
            .reduce((prev, curr) => `${prev},${curr}`)}])`
        : '';

    if (rest.length === 0) {
      return literalProps;
    }

    if (literals.length === 0 && rest.length === 1) {
      return generate(rest[0], options);
    }

    return `${importedName}.oneOfType([${literalProps ? literalProps + ', ' : ''}${rest
      .map(x => generate(x, options))
      .reduce((prev, curr) => `${prev},${curr}`)}])`;
  }

  throw new Error(`Nothing to handle node of type "${node.type}"`);
}
<commit_msg>fix(generator): multiline comments<commit_after>import * as t from './types';
import _ from 'lodash';

export interface GenerateOptions {
  /**
   * Enable/disable the default sorting (ascending) or provide your own sort function
   * @default true
   */
  sortProptypes?: boolean | ((a: t.PropTypeNode, b: t.PropTypeNode) => 0 | -1 | 1);

  /**
   * The name used when importing prop-types
   * @default 'PropTypes'
   */
  importedName?: string;

  /**
   * Enable/disable including JSDoc comments
   * @default true
   */
  includeJSDoc?: boolean;

  /**
   * Control which PropTypes are included in the final result
   * @param proptype The current PropType about to be converted to text
   */
  shouldInclude?(proptype: t.PropTypeNode): boolean;
}

/**
 * Generates code from the given node
 * @param node The node to convert to code
 * @param options The options used to control the way the code gets generated
 */
export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptions = {}): string {
  const {
    sortProptypes = true,
    importedName = 'PropTypes',
    includeJSDoc = true,
    shouldInclude,
  } = options;

  function jsDoc(node: t.PropTypeNode | t.LiteralNode) {
    if (!includeJSDoc || !node.jsDoc) {
      return '';
    }
    return `/**\n* ${node.jsDoc.split(/\r?\n/).reduce((prev, curr) => prev + '\n* ' + curr)}\n*/\n`;
  }

  if (Array.isArray(node)) {
    let propTypes = node;

    if (typeof sortProptypes === 'function') {
      propTypes = propTypes.sort(sortProptypes);
    } else if (sortProptypes === true) {
      propTypes = propTypes.sort((a, b) => a.name.localeCompare(b.name));
    }

    let filteredNodes = node;
    if (shouldInclude) {
      filteredNodes = filteredNodes.filter(x => shouldInclude(x));
    }

    return filteredNodes
      .map(prop => generate(prop, options))
      .reduce((prev, curr) => `${prev}\n${curr}`);
  }

  if (t.isProgramNode(node)) {
    return node.body
      .map(prop => generate(prop, options))
      .reduce((prev, curr) => `${prev}\n${curr}`);
  }

  if (t.isComponentNode(node)) {
    return `${node.name}.propTypes = {\n${generate(node.types, options)}\n}`;
  }

  if (t.isPropTypeNode(node)) {
    let isOptional = false;
    let propType = { ...node.propType };

    if (t.isUnionNode(propType) && propType.types.some(t.isUndefinedNode)) {
      isOptional = true;
      propType.types = propType.types.filter(
        prop => !t.isUndefinedNode(prop) && !(t.isLiteralNode(prop) && prop.value === 'null'),
      );
      if (propType.types.length === 1 && t.isLiteralNode(propType.types[0]) === false) {
        propType = propType.types[0];
      }
    }

    return `${jsDoc(node)}${node.name}: ${generate(propType, options)}${
      isOptional ? '' : '.isRequired'
    },`;
  }

  if (t.isInterfaceNode(node)) {
    return `${importedName}.shape({\n${generate(node.types, {
      ...options,
      shouldInclude: undefined,
    })}\n})`;
  }

  if (t.isFunctionNode(node)) {
    return `${importedName}.func`;
  }

  if (t.isStringNode(node)) {
    return `${importedName}.string`;
  }

  if (t.isBooleanNode(node)) {
    return `${importedName}.bool`;
  }

  if (t.isNumericNode(node)) {
    return `${importedName}.number`;
  }

  if (t.isLiteralNode(node)) {
    return `${importedName}.oneOf([${jsDoc(node)}${node.value}])`;
  }

  if (t.isObjectNode(node)) {
    return `${importedName}.object`;
  }

  if (t.isAnyNode(node)) {
    return `${importedName}.any`;
  }

  if (t.isElementNode(node)) {
    return `${importedName}.${node.elementType}`;
  }

  if (t.isArrayNode(node)) {
    if (t.isAnyNode(node.arrayType)) {
      return `${importedName}.array`;
    }

    return `${importedName}.arrayOf(${generate(node.arrayType, options)})`;
  }

  if (t.isUnionNode(node)) {
    let [literals, rest] = _.partition(node.types, t.isLiteralNode);
    literals = _.uniqBy(literals, x => x.value);
    rest = _.uniqBy(rest, x => x.type);

    literals = literals.sort((a, b) => a.value.localeCompare(b.value));
    rest = rest.sort((a, b) => a.type.localeCompare(b.type));

    if (literals.find(x => x.value === 'true') && literals.find(x => x.value === 'false')) {
      rest.push(t.booleanNode());
      literals = literals.filter(x => x.value !== 'true' && x.value !== 'false');
    }

    const literalProps =
      literals.length !== 0
        ? `${importedName}.oneOf([${literals
            .map(x => `${jsDoc(x)}${x.value}`)
            .reduce((prev, curr) => `${prev},${curr}`)}])`
        : '';

    if (rest.length === 0) {
      return literalProps;
    }

    if (literals.length === 0 && rest.length === 1) {
      return generate(rest[0], options);
    }

    return `${importedName}.oneOfType([${literalProps ? literalProps + ', ' : ''}${rest
      .map(x => generate(x, options))
      .reduce((prev, curr) => `${prev},${curr}`)}])`;
  }

  throw new Error(`Nothing to handle node of type "${node.type}"`);
}

{'fraction_non_alphanumeric': 0.18494338467815974, 'fraction_numerical': 0.002244210955829848, 'mean_word_length': 3.034567901234568, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '301197', 'n_tokens_mistral': 3361, 'n_tokens_neox': 3361, 'n_words': 688}