<commit_before>import inquirerAsync from 'inquirer-async';
import ProgressBar from 'progress';
import { Api, Exp, Logger, NotificationCode, MessageCode } from 'xdl';

import _ from 'lodash';
import log from '../log';

let _currentRequestID = 0;
let _downloadIsSlowPrompt = false;
let _retryObject = {};

async function action(projectDir, options) {
  let validatedOptions = {};
  let templateType;
  let questions = [];

  if (options.projectName) {
    validatedOptions.name = options.projectName;
  } else {
    questions.push({
      type: 'input',
      name: 'name',
      message: 'Project name',
      validate(val) {
        // TODO: Validate
        return val.length > 0;
      },
    });
  }

  if (options.projectType) {
    templateType = options.projectType;
  } else {
    let versions = await Api.versionsAsync();
    let templateIds = _.map(versions.templates, template => `"${template.id}"`);

    questions.push({
      type: 'input',
      name: 'type',
      message: `Project type. Options are: ${templateIds.join(', ')}`,
      validate(val) {
        for (let i = 0; i < versions.templates.length; i++) {
          if (versions.templates[i].id === val) {
            return true;
          }
        }
        return false;
      },
    });
  }

  if (questions.length > 0) {
    var answers = await inquirerAsync.promptAsync(questions);
    if (answers.name) {
      validatedOptions.name = answers.name;
    }
    if (answers.type) {
      templateType = answers.type;
    }
  }
  downloadAndExtractTemplate(templateType, projectDir, validatedOptions);
}

async function downloadAndExtractTemplate(
  templateType,
  projectDir,
  validatedOptions
) {
  _retryObject = { templateType, projectDir, validatedOptions };
  const requestID = _currentRequestID + 1;
  _currentRequestID = requestID;
  let templateDownload = await Exp.downloadTemplateApp(
    templateType,
    projectDir,
    {
      ...validatedOptions,
      progressFunction: progress => {
        const percent = Math.round(progress * 100);
        if (_currentRequestID === requestID) {
          Logger.notifications.info(
            { code: NotificationCode.DOWNLOAD_CLI_PROGRESS },
            percent + '%'
          );
          const bar = new ProgressBar('[:bar] :percent', {
            total: 100,
            complete: '=',
            incomplete: ' ',
          });
          if (!_downloadIsSlowPrompt) {
            bar.tick(percent);
          }
        }
      },
      retryFunction: () => {
        triggerRetryPrompt();
      },
    }
  );

  // Since we cannot cancel the download request, we need a way to ignore all of the requests made except the last one when retrying.
  if (_currentRequestID !== requestID) {
    return;
  }
  let root = await Exp.extractTemplateApp(
    templateDownload.starterAppPath,
    templateDownload.name,
    templateDownload.root
  );
  log(
    `Your project is ready at ${root}. Use "exp start ${root}" to get started.`
  );
  process.exit();
}

async function triggerRetryPrompt() {
  _downloadIsSlowPrompt = true;
  var answer = await inquirerAsync.promptAsync({
    type: 'input',
    name: 'retry',
    message: '\n' + MessageCode.DOWNLOAD_IS_SLOW + '(y/n)',
    validate(val) {
      if (val !== 'y' && val !== 'n') {
        return false;
      }
      return true;
    },
  });

  if (answer.retry === 'n') {
    _downloadIsSlowPrompt = false;
  } else {
    Exp.clearXDLCacheAsync();
    _downloadIsSlowPrompt = false;
    downloadAndExtractTemplate(
      _retryObject.templateType,
      _retryObject.projectDir,
      _retryObject.validatedOptions
    );
  }
}

