"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