Compare commits

...
Author SHA1 Message Date
Christopher Chedeau c1004c7d86 Fix website
Summary:This puts the images inside of the blog folder in the output so we can safely copy them over. And moves the images in /blog/img/ in the source control so it's easier to discover (instead of having to search many levels deep).

Should make the website work!
Closes https://github.com/facebook/react-native/pull/6659

Differential Revision: D3098308

Pulled By: vjeux

fb-gh-sync-id: 99477e27843dd6c88aa854028944f2f0b672db1f
fbshipit-source-id: 99477e27843dd6c88aa854028944f2f0b672db1f
2016-03-28 09:31:21 -07:00
Christopher Chedeau 38f13184a8 Introduce blog
Ported the infrastructure from jest.
2016-03-24 19:21:25 -07:00
Martin Konicek db3c3e460d [0.22.2] Bump version numbers 2016-03-21 22:00:57 +00:00
Martin Konicek 810a7a85ce Revert "added shrinkwrap to package"
This reverts commit cafbeb08fe.
2016-03-21 21:50:54 +00:00
Konstantin Raev e163e6d8b7 fixed podspec verison generation 2016-03-21 20:05:57 +00:00
Konstantin Raev 2360c99d99 Revert "Sync CSSLayout: Make 'stretch' aligned children fill cross-axis in containers of undefined size in the cross-axis"
Reason - to keep IOS and Android in sync
A new update will be coming to master branch

This reverts commit f538032888.
2016-03-21 11:56:26 +00:00
Martín Bigio 0f7e461820 inline __accept call so that sourcemaps work
Summary:Sourcemaps on HMR where a couple of line off. The problem is that since the `__accept` call doesn't go through the sourcemaps pipeline we need to make sure that call is a single-line one.

This was originally written in a single line but I incorrectly updated it on https://github.com/facebook/react-native/commit/436db67126f1e64d1889c2d084b77c98c8d63f8d. Would be great having test coverage for this.

Reviewed By: davidaurelio

Differential Revision: D3075164

fb-gh-sync-id: c77ea99f26bdd675f241c5d20a620eb4ddfbf701
shipit-source-id: c77ea99f26bdd675f241c5d20a620eb4ddfbf701
2016-03-21 11:56:05 +00:00
Konstantin Raev cafbeb08fe added shrinkwrap to package 2016-03-16 12:07:46 +00:00
Konstantin Raev 995aa485d2 revert committed version numbers, confusing for CI npm deployments 2016-03-16 10:56:12 +00:00
Konstantin Raev 09ef2b522b added deployment debugging inf 2016-03-15 18:18:45 +00:00
Konstantin Raev 1228321636 Npm publish
Summary:This work allows automated release deployment.

Previous semi-automation lived in release.sh and I split it into two pieces:
- test-manual-e2e.sh - that just tests that current commit is buildable and makes a quick e2e installation for manual testing
- publish-npm.js - that makes publish based on what current branch and tags are on commit that is tested/deployed by CI

This simplified `Releases.md` guide and requires you to just run
```
git checkout -b 0.22-stable
git tag v0.22.0-rc
git push origin 0.22-stable --tags
```
to have a successful npm release.
Closes https://github.com/facebook/react-native/pull/6453

Reviewed By: mkonicek

Differential Revision: D3047938

Pulled By: bestander

