"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;

function _chalk() {
  const data = _interopRequireDefault(require("chalk"));

  _chalk = function () {
    return data;
  };

  return data;
}

function _fs() {
  const data = _interopRequireDefault(require("fs"));

  _fs = function () {
    return data;
  };

  return data;
}

function _xdl() {
  const data = require("@expo/xdl");

  _xdl = function () {
    return data;
  };

  return data;
}

function _isString() {
  const data = _interopRequireDefault(require("lodash/isString"));

  _isString = function () {
    return data;
  };

  return data;
}

function _padEnd() {
  const data = _interopRequireDefault(require("lodash/padEnd"));

  _padEnd = function () {
    return data;
  };

  return data;
}

function _enquirer() {
  const data = require("enquirer");

  _enquirer = function () {
    return data;
  };

  return data;
}

function _semver() {
  const data = _interopRequireDefault(require("semver"));

  _semver = function () {
    return data;
  };

  return data;
}

function _spawnAsync() {
  const data = _interopRequireDefault(require("@expo/spawn-async"));

  _spawnAsync = function () {
    return data;
  };

  return data;
}

function _npmPackageArg() {
  const data = _interopRequireDefault(require("npm-package-arg"));

  _npmPackageArg = function () {
    return data;
  };

  return data;
}

function _pacote() {
  const data = _interopRequireDefault(require("pacote"));

  _pacote = function () {
    return data;
  };

  return data;
}

function _trimStart() {
  const data = _interopRequireDefault(require("lodash/trimStart"));

  _trimStart = function () {
    return data;
  };

  return data;
}

function _wordwrap() {
  const data = _interopRequireDefault(require("wordwrap"));

  _wordwrap = function () {
    return data;
  };

  return data;
}

function _path() {
  const data = _interopRequireDefault(require("path"));

  _path = function () {
    return data;
  };

  return data;
}

function _prompt() {
  const data = _interopRequireDefault(require("../prompt"));

  _prompt = function () {
    return data;
  };

  return data;
}

function _log() {
  const data = _interopRequireDefault(require("../log"));

  _log = function () {
    return data;
  };

  return data;
}

function _CommandError() {
  const data = _interopRequireDefault(require("../CommandError"));

  _CommandError = function () {
    return data;
  };

  return data;
}

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// @ts-ignore enquirer has no exported member 'Snippet'
const FEATURED_TEMPLATES = ['----- Managed workflow -----', {
  shortName: 'blank',
  name: 'expo-template-blank',
  description: 'a minimal app as clean as an empty canvas'
}, {
  shortName: 'blank (TypeScript)',
  name: 'expo-template-blank-typescript',
  description: 'same as blank but with TypeScript configuration'
}, {
  shortName: 'tabs',
  name: 'expo-template-tabs',
  description: 'several example screens and tabs using react-navigation'
}, '----- Bare workflow -----', {
  shortName: 'minimal',
  name: 'expo-template-bare-minimum',
  description: 'bare and minimal, just the essentials to get you started',
  bare: true
}, {
  shortName: 'minimal (TypeScript)',
  name: 'expo-template-bare-typescript',
  description: 'same as minimal but with TypeScript configuration',
  bare: true
}];
const BARE_WORKFLOW_TEMPLATES = ['expo-template-bare-minimum', 'expo-template-bare-typescript'];

