mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2237e3b1e7 | ||
|
|
303a264076 | ||
|
|
db8273e79b | ||
|
|
2c4dda2a70 | ||
|
|
a5b95d59f2 | ||
|
|
7c031cafe7 | ||
|
|
39832820b3 | ||
|
|
464a2e85c2 | ||
|
|
064d57d0b6 | ||
|
|
3e956f6608 | ||
|
|
9c2c2a5cd2 | ||
|
|
213a7b3eb8 | ||
|
|
298207ce24 | ||
|
|
992d39f20d | ||
|
|
ef1fdf8fb1 | ||
|
|
d782af285b | ||
|
|
aa83db777f | ||
|
|
d83308c4f6 | ||
|
|
44a8eab108 |
@@ -98,12 +98,20 @@ Error: ${e.message}`
|
||||
RCTExceptionsManager && RCTExceptionsManager.dismissRedbox && RCTExceptionsManager.dismissRedbox();
|
||||
}
|
||||
|
||||
let serverHost;
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
serverHost = require('NativeModules').AndroidConstants.ServerHost;
|
||||
} else {
|
||||
serverHost = port ? `${host}:${port}` : host;
|
||||
}
|
||||
|
||||
modules.forEach(({id, code}, i) => {
|
||||
code = code + '\n\n' + sourceMappingURLs[i];
|
||||
|
||||
require('SourceMapsCache').fetch({
|
||||
text: code,
|
||||
url: sourceURLs[i],
|
||||
url: `http://${serverHost}${sourceURLs[i]}`,
|
||||
sourceMappingURL: sourceMappingURLs[i],
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "React"
|
||||
s.version = "0.0.1-master"
|
||||
s.version = "0.23.1"
|
||||
s.summary = "Build high quality mobile apps using React."
|
||||
s.description = <<-DESC
|
||||
React Native apps are built using the React JS
|
||||
|
||||
@@ -239,6 +239,9 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
NSString *scheme = [_bridge.bundleURL scheme];
|
||||
NSNumber *port = [_bridge.bundleURL port];
|
||||
if (!port) {
|
||||
port = @8081; // Packager default port
|
||||
}
|
||||
return [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@:%@/message?role=shell", scheme, host, port]];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=0.12.0-SNAPSHOT
|
||||
VERSION_NAME=0.23.1
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -9,6 +9,7 @@ android_library(
|
||||
react_native_target('java/com/facebook/react/bridge:bridge'),
|
||||
react_native_target('java/com/facebook/react/common:common'),
|
||||
react_native_target('java/com/facebook/react/modules/debug:debug'),
|
||||
react_native_target('java/com/facebook/react/modules/systeminfo:systeminfo'),
|
||||
react_native_dep('libraries/fbcore/src/main/java/com/facebook/common/logging:logging'),
|
||||
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
|
||||
react_native_dep('third-party/java/jsr-305:jsr-305'),
|
||||
|
||||
@@ -9,16 +9,7 @@
|
||||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
|
||||
@@ -26,6 +17,7 @@ import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.modules.systeminfo.AndroidInfoHelpers;
|
||||
import com.squareup.okhttp.Call;
|
||||
import com.squareup.okhttp.Callback;
|
||||
import com.squareup.okhttp.ConnectionPool;
|
||||
@@ -34,6 +26,13 @@ import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.ResponseBody;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import okio.Okio;
|
||||
import okio.Sink;
|
||||
|
||||
@@ -51,10 +50,6 @@ public class DevServerHelper {
|
||||
public static final String RELOAD_APP_EXTRA_JS_PROXY = "jsproxy";
|
||||
private static final String RELOAD_APP_ACTION_SUFFIX = ".RELOAD_APP_ACTION";
|
||||
|
||||
private static final String EMULATOR_LOCALHOST = "10.0.2.2:8081";
|
||||
private static final String GENYMOTION_LOCALHOST = "10.0.3.2:8081";
|
||||
private static final String DEVICE_LOCALHOST = "localhost:8081";
|
||||
|
||||
private static final String BUNDLE_URL_FORMAT =
|
||||
"http://%s/%s.bundle?platform=android&dev=%s&hot=%s&minify=%s";
|
||||
private static final String SOURCE_MAP_URL_FORMAT =
|
||||
@@ -118,7 +113,7 @@ public class DevServerHelper {
|
||||
* @return the host to use when connecting to the bundle server from the host itself.
|
||||
*/
|
||||
private static String getHostForJSProxy() {
|
||||
return DEVICE_LOCALHOST;
|
||||
return AndroidInfoHelpers.DEVICE_LOCALHOST;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,31 +144,21 @@ public class DevServerHelper {
|
||||
// Check debug server host setting first. If empty try to detect emulator type and use default
|
||||
// hostname for those
|
||||
String hostFromSettings = mSettings.getDebugServerHost();
|
||||
|
||||
if (!TextUtils.isEmpty(hostFromSettings)) {
|
||||
return Assertions.assertNotNull(hostFromSettings);
|
||||
}
|
||||
|
||||
// Since genymotion runs in vbox it use different hostname to refer to adb host.
|
||||
// We detect whether app runs on genymotion and replace js bundle server hostname accordingly
|
||||
if (isRunningOnGenymotion()) {
|
||||
return GENYMOTION_LOCALHOST;
|
||||
}
|
||||
if (isRunningOnStockEmulator()) {
|
||||
return EMULATOR_LOCALHOST;
|
||||
}
|
||||
FLog.w(
|
||||
String host = AndroidInfoHelpers.getServerHost();
|
||||
|
||||
if (host.equals(AndroidInfoHelpers.DEVICE_LOCALHOST)) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"You seem to be running on device. Run 'adb reverse tcp:8081 tcp:8081' " +
|
||||
"to forward the debug server's port to the device.");
|
||||
return DEVICE_LOCALHOST;
|
||||
}
|
||||
"to forward the debug server's port to the device.");
|
||||
}
|
||||
|
||||
private boolean isRunningOnGenymotion() {
|
||||
return Build.FINGERPRINT.contains("vbox");
|
||||
}
|
||||
|
||||
private boolean isRunningOnStockEmulator() {
|
||||
return Build.FINGERPRINT.contains("generic");
|
||||
return host;
|
||||
}
|
||||
|
||||
private static String createBundleURL(String host, String jsModulePath, boolean devMode, boolean hmr, boolean jsMinify) {
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.facebook.react.modules.systeminfo;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
public class AndroidInfoHelpers {
|
||||
|
||||
public static final String EMULATOR_LOCALHOST = "10.0.2.2:8081";
|
||||
public static final String GENYMOTION_LOCALHOST = "10.0.3.2:8081";
|
||||
public static final String DEVICE_LOCALHOST = "localhost:8081";
|
||||
|
||||
private static boolean isRunningOnGenymotion() {
|
||||
return Build.FINGERPRINT.contains("vbox");
|
||||
}
|
||||
|
||||
private static boolean isRunningOnStockEmulator() {
|
||||
return Build.FINGERPRINT.contains("generic");
|
||||
}
|
||||
|
||||
public static String getServerHost() {
|
||||
// Since genymotion runs in vbox it use different hostname to refer to adb host.
|
||||
// We detect whether app runs on genymotion and replace js bundle server hostname accordingly
|
||||
|
||||
if (isRunningOnGenymotion()) {
|
||||
return GENYMOTION_LOCALHOST;
|
||||
}
|
||||
|
||||
if (isRunningOnStockEmulator()) {
|
||||
return EMULATOR_LOCALHOST;
|
||||
}
|
||||
|
||||
return DEVICE_LOCALHOST;
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -9,14 +9,14 @@
|
||||
|
||||
package com.facebook.react.modules.systeminfo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import android.os.Build;
|
||||
|
||||
import com.facebook.react.bridge.BaseJavaModule;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import com.facebook.react.bridge.BaseJavaModule;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Module that exposes Android Constants to JS.
|
||||
@@ -32,6 +32,7 @@ public class AndroidInfoModule extends BaseJavaModule {
|
||||
public @Nullable Map<String, Object> getConstants() {
|
||||
HashMap<String, Object> constants = new HashMap<String, Object>();
|
||||
constants.put("Version", Build.VERSION.SDK_INT);
|
||||
constants.put("ServerHost", AndroidInfoHelpers.getServerHost());
|
||||
return constants;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ android_library(
|
||||
react_native_target('java/com/facebook/react/views/art:art'),
|
||||
react_native_target('java/com/facebook/react/views/drawer:drawer'),
|
||||
react_native_target('java/com/facebook/react/views/image:image'),
|
||||
react_native_target('java/com/facebook/react/views/modal:modal'),
|
||||
react_native_target('java/com/facebook/react/views/picker:picker'),
|
||||
react_native_target('java/com/facebook/react/views/progressbar:progressbar'),
|
||||
react_native_target('java/com/facebook/react/views/recyclerview:recyclerview'),
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.facebook.react.views.art.ARTRenderableViewManager;
|
||||
import com.facebook.react.views.art.ARTSurfaceViewManager;
|
||||
import com.facebook.react.views.drawer.ReactDrawerLayoutManager;
|
||||
import com.facebook.react.views.image.ReactImageManager;
|
||||
import com.facebook.react.views.modal.ReactModalHostManager;
|
||||
import com.facebook.react.views.picker.ReactDialogPickerManager;
|
||||
import com.facebook.react.views.picker.ReactDropdownPickerManager;
|
||||
import com.facebook.react.views.progressbar.ReactProgressBarViewManager;
|
||||
@@ -104,6 +105,7 @@ public class MainReactPackage implements ReactPackage {
|
||||
new ReactDropdownPickerManager(),
|
||||
new ReactHorizontalScrollViewManager(),
|
||||
new ReactImageManager(),
|
||||
new ReactModalHostManager(),
|
||||
new ReactProgressBarViewManager(),
|
||||
new ReactRawTextManager(),
|
||||
new ReactScrollViewManager(),
|
||||
|
||||
@@ -6,10 +6,8 @@ import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||
|
||||
/**
|
||||
* Base class that should be suitable for the majority of subclasses of {@link ViewManager}.
|
||||
@@ -27,7 +25,6 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
|
||||
private static final String PROP_DECOMPOSED_MATRIX_SCALE_Y = "scaleY";
|
||||
private static final String PROP_DECOMPOSED_MATRIX_TRANSLATE_X = "translateX";
|
||||
private static final String PROP_DECOMPOSED_MATRIX_TRANSLATE_Y = "translateY";
|
||||
private static final String PROP_DECOMPOSED_MATRIX_PERSPECTIVE = "perspective";
|
||||
private static final String PROP_OPACITY = "opacity";
|
||||
private static final String PROP_ELEVATION = "elevation";
|
||||
private static final String PROP_RENDER_TO_HARDWARE_TEXTURE = "renderToHardwareTextureAndroid";
|
||||
@@ -35,8 +32,6 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
|
||||
private static final String PROP_ACCESSIBILITY_COMPONENT_TYPE = "accessibilityComponentType";
|
||||
private static final String PROP_ACCESSIBILITY_LIVE_REGION = "accessibilityLiveRegion";
|
||||
private static final String PROP_IMPORTANT_FOR_ACCESSIBILITY = "importantForAccessibility";
|
||||
private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2;
|
||||
private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = 5;
|
||||
|
||||
// DEPRECATED
|
||||
private static final String PROP_ROTATION = "rotation";
|
||||
@@ -169,20 +164,6 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
|
||||
(float) matrix.getDouble(PROP_DECOMPOSED_MATRIX_SCALE_X));
|
||||
view.setScaleY(
|
||||
(float) matrix.getDouble(PROP_DECOMPOSED_MATRIX_SCALE_Y));
|
||||
|
||||
if (matrix.hasKey(PROP_DECOMPOSED_MATRIX_PERSPECTIVE)) {
|
||||
ReadableArray perspectiveArray = matrix.getArray(PROP_DECOMPOSED_MATRIX_PERSPECTIVE);
|
||||
if (perspectiveArray.size() > PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX) {
|
||||
float cameraDistance = (float)(-1 / perspectiveArray.getDouble(PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX));
|
||||
float scale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
|
||||
|
||||
// The following converts the matrix's perspective to a camera distance
|
||||
// such that the camera perspective looks the same on Android and iOS
|
||||
float normalizedCameraDistance = scale * cameraDistance * CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER;
|
||||
|
||||
view.setCameraDistance(normalizedCameraDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetTransformMatrix(View view) {
|
||||
@@ -194,4 +175,4 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
|
||||
view.setScaleX(1);
|
||||
view.setScaleY(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-8
@@ -13,10 +13,6 @@ Release schedule:
|
||||
- 0.24.0 - Apr 18
|
||||
- ...
|
||||
|
||||
## One time setup
|
||||
|
||||
Set up Sinopia: https://github.com/facebook/react-native/tree/master/react-native-cli
|
||||
|
||||
#### Check that everything works
|
||||
|
||||
Make absolutely sure a basic iOS and Android workflow works on the commit you are going to use for release.
|
||||
@@ -38,13 +34,16 @@ Run:
|
||||
|
||||
```
|
||||
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
|
||||
node ./scripts/bump-oss-version.js <exact-version_you_are_releasing> # e.g. git node ./scripts/bump-oss-version.js 0.22.0-rc
|
||||
./scripts/test-manual-e2e.sh # to double check that e2e process works
|
||||
git push origin <version_you_are_releasing>-stable --tags # e.g. git push origin 0.22-stable --tags
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
Go to [Circle CI](https://circleci.com/gh/facebook/react-native), look for your branch on the left side and look the npm publish step.
|
||||
|
||||
** 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
|
||||
@@ -79,7 +78,7 @@ git pull origin 0.version_you_are_releasing-stable # e.g. git pull origin 0.22-
|
||||
# Cherry-pick those commits
|
||||
git cherry-pick commitHash1
|
||||
|
||||
# test everything again using sinopia
|
||||
# test everything again
|
||||
./scripts/test-manual-e2e.sh
|
||||
|
||||
# Check that you can Reload JS and the Chrome debugger works
|
||||
@@ -88,9 +87,11 @@ git cherry-pick commitHash1
|
||||
If everything worked:
|
||||
|
||||
```
|
||||
git tag v-version_you_are_releasing # e.g. git tag v0.22.0, git tag v0.22.1
|
||||
node ./scripts/bump-oss-version.js <exact_version_you_are_releasing> # e.g. git node ./scripts/bump-oss-version.js 0.22.0
|
||||
git tag -d latest
|
||||
git push origin :latest
|
||||
git tag latest # for docs [website](https://facebook.github.io/react-native) to be generated
|
||||
git push origin 0.22-stable --tags
|
||||
git push origin version_you_are_releasing-stable --tags # e.g. git push origin 0.22-stable --tags
|
||||
```
|
||||
|
||||
Once you see the version in the top left corner of the website has been updated:
|
||||
|
||||
@@ -25,7 +25,7 @@ module.exports = function(options, filename) {
|
||||
{
|
||||
transforms: [{
|
||||
transform: transform,
|
||||
imports: ['React'],
|
||||
imports: ['react-native'],
|
||||
locals: ['module'],
|
||||
}]
|
||||
},
|
||||
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
|
||||
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):
|
||||
|
||||

|
||||
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
|
||||
## 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:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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 |
@@ -32,8 +32,6 @@ dependencies:
|
||||
- npm install
|
||||
# for eslint bot
|
||||
- npm install github@0.2.4
|
||||
# for deployment scripts
|
||||
- npm install shelljs@0.6.0
|
||||
- cd website && npm install
|
||||
cache_directories:
|
||||
- "ReactAndroid/build/downloads"
|
||||
@@ -78,7 +76,6 @@ test:
|
||||
- ./gradlew :ReactAndroid:installDebugAndroidTest
|
||||
- source scripts/circle-ci-android-setup.sh && retry3 ./scripts/run-android-instrumentation-tests.sh com.facebook.react.tests.gradle
|
||||
|
||||
# Publish to Sinopia, create a new app using 'react-native init' and check the packager starts
|
||||
- ./scripts/e2e-test.sh --packager
|
||||
|
||||
# testing docs generation is not broken
|
||||
|
||||
@@ -18,7 +18,7 @@ Make sure you have the following installed:
|
||||
1. Android SDK version 23 (compileSdkVersion in [`build.gradle`](https://github.com/facebook/react-native/blob/master/ReactAndroid/build.gradle))
|
||||
2. SDK build tools version 23.0.1 (buildToolsVersion in [`build.gradle`](https://github.com/facebook/react-native/blob/master/ReactAndroid/build.gradle))
|
||||
3. Android Support Repository >= 17 (for Android Support Library)
|
||||
4. Android NDK (download & extraction instructions [here](http://developer.android.com/ndk/downloads/index.html))
|
||||
4. Android NDK (download links and installation instructions below)
|
||||
|
||||
Point Gradle to your Android SDK: either have `$ANDROID_SDK` and `$ANDROID_NDK ` defined, or create a local.properties file in the root of your react-native checkout with the following contents:
|
||||
|
||||
@@ -34,6 +34,14 @@ sdk.dir=/Users/your_unix_name/android-sdk-macosx
|
||||
ndk.dir=/Users/your_unix_name/android-ndk/android-ndk-r10e
|
||||
```
|
||||
|
||||
### Download links for Android NDK
|
||||
|
||||
1. Mac OS (64-bit) - http://dl.google.com/android/repository/android-ndk-r10e-darwin-x86_64.zip
|
||||
2. Linux (64-bit) - http://dl.google.com/android/repository/android-ndk-r10e-linux-x86_64.zip
|
||||
3. Windows (64-bit) - http://dl.google.com/android/repository/android-ndk-r10e-windows-x86_64.zip
|
||||
4. Windows (32-bit) - http://dl.google.com/android/repository/android-ndk-r10e-windows-x86.zip
|
||||
|
||||
You can find further instructions on the [official page](http://developer.android.com/ndk/downloads/index.html).
|
||||
|
||||
## Building the source
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
id: newsletter
|
||||
title: Newsletter
|
||||
layout: docs
|
||||
category: Community Resources
|
||||
permalink: http://reactnative.cc/
|
||||
next: style
|
||||
---
|
||||
+3
-3
@@ -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>
|
||||
|
||||
|
||||
@@ -92,6 +92,17 @@ module.exports = yeoman.generators.NamedBase.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
this.npmInstall('react', { '--save': true });
|
||||
var reactNativePackageJson = require('../../package.json');
|
||||
var { peerDependencies } = reactNativePackageJson;
|
||||
if (!peerDependencies) {
|
||||
return;
|
||||
}
|
||||
|
||||
var reactVersion = peerDependencies.react;
|
||||
if (!reactVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.npmInstall(`react@${reactVersion}`, { '--save': true });
|
||||
}
|
||||
});
|
||||
|
||||
+5
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "0.0.0-master",
|
||||
"version": "0.23.1",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
@@ -156,7 +156,7 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"module-deps": "^3.9.1",
|
||||
"node-fetch": "^1.3.3",
|
||||
"node-haste": "~2.9.2",
|
||||
"node-haste": "~2.9.6",
|
||||
"opn": "^3.0.2",
|
||||
"optimist": "^0.6.1",
|
||||
"progress": "^1.1.8",
|
||||
@@ -186,6 +186,7 @@
|
||||
"flow-bin": "0.22.0",
|
||||
"jest-cli": "0.9.2",
|
||||
"portfinder": "0.4.0",
|
||||
"react": "^0.14.5"
|
||||
"react": "^0.14.5",
|
||||
"shelljs": "0.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-6
@@ -170,9 +170,9 @@ class Bundler {
|
||||
});
|
||||
}
|
||||
|
||||
_sourceHMRURL(platform, host, port, path) {
|
||||
_sourceHMRURL(platform, path) {
|
||||
return this._hmrURL(
|
||||
`http://${host}:${port}`,
|
||||
'',
|
||||
platform,
|
||||
'bundle',
|
||||
path,
|
||||
@@ -217,7 +217,7 @@ class Bundler {
|
||||
return this._bundle({
|
||||
...options,
|
||||
bundle: new HMRBundle({
|
||||
sourceURLFn: this._sourceHMRURL.bind(this, options.platform, host, port),
|
||||
sourceURLFn: this._sourceHMRURL.bind(this, options.platform),
|
||||
sourceMappingURLFn: this._sourceMappingHMRURL.bind(
|
||||
this,
|
||||
options.platform,
|
||||
@@ -588,10 +588,11 @@ class Bundler {
|
||||
};
|
||||
|
||||
const json = JSON.stringify(asset);
|
||||
const assetRegistryPath = 'react-native/Libraries/Image/AssetRegistry';
|
||||
const code =
|
||||
`module.exports = require('AssetRegistry').registerAsset(${json});`;
|
||||
const dependencies = ['AssetRegistry'];
|
||||
const dependencyOffsets = [code.indexOf('AssetRegistry') - 1];
|
||||
`module.exports = require(${JSON.stringify(assetRegistryPath)}).registerAsset(${json});`;
|
||||
const dependencies = [assetRegistryPath];
|
||||
const dependencyOffsets = [code.indexOf(assetRegistryPath) - 1];
|
||||
|
||||
return {
|
||||
asset,
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ class Resolver {
|
||||
cache: opts.cache,
|
||||
shouldThrowOnUnresolvedErrors: (_, platform) => platform === 'ios',
|
||||
transformCode: opts.transformCode,
|
||||
assetDependencies: ['AssetRegistry'],
|
||||
assetDependencies: ['react-native/Libraries/Image/AssetRegistry'],
|
||||
});
|
||||
|
||||
this._getModuleId = options.getModuleId;
|
||||
|
||||
Vendored
+46
-21
@@ -42,6 +42,19 @@ var spawn = require('child_process').spawn;
|
||||
var chalk = require('chalk');
|
||||
var prompt = require('prompt');
|
||||
var semver = require('semver');
|
||||
/**
|
||||
* Used arguments:
|
||||
* -v --version - to print current version of react-native-cli and react-native dependency
|
||||
* if you are in a RN app folder
|
||||
* init - to create a new project and npm install it
|
||||
* --verbose - to print logs while init
|
||||
* --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
|
||||
* - "https://registry.npmjs.org/react-native/-/react-native-0.20.0.tgz" - a .tgz archive from any npm repo
|
||||
* - "/Users/home/react-native/react-native-0.22.0.tgz" - for package prepared with `npm pack`, useful for e2e tests
|
||||
*/
|
||||
var argv = require('minimist')(process.argv.slice(2));
|
||||
|
||||
var CLI_MODULE_PATH = function() {
|
||||
return path.resolve(
|
||||
@@ -69,34 +82,34 @@ if (fs.existsSync(cliPath)) {
|
||||
cli = require(cliPath);
|
||||
}
|
||||
|
||||
// minimist api
|
||||
var commands = argv._;
|
||||
if (cli) {
|
||||
cli.run();
|
||||
} else {
|
||||
var args = process.argv.slice(2);
|
||||
if (args.length === 0) {
|
||||
if (commands.length === 0) {
|
||||
console.error(
|
||||
'You did not pass any commands, did you mean to run `react-native init`?'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
switch (args[0]) {
|
||||
switch (commands[0]) {
|
||||
case 'init':
|
||||
if (args[1]) {
|
||||
var verbose = process.argv.indexOf('--verbose') >= 0;
|
||||
init(args[1], verbose);
|
||||
} else {
|
||||
if (!commands[1]) {
|
||||
console.error(
|
||||
'Usage: react-native init <ProjectName> [--verbose]'
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
init(commands[1], argv.verbose, argv.version);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error(
|
||||
'Command `%s` unrecognized. ' +
|
||||
'Did you mean to run this inside a react-native project?',
|
||||
args[0]
|
||||
commands[0]
|
||||
);
|
||||
process.exit(1);
|
||||
break;
|
||||
@@ -123,17 +136,17 @@ function validatePackageName(name) {
|
||||
}
|
||||
}
|
||||
|
||||
function init(name, verbose) {
|
||||
function init(name, verbose, rnPackage) {
|
||||
validatePackageName(name);
|
||||
|
||||
if (fs.existsSync(name)) {
|
||||
createAfterConfirmation(name, verbose);
|
||||
createAfterConfirmation(name, verbose, rnPackage);
|
||||
} else {
|
||||
createProject(name, verbose);
|
||||
createProject(name, verbose, rnPackage);
|
||||
}
|
||||
}
|
||||
|
||||
function createAfterConfirmation(name, verbose) {
|
||||
function createAfterConfirmation(name, verbose, rnPackage) {
|
||||
prompt.start();
|
||||
|
||||
var property = {
|
||||
@@ -146,7 +159,7 @@ function createAfterConfirmation(name, verbose) {
|
||||
|
||||
prompt.get(property, function (err, result) {
|
||||
if (result.yesno[0] === 'y') {
|
||||
createProject(name, verbose);
|
||||
createProject(name, verbose, rnPackage);
|
||||
} else {
|
||||
console.log('Project initialization canceled');
|
||||
process.exit();
|
||||
@@ -154,7 +167,7 @@ function createAfterConfirmation(name, verbose) {
|
||||
});
|
||||
}
|
||||
|
||||
function createProject(name, verbose) {
|
||||
function createProject(name, verbose, rnPackage) {
|
||||
var root = path.resolve(name);
|
||||
var projectName = path.basename(root);
|
||||
|
||||
@@ -181,14 +194,26 @@ function createProject(name, verbose) {
|
||||
console.log('Installing react-native package from npm...');
|
||||
|
||||
if (verbose) {
|
||||
runVerbose(root, projectName);
|
||||
runVerbose(root, projectName, rnPackage);
|
||||
} else {
|
||||
run(root, projectName);
|
||||
run(root, projectName, rnPackage);
|
||||
}
|
||||
}
|
||||
|
||||
function run(root, projectName) {
|
||||
exec('npm install --save react-native', function(e, stdout, stderr) {
|
||||
function getInstallPackage(rnPackage) {
|
||||
var packageToInstall = 'react-native';
|
||||
var valideSemver = semver.valid(rnPackage);
|
||||
if (valideSemver) {
|
||||
packageToInstall += '@' + valideSemver;
|
||||
} else if (rnPackage) {
|
||||
// for tar.gz or alternative paths
|
||||
packageToInstall = rnPackage;
|
||||
}
|
||||
return packageToInstall;
|
||||
}
|
||||
|
||||
function run(root, projectName, rnPackage) {
|
||||
exec('npm install --save ' + getInstallPackage(rnPackage), function(e, stdout, stderr) {
|
||||
if (e) {
|
||||
console.log(stdout);
|
||||
console.error(stderr);
|
||||
@@ -203,8 +228,8 @@ function run(root, projectName) {
|
||||
});
|
||||
}
|
||||
|
||||
function runVerbose(root, projectName) {
|
||||
var proc = spawn('npm', ['install', '--verbose', '--save', 'react-native'], {stdio: 'inherit'});
|
||||
function runVerbose(root, projectName, rnPackage) {
|
||||
var proc = spawn('npm', ['install', '--verbose', '--save', getInstallPackage(rnPackage)], {stdio: 'inherit'});
|
||||
proc.on('close', function (code) {
|
||||
if (code !== 0) {
|
||||
console.error('`npm install --save react-native` failed');
|
||||
@@ -233,7 +258,7 @@ function checkNodeVersion() {
|
||||
}
|
||||
|
||||
function checkForVersionArgument() {
|
||||
if (process.argv.indexOf('-v') >= 0 || process.argv.indexOf('--version') >= 0) {
|
||||
if (argv._.length === 0 && (argv.v || argv.version)) {
|
||||
console.log('react-native-cli: ' + require('./package.json').version);
|
||||
try {
|
||||
console.log('react-native: ' + require(REACT_NATIVE_PACKAGE_JSON_PATH()).version);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-native-cli",
|
||||
"version": "0.1.10",
|
||||
"license" : "BSD-3-Clause",
|
||||
"version": "0.2.0",
|
||||
"license": "BSD-3-Clause",
|
||||
"description": "The React Native CLI tools",
|
||||
"main": "index.js",
|
||||
"engines": {
|
||||
@@ -19,6 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^1.1.1",
|
||||
"minimist": "^1.2.0",
|
||||
"prompt": "^0.2.14",
|
||||
"semver": "^5.0.3"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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 bumps a new version for open source releases.
|
||||
* It updates the version in podspec/json/gradle files and makes sure they are consistent between each other
|
||||
* After changing the files it makes a commit and tags it.
|
||||
* All you have to do is push changes to remote and CI will make a new build.
|
||||
*/
|
||||
/*eslint-disable no-undef */
|
||||
require(`shelljs/global`);
|
||||
|
||||
// - check we are in release branch, e.g. 0.33-stable
|
||||
let branch = exec(`git symbolic-ref --short HEAD`, {silent: true}).stdout.trim();
|
||||
|
||||
if (branch.indexOf(`-stable`) === -1) {
|
||||
echo(`You must be in 0.XX-stable branch to bump a version`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// e.g. 0.33
|
||||
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];
|
||||
if (!version || version.indexOf(versionMajor) !== 0) {
|
||||
echo(`You must pass a tag like ${versionMajor}.[X]-rc[Y] to bump a version`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let packageJson = JSON.parse(cat(`package.json`));
|
||||
packageJson.version = version;
|
||||
JSON.stringify(packageJson, null, 2).to(`package.json`);
|
||||
|
||||
// - change ReactAndroid/gradle.properties
|
||||
if (sed(`-i`, /^VERSION_NAME=.*/, `VERSION_NAME=${version}`, `ReactAndroid/gradle.properties`).code) {
|
||||
echo(`Couldn't update version for Gradle`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// - change React.podspec
|
||||
if (sed(`-i`, /s.version\s*=.*/, `s.version = \"${version}\"`, `React.podspec`).code) {
|
||||
echo(`Couldn't update version for React.podspec`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// verify that files changed, we just do a git diff and check how many times version is added across files
|
||||
let numberOfChangedLinesWithNewVersion = exec(`git diff -U0 | grep '^[+]' | grep -c ${version} `, {silent: true})
|
||||
.stdout.trim();
|
||||
if (+numberOfChangedLinesWithNewVersion !== 3) {
|
||||
echo(`Failed to update all the files. React.podspec, package.json and gradle.properties must have versions in them`);
|
||||
echo(`Fix the issue, revert and try again`);
|
||||
exec(`git diff`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// - make commit [0.21.0-rc] Bump version numbers
|
||||
if (exec(`git commit -a -m "[${version}] Bump version numbers"`).code) {
|
||||
echo(`failed to commit`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// - add tag v0.21.0-rc
|
||||
if (exec(`git tag v${version}`).code) {
|
||||
echo(`failed to tag the commit with v${version}, are you sure this release wasn't made earlier?`);
|
||||
echo(`You may want to rollback the last commit`);
|
||||
echo(`git reset --hard HEAD~1`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
/*eslint-enable no-undef */
|
||||
@@ -1,5 +0,0 @@
|
||||
; travis:travis
|
||||
_auth=dHJhdmlzOnRyYXZpcw==
|
||||
email=travis@example.com
|
||||
spin=false
|
||||
registry=http://localhost:4873/
|
||||
@@ -1,23 +0,0 @@
|
||||
storage: /tmp/sinopia-package-cache
|
||||
|
||||
uplinks:
|
||||
npmjs:
|
||||
url: http://registry.npmjs.org/
|
||||
|
||||
packages:
|
||||
'react-native':
|
||||
allow_access: $all
|
||||
allow_publish: $all
|
||||
|
||||
'react-native-cli':
|
||||
allow_access: $all
|
||||
allow_publish: $all
|
||||
|
||||
'*':
|
||||
allow_access: $all
|
||||
proxy: npmjs
|
||||
|
||||
logs:
|
||||
- {type: file, path: sinopia.log, format: pretty, level: http}
|
||||
|
||||
max_body_size: '50mb'
|
||||
@@ -1,23 +0,0 @@
|
||||
storage: /tmp/sinopia-package-cache
|
||||
|
||||
uplinks:
|
||||
npmjs:
|
||||
url: https://registry.npmjs.org/
|
||||
|
||||
packages:
|
||||
'react-native':
|
||||
allow_access: $all
|
||||
allow_publish: $all
|
||||
|
||||
'react-native-cli':
|
||||
allow_access: $all
|
||||
allow_publish: $all
|
||||
|
||||
'*':
|
||||
allow_access: $all
|
||||
proxy: npmjs
|
||||
|
||||
logs:
|
||||
- {type: file, path: sinopia.log, format: pretty, level: http}
|
||||
|
||||
max_body_size: '50mb'
|
||||
+12
-27
@@ -43,39 +43,23 @@ function cleanup {
|
||||
|
||||
rm $MARKER_IOS
|
||||
rm $MARKER_ANDROID
|
||||
[ $SINOPIA_PID ] && kill -9 $SINOPIA_PID
|
||||
[ $SERVER_PID ] && kill -9 $SERVER_PID
|
||||
[ -f ~/.npmrc.bak ] && mv ~/.npmrc.bak ~/.npmrc
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# pack react-native into a .tgz file
|
||||
npm pack
|
||||
PACKAGE=$(pwd)/react-native-*.tgz
|
||||
|
||||
# get react-native-cli dependencies
|
||||
cd react-native-cli
|
||||
npm pack
|
||||
CLI_PACKAGE=$(pwd)/react-native-cli-*.tgz
|
||||
|
||||
cd $TEMP
|
||||
|
||||
# sinopia is npm registry proxy, it is used to make npm
|
||||
# think react-native and react-native-cli are actually
|
||||
# published on npm
|
||||
# Temporarily installing sinopia from a github fork
|
||||
# TODO t10060166 use npm repo when bug is fixed
|
||||
which sinopia || npm install -g git://github.com/bestander/sinopia.git#68707efbab90569d5f52193138936f9281cc410c
|
||||
|
||||
# but in order to make npm use sinopia we temporarily
|
||||
# replace its config file
|
||||
[ -f ~/.npmrc ] && cp ~/.npmrc ~/.npmrc.bak
|
||||
cp $SCRIPTS/e2e-npmrc ~/.npmrc
|
||||
|
||||
sinopia --config $SCRIPTS/e2e-sinopia.config.yml &
|
||||
SINOPIA_PID=$!
|
||||
|
||||
# Make sure to remove old version of react-native in
|
||||
# case it was cached
|
||||
npm cache clear
|
||||
npm unpublish react-native --force
|
||||
npm unpublish react-native-cli --force
|
||||
npm publish $ROOT
|
||||
npm publish $ROOT/react-native-cli
|
||||
|
||||
npm install -g react-native-cli
|
||||
react-native init EndToEndTest
|
||||
npm install -g $CLI_PACKAGE
|
||||
react-native init EndToEndTest --version $PACKAGE
|
||||
cd EndToEndTest
|
||||
|
||||
case $1 in
|
||||
@@ -83,6 +67,7 @@ case $1 in
|
||||
echo "Running a basic packager test"
|
||||
# Check the packager produces a bundle (doesn't throw an error)
|
||||
react-native bundle --platform android --dev true --entry-file index.android.js --bundle-output android-bundle.js
|
||||
# TODO do flow check
|
||||
;;
|
||||
"--ios"*)
|
||||
echo "Running an iOS app"
|
||||
|
||||
+3
-25
@@ -90,11 +90,6 @@ if (javaVersion.indexOf(requiredJavaVersion) === -1) {
|
||||
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`);
|
||||
@@ -106,6 +101,9 @@ if (exec(`./gradlew :ReactAndroid:installArchives`).code) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// undo uncommenting javadoc setting
|
||||
exec(`git checkout ReactAndroid/gradle.properties`);
|
||||
|
||||
echo("Generated artifacts for Maven");
|
||||
|
||||
let artifacts = ['-javadoc.jar', '-sources.jar', '.aar', '.pom'].map((suffix) => {
|
||||
@@ -119,23 +117,6 @@ artifacts.forEach((name) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ----------- 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`);
|
||||
@@ -144,9 +125,6 @@ if (releaseVersion.indexOf(`-rc`) === -1) {
|
||||
exec(`npm publish --tag next`);
|
||||
}
|
||||
|
||||
exec(`git checkout package.json`);
|
||||
exec(`git checkout React.podspec`);
|
||||
|
||||
echo(`Published to npm ${releaseVersion}`);
|
||||
|
||||
exit(0);
|
||||
|
||||
+23
-36
@@ -1,6 +1,5 @@
|
||||
#! /bin/bash
|
||||
|
||||
RELEASE="$1"
|
||||
JAVA_VERSION="1.7"
|
||||
|
||||
RED="\033[0;31m"
|
||||
@@ -21,43 +20,34 @@ info() {
|
||||
echo -e $BLUE"$@"$ENDCOLOR
|
||||
}
|
||||
|
||||
[[ -z $RELEASE ]] && error "Please specify a version. Example usage: release.sh 0.18"
|
||||
PACKAGE_VERSION=$(cat package.json \
|
||||
| grep version \
|
||||
| head -1 \
|
||||
| awk -F: '{ print $2 }' \
|
||||
| sed 's/[",]//g' \
|
||||
| tr -d '[[:space:]]')
|
||||
|
||||
success "Preparing version $PACKAGE_VERSION"
|
||||
|
||||
repo_root=$(pwd)
|
||||
|
||||
sed -i.bak s/^VERSION_NAME=[0-9\.]*-SNAPSHOT/VERSION_NAME=${RELEASE}.0/g "ReactAndroid/gradle.properties" || error "Couldn't update version for Gradle"
|
||||
|
||||
./gradlew :ReactAndroid:installArchives || error "Couldn't generate artifacts"
|
||||
|
||||
success "Generated artifacts for Maven"
|
||||
|
||||
npm_registry="http://localhost:4873/"
|
||||
npm pack
|
||||
|
||||
npm set registry "${npm_registry}" && [[ $(npm config list | grep registry) == "registry = \"${npm_registry}\"" ]] || error "Couldn't set registry to ${npm_registry}"
|
||||
|
||||
info "npm registry set. Run 'sinopia' in a new Terminal"
|
||||
info " - Make sure it prints 'http address - ${npm_registry}'"
|
||||
info " - Make sure ${npm_registry} shows no old versions"
|
||||
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})"
|
||||
|
||||
success "Published package to sinopia (${npm_registry})"
|
||||
PACKAGE=$(pwd)/react-native-$PACKAGE_VERSION.tgz
|
||||
success "Package bundled ($PACKAGE)"
|
||||
|
||||
project_name="RNTestProject"
|
||||
|
||||
cd /tmp/
|
||||
rm -rf "$project_name"
|
||||
react-native init "$project_name"
|
||||
react-native init "$project_name" --version $PACKAGE
|
||||
|
||||
info "Double checking the versions in package.json and build.gradle are correct:"
|
||||
grep "\"react-native\": \"\^${RELEASE}.0-rc\"" "/tmp/${project_name}/package.json" || error "Incorrect version number in /tmp/${project_name}/package.json"
|
||||
info "Double checking the versions in package.json are correct:"
|
||||
grep "\"react-native\": \".*react-native-$PACKAGE_VERSION.tgz\"" "/tmp/${project_name}/package.json" || error "Incorrect version number in /tmp/${project_name}/package.json"
|
||||
grep -E "com.facebook.react:react-native:\\+" "${project_name}/android/app/build.gradle" || error "Dependency in /tmp/${project_name}/android/app/build.gradle must be com.facebook.react:react-native:+"
|
||||
|
||||
success "New sample project generated at /tmp/${project_name}"
|
||||
@@ -65,28 +55,25 @@ success "New sample project generated at /tmp/${project_name}"
|
||||
info "Test the following both on Android and iOS:"
|
||||
info " - Verify that packager opens in new Window"
|
||||
info " - Verify that you see the 'Welcome to React Native' screen"
|
||||
info " - Disable Hot Reloading. It might be enabled from last time (the setting is stored on the device)"
|
||||
info " - Verify 'Reload JS' works"
|
||||
info " - Test Chrome debugger by adding breakpoints. We don't have tests for Chrome debugging."
|
||||
info " - Test Chrome debugger by adding breakpoints and reloading JS. We don't have tests for Chrome debugging."
|
||||
info " - Disable Chrome debugging."
|
||||
info " - Enable Hot Reloading, change a file (index.ios.js, index.android.js) and save. The UI should refresh."
|
||||
info " - Disable Hot Reloading."
|
||||
info ""
|
||||
|
||||
info "Press any key to run the sample in Android emulator/device"
|
||||
info ""
|
||||
read -n 1
|
||||
cd "${project_name}" && react-native run-android
|
||||
cd "/tmp/${project_name}" && react-native run-android
|
||||
|
||||
info "Press any key to open the project in XCode"
|
||||
info ""
|
||||
read -n 1
|
||||
open "/tmp/${project_name}/ios/${project_name}.xcodeproj"
|
||||
|
||||
cd "$repo_root"
|
||||
|
||||
# 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 tag v${RELEASE}.0-rc"
|
||||
info " - git push origin ${RELEASE}-stable --tags"
|
||||
info " - https://github.com/facebook/react-native/blob/master/Releases.md"
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
src/react-native/docs/**
|
||||
core/metadata.js
|
||||
src/react-native/blog/**
|
||||
core/metadata*
|
||||
*.log
|
||||
/build/
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -16,8 +16,8 @@ var HeaderLinks = React.createClass({
|
||||
linksInternal: [
|
||||
{section: 'docs', href: 'docs/getting-started.html', text: 'Docs'},
|
||||
{section: 'support', href: 'support.html', text: 'Support'},
|
||||
{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'},
|
||||
|
||||
@@ -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)}>← Prev</a>}
|
||||
{MetadataBlog.files.length > (page + 1) * perPage &&
|
||||
<a className="docs-next" href={this.getPageURL(page + 1)}>Next →</a>}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Site>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BlogPageLayout;
|
||||
@@ -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;
|
||||
+120
-45
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user