diff --git a/local-cli/generator/templates.js b/local-cli/generator/templates.js
new file mode 100644
index 000000000..dfbf598e2
--- /dev/null
+++ b/local-cli/generator/templates.js
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+'use strict';
+
+const copyProjectTemplateAndReplace = require('./copyProjectTemplateAndReplace');
+const execSync = require('child_process').execSync;
+const fs = require('fs');
+const path = require('path');
+
+const availableTemplates = {
+ navigation: 'HelloNavigation',
+};
+
+function listTemplatesAndExit(newProjectName, options) {
+ if (options.template === true) {
+ // Just listing templates using 'react-native init --template'.
+ // Not creating a new app.
+ // Print available templates and exit.
+ const templateKeys = Object.keys(availableTemplates);
+ if (templateKeys.length === 0) {
+ // Just a guard, should never happen as long availableTemplates
+ // above is defined correctly :)
+ console.log(
+ 'There are no templates available besides ' +
+ 'the default "Hello World" one.'
+ );
+ } else {
+ console.log(
+ 'The available templates are:\n' +
+ templateKeys.join('\n') +
+ '\nYou can use these to create an app based on a template, for example: ' +
+ 'you could run: ' +
+ 'react-native init ' + newProjectName + ' --template ' + templateKeys[0]
+ );
+ }
+ // Exit 'react-native init'
+ return true;
+ }
+ // Continue 'react-native init'
+ return false;
+}
+
+/**
+ * @param newProjectName For example 'AwesomeApp'.
+ * @param templateKey Template to use, for example 'navigation'.
+ * @param yarnVersion Version of yarn available on the system, or null if
+ * yarn is not available. For example '0.18.1'.
+ */
+function createProjectFromTemplate(destPath, newProjectName, templateKey, yarnVersion) {
+ // Expand the basic 'HelloWorld' template
+ copyProjectTemplateAndReplace(
+ path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld'),
+ destPath,
+ newProjectName
+ );
+
+ if (templateKey !== undefined) {
+ // Keep the files from the 'HelloWorld' template, and overwrite some of them
+ // with the specified project template.
+ // The 'HelloWorld' template contains the native files (these are used by
+ // all templates) and every other template only contains additional JS code.
+ // Reason:
+ // This way we don't have to duplicate the native files in every template.
+ // If we duplicated them we'd make RN larger and risk that people would
+ // forget to maintain all the copies so they would go out of sync.
+ const templateName = availableTemplates[templateKey];
+ if (templateName) {
+ copyProjectTemplateAndReplace(
+ path.resolve(
+ 'node_modules', 'react-native', 'local-cli', 'templates', templateName
+ ),
+ destPath,
+ newProjectName
+ );
+ } else {
+ throw new Error('Uknown template: ' + templateKey);
+ }
+
+ // Add dependencies:
+
+ // dependencies.json is a special file that lists additional dependencies
+ // that are required by this template
+ const dependenciesJsonPath = path.resolve(
+ 'node_modules', 'react-native', 'local-cli', 'templates', templateName, 'dependencies.json'
+ );
+ if (fs.existsSync(dependenciesJsonPath)) {
+ console.log('Adding dependencies for the project...');
+ const dependencies = JSON.parse(fs.readFileSync(dependenciesJsonPath));
+ for (let depName in dependencies) {
+ const depVersion = dependencies[depName];
+ const depToInstall = depName + '@' + depVersion;
+ console.log('Adding ' + depToInstall + '...');
+ if (yarnVersion) {
+ execSync(`yarn add ${depToInstall}`, {stdio: 'inherit'});
+ } else {
+ execSync(`npm install ${depToInstall} --save --save-exact`, {stdio: 'inherit'});
+ }
+ }
+ }
+ }
+}
+
+module.exports = {
+ listTemplatesAndExit,
+ createProjectFromTemplate,
+};
diff --git a/local-cli/init/init.js b/local-cli/init/init.js
index 15f9837df..3537a97ba 100644
--- a/local-cli/init/init.js
+++ b/local-cli/init/init.js
@@ -8,7 +8,10 @@
*/
'use strict';
-const copyProjectTemplateAndReplace = require('../generator/copyProjectTemplateAndReplace');
+const {
+ listTemplatesAndExit,
+ createProjectFromTemplate,
+} = require('../generator/templates');
const execSync = require('child_process').execSync;
const fs = require('fs');
const minimist = require('minimist');
@@ -23,15 +26,15 @@ const yarn = require('../util/yarn');
* @param projectDir Templates will be copied here.
* @param argsOrName Project name or full list of custom arguments
* for the generator.
+ * @param options Command line options passed from the react-native-cli directly.
+ * E.g. `{ version: '0.43.0', template: 'navigation' }`
*/
function init(projectDir, argsOrName) {
- console.log('Setting up new React Native app in ' + projectDir);
-
const args = Array.isArray(argsOrName)
? argsOrName // argsOrName was e.g. ['AwesomeApp', '--verbose']
: [argsOrName].concat(process.argv.slice(4)); // argsOrName was e.g. 'AwesomeApp'
- // args array is e.g. ['AwesomeApp', '--verbose']
+ // args array is e.g. ['AwesomeApp', '--verbose', '--template', 'navigation']
if (!args || args.length === 0) {
console.error('react-native init requires a project name.');
return;
@@ -40,7 +43,14 @@ function init(projectDir, argsOrName) {
const newProjectName = args[0];
const options = minimist(args);
- generateProject(projectDir, newProjectName, options);
+ if (listTemplatesAndExit(newProjectName, options)) {
+ // Just listing templates using 'react-native init --template'
+ // Not creating a new app.
+ return;
+ } else {
+ console.log('Setting up new React Native app in ' + projectDir);
+ generateProject(projectDir, newProjectName, options);
+ }
}
/**
@@ -67,11 +77,7 @@ function generateProject(destinationRoot, newProjectName, options) {
yarn.getYarnVersionIfAvailable() &&
yarn.isGlobalCliUsingYarn(destinationRoot);
- copyProjectTemplateAndReplace(
- path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld'),
- destinationRoot,
- newProjectName
- );
+ createProjectFromTemplate(destinationRoot, newProjectName, options.template, yarnVersion);
if (yarnVersion) {
console.log('Adding React...');
diff --git a/local-cli/runAndroid/runAndroid.js b/local-cli/runAndroid/runAndroid.js
index 2adf2386e..815c8ebaa 100644
--- a/local-cli/runAndroid/runAndroid.js
+++ b/local-cli/runAndroid/runAndroid.js
@@ -193,7 +193,7 @@ function runOnAllDevices(args, cmd, packageName, adbPath){
}
console.log(chalk.bold(
- `Building and installing the app on the device (cd android && ${cmd} ${gradleArgs.join(' ')}...`
+ `Building and installing the app on the device (cd android && ${cmd} ${gradleArgs.join(' ')})...`
));
child_process.execFileSync(cmd, gradleArgs, {
diff --git a/local-cli/templates/HelloNavigation/dependencies.json b/local-cli/templates/HelloNavigation/dependencies.json
new file mode 100644
index 000000000..130c08ed8
--- /dev/null
+++ b/local-cli/templates/HelloNavigation/dependencies.json
@@ -0,0 +1,3 @@
+{
+ "react-navigation": "1.0.0-beta.1"
+}
diff --git a/local-cli/templates/HelloNavigation/index.android.js b/local-cli/templates/HelloNavigation/index.android.js
index b122bdc5d..e9ea66bf6 100644
--- a/local-cli/templates/HelloNavigation/index.android.js
+++ b/local-cli/templates/HelloNavigation/index.android.js
@@ -2,4 +2,4 @@ import { AppRegistry } from 'react-native';
import MainNavigator from './views/MainNavigator';
-AppRegistry.registerComponent('ChatExample', () => MainNavigator);
+AppRegistry.registerComponent('HelloWorld', () => MainNavigator);
diff --git a/local-cli/templates/HelloNavigation/index.ios.js b/local-cli/templates/HelloNavigation/index.ios.js
index b122bdc5d..e9ea66bf6 100644
--- a/local-cli/templates/HelloNavigation/index.ios.js
+++ b/local-cli/templates/HelloNavigation/index.ios.js
@@ -2,4 +2,4 @@ import { AppRegistry } from 'react-native';
import MainNavigator from './views/MainNavigator';
-AppRegistry.registerComponent('ChatExample', () => MainNavigator);
+AppRegistry.registerComponent('HelloWorld', () => MainNavigator);
diff --git a/local-cli/templates/HelloNavigation/views/HomeScreenTabNavigator.js b/local-cli/templates/HelloNavigation/views/HomeScreenTabNavigator.js
index 123ee3a78..bcbfaaa5d 100644
--- a/local-cli/templates/HelloNavigation/views/HomeScreenTabNavigator.js
+++ b/local-cli/templates/HelloNavigation/views/HomeScreenTabNavigator.js
@@ -7,18 +7,18 @@ import {
import { TabNavigator } from 'react-navigation';
import ChatListScreen from './chat/ChatListScreen';
-import FriendListScreen from './friends/FriendListScreen';
+import WelcomeScreen from './welcome/WelcomeScreen';
/**
* Screen with tabs shown on app startup.
*/
const HomeScreenTabNavigator = TabNavigator({
+ Welcome: {
+ screen: WelcomeScreen,
+ },
Chats: {
screen: ChatListScreen,
},
- Friends: {
- screen: FriendListScreen,
- },
});
export default HomeScreenTabNavigator;
diff --git a/local-cli/templates/HelloNavigation/views/chat/ChatListScreen.js b/local-cli/templates/HelloNavigation/views/chat/ChatListScreen.js
index a29fa1443..146e7e214 100644
--- a/local-cli/templates/HelloNavigation/views/chat/ChatListScreen.js
+++ b/local-cli/templates/HelloNavigation/views/chat/ChatListScreen.js
@@ -10,7 +10,7 @@ import ListItem from '../../components/ListItem';
export default class ChatListScreen extends Component {
static navigationOptions = {
- title: 'Chats',
+ title: 'Friends',
header: {
visible: Platform.OS === 'ios',
},
diff --git a/local-cli/templates/HelloNavigation/views/friends/FriendListScreen.js b/local-cli/templates/HelloNavigation/views/welcome/WelcomeScreen.js
similarity index 75%
rename from local-cli/templates/HelloNavigation/views/friends/FriendListScreen.js
rename to local-cli/templates/HelloNavigation/views/welcome/WelcomeScreen.js
index 85d8cdd79..d149714ff 100644
--- a/local-cli/templates/HelloNavigation/views/friends/FriendListScreen.js
+++ b/local-cli/templates/HelloNavigation/views/welcome/WelcomeScreen.js
@@ -8,11 +8,12 @@ import {
} from 'react-native';
import ListItem from '../../components/ListItem';
+import WelcomeText from './WelcomeText';
-export default class FriendListScreen extends Component {
+export default class WelcomeScreen extends Component {
static navigationOptions = {
- title: 'Friends',
+ title: 'Welcome',
header: {
visible: Platform.OS === 'ios',
},
@@ -20,7 +21,7 @@ export default class FriendListScreen extends Component {
icon: ({ tintColor }) => (
),
@@ -29,9 +30,7 @@ export default class FriendListScreen extends Component {
render() {
return (
-
- A list of friends here.
-
+
);
}
}
diff --git a/local-cli/templates/HelloNavigation/views/welcome/WelcomeText.android.js b/local-cli/templates/HelloNavigation/views/welcome/WelcomeText.android.js
new file mode 100644
index 000000000..a082661a2
--- /dev/null
+++ b/local-cli/templates/HelloNavigation/views/welcome/WelcomeText.android.js
@@ -0,0 +1,51 @@
+import React, { Component } from 'react';
+import {
+ AppRegistry,
+ StyleSheet,
+ Text,
+ View
+} from 'react-native';
+
+export default class WelcomeText extends Component {
+ render() {
+ return (
+
+
+ Welcome to React Native!
+
+
+ This app shows the basics of navigating between a few screens,
+ working with ListView and handling text input.
+
+
+ Modify any files to get started. For example try changing the
+ file views/welcome/WelcomeText.android.js.
+
+
+ Press Cmd+R to reload,{'\n'}
+ Cmd+D or shake for dev menu.
+
+
+ );
+ }
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'white',
+ padding: 20,
+ },
+ welcome: {
+ fontSize: 20,
+ textAlign: 'center',
+ margin: 16,
+ },
+ instructions: {
+ textAlign: 'center',
+ color: '#333333',
+ marginBottom: 12,
+ },
+});
diff --git a/local-cli/templates/HelloNavigation/views/welcome/WelcomeText.ios.js b/local-cli/templates/HelloNavigation/views/welcome/WelcomeText.ios.js
new file mode 100644
index 000000000..9d5ce075f
--- /dev/null
+++ b/local-cli/templates/HelloNavigation/views/welcome/WelcomeText.ios.js
@@ -0,0 +1,51 @@
+import React, { Component } from 'react';
+import {
+ AppRegistry,
+ StyleSheet,
+ Text,
+ View
+} from 'react-native';
+
+export default class WelcomeText extends Component {
+ render() {
+ return (
+
+
+ Welcome to React Native!
+
+
+ This app shows the basics of navigating between a few screens,
+ working with ListView and handling text input.
+
+
+ Modify any files to get started. For example try changing the
+ file{'\n'}views/welcome/WelcomeText.ios.js.
+
+
+ Press Cmd+R to reload,{'\n'}
+ Cmd+D or shake for dev menu.
+
+
+ );
+ }
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'white',
+ padding: 20,
+ },
+ welcome: {
+ fontSize: 20,
+ textAlign: 'center',
+ margin: 16,
+ },
+ instructions: {
+ textAlign: 'center',
+ color: '#333333',
+ marginBottom: 12,
+ },
+});
diff --git a/local-cli/templates/HelloNavigation/views/friends/friend-icon.png b/local-cli/templates/HelloNavigation/views/welcome/welcome-icon.png
similarity index 100%
rename from local-cli/templates/HelloNavigation/views/friends/friend-icon.png
rename to local-cli/templates/HelloNavigation/views/welcome/welcome-icon.png
diff --git a/react-native-cli/index.js b/react-native-cli/index.js
index 95355f835..795e8143e 100755
--- a/react-native-cli/index.js
+++ b/react-native-cli/index.js
@@ -48,6 +48,7 @@ var semver = require('semver');
* if you are in a RN app folder
* init - to create a new project and npm install it
* --verbose - to print logs while init
+ * --template - name of the template to use, e.g. --template navigation
* --version - override default (https://registry.npmjs.org/react-native@latest),
* package to install, examples:
* - "0.22.0-rc1" - A new app will be created using a specific version of React Native from npm repo
@@ -129,7 +130,8 @@ if (cli) {
' Options:',
'',
' -h, --help output usage information',
- ' -v, --version output the version number',
+ ' -v, --version use a specific version of React Native',
+ ' --template use an app template. Use --template to see available templates.',
'',
].join('\n'));
process.exit(0);
@@ -264,8 +266,7 @@ function getInstallPackage(rnPackage) {
}
function run(root, projectName, options) {
- // E.g. '0.38' or '/path/to/archive.tgz'
- const rnPackage = options.version;
+ const rnPackage = options.version; // e.g. '0.38' or '/path/to/archive.tgz'
const forceNpmClient = options.npm;
const yarnVersion = (!forceNpmClient) && getYarnVersionIfAvailable();
var installCommand;