async function action(projectDir, options) {
  let parentDir;
  let dirName;

  if (projectDir) {
    let root = _path().default.resolve(projectDir);

    parentDir = _path().default.dirname(root);
    dirName = _path().default.basename(root);
    let validationResult = validateName(parentDir, dirName);

    if (validationResult !== true) {
      throw new (_CommandError().default)('INVALID_PROJECT_DIR', validationResult);
    }
  } else if (options.parent && options.parent.nonInteractive) {
    throw new (_CommandError().default)('NON_INTERACTIVE', 'The project dir argument is required in non-interactive mode.');
  } else {
    parentDir = process.cwd();
  }

  let templateSpec;

  if (options.template) {
    templateSpec = (0, _npmPackageArg().default)(options.template); // For backwards compatibility, 'blank' and 'tabs' are aliases for
    // 'expo-template-blank' and 'expo-template-tabs', respectively.

    if ((templateSpec.name === 'blank' || templateSpec.name === 'tabs' || templateSpec.name === 'bare-minimum' || templateSpec.name === 'bare-foundation') && templateSpec.registry) {
      templateSpec.escapedName = `expo-template-${templateSpec.name}`;
      templateSpec.name = templateSpec.escapedName;
    }
  } else {
    let descriptionColumn = Math.max(...FEATURED_TEMPLATES.map(t => typeof t === 'object' ? t.shortName.length : 0)) + 2;
    let {
      template
    } = await (0, _prompt().default)({
      type: 'list',
      name: 'template',
      message: 'Choose a template:',
      pageSize: 20,
      choices: FEATURED_TEMPLATES.map(template => {
        if (typeof template === 'string') {
          return _prompt().default.separator(template);
        } else {
          return {
            value: template.name,
            name: _chalk().default.bold((0, _padEnd().default)(template.shortName, descriptionColumn)) + (0, _trimStart().default)((0, _wordwrap().default)(descriptionColumn + 2, process.stdout.columns || 80)(template.description)),
            short: template.name
          };
        }
      })
    }, {
      nonInteractiveHelp: '--template: argument is required in non-interactive mode. Valid choices are: ' + FEATURED_TEMPLATES.map(template => typeof template === 'object' && template.shortName ? `'${template.shortName}'` : '').filter(text => text).join(', ') + ' or any custom template (name of npm package).'
    });
    templateSpec = (0, _npmPackageArg().default)(template);
  }

  if (options.workflow) {
    _log().default.warn(`The --workflow flag is deprecated. Workflow is chosen automatically based on the chosen template.`);
  }

  let initialConfig;
  let templateManifest = await _pacote().default.manifest(templateSpec);
  let isBare = BARE_WORKFLOW_TEMPLATES.includes(templateManifest.name);

  if (isBare) {
    initialConfig = await promptForBareConfig(parentDir, dirName, options);
  } else {
    initialConfig = await promptForManagedConfig(parentDir, dirName, options);
  }

  let packageManager;

  if (options.yarn) {
    packageManager = 'yarn';
  } else if (options.npm) {
    packageManager = 'npm';
  } else {
    packageManager = (await shouldUseYarnAsync()) ? 'yarn' : 'npm';
  }

  let projectPath = await _xdl().Exp.extractAndInitializeTemplateApp(templateSpec, _path().default.join(parentDir, dirName || ('expo' in initialConfig ? initialConfig.expo.slug : initialConfig.name)), packageManager, initialConfig);

  let cdPath = _path().default.relative(process.cwd(), projectPath);

  if (cdPath.length > projectPath.length) {
    cdPath = projectPath;
  }

  _log().default.nested(`\nYour project is ready at ${projectPath}`);

  _log().default.nested('');

  if (isBare) {
    _log().default.nested(`Before running your app on iOS, make sure you have CocoaPods installed and initialize the project:`);

    _log().default.nested('');

    _log().default.nested(`  cd ${cdPath || '.'}/ios`);

    _log().default.nested(`  pod install`);

    _log().default.nested('');

    _log().default.nested('Then you can run the project:');

    _log().default.nested('');

    if (cdPath) {
      // empty string if project was created in current directory
      _log().default.nested(`  cd ${cdPath}`);
    }

    _log().default.nested(`  ${packageManager === 'npm' ? 'npm run android' : 'yarn android'}`);

    _log().default.nested(`  ${packageManager === 'npm' ? 'npm run ios' : 'yarn ios'}`);
  } else {
    _log().default.nested(`To get started, you can type:\n`);

    if (cdPath) {
      // empty string if project was created in current directory
      _log().default.nested(`  cd ${cdPath}`);
    }

    _log().default.nested(`  ${packageManager} start`);
  }

  _log().default.nested('');
}

function validateName(parentDir, name) {
  if (typeof name !== 'string' || name === '') {
    return 'The project name can not be empty.';
  }

  if (!/^[a-z0-9@.\-_]+$/i.test(name)) {
    return 'The project name can only contain URL-friendly characters.';
  }

  let dir = _path().default.join(parentDir, name);

  if (!isNonExistentOrEmptyDir(dir)) {
    return `The path "${dir}" already exists. Please choose a different parent directory or project name.`;
  }

  return true;
}

function validateProjectName(name) {
  return /^[a-z0-9]+$/i.test(name) || 'Project name can only include ASCII characters A-Z, a-z and 0-9';
}

