<commit_before>import fs from 'fs';
import path from 'path';
import tar from 'tar-fs';
import chalk from 'chalk';
import fetch from 'node-fetch';

// @ts-ignore
import listInput from '../../util/input/list';
import listItem from '../../util/output/list-item';
import promptBool from '../../util/input/prompt-bool';
import toHumanPath from '../../util/humanize-path';
import { Output } from '../../util/output';
import { NowContext } from '../../types';
import success from '../../util/output/success';
import info from '../../util/output/info';
import cmd from '../../util/output/cmd';
import didYouMean from '../../util/init/did-you-mean';

type Options = {
  '--debug': boolean;
  '--force': boolean;
  '-f': boolean;
};

type Example = {
  name: string;
  visible: boolean;
  suggestions: string[];
};

const EXAMPLE_API = 'https://now-example-files.zeit.sh';

export default async function init(
  ctx: NowContext,
  opts: Options,
  args: string[],
  output: Output
) {
  const [name, dir] = args;
  const force = opts['-f'] || opts['--force'];

  const examples = await fetchExampleList(output);

  if (!examples) {
    throw new Error(`Could not fetch example list.`);
  }

  const exampleList = examples.filter(x => x.visible).map(x => x.name);

  if (!name) {
    const chosen = await chooseFromDropdown('Select example:', exampleList);

    if (!chosen) {
      output.log('Aborted');
      return 0;
    }

    return extractExample(output, chosen, dir, force);
  }

  if (exampleList.includes(name)) {
    return extractExample(output, name, dir, force);
  }

  const oldExample = examples.find(x => !x.visible && x.name === name);
  if (oldExample) {
    return extractExample(output, name, dir, force, 'v1');
  }

  const found = await guess(exampleList, name);

  if (typeof found === 'string') {
    return extractExample(output, found, dir, force);
  }

  console.log(info('No changes made.'));
  return 0;
}

/**
 * Fetch example list json
 */
async function fetchExampleList(output: Output) {
  const stopSpinner = output.spinner('Fetching examples');
  const url = `${EXAMPLE_API}/v2/list.json`;

  try {
    const resp = await fetch(url);
    stopSpinner();

    if (resp.status !== 200) {
      throw new Error(`Failed fetching list.json (${resp.statusText}).`);
    }

    return (await resp.json()) as Example[];
  } catch (e) {
    stopSpinner();
  }
}

/**
 * Prompt user for choosing which example to init
 */
async function chooseFromDropdown(message: string, exampleList: string[]) {
  const choices = exampleList.map(name => ({
    name,
    value: name,
    short: name,
  }));

  return listInput({
    message,
    separator: false,
    choices,
  });
}

/**
 * Extract example to directory
 */
async function extractExample(
  output: Output,
  name: string,
  dir: string,
  force?: boolean,
  ver: string = 'v2'
) {
  const folder = prepareFolder(process.cwd(), dir || name, force);
  const stopSpinner = output.spinner(`Fetching ${name}`);

  const url = `${EXAMPLE_API}/${ver}/download/${name}.tar.gz`;

  return fetch(url)
    .then(async resp => {
      stopSpinner();

      if (resp.status !== 200) {
        throw new Error(`Could not get ${name}.tar.gz`);
      }

      await new Promise((resolve, reject) => {
        const extractor = tar.extract(folder);
        resp.body.on('error', reject);
        extractor.on('error', reject);
        extractor.on('finish', resolve);
        resp.body.pipe(extractor);
      });

      const successLog = `Initialized "${chalk.bold(
        name
      )}" example in ${chalk.bold(toHumanPath(folder))}.`;
      const folderRel = path.relative(process.cwd(), folder);
      const deployHint =
        folderRel === ''
          ? listItem(`To deploy, run ${cmd('now')}.`)
          : listItem(
              `To deploy, ${cmd(`cd ${folderRel}`)} and run ${cmd('now')}.`
            );
      console.log(success(`${successLog}\n${deployHint}`));
      return 0;
    })
    .catch(e => {
      stopSpinner();
      throw e;
    });
}

/**
 * Check & prepare destination folder for extracting.
 */
function prepareFolder(cwd: string, folder: string, force?: boolean) {
  const dest = path.join(cwd, folder);

  if (fs.existsSync(dest)) {
    if (!fs.lstatSync(dest).isDirectory()) {
      throw new Error(
        `Destination path "${chalk.bold(
          folder
        )}" already exists and is not a directory.`
      );
    }
    if (!force && fs.readdirSync(dest).length !== 0) {
      throw new Error(
        `Destination path "${chalk.bold(
          folder
        )}" already exists and is not an empty directory. You may use ${cmd(
          '--force'
        )} or ${cmd('--f')} to override it.`
      );
    }
  } else if (dest !== cwd) {
    try {
      fs.mkdirSync(dest);
    } catch (e) {
      throw new Error(`Could not create directory "${chalk.bold(folder)}".`);
    }
  }

  return dest;
}

/**
 * Guess which example user try to init
 */
