"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