fb-gh-sync-id: dbebf4c3a0bc2c2a0ef75c54595ab5654f91b8ea
shipit-source-id: dbebf4c3a0bc2c2a0ef75c54595ab5654f91b8ea
2016-03-15 16:48:24 +00:00
Laurence Bortfeld 31c3b08a7b getCurrentConnectivity should resolve with object
Summary:`getCurrentConnectivity()` should resolve with object instead of array otherwise in `NetInfo.fetch().done(reach => console.log('Initial: ' + reach))` `reach` will be undefined (see https://github.com/l-urence/react-native/blob/master/Libraries/Network/NetInfo.js#L200).

cc satya164
Closes https://github.com/facebook/react-native/pull/6373

Differential Revision: D3032773

fb-gh-sync-id: edc545548d3c17387545b6c59894c27e4563cb18
shipit-source-id: edc545548d3c17387545b6c59894c27e4563cb18
2016-03-15 16:42:48 +00:00
Martín Bigio 25496d4d97 Unbreak HMR listener
Reviewed By: sam-swarr

Differential Revision: D3042801

fb-gh-sync-id: 2f06f2efbdf3d04ce4db0cf847ebe76d05fb109f
shipit-source-id: 2f06f2efbdf3d04ce4db0cf847ebe76d05fb109f
2016-03-15 16:42:37 +00:00
Dan Abramov 4113da70f0 Update hot reloading dependencies
Summary:These updates include important changes to correctness in the linked and underlying packages:

* https://github.com/gaearon/babel-plugin-react-transform/releases/tag/v2.0.1
* https://github.com/gaearon/babel-plugin-react-transform/releases/tag/v2.0.2
* https://github.com/gaearon/react-proxy/releases/tag/v1.1.3
* https://github.com/gaearon/react-proxy/releases/tag/v1.1.4
* etc

We need to include these if we want to ship hot reloading.

Create a new project with those versions of packages.

<img width="761" alt="screen shot 2016-03-06 at 18 49 20" src="https://cloud.githubusercontent.com/assets/810438/13556222/2d45ca62-e3cc-11e5-8a3f-a2346efe19dd.png">

Verify hot reloading still works.

![](http://cl.ly/2J150y0Y3E1z/download/Screen%20Recording%202016-03-06%20at%2018.48.gif)

martinbigio skevy

I wasn’t sure whether to touch the shrinkwrap.
Closes https://github.com/facebook/react-native/pull/6325

Reviewed By: yungsters

Differential Revision: D3019470

Pulled By: martinbigio

fb-gh-sync-id: 99bad390cd898f58aa7f74494066561a81eb40e6
shipit-source-id: 99bad390cd898f58aa7f74494066561a81eb40e6
2016-03-15 16:42:23 +00:00
Janic Duplessis c5feed4991 Update node-haste and replace fast-path with node-haste's version to fix Windows compatibility
Summary:This is the last bits needed to fix Windows compatibility on master, most of the work was done in node-haste.

**Test plan**
Run npm test
Run the packager using Windows and Mac

cc cpojer davidaurelio
Closes https://github.com/facebook/react-native/pull/6260

Reviewed By: dmmiller, bestander

Differential Revision: D3005397

Pulled By: davidaurelio

fb-gh-sync-id: e16847808ebfa8b234315b2093dba204c9c1e869
shipit-source-id: e16847808ebfa8b234315b2093dba204c9c1e869
2016-03-15 16:41:43 +00:00
Konstantin Raev 9ed661601b [0.22.0-rc] Bump version numbers 2016-03-08 20:07:23 +00:00
34 changed files with 784 additions and 8497 deletions
+1
View File
@@ -37,6 +37,7 @@ buck-out
.gradle
local.properties
*.iml
/android/
# Node
node_modules
+1 -1
View File
@@ -90,7 +90,7 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC
RCT_EXPORT_METHOD(getCurrentConnectivity:(RCTPromiseResolveBlock)resolve
reject:(__unused RCTPromiseRejectBlock)reject)
{
resolve(@[@{@"network_info": _status}]);
resolve(@{@"network_info": _status});
}
@end
+9 -9
View File
@@ -96,15 +96,15 @@ Error: ${e.message}`
? global.nativeInjectHMRUpdate
: eval;
// TODO: (martinb) yellow box if cannot accept module
code = `
__accept(
${name},
function(global, require, module, exports) {
${code}
},
${JSON.stringify(inverseDependencies)}
);`;
code = [
`__accept(`,
`${name},`,
`function(global,require,module,exports){`,
`${code}`,
'\n},',
`${JSON.stringify(inverseDependencies)}`,
`);`,
].join('');
injectFunction(code, sourceURLs[i]);
});
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "React"
s.version = "0.0.0-master"
s.version = "0.22.2"
s.summary = "Build high quality mobile apps using React."
s.description = <<-DESC
React Native apps are built using the React JS
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_NAME=0.12.0-SNAPSHOT
VERSION_NAME=0.22.2
GROUP=com.facebook.react
POM_NAME=ReactNative
@@ -7,7 +7,7 @@
*/
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
// @generated SignedSource<<dcc87213906997ff353adb1148c8e77c>>
// @generated SignedSource<<df03fd95c4520badeed398c76e70242d>>
package com.facebook.csslayout;
@@ -258,7 +258,7 @@ public class LayoutEngine {
float paddingAndBorderAxisColumn = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])));
if (isMeasureDefined(node)) {
boolean isResolvedRowDimDefined = (!Float.isNaN(node.layout.dimensions[dim[resolvedRowAxis]]) && node.layout.dimensions[dim[resolvedRowAxis]] >= 0.0);
boolean isResolvedRowDimDefined = !Float.isNaN(node.layout.dimensions[dim[resolvedRowAxis]]);
float width = CSSConstants.UNDEFINED;
if ((!Float.isNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
@@ -274,7 +274,7 @@ public class LayoutEngine {
float height = CSSConstants.UNDEFINED;
if ((!Float.isNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
height = node.style.dimensions[DIMENSION_HEIGHT];
} else if ((!Float.isNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
} else if (!Float.isNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]])) {
height = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]];
} else {
height = parentMaxHeight -
@@ -320,8 +320,8 @@ public class LayoutEngine {
float paddingAndBorderAxisMain = ((node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
float paddingAndBorderAxisCross = ((node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])));
boolean isMainDimDefined = (!Float.isNaN(node.layout.dimensions[dim[mainAxis]]) && node.layout.dimensions[dim[mainAxis]] >= 0.0);
boolean isCrossDimDefined = (!Float.isNaN(node.layout.dimensions[dim[crossAxis]]) && node.layout.dimensions[dim[crossAxis]] >= 0.0);
boolean isMainDimDefined = !Float.isNaN(node.layout.dimensions[dim[mainAxis]]);
boolean isCrossDimDefined = !Float.isNaN(node.layout.dimensions[dim[crossAxis]]);
boolean isMainRowDirection = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE);
int i;
@@ -383,8 +383,8 @@ public class LayoutEngine {
float mainDim = leadingPaddingAndBorderMain;
float crossDim = 0;
float maxWidth = CSSConstants.UNDEFINED;
float maxHeight = CSSConstants.UNDEFINED;
float maxWidth;
float maxHeight;
for (i = startLine; i < childCount; ++i) {
child = node.getChildAt(i);
child.lineIndex = linesCount;
@@ -421,7 +421,7 @@ public class LayoutEngine {
// left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if ((!Float.isNaN(node.layout.dimensions[dim[axis]]) && node.layout.dimensions[dim[axis]] >= 0.0) &&
if (!Float.isNaN(node.layout.dimensions[dim[axis]]) &&
!(!Float.isNaN(child.style.dimensions[dim[axis]]) && child.style.dimensions[dim[axis]] >= 0.0) &&
!Float.isNaN(child.style.position[leading[axis]]) &&
!Float.isNaN(child.style.position[trailing[axis]])) {
@@ -468,7 +468,7 @@ public class LayoutEngine {
maxHeight = CSSConstants.UNDEFINED;
if (!isMainRowDirection) {
if ((!Float.isNaN(node.layout.dimensions[dim[resolvedRowAxis]]) && node.layout.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
if ((!Float.isNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] -
paddingAndBorderAxisResolvedRow;
} else {
@@ -477,7 +477,7 @@ public class LayoutEngine {
paddingAndBorderAxisResolvedRow;
}
} else {
if ((!Float.isNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
if ((!Float.isNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
maxHeight = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
paddingAndBorderAxisColumn;
} else {
@@ -528,7 +528,7 @@ public class LayoutEngine {
if (isSimpleStackCross &&
(child.style.positionType != CSSPositionType.RELATIVE ||
(alignItem != CSSAlign.STRETCH && alignItem != CSSAlign.FLEX_START) ||
(alignItem == CSSAlign.STRETCH && !isCrossDimDefined))) {
Float.isNaN(child.layout.dimensions[dim[crossAxis]]))) {
isSimpleStackCross = false;
firstComplexCross = i;
}
@@ -611,7 +611,7 @@ public class LayoutEngine {
);
maxWidth = CSSConstants.UNDEFINED;
if ((!Float.isNaN(node.layout.dimensions[dim[resolvedRowAxis]]) && node.layout.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
if ((!Float.isNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] -
paddingAndBorderAxisResolvedRow;
} else if (!isMainRowDirection) {
@@ -620,7 +620,7 @@ public class LayoutEngine {
paddingAndBorderAxisResolvedRow;
}
maxHeight = CSSConstants.UNDEFINED;
if ((!Float.isNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
if ((!Float.isNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
maxHeight = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
paddingAndBorderAxisColumn;
} else if (isMainRowDirection) {
@@ -738,31 +738,15 @@ public class LayoutEngine {
CSSAlign alignItem = getAlignItem(node, child);
/*eslint-enable */
if (alignItem == CSSAlign.STRETCH) {
// You can only stretch if the dimension has not already been defined
// You can only stretch if the dimension has not already been set
// previously.
if (!(!Float.isNaN(child.style.dimensions[dim[crossAxis]]) && child.style.dimensions[dim[crossAxis]] >= 0.0)) {
float dimCrossAxis = child.layout.dimensions[dim[crossAxis]];
if (Float.isNaN(child.layout.dimensions[dim[crossAxis]])) {
child.layout.dimensions[dim[crossAxis]] = Math.max(
boundAxis(child, crossAxis, containerCrossAxis -
paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))),
// You never want to go smaller than padding
((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))
);
// If the size has changed, and this child has children we need to re-layout this child
if (dimCrossAxis != child.layout.dimensions[dim[crossAxis]] && child.getChildCount() > 0) {
// Reset child margins before re-layout as they are added back in layoutNode and would be doubled
child.layout.position[leading[mainAxis]] -= child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
getRelativePosition(child, mainAxis);
child.layout.position[trailing[mainAxis]] -= child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) +
getRelativePosition(child, mainAxis);
child.layout.position[leading[crossAxis]] -= child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
getRelativePosition(child, crossAxis);
child.layout.position[trailing[crossAxis]] -= child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) +
getRelativePosition(child, crossAxis);
layoutNode(layoutContext, child, maxWidth, maxHeight, direction);
}
}
} else if (alignItem != CSSAlign.FLEX_START) {
// The remaining space between the parent dimensions+padding and child
@@ -840,7 +824,7 @@ public class LayoutEngine {
if (child.lineIndex != i) {
break;
}
if ((!Float.isNaN(child.layout.dimensions[dim[crossAxis]]) && child.layout.dimensions[dim[crossAxis]] >= 0.0)) {
if (!Float.isNaN(child.layout.dimensions[dim[crossAxis]])) {
lineHeight = Math.max(
lineHeight,
child.layout.dimensions[dim[crossAxis]] + (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))
@@ -933,7 +917,7 @@ public class LayoutEngine {
for (ii = 0; ii < 2; ii++) {
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if ((!Float.isNaN(node.layout.dimensions[dim[axis]]) && node.layout.dimensions[dim[axis]] >= 0.0) &&
if (!Float.isNaN(node.layout.dimensions[dim[axis]]) &&
!(!Float.isNaN(currentAbsoluteChild.style.dimensions[dim[axis]]) && currentAbsoluteChild.style.dimensions[dim[axis]] >= 0.0) &&
!Float.isNaN(currentAbsoluteChild.style.position[leading[axis]]) &&
!Float.isNaN(currentAbsoluteChild.style.position[trailing[axis]])) {
@@ -1,7 +1,7 @@
The source of truth for css-layout is: https://github.com/facebook/css-layout
The code here should be kept in sync with GitHub.
HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/3f0f4be2e868a9af4f3ed1e019f71efbc814e8a2
HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/219bdaed15c16bbf7c1f2bab17ad629d04cc4199
There is generated code in:
- README (this file)
@@ -1,7 +1,7 @@
The source of truth for css-layout is: https://github.com/facebook/css-layout
The code here should be kept in sync with GitHub.
HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/3f0f4be2e868a9af4f3ed1e019f71efbc814e8a2
HEAD at the time this code was synced: https://github.com/facebook/css-layout/commit/219bdaed15c16bbf7c1f2bab17ad629d04cc4199
There is generated code in:
- README.facebook (this file)
+40 -67
View File
@@ -5,46 +5,54 @@ Release schedule:
- **0.21 branch cut**, 0.21.0-rc - **week of Feb 15**
- 0.21.0 - Feb 29
- **0.22 branch cut**, 0.22.0-rc - **week of Feb 29**
- 0.22.0 - Mar 14
- **0.23 branch cut**, 0.23.0-rc - **week of Mar 14**
- 0.23.0 - Mar 28
- **0.24 branch cut**, 0.23.0-rc - **week of Mar 28**
- 0.24.0 - Apr 11
- **0.22 branch cut**, 0.22.0-rc - **week of Mar 7**
- 0.22.0 - Mar 21
- **0.23 branch cut**, 0.23.0-rc - **week of Mar 21**
- 0.23.0 - Apr 4
- **0.24 branch cut**, 0.23.0-rc - **week of Apr 4**
- 0.24.0 - Apr 18
- ...
## One time setup
Set up Sinopia: https://github.com/facebook/react-native/tree/master/react-native-cli
## Cut a release branch
#### Check that everything works
To cut a release branch and check that everything works, you'll need Mac OS with the [Android dev environment set up](https://github.com/facebook/react-native/blob/master/ReactAndroid/README.md).
Make absolutely sure a basic iOS and Android workflow works on the commit you are going to use for release.
Make sure CI systems [Travis](https://travis-ci.org/facebook/react-native) and [Circle](https://circleci.com/gh/facebook/react-native)
are green and then run
```
./scripts/test-manual-e2e.sh
```
This script runs end to end with a proxy npm repository on local PC and asks to check that Chrome Debugging works.
## Cut a release branch and push to github
To cut a release branch and check that everything works, you'll need Mac OS with the
[Android dev environment set up](https://github.com/facebook/react-native/blob/master/ReactAndroid/README.md).
Run:
cd react-native
./scripts/release.sh version_you_are_releasing # e.g. ./scripts/release.sh 0.22
```
git checkout -b <version_you_are_releasing>-stable # e.g. git checkout -b 0.22-stable
git tag v<version_you_are_releasing>.0-rc # e.g. git tag v0.22.0-rc
git push origin <version_you_are_releasing>-stable --tags # e.g. git push origin 0.22-stable --tags
```
#### Check that everything works
Circle CI will run the tests and publish to npm with version `0.22.0-rc` and tag `next` meaning that
this version will not be installed for users by default.
Make absolutely sure a basic iOS and Android workflow works on the release branch you've just created, see the instructions printed by `release.sh`.
#### Push to github
- Check git history, the last commit should be "[0.22-rc] Bump version numbers" (with the correct version)
- `git push origin 0.version_you_are_releasing-stable # e.g. git push origin 0.22-stable`
** Note ** CI won't publish to npm if the `last` commit on the new branch does not have a tag `v<branch-name-without-stable>.0-[rc]`.
## Make sure we have release notes
Post that we're ready to release so a voluteer can write release notes:
https://github.com/facebook/react-native/releases
To go through all the commits that went into a release, one way is to use the GitHub compare view: https://github.com/facebook/react-native/compare/0.18-stable...0.19-stable
## Do an RC release (e.g. 0.22.0-rc)
IMPORTANT: `npm publish` will automatically set the latest tag. **When doing an RC release**, run `npm publish --tag next` - this way people need to opt in to get the RC release.
To go through all the commits that went into a release, one way is to use the GitHub compare view: https://github.com/facebook/react-native/compare/0.21-stable...0.22-stable
## IMPORTANT: Track bug reports from the community during the following two weeks and make sure they get fixed
@@ -56,7 +64,7 @@ We should only be tracking bugs with small and non-risky fixes. Don't pick new f
## Do a release (e.g. 0.22.0, 0.22.1)
Roughly two weeks after the branch cut (see the release schedule above) it's time to promote the RC to a real realease.
Roughly two weeks after the branch cut (see the release schedule above) it's time to promote the RC to a real release.
Make sure you know which bug fixes should definitely be cheery-picked, example: https://github.com/facebook/react-native/issues/6087
@@ -64,65 +72,30 @@ We should only cherry-pick small and non-risky bug fixes. Don't pick new feature
Do the following:
**NOTE: Most of these steps are similar to what the script `release.sh` does. The script is used to cut the release branch only, can be made more generic to help with this step too.**
```
cd react-native
git checkout master
git pull
git checkout 0.version_you_are_releasing-stable # e.g. git checkout 0.22-stable
git pull origin 0.version_you_are_releasing-stable # e.g. git pull origin 0.22-stable
# Cherry-pick those commits, test everything again using Sinopia
# Cherry-pick those commits
git cherry-pick commitHash1
# Create the 'android' folder to be published to npm.
./gradlew :ReactAndroid:installArchives
# Check that it's there: `ls android`
...
npm set registry http://localhost:4873
sinopia
# change versions in package.json and React.podspec
npm publish
cd /tmp
react-native init TestAapp
cd TestApp
react-native run-ios
# Check that you can Reload JS and the Chrome debugger works
# Kill packager
open ios/TestApp.xcodeproj
# Click run
# Check that you can Reload JS and the Chrome debugger works
cd android && ./gradlew dependencies
# Double check the react-native dep has the correct version
cd ..
react-native run-android
# test everything again using sinopia
./scripts/test-manual-e2e.sh
# Check that you can Reload JS and the Chrome debugger works
```
If everything worked:
```
npm set registry https://registry.npmjs.org
npm publish
```
Tag the release in Git:
```
git tag v-version_you_are_releasing # e.g. git tag v0.22.0, git tag v0.22.1
git push --tags
```
To update the [website](https://facebook.github.io/react-native), move the `latest` tag and push to the `0.x-stable` branch. CircleCI will build and deploy the latest docs to the website.
```
git tag -d latest
git push origin :latest
git tag latest
git push origin version_you_are_releasing-stable --tags # e.g git push origin 0.22-stable --tags
git tag latest # for docs [website](https://facebook.github.io/react-native) to be generated
git push origin 0.22-stable --tags
```
Once you see the version in the top left corner of the website has been updated:
Move the release notes to the tag you've just created. We want single release notes per version, for example if there is v0.22.0-rc and later we release v0.22.0, the release notes should live on v0.22.0:
Move the release notes to the tag you've just created. We want single release notes per version,
for example if there is v0.22.0-rc and later we release v0.22.0, the release notes should live on v0.22.0:
https://github.com/facebook/react-native/tags
Uncheck the box "This is a pre-release" and publish the notes.
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "babel-preset-react-native",
"version": "1.5.1",
"version": "1.5.2",
"description": "Babel preset for React Native applications",
"main": "index.js",
"repository": "https://github.com/facebook/react-native/tree/master/babel-preset",
@@ -15,7 +15,7 @@
},
"homepage": "https://github.com/facebook/react-native/tree/master/babel-preset/README.md",
"dependencies": {
"babel-plugin-react-transform": "2.0.0-beta1",
"babel-plugin-react-transform": "2.0.2",
"babel-plugin-syntax-async-functions": "^6.5.0",
"babel-plugin-syntax-class-properties": "^6.5.0",
"babel-plugin-syntax-flow": "^6.5.0",
@@ -40,6 +40,6 @@
"babel-plugin-transform-react-display-name": "^6.5.0",
"babel-plugin-transform-react-jsx": "^6.5.0",
"babel-plugin-transform-regenerator": "^6.5.0",
"react-transform-hmr": "^1.0.2"
"react-transform-hmr": "^1.0.4"
}
}
@@ -0,0 +1,209 @@
---
title: Introducing Hot Reloading
author: Martín Bigio
---
React Native goal is to give you the best possible developer experience. A big part of it is the time it takes between you save a file and be able to see the changes. Our goal is to get this feedback loop to be under 1 second, even as your app grows.
We got close to this ideal via three main features:
* Use JavaScript as the language doesn't have a long compilation cycle time.
* Implement a tool called Packager that transforms es6/flow/jsx files into normal JavaScript that the VM can understand. It was designed as a server that keeps intermediate state in memory to enable fast incremental changes and uses multiple cores.
* Build a feature called Live Reload that reloads the app on save.
At this point, the bottleneck for developers is no longer the time it takes to reload the app but losing the state of your app. A common scenario is to work on a feature that is multiple screens away from the launch screen. Every time you reload, you've got to click on the same path again and again to get back to your feature, making the cycle multiple-seconds long.
## Hot Reloading
The idea behind hot reloading is to keep the app running and to inject new versions of the files that you edited at runtime. This way, you don't lose any of your state which is especially useful if you are tweaking the UI.
A video is worth a thousand words. Check out the difference between Live Reload (current) and Hot Reload (new).
<iframe width="100%" height="315" src="https://www.youtube.com/embed/2uQzVi-KFuc" frameborder="0" allowfullscreen></iframe>
If you look closely, you can notice that it is possible to recover from a red box and you can also start importing modules that were not previously there without having to do a full reload.
**Word of warning:** because JavaScript is a very stateful language, hot reloading cannot be perfectly implemented. In practice, we found out that the current setup is working well for a large amount of usual use cases and a full reload is always available in case something gets messed up.
Hot reloading is available as of 0.22, you can enable it:
* Open the developper menu
* Tap on "Enable Hot Reloading"
## Implementation in a nutshell
Now that we've seen why we want it and how to use it, the fun part begins: how does it actually works.
Hot Reloading is built on top of a feature [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html), or HMR. It was first introduced by Webpack and we implemented it inside of React Native Packager. HMR makes the Packager watch for file changes and send HMR updates to a thin HMR runtime included on the app.
In a nutshell, the HMR update contains the new code of the JS modules that changed. When the runtime receives them, it replaces the old modules' code with the new one:
![](/react-native/blog/img/hmr-architecture.png)
The HMR update contains a bit more than just the module's code we want to change because replacing it, it's not enough for the runtime to pick up the changes. The problem is that the module system may have already cached the *exports* of the module we want to update. For instance, say you have an app composed by these two modules:
```
// log.js
function log(message) {
const time = require('./time');
console.log(`[${time()}] ${message}`);
}
module.exports = log;
```
```
// time.js
function time() {
return new Date().getTime();
}
module.exports = time;
```
The module `log`, prints out the provided message including the current date provided by the module `time`.
When the app is bundled, React Native registers each module on the module system using the `__d` function. For this app, among many `__d` definitions, there will one for `log`:
```
__d('log', function() {
... // module's code
});
```
This invocation wraps each module's code into an anonymous function which we generally refer to as the factory function. The module system runtime keeps track of each module's factory function, whether it has already been executed, and the result of such execution (exports). When a module is required, the module system either provides the already cached exports or executes the module's factory function for the first time and saves the result.
So say you start your app and require `log`. At this point, neither `log` nor `time`'s factory functions have been executed so no exports have been cached. Then, the user modifies `time` to return the date in `MM/DD`:
```
// time.js
function bar() {
var date = new Date();
return `${date.getMonth() + 1}/${date.getDate()}`;
}
module.exports = bar;
```
The Packager will send time's new code to the runtime (step 1), and when `log` gets eventually required the exported function gets executed it will do so with `time`'s changes (step 2):
![](/react-native/blog/img/hmr-step.png)
Now say the code of `log` requires `time` as a top level require:
```
const time = require('./time'); // top level require
// log.js
function log(message) {
console.log(`[${time()}] ${message}`);
}
module.exports = log;
```
When `log` is required, the runtime will cache its exports and `time`'s one. (step 1). Then, when `time` is modified, the HMR process cannot simply finish after replacing `time`'s code. If it did, when `log` gets executed, it would do so with a cached copy of `time` (old code).
For `log` to pick up `time` changes, we'll need to clear its cached exports because one of the modules it depends on was hot swapped (step 3). Finally, when `log` gets required again, its factory function will get executed requiring `time` and getting its new code.
![](/react-native/blog/img/hmr-log.png)
## HMR API
HMR in React Native extends the module system by introducing the `hot` object. This API is based on [Webpack](https://webpack.github.io/docs/hot-module-replacement.html)'s one. The `hot` object exposes a function called `accept` which allows you to define a callback that will be executed when the module needs to be hot swapped. For instance, if we would change `time`'s code as follows, every time we save time, we'll see “time changed” in the console:
```
// time.js
function time() {
... // new code
}
module.hot.accept(() => {
console.log('time changed');
});
module.exports = time;
```
Note that only in rare cases you would need to use this API manually. Hot Reloading should work out of the box for the most common use cases.
## HMR Runtime
As we've seen before, sometimes it's not enough only accepting the HMR update because a module that uses the one being hot swapped may have been already executed and its imports cached. For instance, suppose the dependency tree for the movies app example had a top-level `MovieRouter` that depended on the `MovieSearch` and `MovieScreen` views, which depended on the `log` and `time` modules from the previous examples:
![](/react-native/blog/img/hmr-diamond.png)
If the user access the movies' search view but not the other one, all the modules but `MovieScreen` would have cached exports. If a change is made to module `time`, the runtime will have to clear the exports of `log` for it to pick up `time`'s changes. The process wouldn't finish there: the runtime will repeat this process recursively up until all the parents have been accepted. So, it'll grab the modules that depend on `log` and try to accept them. For `MovieScreen` it can bail, as it hasn't been required yet. For `MovieSearch`, it will have to clear its exports and process its parents recursively. Finally, it will do the same thing for `MovieRouter` and finish there as no modules depends on it.
In order to walk the dependency tree, the runtime receives the inverse dependency tree from the Packager on the HMR update. For this example the runtime will receive a JSON object like this one:
```
{
modules: [
{
name: 'time',
code: /* time's new code */
}
],
inverseDependencies: {
MovieRouter: [],
MovieScreen: ['MovieRouter'],
MovieSearch: ['MovieRouter'],
log: ['MovieScreen', 'MovieSearch'],
time: ['log'],
}
}
```
## React Components
React components are a bit harder to get to work with Hot Reloading. The problem is that we can't simply replace the old code with the new one as we'd loose the component's state. For React web applications, [Dan Abramov](https://twitter.com/dan_abramov) implemented a babel [transform](http://gaearon.github.io/react-hot-loader/) that uses Webpack's HMR API to solve this issue. In a nutshell, his solution works by creating a proxy for every single React component on *transform time*. The proxies hold the component's state and delegate the lifecycle methods to the actual components, which are the ones we hot reload:
![](/react-native/blog/img/hmr-proxy.png)
Besides creating the proxy component, the transform also defines the `accept` function with a piece of code to force React to re-render the component. This way, we can hot reload rendering code without losing any of the app's state.
The default [transformer](https://github.com/facebook/react-native/blob/master/packager/transformer.js#L92-L95) that comes with React Native uses the `babel-preset-react-native`, which is [configured](https://github.com/facebook/react-native/blob/master/babel-preset/configs/hmr.js#L24-L31) to use `react-transform` the same way you'd use it on a React web project that uses Webpack.
## Redux Stores
To enable Hot Reloading on [Redux](http://redux.js.org/) stores you will just need to use the HMR API similarly to what you'd do on a web project that uses Webpack:
```
// configureStore.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from '../reducers';
export default function configureStore(initialState) {
const store = createStore(
reducer,
initialState,
applyMiddleware(thunk),
);
if (module.hot) {
module.hot.accept(() => {
const nextRootReducer = require('../reducers/index').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
};
```
When you change a reducer, the code to accept that reducer will be sent to the client. Then the client will realize that the reducer doesn't know how to accept itself, so it will look for all the modules that refer it and try to accept them. Eventually, the flow will get to the single store, the `configureStore` module, which will accept the HMR update.
## Conclusion
If you are interested in helping making hot reloading better, I encourage you to read [Dan Abramov's post around the future of hot reloading](https://medium.com/@dan_abramov/hot-reloading-in-react-1140438583bf#.jmivpvmz4) and to contribute. For example, Johny Days is going to [make it work with multiple connected clients](https://github.com/facebook/react-native/pull/6179). We're relying on you all to maintain and improve this feature.
With React Native, we have the opportunity to rethink the way we build apps in order to make it a great developer experience. Hot reloading is only one piece of the puzzle, what other crazy hacks can we do to make it better?
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+5 -1
View File
@@ -30,6 +30,9 @@ dependencies:
- "buck-out/bin"
- "website/node_modules"
override:
# CIRCLE_NPM_TOKEN is in React Native project settings in Circle CI.
# It was generated for bestander user, easy to replace with anyone's else
- echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
- npm config set spin=false
- npm config set progress=false
- npm install
@@ -80,7 +83,7 @@ test:
#- find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
deployment:
website:
stable:
branch: [/.*-stable/, /master/]
commands:
# generate docs website
@@ -88,3 +91,4 @@ deployment:
- git config --global user.name "Website Deployment Script"
- echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc
- cd website && GIT_USER=reactjs-bot npm run gh-pages
- node ./scripts/publish-npm.js
+8
View File
@@ -0,0 +1,8 @@
---
id: newsletter
title: Newsletter
layout: docs
category: Community Resources
permalink: http://reactnative.cc/
next: style
---
+3 -3
View File
@@ -2,9 +2,9 @@
id: videos
title: Videos
layout: docs
category: Quick Start
category: Community Resources
permalink: docs/videos.html
next: style
next: newsletter
---
### React.js Conf 2016
@@ -34,7 +34,7 @@ next: style
<iframe width="650" height="315" src="//www.youtube.com/embed/Zoerbz5Mu5U" frameborder="0" allowfullscreen></iframe>
### React.js Conf 2015
### React.js Conf 2015
<iframe width="650" height="315" src="//www.youtube.com/embed/KVZ-P-ZI6W4" frameborder="0" allowfullscreen></iframe>
+1 -1
View File
@@ -275,7 +275,7 @@ function attachHMRServer({httpServer, path, packagerServer}) {
() => {
// do nothing, file was removed
},
).finally(() => {
).then(() => {
client.ws.send(JSON.stringify({type: 'update-done'}));
});
});
-8280
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "0.0.0-master",
"version": "0.22.2",
"description": "A framework for building native apps using React",
"license": "BSD-3-Clause",
"repository": {
@@ -92,7 +92,7 @@
"unmockedModulePathPatterns": [
"promise",
"source-map",
"fast-path",
"node-haste/lib/fastpath",
"fbjs"
]
},
@@ -131,7 +131,7 @@
"babel-core": "^6.6.4",
"babel-plugin-external-helpers": "^6.5.0",
"babel-polyfill": "^6.6.1",
"babel-preset-react-native": "^1.5.1",
"babel-preset-react-native": "^1.5.2",
"babel-register": "^6.6.0",
"babel-types": "^6.6.4",
"babylon": "^6.6.4",
@@ -141,7 +141,6 @@
"connect": "^2.8.3",
"debug": "^2.2.0",
"event-target-shim": "^1.0.5",
"fast-path": "^1.1.0",
"fbjs": "^0.7.2",
"fbjs-scripts": "^0.4.0",
"graceful-fs": "^4.1.2",
@@ -154,13 +153,13 @@
"mkdirp": "^0.5.1",
"module-deps": "^3.9.1",
"node-fetch": "^1.3.3",
"node-haste": "~2.4.0",
"node-haste": "~2.8.0",
"opn": "^3.0.2",
"optimist": "^0.6.1",
"progress": "^1.1.8",
"promise": "^7.1.1",
"react-timer-mixin": "^0.13.2",
"react-transform-hmr": "^1.0.2",
"react-transform-hmr": "^1.0.4",
"rebound": "^0.0.13",
"regenerator": "^0.8.36",
"sane": "^1.2.0",
@@ -184,6 +183,7 @@
"flow-bin": "^0.21.0",
"jest-cli": "0.9.0-fb2",
"portfinder": "0.4.0",
"react": "^0.14.5"
"react": "^0.14.5",
"shelljs": "^0.6.0"
}
}
+1 -1
View File
@@ -10,7 +10,7 @@
require('../babelRegisterOnly')([/react-packager\/src/]);
require('fast-path').replace();
require('node-haste/lib/fastpath').replace();
useGracefulFs();
var debug = require('debug');
+154
View File
@@ -0,0 +1,154 @@
/**
* 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';
/**
* This script publishes a new version of react-native to NPM.
* It is supposed to run in CI environment, not on a developer's machine.
*
* To make it easier for developers it uses some logic to identify with which version to publish the package.
*
* To cut a branch (and release RC):
* - Developer: `git checkout -b 0.XY-stable`
* - Developer: `git tag v0.XY.0-rc` and `git push --tags` to git@github.com:facebook/react-native.git
* - CI: test and deploy to npm (run this script) with version 0.XY.0-rc with tag "next"
*
* To update RC release:
* - Developer: `git checkout 0.XY-stable`
* - Developer: cherry-pick whatever changes needed
* - Developer: `git tag v0.XY.0-rc1` and `git push --tags` to git@github.com:facebook/react-native.git
* - CI: test and deploy to npm (run this script) with version 0.XY.0-rc1 with tag "next"
*
* To publish a release:
* - Developer: `git checkout 0.XY-stable`
* - Developer: cherry-pick whatever changes needed
* - Developer: `git tag latest`
* - Developer: `git tag v0.XY.0`
* - Developer: `git push --tags` to git@github.com:facebook/react-native.git
* - CI: test and deploy to npm (run this script) with version 0.XY.0 with and not tag (latest is implied by npm)
*
* To patch old release:
* - Developer: `git checkout 0.XY-stable`
* - Developer: cherry-pick whatever changes needed
* - Developer: `git tag v0.XY.Z`
* - Developer: `git push` to git@github.com:facebook/react-native.git (or merge as pull request)
* - CI: test and deploy to npm (run this script) with version 0.XY.Z with no tag, npm will not mark it as latest if
* there is a version higher than XY
*
* Important tags:
* If tag v0.XY.0-rcZ is present on the commit then publish to npm with version 0.XY.0-rcZ and tag next
* If tag v0.XY.Z is present on the commit then publish to npm with version 0.XY.Z and no tag (npm will consider it latest)
*/
/*eslint-disable no-undef */
require(`shelljs/global`);
const buildBranch = process.env.CIRCLE_BRANCH;
const requiredJavaVersion = `1.7`;
let branchVersion;
if (buildBranch.indexOf(`-stable`) !== -1) {
branchVersion = buildBranch.slice(0, buildBranch.indexOf(`-stable`));
} else {
echo(`Error: We publish only from stable branches`);
exit(0);
}
// ['latest', 'v0.33.0', 'v0.33.0-rc', 'v0.33.0-rc1', 'v0.33.0-rc2', 'v0.34.0', '']
const tagsWithVersion = exec(`git tag -l --points-at HEAD`).stdout.split(/\s/)
// ['v0.33.0', 'v0.33.0-rc', 'v0.33.0-rc1', 'v0.33.0-rc2', 'v0.34.0']
.filter(version => !!version && version.indexOf(`v${branchVersion}`) === 0)
// ['v0.33.0', 'v0.33.0-rc', 'v0.33.0-rc1', 'v0.33.0-rc2']
.filter(version => version.indexOf(branchVersion) !== -1);
if (tagsWithVersion.length === 0) {
echo(`Error: Can't find version tag in current commit. To deploy to NPM you must add tag v0.XY.Z[-rc] to your commit`);
exit(1);
}
let releaseVersion;
if (tagsWithVersion[0].indexOf(`-rc`) === -1) {
// if first tag on this commit is non -rc then we are making a stable release
// '0.33.0'
releaseVersion = tagsWithVersion[0].slice(1);
} else {
// otherwise pick last -rc tag alphabetically
// 0.33.0-rc2
releaseVersion = tagsWithVersion[tagsWithVersion.length - 1].slice(1);
}
// -------- Generating Android Artifacts with JavaDoc
// Java -version outputs to stderr 0_o
const javaVersion = exec(`java -version`).stderr;
if (javaVersion.indexOf(requiredJavaVersion) === -1) {
echo(`Java version must be 1.7.x in order to generate Javadoc. Check: java -version`);
exit(1);
}
if (sed(`-i`, /^VERSION_NAME=[0-9\.]*-SNAPSHOT/, `VERSION_NAME=${releaseVersion}`, `ReactAndroid/gradle.properties`).code) {
echo(`Couldn't update version for Gradle`);
exit(1);
}
// Uncomment Javadoc generation
if (sed(`-i`, `// archives androidJavadocJar`, `archives androidJavadocJar`, `ReactAndroid/release.gradle`).code) {
echo(`Couldn't enable Javadoc generation`);
exit(1);
}
if (exec(`./gradlew :ReactAndroid:installArchives`).code) {
echo(`Couldn't generate artifacts`);
exit(1);
}
echo("Generated artifacts for Maven");
let artifacts = ['-javadoc.jar', '-sources.jar', '.aar', '.pom'].map((suffix) => {
return `react-native-${releaseVersion}${suffix}`;
});
artifacts.forEach((name) => {
if (!test(`-e`, `./android/com/facebook/react/react-native/${releaseVersion}/${name}`)) {
echo(ls(`./android/com/facebook/react/react-native/${releaseVersion}`));
echo(`file ${name} was not generated`);
exit(1);
}
});
// ----------- Reverting changes to local files
exec(`git checkout ReactAndroid/gradle.properties`);
exec(`git checkout ReactAndroid/release.gradle`);
if (exec(`npm version --no-git-tag-version ${releaseVersion}`).code) {
echo(`Couldn't update version for npm`);
exit(1);
}
if (sed(`-i`, `s.version = "0.0.1-master"`, `s.version = \"${releaseVersion}\"`, `React.podspec`).code) {
echo(`Couldn't update version for React.podspec`);
exit(1);
}
// shrinkwrapping without dev dependencies
exec(`npm shrinkwrap`);
if (releaseVersion.indexOf(`-rc`) === -1) {
// release, package will be installed by default
exec(`npm publish`);
} else {
// RC release, package will be installed only if users specifically do it
exec(`npm publish --tag next`);
}
exec(`git checkout package.json`);
exec(`git checkout React.podspec`);
echo(`Published to npm ${releaseVersion}`);
exit(0);
/*eslint-enable no-undef */
@@ -25,37 +25,12 @@ info() {
repo_root=$(pwd)
git branch | grep -o ${RELEASE}-stable && error "Branch already exists"
java -version 2>&1 | grep ${JAVA_VERSION} || error "Java version must be 1.7.x in order to generate Javadoc. Check: java -version"
git pull || error "Couldn't pull from remote repository"
git checkout -b ${RELEASE}-stable || error "Couldn't create branch"
success "Created release branch: ${RELEASE}-stable"
sed -i.bak s/^VERSION_NAME=[0-9\.]*-SNAPSHOT/VERSION_NAME=${RELEASE}.0/g "ReactAndroid/gradle.properties" || error "Couldn't update version for Gradle"
# Uncomment Javadoc generation
sed -i.bak s:\/\/\ archives\ androidJavadocJar:archives\ androidJavadocJar:g "ReactAndroid/release.gradle" || error "Couldn't enable Javadoc generation"
./gradlew :ReactAndroid:installArchives || error "Couldn't generate artifacts"
# Revert Javadoc generation
sed -i.bak s:archives\ androidJavadocJar:\/\/\ archives\ androidJavadocJar:g "ReactAndroid/release.gradle" || error "Couldn't enable Javadoc generation"
artifacts_list=( -javadoc.jar -sources.jar .aar .pom )
# ./gradlew :ReactAndroid:installArchives put the artifacts in react-native/android, ready to be published to npm
artifacts_dir="${repo_root}/android/com/facebook/react/react-native/${RELEASE}.0"
for i in "${artifacts_list[@]}"; do
artifact_file="${artifacts_dir}/react-native-${RELEASE}.0${i}"
[ -e "${artifact_file}" ] || error "Couldn't find file: ${artifact_file}"
done
success "Generated artifacts for Maven"
sed -i.bak -E "s/(\"version\":[[:space:]]*\").+(\")/\"version\": \"${RELEASE}.0-rc\"/g" "package.json" || error "Couldn't update version for npm"
sed -i.bak -E "s/(s.version[[:space:]]{13}=[[:space:]].+)/s.version = \"${RELEASE}.0-rc\"/g" "React.podspec" || error "Couldn't update version for CocoaPods"
success "Updated version numbers"
npm_registry="http://localhost:4873/"
npm set registry "${npm_registry}" && [[ $(npm config list | grep registry) == "registry = \"${npm_registry}\"" ]] || error "Couldn't set registry to ${npm_registry}"
@@ -67,6 +42,9 @@ info ""
info "Press any key to continue"
read -n 1
sed -i.bak -E "s/(\"version\":[[:space:]]*\").+(\")/\"version\": \"${RELEASE}.0-rc\"/g" "package.json" || error "Couldn't update version for npm"
sed -i.bak -E "s/(s.version[[:space:]]{13}=[[:space:]].+)/s.version = \"${RELEASE}.0-rc\"/g" "React.podspec" || error "Couldn't update version for CocoaPods"
npm unpublish --force || error "Couldn't unpublish package from sinopia (${npm_registry})"
npm publish || error "Couldn't publish package to sinopia (${npm_registry})"
@@ -101,16 +79,14 @@ open "/tmp/${project_name}/ios/${project_name}.xcodeproj"
cd "$repo_root"
info "Press any key to commit changes"
read -n 1
git commit -am "[${RELEASE}.0-rc] Bump version numbers"
# undo changes to files
git checkout package.json
git checkout React.podspec
git checkout ReactAndroid/gradle.properties
find . -path "*.bak" | xargs rm
npm set registry "https://registry.npmjs.org/" || error "Couldn't set registry to ${npm_registry}"
info "Next steps:"
info " - git push origin ${RELEASE}-stable"
info " - git tag v${RELEASE}.0-rc ${RELEASE}-stable"
info " - git push --tags"
info " - npm set registry https://registry.npmjs.org/"
info " - When doing a RC release: npm publish --tag next"
info " - When doing a non-RC release: npm publish # Sets the latest tag automatically"
info " - git tag v${RELEASE}.0-rc"
info " - git push origin ${RELEASE}-stable --tags"
+2 -1
View File
@@ -1,4 +1,5 @@
src/react-native/docs/**
core/metadata.js
src/react-native/blog/**
core/metadata*
*.log
/build/
+45
View File
@@ -0,0 +1,45 @@
/**
* 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.
*
* @providesModule BlogPost
*/
'use strict';
var Marked = require('Marked');
var React = require('React');
var BlogPost = React.createClass({
render: function() {
var post = this.props.post;
var content = this.props.content;
var match = post.path.match(/([0-9]+)\/([0-9]+)\/([0-9]+)/);
// Because JavaScript sucks at date handling :(
var year = match[1];
var month = [
'January', 'February', 'March', 'April', 'May', 'June', 'July',
'August', 'September', 'October', 'November', 'December'
][parseInt(match[2], 10) - 1];
var day = parseInt(match[3], 10);
return (
<div>
<h1>{post.title}</h1>
<p className="meta">
{month} {day}, {year} by{' '}
<a href={post.authorURL} target="_blank">{post.author}</a>
</p>
<hr />
<Marked>{content}</Marked>
</div>
);
}
});
module.exports = BlogPost;
+42
View File
@@ -0,0 +1,42 @@
/**
* 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.
*
* @providesModule BlogSidebar
*/
'use strict';
var MetadataBlog = require('MetadataBlog');
var React = require('React');
var BlogSidebar = React.createClass({
render: function() {
return (
<div className="nav-docs">
<div className="nav-docs-section">
<h3>Recent Posts</h3>
<ul>
{MetadataBlog.files.map(function(post) {
return (
<li key={post.path}>
<a
className={this.props.title === post.title ? 'active' : ''}
href={'/react-native/blog/' + post.path}>
{post.title}
</a>
</li>
);
}.bind(this))}
</ul>
</div>
</div>
);
}
});
module.exports = BlogSidebar;
+1 -2
View File
@@ -16,9 +16,8 @@ var HeaderLinks = React.createClass({
linksInternal: [
{section: 'docs', href: 'docs/getting-started.html', text: 'Docs'},
{section: 'support', href: 'support.html', text: 'Support'},
{section: 'releases', href: 'https://github.com/facebook/react-native/releases', text: 'Releases'},
{section: 'newsletter', href: 'http://reactnative.cc', text: 'Newsletter'},
{section: 'showcase', href: 'showcase.html', text: 'Showcase'},
{section: 'blog', href: 'blog/', text: 'Blog'},
],
linksExternal: [
{section: 'github', href: 'https://github.com/facebook/react-native', text: 'GitHub'},
+58
View File
@@ -0,0 +1,58 @@
/**
* 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.
*
* @providesModule BlogPageLayout
*/
'use strict';
var BlogPost = require('BlogPost');
var BlogSidebar = require('BlogSidebar');
var MetadataBlog = require('MetadataBlog');
var React = require('React');
var Site = require('Site');
var BlogPageLayout = React.createClass({
getPageURL: function(page) {
var url = '/jest/blog/';
if (page > 0) {
url += 'page' + (page + 1) + '/';
}
return url + '#content';
},
render: function() {
var perPage = this.props.metadata.perPage;
var page = this.props.metadata.page;
return (
<Site
section="blog"
title="Blog">
<section className="content wrap documentationContent">
<BlogSidebar />
<div className="inner-content">
{MetadataBlog.files
.slice(page * perPage, (page + 1) * perPage)
.map((post) => {
return <BlogPost post={post} content={post.content} />
})
}
<div className="docs-prevnext">
{page > 0 &&
<a className="docs-prev" href={this.getPageURL(page - 1)}>&larr; Prev</a>}
{MetadataBlog.files.length > (page + 1) * perPage &&
<a className="docs-next" href={this.getPageURL(page + 1)}>Next &rarr;</a>}
</div>
</div>
</section>
</Site>
);
}
});
module.exports = BlogPageLayout;
+39
View File
@@ -0,0 +1,39 @@
/**
* 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.
*
* @providesModule BlogPostLayout
*/
'use strict';
var BlogPost = require('BlogPost');
var BlogSidebar = require('BlogSidebar');
var Marked = require('Marked');
var MetadataBlog = require('MetadataBlog');
var React = require('React');
var Site = require('Site');
var BlogPostLayout = React.createClass({
render: function() {
return (
<Site
section="blog"
title={this.props.metadata.title}
description={this.props.children.trim().split('\n')[0]}>
<section className="content wrap documentationContent">
<BlogSidebar title={this.props.metadata.title} />
<div className="inner-content">
<BlogPost post={this.props.metadata} content={this.props.children} />
</div>
</section>
</Site>
);
}
});
module.exports = BlogPostLayout;
+1 -2
View File
@@ -16,7 +16,6 @@
"react-docgen": "^2.0.1",
"react-page-middleware": "git://github.com/facebook/react-page-middleware.git",
"request": "^2.69.0",
"semver-compare": "^1.0.0",
"shelljs": "^0.6.0"
"semver-compare": "^1.0.0"
}
}
+120 -45
View File
@@ -7,6 +7,8 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
var fs = require('fs')
var glob = require('glob');
var mkdirp = require('mkdirp');
@@ -29,6 +31,14 @@ function splitHeader(content) {
};
}
function rmFile(file) {
try {
fs.unlinkSync(file);
} catch(e) {
/* seriously, unlink throws when the file doesn't exist :( */
}
}
function backtickify(str) {
var escaped = '`' + str.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/{/g, '\\{') + '`';
// Replace require( with require\( so node-haste doesn't replace example
@@ -36,16 +46,60 @@ function backtickify(str) {
return escaped.replace(/require\(/g, 'require\\(');
}
function execute() {
var MD_DIR = '../docs/';
function writeFileAndCreateFolder(file, content) {
mkdirp.sync(file.replace(new RegExp('/[^/]*$'), ''));
fs.writeFileSync(file, content);
}
var files = glob.sync('src/react-native/docs/*.*');
files.forEach(function(file) {
try {
fs.unlinkSync(file);
} catch(e) {
/* seriously, unlink throws when the file doesn't exist :( */
}
// Extract markdown metadata header
function extractMetadata(content) {
var metadata = {};
var both = splitHeader(content);
var lines = both.header.split('\n');
for (var i = 0; i < lines.length - 1; ++i) {
var keyvalue = lines[i].split(':');
var key = keyvalue[0].trim();
var value = keyvalue.slice(1).join(':').trim();
// Handle the case where you have "Community #10"
try { value = JSON.parse(value); } catch(e) { }
metadata[key] = value;
}
return {metadata: metadata, rawContent: both.content};
}
function buildFile(layout, metadata, rawContent) {
return [
'/**',
' * @generated',
' */',
'var React = require("React");',
'var Layout = require("' + layout + '");',
rawContent && 'var content = ' + backtickify(rawContent) + ';',
'var Post = React.createClass({',
rawContent && ' statics: { content: content },',
' render: function() {',
' return (',
' <Layout metadata={' + JSON.stringify(metadata) + '}>',
rawContent && ' {content}',
' </Layout>',
' );',
' }',
'});',
'module.exports = Post;'
].filter(e => e).join('\n');
}
function execute() {
var DOCS_MD_DIR = '../docs/';
var BLOG_MD_DIR = '../blog/';
glob.sync('src/react-native/docs/*.*').forEach(rmFile);
glob.sync('src/react-native/blog/*.*').forEach(rmFile);
glob.sync('../blog/img/*.*').forEach(file => {
writeFileAndCreateFolder(
'src/react-native/blog/img/' + path.basename(file),
fs.readFileSync(file)
);
});
var metadatas = {
@@ -53,22 +107,13 @@ function execute() {
};
function handleMarkdown(content, filename) {
var metadata = {};
// Extract markdown metadata header
var both = splitHeader(content);
if (!both.header) {
if (content.slice(0, 3) !== '---') {
return;
}
var lines = both.header.split('\n');
for (var i = 0; i < lines.length - 1; ++i) {
var keyvalue = lines[i].split(':');
var key = keyvalue[0].trim();
var value = keyvalue.slice(1).join(':').trim();
// Handle the case where you have "Community #10"
try { value = JSON.parse(value); } catch(e) { }
metadata[key] = value;
}
const res = extractMetadata(content);
const metadata = res.metadata;
const rawContent = res.rawContent;
if (metadata.sidebar !== false) {
metadatas.files.push(metadata);
@@ -83,35 +128,17 @@ function execute() {
// Create a dummy .js version that just calls the associated layout
var layout = metadata.layout[0].toUpperCase() + metadata.layout.substr(1) + 'Layout';
var content = (
'/**\n' +
' * @generated\n' +
' * @jsx React.DOM\n' +
' */\n' +
'var React = require("React");\n' +
'var Layout = require("' + layout + '");\n' +
'var content = ' + backtickify(both.content) + '\n' +
'var Post = React.createClass({\n' +
' statics: {\n' +
' content: content\n' +
' },\n' +
' render: function() {\n' +
' return <Layout metadata={' + JSON.stringify(metadata) + '}>{content}</Layout>;\n' +
' }\n' +
'});\n' +
'module.exports = Post;\n'
writeFileAndCreateFolder(
'src/react-native/' + metadata.permalink.replace(/\.html$/, '.js'),
buildFile(layout, metadata, rawContent)
);
var targetFile = 'src/react-native/' + metadata.permalink.replace(/\.html$/, '.js');
mkdirp.sync(targetFile.replace(new RegExp('/[^/]*$'), ''));
fs.writeFileSync(targetFile, content);
}
extractDocs().forEach(function(content) {
handleMarkdown(content, null);
});
var files = glob.sync(MD_DIR + '**/*.*');
var files = glob.sync(DOCS_MD_DIR + '**/*.*');
files.forEach(function(file) {
var extension = path.extname(file);
if (extension === '.md' || extension === '.markdown') {
@@ -144,6 +171,54 @@ function execute() {
' */\n' +
'module.exports = ' + JSON.stringify(metadatas, null, 2) + ';'
);
files = glob.sync(BLOG_MD_DIR + '**/*.md');
const metadatasBlog = {
files: [],
};
files.sort().reverse().forEach(file => {
// Transform
// 2015-08-13-blog-post-name-0.5.md
// into
// 2015/08/13/blog-post-name-0-5.html
var filePath = path.basename(file)
.replace('-', '/')
.replace('-', '/')
.replace('-', '/')
// react-middleware is broken with files that contains multiple . like react-0.14.js
.replace(/\./g, '-')
.replace(/\-md$/, '.html');
var res = extractMetadata(fs.readFileSync(file, {encoding: 'utf8'}));
var rawContent = res.rawContent;
var metadata = Object.assign({path: filePath, content: rawContent}, res.metadata);
metadatasBlog.files.push(metadata);
writeFileAndCreateFolder(
'src/react-native/blog/' + filePath.replace(/\.html$/, '.js'),
buildFile('BlogPostLayout', metadata, rawContent)
);
});
var perPage = 5;
for (var page = 0; page < Math.ceil(metadatasBlog.files.length / perPage); ++page) {
writeFileAndCreateFolder(
'src/react-native/blog' + (page > 0 ? '/page' + (page + 1) : '') + '/index.js',
buildFile('BlogPageLayout', { page: page, perPage: perPage })
);
}
fs.writeFileSync(
'core/metadata-blog.js',
'/**\n' +
' * @generated\n' +
' * @providesModule MetadataBlog\n' +
' */\n' +
'module.exports = ' + JSON.stringify(metadatasBlog, null, 2) + ';'
);
}
if (argv.convert) {
+1 -1
View File
@@ -45,7 +45,7 @@ var app = connect()
.use(function(req, res, next) {
// convert all the md files on every request. This is not optimal
// but fast enough that we don't really need to care right now.
if (!server.noconvert && req.url.match(/\.html$/)) {
if (!server.noconvert && req.url.match(/\.html|\/$/)) {
convert();
}
next();