function isNonExistentOrEmptyDir(dir) {
  try {
    return _fs().default.statSync(dir).isDirectory() && _fs().default.readdirSync(dir).length === 0;
  } catch (error) {
    if (error.code === 'ENOENT') {
      return true;
    }

    throw error;
  }
}

async function shouldUseYarnAsync() {
  try {
    let version = (await (0, _spawnAsync().default)('yarnpkg', ['--version'])).stdout.trim();

    if (!_semver().default.valid(version)) {
      return false;
    }

    let answer = await (0, _prompt().default)({
      type: 'confirm',
      name: 'useYarn',
      message: `Yarn v${version} found. Use Yarn to install dependencies?`
    }, {
      nonInteractiveHelp: 'Please specify either --npm or --yarn to choose the installation method.'
    });
    return answer.useYarn;
  } catch (e) {
    return false;
  }
}

async function promptForBareConfig(parentDir, dirName, options) {
  let projectName = undefined;

  if (dirName) {
    let validationResult = validateProjectName(dirName);

    if (validationResult === true) {
      projectName = dirName;
    } else {
      throw new (_CommandError().default)('INVALID_PROJECT_NAME', validationResult);
    }
  }

  if (options.parent && options.parent.nonInteractive) {
    if (!projectName) {
      throw new (_CommandError().default)('NON_INTERACTIVE', 'The project dir argument is required in non-interactive mode.');
    }

    if (typeof options.name !== 'string' || options.name === '') {
      throw new (_CommandError().default)('NON_INTERACTIVE', '--name: argument is required in non-interactive mode.');
    }

    return {
      name: projectName,
      displayName: options.name
    };
  }

  let {
    values
  } = await new (_enquirer().Snippet)({
    name: 'app',
    message: 'Please enter names for your project.',
    required: true,
    fields: [{
      name: 'name',
      message: 'The name of the Android Studio and Xcode projects to be created',
      initial: projectName,
      filter: name => name.trim(),
      validate: name => validateProjectName(name),
      required: true
    }, {
      name: 'displayName',
      message: 'The name of your app visible on the home screen',
      initial: (0, _isString().default)(options.name) ? options.name : undefined,
      filter: name => name.trim(),
      required: true
    }],
    initial: 'name',
    template: JSON.stringify({
      name: '{{name}}',
      displayName: '{{displayName}}'
    }, null, 2)
  }).run();
  return values;
}

async function promptForManagedConfig(parentDir, dirName, options) {
  if (options.parent && options.parent.nonInteractive) {
    if (!(0, _isString().default)(options.name) || options.name === '') {
      throw new (_CommandError().default)('NON_INTERACTIVE', '--name: argument is required in non-interactive mode.');
    } else {
      return {
        expo: {
          slug: dirName,
          name: options.name
        }
      };
    }
  }

  let {
    values
  } = await new (_enquirer().Snippet)({
    name: 'expo',
    message: 'Please enter a few initial configuration values.\n  Read more: https://docs.expo.io/versions/latest/workflow/configuration/',
    required: true,
    fields: [{
      name: 'name',
      message: 'The name of your app visible on the home screen',
      initial: (0, _isString().default)(options.name) ? options.name : undefined,
      filter: name => name.trim(),
      required: true
    }, {
      name: 'slug',
      message: 'A URL friendly name for your app',
      initial: dirName,
      filter: name => name.trim(),
      validate: name => validateName(parentDir, name),
      required: true
    }],
    initial: 'slug',
    template: JSON.stringify({
      expo: {
        name: '{{name}}',
        slug: '{{slug}}'
      }
    }, null, 2)
  }).run();
  return {
    expo: values
  };
}

function _default(program) {
  program.command('init [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('-t, --template [name]', 'Specify which template to use. Valid options are "blank", "tabs", "bare-minimum" or any npm package that includes an Expo project template.').option('--npm', 'Use npm to install dependencies. (default when Yarn is not installed)').option('--yarn', 'Use Yarn to install dependencies. (default when Yarn is installed)').option('--workflow [name]', '(Deprecated) The workflow to use. managed (default) or advanced').option('--name [name]', 'The name of your app visible on the home screen.').option('--android-package [name]', 'The package name for your Android app.').option('--ios-bundle-identifier [name]', 'The bundle identifier for your iOS app.').asyncAction(action);
}
//# sourceMappingURL=../__sourcemaps__/commands/init.js.map