Compare commits

...
Author SHA1 Message Date
Mike Grabowski 6e650937fd [0.42.0] Bump version numbers 2017-03-01 15:59:50 +01:00
James Ide 852775fd1a Support the --remote option in bump-oss-version.js script
Summary:
In my RN checkout, I use "upstream" as my remote instead of "origin" -> this lets me run `scripts/bump-oss-version.js --remote upstream 0.41.1` for example.

Also made the script executable so we don't need to put `node` in front of it, and updated the Releases.md doc.
Closes https://github.com/facebook/react-native/pull/12230

Differential Revision: D4515070

Pulled By: mkonicek

fbshipit-source-id: f218a6b77959588ee5f625b8589ac080dd010034
2017-02-28 11:40:43 -08:00
Mike Grabowski 3ccf8dfb09 [0.42.0-rc.3] Bump version numbers 2017-02-14 15:01:05 +01:00
Mike Grabowski d877c25cc4 Temp fix for the bump script 2017-02-14 14:59:41 +01:00
Mike Grabowski 9c2760ae20 Merge branch 2017-02-14 14:53:12 +01:00
Eloy DuránandMike Grabowski 2981560e40 Make podspec great again.
Summary:
Fixes #11272
Fixes #11572
Fixes #11781

The main changes here are:

* This depends on the latest CocoaPods (1.2.0). It’s currently in RC, but if I’m not mistaken a proper release is expected soon. /cc dantoml
* Adds required header search paths for the jschelpers and cxxreact subspecs.
* Makes the jschelpers and cxxreact headers private to building React Native, not visible to the user’s project.
* It uses the canonical upstream Yoga v1.0.0 podspec: https://github.com/facebook/yoga/blob/master/Yoga.podspec
* Consistent styling.

I have been able to get our app to build again using this https://github.com/artsy/emission/pull/437. The spec has some warnings, but otherwise fully passes lint.

rh389 sjmueller Could you please test with your projects?
Closes https://github.com/facebook/react-native/pull/12089

Differential Revision: D4518605

fbshipit-source-id: ecf86232d8b1af52d139eadd1acc10f5c1d42c29
2017-02-14 14:50:47 +01:00
James Ide ead6dcd092 [React] Update React version to ~15.4.1 instead of the RC
React 15.4.x has been out for awhile. Test Plan: open UI Explorer, go through the various screens and look for warnings or errors.
2017-02-08 17:51:51 -08:00
Martin Konicek 363b904d59 [0.42.0-rc.2] Bump version numbers 2017-02-08 12:03:16 +00:00
Martin Konicek 021e754c8f CLI: Add README to app template, lint
Reviewed By: mkonicek

Differential Revision:
D4522116
Ninja: oss only

fbshipit-source-id: d17e0e8badcfe97eaea092e5d5274e02904a534d
2017-02-08 11:47:00 +00:00
Martin Konicek 2bc2ae761a CLI: Update Navigation app template
Summary:
Update the template that will be used by `react-native init --template navigation`. The goal is to make it easier for people to get started by demonstrating a few very basic concepts such as navigation, lists and text input.

**Test plan (required)**

<img src="https://cloud.githubusercontent.com/assets/346214/22697898/ced66f52-ed4a-11e6-9b90-df6daef43199.gif" alt="Android Example" height="800" style="float: left"/>

<img src="https://cloud.githubusercontent.com/assets/346214/22697901/cfeab3e4-ed4a-11e6-8552-d76585317ac2.gif" alt="iOS Example" height="800"/>
Closes https://github.com/facebook/react-native/pull/12260

Differential Revision: D4521758

Pulled By: mkonicek

fbshipit-source-id: d7d9e481dd3373917ac68ec9169b9ac3267547a9
2017-02-08 11:46:53 +00:00
Martin Konicek 3a6dff4f4f CLI: Add support for project templates
Summary:
Currently it is not trivial for people to get started with React Native. `react-native init MyApp` just creates a simple app with a single screen. People have to spend time figuring out how to add more screens, or how to accomplish very basic tasks such as rendering a list of data or handling text input.

Let's add an option: `react-native init --template navigation` - this creates a "starter" app which can be easily tweaked into the actual app the person wants to build.

**Test plan (required)**

- Checked that 'react-native init MyApp' still works as before:

<img width="487" alt="screenshot 2017-02-02 16 56 28" src="https://cloud.githubusercontent.com/assets/346214/22559344/b2348ebe-e968-11e6-9032-d1c33216f490.png">