export default program => {
  program
    .command('init [parent-of-project-dir]')
    .alias('i')
    .description(
      'Initializes a directory with an example project. Run it without any options and you will be prompted for the name and type.'
    )
    .option('-n, --projectName [name]', 'Specify a name for the new project')
    .option(
      '-t, --projectType [type]',
      'Specify what type of template to use. Run without this option to see all choices.'
    )
    .allowNonInteractive()
    .asyncActionProjectDir(action, true); // pass true to skip validation
};
<commit_msg>updates bin/init.js to behave like crna where the project folder is not duplicated (#236)<commit_after>import inquirerAsync from 'inquirer-async';
import ProgressBar from 'progress';
import { Api, Exp, Logger, NotificationCode, MessageCode } from 'xdl';

import _ from 'lodash';
import log from '../log';

let _currentRequestID = 0;
let _downloadIsSlowPrompt = false;
let _retryObject = {};

async function action(projectDir, options) {
  const givenPath = projectDir.split('/');
  let validatedOptions = {};
  let templateType;
  let questions = [];
  let insertPath = undefined;

  if (givenPath.length > 1) {
    insertPath = givenPath.slice(0, givenPath.length - 1).join('/');
  }

  if (givenPath.length) {
    validatedOptions.name = givenPath[givenPath.length - 1];
  }

  if (options.projectType) {
    templateType = options.projectType;
  } else {
    let versions = await Api.versionsAsync();
    let templateIds = _.map(versions.templates, template => `"${template.id}"`);

    questions.push({
      type: 'input',
      name: 'type',
      message: `Project type. Options are: ${templateIds.join(', ')}`,
      validate(val) {
        for (let i = 0; i < versions.templates.length; i++) {
          if (versions.templates[i].id === val) {
            return true;
          }
        }
        return false;
      },
    });
  }

  if (questions.length > 0) {
    var answers = await inquirerAsync.promptAsync(questions);
    if (answers.name) {
      validatedOptions.name = answers.name;
    }
    if (answers.type) {
      templateType = answers.type;
    }
  }

  // TODO(jim): We will need to update this method later to not force
  // us to strip out the <name> from /path/to/<name> if we don't want
  // to duplicate the folder at creation time. (example: test => test/test)
  downloadAndExtractTemplate(templateType, insertPath, validatedOptions);
}

async function downloadAndExtractTemplate(
  templateType,
  projectDir,
  validatedOptions
) {
  _retryObject = { templateType, projectDir, validatedOptions };
  const requestID = _currentRequestID + 1;
  _currentRequestID = requestID;
  let templateDownload = await Exp.downloadTemplateApp(
    templateType,
    projectDir,
    {
      ...validatedOptions,
      progressFunction: progress => {
        const percent = Math.round(progress * 100);
        if (_currentRequestID === requestID) {
          Logger.notifications.info(
            { code: NotificationCode.DOWNLOAD_CLI_PROGRESS },
            percent + '%'
          );
          const bar = new ProgressBar('[:bar] :percent', {
            total: 100,
            complete: '=',
            incomplete: ' ',
          });
          if (!_downloadIsSlowPrompt) {
            bar.tick(percent);
          }
        }
      },
      retryFunction: () => {
        triggerRetryPrompt();
      },
    }
  );

  // Since we cannot cancel the download request, we need a way to ignore all of the requests made except the last one when retrying.
  if (_currentRequestID !== requestID) {
    return;
  }
  let root = await Exp.extractTemplateApp(
    templateDownload.starterAppPath,
    templateDownload.name,
    templateDownload.root
  );
  log(
    `Your project is ready at ${root}. Use "exp start ${root}" to get started.`
  );
  process.exit();
}

async function triggerRetryPrompt() {
  _downloadIsSlowPrompt = true;
  var answer = await inquirerAsync.promptAsync({
    type: 'input',
    name: 'retry',
    message: '\n' + MessageCode.DOWNLOAD_IS_SLOW + '(y/n)',
    validate(val) {
      if (val !== 'y' && val !== 'n') {
        return false;
      }
      return true;
    },
  });

  if (answer.retry === 'n') {
    _downloadIsSlowPrompt = false;
  } else {
    Exp.clearXDLCacheAsync();
    _downloadIsSlowPrompt = false;
    downloadAndExtractTemplate(
      _retryObject.templateType,
      _retryObject.projectDir,
      _retryObject.validatedOptions
    );
  }
}

export default program => {
  program
    .command('init <project-directory>')
    .alias('i')
    .description(
      'Initializes a directory with an example project. Run it without any options and you will be prompted for the name and type.'
    )
    .option(
      '-t, --projectType [type]',
      'Specify what type of template to use. Run without this option to see all choices.'
    )
    .allowNonInteractive()
    .asyncActionProjectDir(action, true /* skipProjectValidation */);
};

{'fraction_non_alphanumeric': 0.12124376377770042, 'fraction_numerical': 0.0032486367327996288, 'mean_word_length': 2.6448202959830867, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '728433', 'n_tokens_mistral': 2597, 'n_tokens_neox': 2538, 'n_words': 684}