async function guess(exampleList: string[], name: string) {
  const GuessError = new Error(
    `No example found for ${chalk.bold(name)}, run ${cmd(
      `now init`
    )} to see the list of available examples.`
  );

  if (process.stdout.isTTY !== true) {<commit_msg>Fix slight spelling mistake: `--f` -> `-f` in `now --force` error (#3958)<commit_after>import fs from 'fs';
import path from 'path';
import tar from 'tar-fs';
import chalk from 'chalk';
import fetch from 'node-fetch';

// @ts-ignore
import listInput from '../../util/input/list';
import listItem from '../../util/output/list-item';
import promptBool from '../../util/input/prompt-bool';
import toHumanPath from '../../util/humanize-path';
import { Output } from '../../util/output';
import { NowContext } from '../../types';
import success from '../../util/output/success';
import info from '../../util/output/info';
import cmd from '../../util/output/cmd';
import didYouMean from '../../util/init/did-you-mean';

type Options = {
  '--debug': boolean;
  '--force': boolean;
  '-f': boolean;
};

type Example = {
  name: string;
  visible: boolean;
  suggestions: string[];
};

const EXAMPLE_API = 'https://now-example-files.zeit.sh';

export default async function init(
  ctx: NowContext,
  opts: Options,
  args: string[],
  output: Output
) {
  const [name, dir] = args;
  const force = opts['-f'] || opts['--force'];

  const examples = await fetchExampleList(output);

  if (!examples) {
    throw new Error(`Could not fetch example list.`);
  }

  const exampleList = examples.filter(x => x.visible).map(x => x.name);

  if (!name) {
    const chosen = await chooseFromDropdown('Select example:', exampleList);

    if (!chosen) {
      output.log('Aborted');
      return 0;
    }

    return extractExample(output, chosen, dir, force);
  }

  if (exampleList.includes(name)) {
    return extractExample(output, name, dir, force);
  }

  const oldExample = examples.find(x => !x.visible && x.name === name);
  if (oldExample) {
    return extractExample(output, name, dir, force, 'v1');
  }

  const found = await guess(exampleList, name);

  if (typeof found === 'string') {
    return extractExample(output, found, dir, force);
  }

  console.log(info('No changes made.'));
  return 0;
}

/**
 * Fetch example list json
 */
async function fetchExampleList(output: Output) {
  const stopSpinner = output.spinner('Fetching examples');
  const url = `${EXAMPLE_API}/v2/list.json`;

  try {
    const resp = await fetch(url);
    stopSpinner();

    if (resp.status !== 200) {
      throw new Error(`Failed fetching list.json (${resp.statusText}).`);
    }

    return (await resp.json()) as Example[];
  } catch (e) {
    stopSpinner();
  }
}

/**
 * Prompt user for choosing which example to init
 */
async function chooseFromDropdown(message: string, exampleList: string[]) {
  const choices = exampleList.map(name => ({
    name,
    value: name,
    short: name,
  }));

  return listInput({
    message,
    separator: false,
    choices,
  });
}

/**
 * Extract example to directory
 */
async function extractExample(
  output: Output,
  name: string,
  dir: string,
  force?: boolean,
  ver: string = 'v2'
) {
  const folder = prepareFolder(process.cwd(), dir || name, force);
  const stopSpinner = output.spinner(`Fetching ${name}`);

  const url = `${EXAMPLE_API}/${ver}/download/${name}.tar.gz`;

  return fetch(url)
    .then(async resp => {
      stopSpinner();

      if (resp.status !== 200) {
        throw new Error(`Could not get ${name}.tar.gz`);
      }

      await new Promise((resolve, reject) => {
        const extractor = tar.extract(folder);
        resp.body.on('error', reject);
        extractor.on('error', reject);
        extractor.on('finish', resolve);
        resp.body.pipe(extractor);
      });

      const successLog = `Initialized "${chalk.bold(
        name
      )}" example in ${chalk.bold(toHumanPath(folder))}.`;
      const folderRel = path.relative(process.cwd(), folder);
      const deployHint =
        folderRel === ''
          ? listItem(`To deploy, run ${cmd('now')}.`)
          : listItem(
              `To deploy, ${cmd(`cd ${folderRel}`)} and run ${cmd('now')}.`
            );
      console.log(success(`${successLog}\n${deployHint}`));
      return 0;
    })
    .catch(e => {
      stopSpinner();
      throw e;
    });
}

/**
 * Check & prepare destination folder for extracting.
 */
function prepareFolder(cwd: string, folder: string, force?: boolean) {
  const dest = path.join(cwd, folder);

  if (fs.existsSync(dest)) {
    if (!fs.lstatSync(dest).isDirectory()) {
      throw new Error(
        `Destination path "${chalk.bold(
          folder
        )}" already exists and is not a directory.`
      );
    }
    if (!force && fs.readdirSync(dest).length !== 0) {
      throw new Error(
        `Destination path "${chalk.bold(
          folder
        )}" already exists and is not an empty directory. You may use ${cmd(
          '--force'
        )} or ${cmd('-f')} to override it.`
      );
    }
  } else if (dest !== cwd) {
    try {
      fs.mkdirSync(dest);
    } catch (e) {
      throw new Error(`Could not create directory "${chalk.bold(folder)}".`);
    }
  }

  return dest;
}

/**
 * Guess which example user try to init
 */
async function guess(exampleList: string[], name: string) {
  const GuessError = new Error(
    `No example found for ${chalk.bold(name)}, run ${cmd(
      `now init`
    )} to see the list of available examples.`
  );

  if (process.stdout.isTTY !== true) {
{'fraction_non_alphanumeric': 0.16833476313030216, 'fraction_numerical': 0.0028595939376608523, 'mean_word_length': 2.8028271112722, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 18, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2261709', 'n_tokens_mistral': 3305, 'n_tokens_neox': 3273, 'n_words': 916}