<img width="603" alt="screenshot 2017-02-02 16 58 04" src="https://cloud.githubusercontent.com/assets/346214/22559370/c96a2ca6-e968-11e6-91f7-7afb967920fc.png">

- Ran 'react-native init MyNavApp --template'. This prints the available templates:

```
$ react-native init MyNavApp
Closes https://github.com/facebook/react-native/pull/12170

Differential Revision: D4516241

Pulled By: mkonicek

fbshipit-source-id: 8ac081157919872e92947ed64ea64fb48078614d
2017-02-08 11:46:41 +00:00
Mike Grabowski ff8257c47e [0.42.0-rc.1] Bump version numbers 2017-02-03 09:19:19 +01:00
Mike Grabowski 0ec85db3bf Remove bundle.js 2017-02-03 09:18:48 +01:00
Mike Grabowski 26060a4526 [0.42.0-rc.0] Bump version numbers 2017-02-02 18:47:00 +01:00
29 changed files with 757 additions and 22900 deletions
+2
View File
@@ -21,6 +21,7 @@ script:
- if [[ "$TEST_TYPE" = e2e-objc-tvos ]]; then node ./scripts/run-ci-e2e-tests.js --tvos --retries 3; fi
- if [[ "$TEST_TYPE" = js ]]; then npm run flow check; fi
- if [[ "$TEST_TYPE" = js ]]; then npm test -- --maxWorkers=1; fi
- if [[ ( "$TEST_TYPE" = podspecs ) && ( "$TRAVIS_PULL_REQUEST" = "false" ) ]]; then gem install cocoapods && ./scripts/process-podspecs.sh; fi
env:
matrix:
@@ -29,6 +30,7 @@ env:
- TEST_TYPE=objc-ios
- TEST_TYPE=objc-tvos
- TEST_TYPE=js
- TEST_TYPE=podspecs
branches:
only:
+96 -98
View File
@@ -1,140 +1,138 @@
require 'json'
require "json"
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = "React"
s.version = package['version']
s.summary = package['description']
s.description = <<-DESC
React Native apps are built using the React JS
framework, and render directly to native UIKit
elements using a fully asynchronous architecture.
There is no browser and no HTML. We have picked what
we think is the best set of features from these and
other technologies to build what we hope to become
the best product development framework available,
with an emphasis on iteration speed, developer
delight, continuity of technology, and absolutely
beautiful and fast products with no compromises in
quality or capability.
DESC
s.homepage = "http://facebook.github.io/react-native/"
s.license = package['license']
s.author = "Facebook"
s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "v#{s.version}" }
s.default_subspec = 'Core'
s.requires_arc = true
s.platform = :ios, "8.0"
s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" }
s.header_dir = 'React'
s.preserve_paths = "package.json", "LICENSE", "LICENSE-CustomComponents", "PATENTS"
s.name = "React"
s.version = "0.42.0"
s.summary = package["description"]
s.description = <<-DESC
React Native apps are built using the React JS
framework, and render directly to native UIKit
elements using a fully asynchronous architecture.
There is no browser and no HTML. We have picked what
we think is the best set of features from these and
other technologies to build what we hope to become
the best product development framework available,
with an emphasis on iteration speed, developer
delight, continuity of technology, and absolutely
beautiful and fast products with no compromises in
quality or capability.
DESC
s.homepage = "http://facebook.github.io/react-native/"
s.license = package["license"]
s.author = "Facebook"
s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "v#{s.version}" }
s.default_subspec = "Core"
s.requires_arc = true
s.platform = :ios, "8.0"
s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" }
s.preserve_paths = "package.json", "LICENSE", "LICENSE-CustomComponents", "PATENTS"
s.cocoapods_version = ">= 1.2.0"
s.subspec 'Core' do |ss|
ss.dependency 'React/yoga'
ss.dependency 'React/cxxreact'
ss.source_files = "React/**/*.{c,h,m,mm,S}"
ss.exclude_files = "**/__tests__/*", "IntegrationTests/*", "React/**/RCTTVView.*", "ReactCommon/yoga/*"
ss.frameworks = "JavaScriptCore"
ss.libraries = "stdc++"
s.subspec "Core" do |ss|
ss.dependency "Yoga", "#{package["version"]}.React"
ss.dependency "React/cxxreact"
ss.source_files = "React/**/*.{c,h,m,mm,S}"
ss.exclude_files = "**/__tests__/*", "IntegrationTests/*", "React/**/RCTTVView.*", "ReactCommon/yoga/*", "React/Cxx*/*"
ss.framework = "JavaScriptCore"
ss.libraries = "stdc++"
end
s.subspec 'tvOS' do |ss|
ss.dependency 'React/Core'
ss.source_files = "React/**/RCTTVView.{h, m}"
s.subspec "tvOS" do |ss|
ss.dependency "React/Core"
ss.source_files = "React/**/RCTTVView.{h, m}"
end
s.subspec 'jschelpers' do |ss|
ss.source_files = 'ReactCommon/jschelpers/{JavaScriptCore,JSCWrapper}.{cpp,h}'
ss.header_dir = 'jschelpers'
s.subspec "jschelpers" do |ss|
ss.source_files = "ReactCommon/jschelpers/{JavaScriptCore,JSCWrapper}.{cpp,h}"
ss.private_header_files = "ReactCommon/jschelpers/{JavaScriptCore,JSCWrapper}.h"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "$(PODS_TARGET_SRCROOT)/ReactCommon" }
ss.framework = "JavaScriptCore"
end
s.subspec 'cxxreact' do |ss|
ss.dependency 'React/jschelpers'
ss.source_files = 'ReactCommon/cxxreact/{JSBundleType,oss-compat-util}.{cpp,h}'
ss.header_dir = 'cxxreact'
s.subspec "cxxreact" do |ss|
ss.dependency "React/jschelpers"
ss.source_files = "ReactCommon/cxxreact/{JSBundleType,oss-compat-util}.{cpp,h}"
ss.private_header_files = "ReactCommon/cxxreact/{JSBundleType,oss-compat-util}.h"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "$(PODS_TARGET_SRCROOT)/ReactCommon" }
end
s.subspec 'yoga' do |ss|
ss.source_files = 'ReactCommon/yoga/**/*.{c,h}'
ss.header_dir = 'yoga'
s.subspec "ART" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/ART/**/*.{h,m}"
end
s.subspec 'ART' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/ART/**/*.{h,m}"
s.subspec "RCTActionSheet" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
end
s.subspec 'RCTActionSheet' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
s.subspec "RCTAdSupport" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/AdSupport/*.{h,m}"
end
s.subspec 'RCTAdSupport' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/AdSupport/*.{h,m}"
s.subspec "RCTAnimation" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/NativeAnimation/{Drivers/*,Nodes/*,*}.{h,m}"
end
s.subspec 'RCTAnimation' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/NativeAnimation/{Drivers/*,Nodes/*,*}.{h,m}"
s.subspec "RCTCameraRoll" do |ss|
ss.dependency "React/Core"
ss.dependency "React/RCTImage"
ss.source_files = "Libraries/CameraRoll/*.{h,m}"
end
s.subspec 'RCTCameraRoll' do |ss|
ss.dependency 'React/Core'
ss.dependency 'React/RCTImage'
ss.source_files = "Libraries/CameraRoll/*.{h,m}"
s.subspec "RCTGeolocation" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Geolocation/*.{h,m}"
end
s.subspec 'RCTGeolocation' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Geolocation/*.{h,m}"
s.subspec "RCTImage" do |ss|
ss.dependency "React/Core"
ss.dependency "React/RCTNetwork"
ss.source_files = "Libraries/Image/*.{h,m}"
end
s.subspec 'RCTImage' do |ss|
ss.dependency 'React/Core'
ss.dependency 'React/RCTNetwork'
ss.source_files = "Libraries/Image/*.{h,m}"
s.subspec "RCTNetwork" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Network/*.{h,m,mm}"
end
s.subspec 'RCTNetwork' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Network/*.{h,m,mm}"
s.subspec "RCTPushNotification" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/PushNotificationIOS/*.{h,m}"
end
s.subspec 'RCTPushNotification' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/PushNotificationIOS/*.{h,m}"
s.subspec "RCTSettings" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Settings/*.{h,m}"
end
s.subspec 'RCTSettings' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Settings/*.{h,m}"
s.subspec "RCTText" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Text/*.{h,m}"
end
s.subspec 'RCTText' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Text/*.{h,m}"
s.subspec "RCTVibration" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Vibration/*.{h,m}"
end
s.subspec 'RCTVibration' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Vibration/*.{h,m}"
s.subspec "RCTWebSocket" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/WebSocket/*.{h,m}"
end
s.subspec 'RCTWebSocket' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/WebSocket/*.{h,m}"
s.subspec "RCTLinkingIOS" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/LinkingIOS/*.{h,m}"
end
s.subspec 'RCTLinkingIOS' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/LinkingIOS/*.{h,m}"
end
s.subspec 'RCTTest' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/RCTTest/**/*.{h,m}"
ss.frameworks = "XCTest"
s.subspec "RCTTest" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/RCTTest/**/*.{h,m}"
ss.frameworks = "XCTest"
end
end
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0-master
VERSION_NAME=0.42.0
GROUP=com.facebook.react
POM_NAME=ReactNative
+44
View File
@@ -0,0 +1,44 @@
package = JSON.parse(File.read(File.expand_path('../../package.json', __dir__)))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip
else
source[:tag] = "v#{version}"
end
Pod::Spec.new do |spec|
spec.name = 'Yoga'
spec.version = "#{version}.React"
spec.license = { :type => 'BSD' }
spec.homepage = 'https://facebook.github.io/yoga/'
spec.documentation_url = 'https://facebook.github.io/yoga/docs/api/c/'
spec.summary = 'Yoga is a cross-platform layout engine which implements Flexbox.'
spec.description = 'Yoga is a cross-platform layout engine enabling maximum collaboration within your team by implementing an API many designers are familiar with, and opening it up to developers across different platforms.'
spec.authors = 'Facebook'
spec.source = source
spec.module_name = 'yoga'
spec.requires_arc = false
spec.compiler_flags = [
'-fno-omit-frame-pointer',
'-fexceptions',
'-Wall',
'-Werror',
'-std=c11',
'-fPIC'
]
# Pinning to the same version as React.podspec.
spec.platform = :ios, "8.0"
# Set this environment variable when not using the `:path` option to install the pod.
# E.g. when publishing this spec to a spec repo.
source_files = 'yoga/**/*.{c,h}'
source_files = File.join('ReactCommon/yoga', source_files) if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION']
spec.source_files = source_files
end
+7 -6
View File
@@ -44,8 +44,9 @@ Run:
git checkout -b <version_you_are_releasing>-stable
# e.g. git checkout -b 0.22-stable
node ./scripts/bump-oss-version.js <exact-version_you_are_releasing>
# e.g. node ./scripts/bump-oss-version.js 0.22.0-rc
./scripts/bump-oss-version.js <exact-version_you_are_releasing>
# e.g. ./scripts/bump-oss-version.js 0.22.0-rc
# You can use the --remote option to specify a Git remote other than the default "origin"
```
Circle CI will automatically run the tests and publish to npm with the version you have specified (e.g `0.22.0-rc`) and tag `next` meaning that this version will not be installed for users by default.
@@ -112,8 +113,8 @@ git cherry-pick commitHash1
If everything worked:
```bash
node ./scripts/bump-oss-version.js <exact_version_you_are_releasing>
# e.g. node ./scripts/bump-oss-version.js 0.28.0-rc.1
./scripts/bump-oss-version.js <exact_version_you_are_releasing>
# e.g. ./scripts/bump-oss-version.js 0.28.0-rc.1
````
-------------------
@@ -141,8 +142,8 @@ git cherry-pick commitHash1
If everything worked:
```bash
node ./scripts/bump-oss-version.js <exact_version_you_are_releasing>
# e.g. node ./scripts/bump-oss-version.js 0.22.0
./scripts/bump-oss-version.js <exact_version_you_are_releasing>
# e.g. ./scripts/bump-oss-version.js 0.22.0
```
#### Update the release notes
+112
View File
@@ -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,
};
+16 -10
View File
@@ -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...');
+1 -1
View File
@@ -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, {
@@ -1,2 +1,3 @@
/captures/*
preLoadedCapture.js
bundle.js
File diff suppressed because one or more lines are too long
@@ -0,0 +1,41 @@
# App template for new React Native apps
This is a simple React Native app template which demonstrates a few basics concepts such as navigation between a few screens, ListViews, and handling text input.
<img src="https://cloud.githubusercontent.com/assets/346214/22697898/ced66f52-ed4a-11e6-9b90-df6daef43199.gif" alt="Android Example" height="800" style="float: left"/>
<img src="https://cloud.githubusercontent.com/assets/346214/22697901/cfeab3e4-ed4a-11e6-8552-d76585317ac2.gif" alt="iOS Example" height="800"/>
## Purpose
The idea is to make it easier for people to get started with React Native. Currently `react-native init` creates a very simple app that contains one screen with static text. Everyone new to React Native then needs to figure out how to do very basic things such as:
- Rendering a list of items fetched from a server
- Navigating between screens
- Handling text input and the software keyboard
This app serves as a template used by `react-native init` so it is easier for anyone to get up and running quickly by having an app with a few screens and a ListView ready to go.
### Best practices
Another purpose of this app is to define best practices such as the folder structure of a standalone React Native app and naming conventions.
## Not using Redux
This template intentionally doesn't use Redux. After discussing with a few people who have experience using Redux we concluded that adding Redux to this app targeted at beginners would make the code more confusing, and wouldn't clearly show the benefits of Redux (because the app is too small). There are already a few concepts to grasp - the React component lifecycle, rendeing lists, using async / await, handling the software keyboard. We thought that's the maximum amount of things to learn at once. It's better for everyone to see patterns in their codebase as the app grows and decide for themselves whether and when they need Redux. See also the post [You Might Not Need Redux](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367#.f3q7kq4b3) by [Dan Abramov](https://twitter.com/dan_abramov).
## Not using Flow (for now)
Many people are new to React Native, some are new to ES6 and most people will be new to Flow. Therefore we didn't want to introduce all these concepts all at once in a single codebase. However, it might make sense to later introduce a separate version of this template that uses Flow annotations.
## Provide feedback
We need your feedback. Do you have a lot of experience building React Native apps? If so, please carefully read the code of the template and if you think something should be done differently, use issues in the repo [mkonicek/AppTemplateFeedback](https://github.com/mkonicek/AppTemplateFeedback) to discuss what should be done differently.
## How to use the template
```
$ react-native init MyApp --version 0.42.0-rc.2 --template navigation
$ cd MyApp
$ react-native run-android
$ react-native run-ios
```
@@ -1,3 +1,5 @@
'use strict';
/* @flow */
import React, { PropTypes, Component } from 'react';
@@ -10,7 +12,7 @@ import {
} from 'react-native';
type Props = {
offset?: number;
offset?: number,
}
type State = {
@@ -41,7 +43,7 @@ type State = {
*/
const KeyboardSpacer = () => (
Platform.OS === 'ios' ? <KeyboardSpacerIOS /> : null
)
);
class KeyboardSpacerIOS extends Component<Props, Props, State> {
static propTypes = {
@@ -1,3 +1,5 @@
'use strict';
import React, { Component } from 'react';
import {
Platform,
@@ -21,7 +23,7 @@ const Touchable = ({onPress, children}) => {
);
} else {
return (
<TouchableHighlight onPress={onPress} underlayColor='#ddd'>
<TouchableHighlight onPress={onPress} underlayColor="#ddd">
{child}
</TouchableHighlight>
);
@@ -0,0 +1,3 @@
{
"react-navigation": "1.0.0-beta.1"
}
@@ -2,4 +2,4 @@ import { AppRegistry } from 'react-native';
import MainNavigator from './views/MainNavigator';
AppRegistry.registerComponent('ChatExample', () => MainNavigator);
AppRegistry.registerComponent('HelloWorld', () => MainNavigator);
@@ -2,4 +2,4 @@ import { AppRegistry } from 'react-native';
import MainNavigator from './views/MainNavigator';
AppRegistry.registerComponent('ChatExample', () => MainNavigator);
AppRegistry.registerComponent('HelloWorld', () => MainNavigator);
@@ -0,0 +1,117 @@
'use strict';
// This file just a dummy example of a HTTP API to talk to the backend.
// The state of the "database" that would normally live on the server
// is simply held here in memory.
const backendStateForLoggedInPerson = {
chats: [
{
name: 'Claire',
messages: [
{
name: 'Claire',
text: 'I ❤️ React Native!',
},
],
},
{
name: 'John',
messages: [
{
name: 'John',
text: 'I ❤️ React Native!',
},
],
}
],
};
/**
* Randomly simulate network failures.
* It is useful to enable this during development to make sure our app works
* in real-world conditions.
*/
function isNetworkFailure() {
const chanceOfFailure = 0; // 0..1
return Math.random() < chanceOfFailure;
}
/**
* Helper for the other functions in this file.
* Simulates a short delay and then returns a provided value or failure.
* This is just a dummy example. Normally we'd make a HTTP request,
* see http://facebook.github.io/react-native/docs/network.html
*/
function _makeSimulatedNetworkRequest(getValue) {
const durationMs = 400;
return new Promise(function (resolve, reject) {
setTimeout(function () {
if (isNetworkFailure()) {
reject(new Error('Network failure'));
} else {
getValue(resolve, reject);
}
}, durationMs);
});
}
/**
* Fetch a list of all chats for the logged in person.
*/
async function fetchChatList() {
return _makeSimulatedNetworkRequest((resolve, reject) => {
resolve(backendStateForLoggedInPerson.chats.map(chat => chat.name));
});
}
/**
* Fetch a single chat.
*/
async function fetchChat(name) {
return _makeSimulatedNetworkRequest((resolve, reject) => {
resolve(
backendStateForLoggedInPerson.chats.find(
chat => chat.name === name
)
);
});
}
/**
* Send given message to given person.
*/
async function sendMessage({name, message}) {
return _makeSimulatedNetworkRequest((resolve, reject) => {
const chatForName = backendStateForLoggedInPerson.chats.find(
chat => chat.name === name
);
if (chatForName) {
chatForName.messages.push({
name: 'Me',
text: message,
});
resolve();
} else {
reject(new Error('Uknown person: ' + name));
}
});
}
const Backend = {
fetchChatList,
fetchChat,
sendMessage,
};
export default Backend;
// In case you are looking into using Redux for state management,
// this is how network requests are done in the f8 app which uses Redux:
// - To load some data, a Component fires a Redux action, such as loadSession()
// - That action makes the HTTP requests and then dispatches a redux action
// {type: 'LOADED_SESSIONS', results}
// - Then all reducers get called and one of them updates a part of the application
// state by storing the results
// - Redux re-renders the connected Components
// See https://github.com/fbsamples/f8app/search?utf8=%E2%9C%93&q=loaded_sessions
@@ -1,24 +1,20 @@
import React, { Component } from 'react';
import {
ListView,
Platform,
Text,
} from 'react-native';
'use strict';
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;
@@ -1,3 +1,5 @@
'use strict';
/**
* This is an example React Native app demonstrates ListViews, text input and
* navigation between a few screens.
@@ -1,11 +1,16 @@
'use strict';
import React, { Component } from 'react';
import {
ActivityIndicator,
Image,
ListView,
Platform,
StyleSheet,
View,
} from 'react-native';
import ListItem from '../../components/ListItem';
import Backend from '../../lib/Backend';
export default class ChatListScreen extends Component {
@@ -29,28 +34,47 @@ export default class ChatListScreen extends Component {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
'Claire', 'John'
])
isLoading: true,
dataSource: ds,
};
}
async componentDidMount() {
const chatList = await Backend.fetchChatList();
this.setState((prevState) => ({
dataSource: prevState.dataSource.cloneWithRows(chatList),
isLoading: false,
}));
}
// Binding the function so it can be passed to ListView below
// and 'this' works properly inside _renderRow
_renderRow = (name) => {
// and 'this' works properly inside renderRow
renderRow = (name) => {
return (
<ListItem
label={name}
onPress={() => this.props.navigation.navigate('Chat', {name: name})}
onPress={() => {
// Start fetching in parallel with animating
this.props.navigation.navigate('Chat', {
name: name,
});
}}
/>
)
);
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.loadingScreen}>
<ActivityIndicator />
</View>
);
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderRow={this.renderRow}
style={styles.listView}
/>
);
@@ -58,6 +82,11 @@ export default class ChatListScreen extends Component {
}
const styles = StyleSheet.create({
loadingScreen: {
backgroundColor: 'white',
paddingTop: 8,
flex: 1,
},
listView: {
backgroundColor: 'white',
},
@@ -1,14 +1,17 @@
'use strict';
import React, { Component } from 'react';
import {
ActivityIndicator,
Button,
ListView,
Platform,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import KeyboardSpacer from '../../components/KeyboardSpacer';
import Backend from '../../lib/Backend';
export default class ChatScreen extends Component {
@@ -19,23 +22,58 @@ export default class ChatScreen extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
const messages = [
{
name: props.navigation.state.params.name,
name: 'Claire',
text: 'I ❤️ React Native!',
},
];
this.state = {
messages: messages,
dataSource: ds.cloneWithRows(messages),
messages: [],
dataSource: ds,
myMessage: '',
isLoading: true,
};
}
async componentDidMount() {
let chat;
try {
chat = await Backend.fetchChat(this.props.navigation.state.params.name);
} catch (err) {
// Here we would handle the fact the request failed, e.g.
// set state to display "Messages could not be loaded".
// We should also check network connection first before making any
// network requests - maybe we're offline? See React Native's NetInfo
// module.
this.setState({
isLoading: false,
});
return;
}
this.setState((prevState) => ({
messages: chat.messages,
dataSource: prevState.dataSource.cloneWithRows(chat.messages),
isLoading: false,
}));
}
onAddMessage = async () => {
// Optimistically update the UI
this.addMessageLocal();
// Send the request
try {
await Backend.sendMessage({
name: this.props.navigation.state.params.name,
// TODO Is reading state like this outside of setState OK?
// Can it contain a stale value?
message: this.state.myMessage,
});
} catch (err) {
// Here we would handle the request failure, e.g. call setState
// to display a visual hint showing the message could not be sent.
}
}
addMessage = () => {
addMessageLocal = () => {
this.setState((prevState) => {
if (!prevState.myMessage) return prevState;
if (!prevState.myMessage) {
return prevState;
}
const messages = [
...prevState.messages, {
name: 'Me',
@@ -48,10 +86,10 @@ export default class ChatScreen extends Component {
myMessage: '',
}
});
this.refs.textInput.clear();
this.textInput.clear();
}
myMessageChange = (event) => {
onMyMessageChange = (event) => {
this.setState({myMessage: event.nativeEvent.text});
}
@@ -63,10 +101,16 @@ export default class ChatScreen extends Component {
)
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.container}>
<ListView
ref="listView"
dataSource={this.state.dataSource}
renderRow={this.renderRow}
style={styles.listView}
@@ -74,17 +118,17 @@ export default class ChatScreen extends Component {
/>
<View style={styles.composer}>
<TextInput
ref='textInput'
ref={(textInput) => { this.textInput = textInput; }}
style={styles.textInput}
placeholder='Type a message...'
placeholder="Type a message..."
text={this.state.myMessage}
onSubmitEditing={this.addMessage}
onChange={this.myMessageChange}
onSubmitEditing={this.onAddMessage}
onChange={this.onMyMessageChange}
/>
{this.state.myMessage !== '' && (
<Button
title="Send"
onPress={this.addMessage}
onPress={this.onAddMessage}
/>
)}
</View>
@@ -99,7 +143,6 @@ const styles = StyleSheet.create({
flex: 1,
padding: 8,
backgroundColor: 'white',
alignItems: 'flex-end',
},
listView: {
flex: 1,
@@ -1,18 +1,19 @@
'use strict';
import React, { Component } from 'react';
import {
Image,
Platform,
StyleSheet,
Text,
View,
} 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 }) => (
<Image
// Using react-native-vector-icons works here too
source={require('./friend-icon.png')}
source={require('./welcome-icon.png')}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
@@ -29,19 +30,12 @@ export default class FriendListScreen extends Component {
render() {
return (
<View style={styles.container}>
<Text>A list of friends here.</Text>
</View>
<WelcomeText />
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
flex: 1,
padding: 16,
},
icon: {
width: 30,
height: 26,
@@ -0,0 +1,52 @@
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
} from 'react-native';
export default class WelcomeText extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
This app shows the basics of navigating between a few screens,
working with ListView and handling text input.
</Text>
<Text style={styles.instructions}>
Modify any files to get started. For example try changing the
file views/welcome/WelcomeText.android.js.
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu.
</Text>
</View>
);
}
}
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,
},
});
@@ -0,0 +1,52 @@
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
} from 'react-native';
export default class WelcomeText extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
This app shows the basics of navigating between a few screens,
working with ListView and handling text input.
</Text>
<Text style={styles.instructions}>
Modify any files to get started. For example try changing the
file{'\n'}views/welcome/WelcomeText.ios.js.
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu.
</Text>
</View>
);
}
}
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,
},
});

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.42.0",
"description": "A framework for building native apps using React",
"license": "BSD-3-Clause",
"repository": {
@@ -115,7 +115,7 @@
"react-native": "local-cli/wrong-react-native.js"
},
"peerDependencies": {
"react": "~15.4.0-rc.4"
"react": "~15.4.1"
},
"dependencies": {
"absolute-path": "^0.0.0",
@@ -205,10 +205,10 @@
"jest-repl": "18.0.0",
"jest-runtime": "18.0.0",
"mock-fs": "^3.11.0",
"react": "~15.4.0-rc.4",
"react-dom": "~15.4.0-rc.4",
"react-test-renderer": "~15.4.0-rc.4",
"react": "~15.4.1",
"react-dom": "~15.4.1",
"react-test-renderer": "~15.4.1",
"shelljs": "0.6.0",
"sinon": "^2.0.0-pre.2"
}
}
}
+4 -3
View File
@@ -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 <alternative react-native package> - 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;
Regular → Executable
+15 -6
View File
@@ -1,3 +1,4 @@
#!/usr/bin/env node
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
@@ -17,6 +18,13 @@
/*eslint-disable no-undef */
require(`shelljs/global`);
const minimist = require('minimist');
let argv = minimist(process.argv.slice(2), {
alias: {remote: 'r'},
default: {remote: 'origin'},
});
// - check we are in release branch, e.g. 0.33-stable
let branch = exec(`git symbolic-ref --short HEAD`, {silent: true}).stdout.trim();
@@ -30,7 +38,7 @@ let versionMajor = branch.slice(0, branch.indexOf(`-stable`));
// - check that argument version matches branch
// e.g. 0.33.1 or 0.33.0-rc4
let version = process.argv[2];
let version = argv._[0];
if (!version || version.indexOf(versionMajor) !== 0) {
echo(`You must pass a tag like ${versionMajor}.[X]-rc[Y] to bump a version`);
exit(1);
@@ -47,7 +55,7 @@ if (sed(`-i`, /^VERSION_NAME=.*/, `VERSION_NAME=${version}`, `ReactAndroid/gradl
}
// - change React.podspec
if (sed(`-i`, /s.version\s*=.*/, `s.version = \"${version}\"`, `React.podspec`).code) {
if (sed(`-i`, /s\.version\s*=.*/, `s.version = \"${version}\"`, `React.podspec`).code) {
echo(`Couldn't update version for React.podspec`);
exit(1);
}
@@ -77,17 +85,18 @@ if (exec(`git tag v${version}`).code) {
}
// Push newly created tag
exec(`git push origin v${version}`);
let remote = argv.remote;
exec(`git push ${remote} v${version}`);
// Tag latest if doing stable release
if (version.indexOf(`rc`) === -1) {
exec(`git tag -d latest`);
exec(`git push origin :latest`);
exec(`git push ${remote} :latest`);
exec(`git tag latest`);
exec(`git push origin latest`);
exec(`git push ${remote} latest`);
}
exec(`git push origin ${branch} --follow-tags`);
exec(`git push ${remote} ${branch} --follow-tags`);
exit(0);
/*eslint-enable no-undef */
+62
View File
@@ -0,0 +1,62 @@
#!/bin/bash
set -ex
SCRIPTS=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
ROOT=$(dirname $SCRIPTS)
YOGA_ROOT="$ROOT/ReactCommon/yoga"
# Specify `SPEC_REPO` as an env variable if you want to push to a specific spec repo.
# Defaults to `react-test`, which is meant to be a dummy repo used to test that the specs fully lint.
: ${SPEC_REPO:="react-test"}
SPEC_REPO_DIR="$HOME/.cocoapods/repos/$SPEC_REPO"
# If the `SPEC_REPO` does not exist yet, assume this is purely for testing and create a dummy repo.
if ! [ -d "$SPEC_REPO_DIR" ]; then
mkdir -p "$SPEC_REPO_DIR"
cd "$SPEC_REPO_DIR"
echo "testing" > .gitkeep
git init
git add .gitkeep
git commit -m "init"
git remote add origin "https://example.com/$SPEC_REPO.git"
fi
cd "$SPEC_REPO_DIR"
SPEC_REPO_REMOTE=$(git remote get-url origin)
POD_LINT_OPT="--verbose --no-subspecs --allow-warnings --fail-fast --private --swift-version=3.0 --sources=$SPEC_REPO_REMOTE"
# Get the version from a podspec.
version() {
ruby -rcocoapods-core -rjson -e "puts Pod::Specification.from_file('$1').version"
}
# Lint both framework and static library builds.
lint() {
pod lib lint $POD_LINT_OPT
pod lib lint $POD_LINT_OPT --use-libraries
}
# Push the spec in arg `$1`, which is expected to be in the cwd, to the `SPEC_REPO` in JSON format.
push() {
local SPEC_NAME=$1
local POD_NAME=$(basename $SPEC_NAME .podspec)
local SPEC_DIR="$SPEC_REPO_DIR/$POD_NAME/$(version $SPEC_NAME)"
local SPEC_PATH="$SPEC_DIR/$SPEC_NAME.json"
mkdir -p $SPEC_DIR
env INSTALL_YOGA_WITHOUT_PATH_OPTION=1 pod ipc spec $SPEC_NAME > $SPEC_PATH
}
# Perform linting and publishing of podspec in cwd.
# Skip linting with `SKIP_LINT` if e.g. publishing to a private spec repo.
process() {
cd $1
if [ -z "$SKIP_LINT" ]; then
lint
fi
local SPEC_NAME=(*.podspec)
push $SPEC_NAME
}
process $YOGA_ROOT
process $ROOT