Compare commits

...
Author SHA1 Message Date
Mike Grabowski 9a3cad4d89 [0.54.2] Bump version numbers 2018-03-12 22:17:00 +01:00
Rafael OlezaandMike Grabowski 48f29a74c5 Make the chrome debugger handle dynamic delta ids
Differential Revision: D7112419

fbshipit-source-id: 1d80c0c13144dd19bbcd5535383befc6567cacf7
2018-03-12 22:16:44 +01:00
Eric RozellandMike Grabowski 8e03ced500 Fixing bugs in link and unlink
Summary:
Android uses the name of the package, not the config, for the `isInstalled` check. Sending both parameters to `isInstalled` so we have a consistent API.

<!--
Thank you for sending the PR! We appreciate you spending the time to work on these changes.

Help us understand your motivation by explaining why you decided to make this change.

You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html

Happy contributing!

-->

A bug was uncovered in the react-native link command where Android would not unlink because the wrong parameters were being sent to `isInstalled`.

Successfully linked and unlinked `react-native-fs` on Windows and Mac. Jest tests pass.

<!--
Help reviewers and the release process by writing your own release notes

**INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.**

  CATEGORY
[----------]        TYPE
[ CLI      ]   [-------------]      LOCATION
[ DOCS     ]   [ BREAKING    ]   [-------------]
[ GENERAL  ]   [ BUGFIX      ]   [-{Component}-]
[ INTERNAL ]   [ ENHANCEMENT ]   [ {File}      ]
[ IOS      ]   [ FEATURE     ]   [ {Directory} ]   |-----------|
[ ANDROID  ]   [ MINOR       ]   [ {Framework} ] - | {Message} |
[----------]   [-------------]   [-------------]   |-----------|

[CATEGORY] [TYPE] [LOCATION] - MESSAGE

 EXAMPLES:

 [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things
 [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput
 [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with
 [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word
 [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position
 [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see
-->

[CLI][BUGFIX][local-cli/link/link.js] - Fix issue with `isInstalled` check for Android
[CLI][BUGFIX][local-cli/link/unlink.js] - Fix issue with `isInstalled` check for Android
[CLI][BUGFIX][local-cli/link/ios/common/unregisterNativeModule.js] - Fix references to unregister implementations.
Closes https://github.com/facebook/react-native/pull/18207

Differential Revision: D7180885

Pulled By: hramos

fbshipit-source-id: 5f479cd9d7b1ebd8626b461e9dc1f22988e2c61f
2018-03-12 22:16:38 +01:00
Mike Grabowski ac55ffd777 Revert "Fix HmrClient path"
This reverts commit 8bdd98ea48.
2018-03-12 22:16:32 +01:00
Mike GrabowskiandGitHub 8bdd98ea48 Fix HmrClient path 2018-03-09 20:39:13 +01:00
Mike Grabowski 2d57335fa7 [0.54.1] Bump version numbers 2018-03-09 18:20:10 +01:00
Mike GrabowskiandGitHub 2f8446319a Update ReactFeatureFlags.js 2018-03-07 17:11:46 +01:00
Mike Grabowski 815a07c77b [0.54.0] Bump version numbers 2018-03-01 18:01:19 +01:00
Josh HargreavesandMike Grabowski 53c1a4cc7b Fix crashes onKeyPress Android
Summary:
There appear to be two different types of crashes related to the recent addition of `onKeyPress` on Android introduce in `0.53`. This PR addresses the cause of both of them.

Firstly, it seems possible to get an `indexOutOfBoundsException` with some 3rd-party keyboards as observed in https://github.com/facebook/react-native/issues/17974 & https://github.com/facebook/react-native/issues/17922. I have simplified the backspace determining logic slightly, and also put in an explicit check for zero case so it is not possible to get an indexOutOfBoundsException & it should make sense in the context of the onKeyPress logic.

Secondly, it appears that `EditText#onCreateInputConnection` can return null. In this case, if we set `null` to be the target of our subclass of `ReactEditTextInputConnectionWrapper`, we will see the crashes as seen [here](https://github.com/facebook/react-native/issues/17974#issuecomment-368471737), whereby any of methods executed in the `InputConnection` interface can result in a crash. It's hard to reason about the state when `null` is returned from `onCreateInputConnection`, however I would might reason that any soft keyboard input cannot update the `EditText` with a `null` `input connection`, as there is no way of interfacing with the `EditText`. I'm am not sure, if there is a later point where we might return/set this input connection at a later point? As without the `InputConnection` onKeyPress will not work. But for now, this will fix this crash at least.

I have not managed to reproduce these crashes myself yet, but users have confirmed that the `indexOutOfBounds` exception is fixed with the 'zero' case and has been confirmed on the respective issues https://github.com/facebook/react-native/issues/17974#issuecomment-368471737.

For the `null` inputConnection target case, I have verified that explicitly setting the target as null in the constructor of `onCreateInputConnection` results in the same stack trace as the one linked. Here is also a [reference](https://github.com/stripe/stripe-android/pull/392/files#diff-6cc1685c98457d07fd4e2dd83f54d5bb) to the same issue closed with the same fix for another project on github.

It is also important to verify that the behavior of `onKeyPress` still functions the same after this change, which can be verified by running the RNTesterProject and the `KeyboardEvents` section in `InputText`.
The cases to check that I think are important to check are:
- Cursor at beginning of input & backspace
- Return key & return key at beginning of input
- Select text then press delete
- Selection then press a key
- Space key
- Different keyboard types

This should not be a breaking change.

 [ANDROID] [BUGFIX] [TextInput] - Fixes crashes with TextInput introduced in 0.53.
Closes https://github.com/facebook/react-native/pull/18114

Differential Revision: D7099570

Pulled By: hramos

fbshipit-source-id: 75b2dc468c1ed398a33eb00487c6aa14ae04e5c2
2018-03-01 18:00:10 +01:00
Mike Grabowski 67e67ec83c [0.54.0-rc.4] Bump version numbers 2018-02-27 09:58:06 +01:00
Mike Grabowski 0f96ebd93b Bump Metro version to fix issue for npm users 2018-02-27 09:57:58 +01:00
Rafael OlezaandMike Grabowski f4fde9d84a Bump metro@0.26.0
Reviewed By: cpojer

Differential Revision: D6976161

fbshipit-source-id: 0cf20f4b2372997a8aac41cc07a9bdd641a93ad4
2018-02-21 12:56:50 +01:00
Tadeu ValenttandMike Grabowski 4194bb242d Fix #17610, Add fixtures to metro blacklist
Summary:
Include a default blacklist into the build settings to prevent
processing of incorrect fixture files by Metro.

<!--
Thank you for sending the PR! We appreciate you spending the time to work on these changes.

Help us understand your motivation by explaining why you decided to make this change.

You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html

Happy contributing!

-->

Fix #17610 issue, preventing metro from processing fixture files

1. Have a working demo
2. Install https://github.com/oblador/react-native-vector-icons
3. Use in a component
4. Start the app
5. The app starts successfully and display the icons

[ GENERAL  ]  [ BUGFIX ]  [local-cli/util/Config.js] - Add default file blacklist
Closes https://github.com/facebook/react-native/pull/17672

Differential Revision: D7014627

Pulled By: hramos

fbshipit-source-id: 20974e6fdd0977eeeb1048c29c9d621c803c26e9
2018-02-21 12:55:54 +01:00
Janic DuplessisandJames Ide 675f14257a Bundle download progress on Android
Summary:
Android equivalent of #15066

Tested that download progress shows up properly when reloading the app.

[ANDROID] [FEATURE] [DevSupport] - Show bundle download progress on Android
Closes https://github.com/facebook/react-native/pull/17809

Differential Revision: D6982823

Pulled By: hramos

fbshipit-source-id: da01e42b8ebb1c603f4407f6bafd68e0b6b3ecba
2018-02-14 23:48:02 -08:00
Mike Grabowski 21dd3dd296 [0.54.0-rc.3] Bump version numbers 2018-02-13 18:11:48 +01:00
Mike Grabowski 03d7b2aa0e Bump React dependency 2018-02-13 18:11:04 +01:00
Eric RozellandMike Grabowski 2c5fbd79a2 Uses a single code path to link and unlink all platforms
Summary:
This commit removes special cases for linking iOS and Android platforms.

A previous commit opened up link and other commands for other platforms to provide their own behaviors. It left special cases in tact for iOS and Android. This PR removes the special case.

- Added jest tests related to the link command.
- Ran the `link` and `unlink` commands for iOS and Android and confirmed no changes.

https://github.com/facebook/react-native/pull/17745

<!--
Help reviewers and the release process by writing your own release notes

**INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.**

  CATEGORY
[----------]        TYPE
[ CLI      ]   [-------------]      LOCATION
[ DOCS     ]   [ BREAKING    ]   [-------------]
[ GENERAL  ]   [ BUGFIX      ]   [-{Component}-]
[ INTERNAL ]   [ ENHANCEMENT ]   [ {File}      ]
[ IOS      ]   [ FEATURE     ]   [ {Directory} ]   |-----------|
[ ANDROID  ]   [ MINOR       ]   [ {Framework} ] - | {Message} |
[----------]   [-------------]   [-------------]   |-----------|

[CATEGORY] [TYPE] [LOCATION] - MESSAGE

 EXAMPLES:

 [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things
 [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput
 [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with
 [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word
 [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position
 [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see
-->

[CLI][FEATURE][local-cli/link/link.js] - Removes special cases for linking in iOS and Android.
Closes https://github.com/facebook/react-native/pull/17961

Differential Revision: D6975951

Pulled By: hramos

fbshipit-source-id: 8dd5da35619e2124ce4b3b18db8b694757792363
2018-02-13 18:10:34 +01:00
Hector Ramos 49e35bd939 [0.54.0-rc.0] Bump version numbers 2018-02-12 12:19:05 -08:00
Mike Grabowski 829f675b8b [0.54.0-rc.2] Bump version numbers 2018-02-12 17:38:28 +01:00
Mike Grabowski b58d848d9c Fix CI publish step 2018-02-12 17:38:22 +01:00
Mike Grabowski 294d95a236 [0.54.0-rc.0] Bump version numbers 2018-02-12 13:10:05 +01:00
28 changed files with 240 additions and 221 deletions
+4
View File
@@ -383,6 +383,9 @@ jobs:
publish_npm_package:
<<: *android_defaults
steps:
# Checkout code so that we can work with `git` in publish.js
- checkout
- attach_workspace:
at: ~/react-native
@@ -606,6 +609,7 @@ workflows:
- approve_publish_npm_package:
filters: *filter-only-stable
type: approval
- publish_npm_package:
requires:
- checkout_code
+2 -2
View File
@@ -14,7 +14,7 @@
exports.version = {
major: 0,
minor: 0,
patch: 0,
minor: 54,
patch: 2,
prerelease: null,
};
@@ -12,7 +12,7 @@
const ReactFeatureFlags = {
debugRenderPhaseSideEffects: false,
debugRenderPhaseSideEffectsForStrictMode: false,
warnAboutDeprecatedLifecycles: true,
warnAboutDeprecatedLifecycles: false,
};
module.exports = ReactFeatureFlags;
+2 -2
View File
@@ -11,7 +11,7 @@
#define RCT_REACT_NATIVE_VERSION @{ \
@"major": @(0), \
@"minor": @(0), \
@"patch": @(0), \
@"minor": @(54), \
@"patch": @(2), \
@"prerelease": [NSNull null], \
}
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0-master
VERSION_NAME=0.54.2
GROUP=com.facebook.react
POM_NAME=ReactNative
@@ -146,13 +146,13 @@ public class BundleDownloader {
if (match.find()) {
String boundary = match.group(1);
MultipartStreamReader bodyReader = new MultipartStreamReader(response.body().source(), boundary);
boolean completed = bodyReader.readAllParts(new MultipartStreamReader.ChunkCallback() {
boolean completed = bodyReader.readAllParts(new MultipartStreamReader.ChunkListener() {
@Override
public void execute(Map<String, String> headers, Buffer body, boolean finished) throws IOException {
public void onChunkComplete(Map<String, String> headers, Buffer body, boolean isLastChunk) throws IOException {
// This will get executed for every chunk of the multipart response. The last chunk
// (finished = true) will be the JS bundle, the other ones will be progress events
// (isLastChunk = true) will be the JS bundle, the other ones will be progress events
// encoded as JSON.
if (finished) {
if (isLastChunk) {
// The http status code for each separate chunk is in the X-Http-Status header.
int status = response.code();
if (headers.containsKey("X-Http-Status")) {
@@ -184,6 +184,15 @@ public class BundleDownloader {
}
}
}
@Override
public void onChunkProgress(Map<String, String> headers, long loaded, long total) throws IOException {
if ("application/javascript".equals(headers.get("Content-Type"))) {
callback.onProgress(
"Downloading JavaScript bundle",
(int) (loaded / 1024),
(int) (total / 1024));
}
}
});
if (!completed) {
callback.onFailure(new DebugServerException(
@@ -26,9 +26,18 @@ public class MultipartStreamReader {
private final BufferedSource mSource;
private final String mBoundary;
private long mLastProgressEvent;
public interface ChunkCallback {
void execute(Map<String, String> headers, Buffer body, boolean done) throws IOException;
public interface ChunkListener {
/**
* Invoked when a chunk of a multipart response is fully downloaded.
*/
void onChunkComplete(Map<String, String> headers, Buffer body, boolean isLastChunk) throws IOException;
/**
* Invoked as bytes of the current chunk are read.
*/
void onChunkProgress(Map<String, String> headers, long loaded, long total) throws IOException;
}
public MultipartStreamReader(BufferedSource source, String boundary) {
@@ -55,34 +64,50 @@ public class MultipartStreamReader {
return headers;
}
private void emitChunk(Buffer chunk, boolean done, ChunkCallback callback) throws IOException {
private void emitChunk(Buffer chunk, boolean done, ChunkListener listener) throws IOException {
ByteString marker = ByteString.encodeUtf8(CRLF + CRLF);
long indexOfMarker = chunk.indexOf(marker);
if (indexOfMarker == -1) {
callback.execute(null, chunk, done);
listener.onChunkComplete(null, chunk, done);
} else {
Buffer headers = new Buffer();
Buffer body = new Buffer();
chunk.read(headers, indexOfMarker);
chunk.skip(marker.size());
chunk.readAll(body);
callback.execute(parseHeaders(headers), body, done);
listener.onChunkComplete(parseHeaders(headers), body, done);
}
}
private void emitProgress(Map<String, String> headers, long contentLength, boolean isFinal, ChunkListener listener) throws IOException {
if (headers == null || listener == null) {
return;
}
long currentTime = System.currentTimeMillis();
if (currentTime - mLastProgressEvent > 16 || isFinal) {
mLastProgressEvent = currentTime;
long headersContentLength = headers.get("Content-Length") != null ? Long.parseLong(headers.get("Content-Length")) : 0;
listener.onChunkProgress(headers, contentLength, headersContentLength);
}
}
/**
* Reads all parts of the multipart response and execute the callback for each chunk received.
* @param callback Callback executed when a chunk is received
* Reads all parts of the multipart response and execute the listener for each chunk received.
* @param listener Listener invoked when chunks are received.
* @return If the read was successful
*/
public boolean readAllParts(ChunkCallback callback) throws IOException {
public boolean readAllParts(ChunkListener listener) throws IOException {
ByteString delimiter = ByteString.encodeUtf8(CRLF + "--" + mBoundary + CRLF);
ByteString closeDelimiter = ByteString.encodeUtf8(CRLF + "--" + mBoundary + "--" + CRLF);
ByteString headersDelimiter = ByteString.encodeUtf8(CRLF + CRLF);
int bufferLen = 4 * 1024;
long chunkStart = 0;
long bytesSeen = 0;
Buffer content = new Buffer();
Map<String, String> currentHeaders = null;
long currentHeadersLength = 0;
while (true) {
boolean isCloseDelimiter = false;
@@ -98,6 +123,20 @@ public class MultipartStreamReader {
if (indexOfDelimiter == -1) {
bytesSeen = content.size();
if (currentHeaders == null) {
long indexOfHeaders = content.indexOf(headersDelimiter, searchStart);
if (indexOfHeaders >= 0) {
mSource.read(content, indexOfHeaders);
Buffer headers = new Buffer();
content.copyTo(headers, searchStart, indexOfHeaders - searchStart);
currentHeadersLength = headers.size() + headersDelimiter.size();
currentHeaders = parseHeaders(headers);
}
} else {
emitProgress(currentHeaders, content.size() - currentHeadersLength, false, listener);
}
long bytesRead = mSource.read(content, bufferLen);
if (bytesRead <= 0) {
return false;
@@ -113,7 +152,10 @@ public class MultipartStreamReader {
Buffer chunk = new Buffer();
content.skip(chunkStart);
content.read(chunk, length);
emitChunk(chunk, isCloseDelimiter, callback);
emitProgress(currentHeaders, chunk.size() - currentHeadersLength, true, listener);
emitChunk(chunk, isCloseDelimiter, listener);
currentHeaders = null;
currentHeadersLength = 0;
} else {
content.skip(chunkEnd);
}
@@ -18,7 +18,7 @@ import java.util.Map;
public class ReactNativeVersion {
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
"major", 0,
"minor", 0,
"patch", 0,
"minor", 54,
"patch", 2,
"prerelease", null);
}
@@ -174,14 +174,16 @@ public class ReactEditText extends EditText {
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
ReactContext reactContext = (ReactContext) getContext();
ReactEditTextInputConnectionWrapper inputConnectionWrapper =
new ReactEditTextInputConnectionWrapper(super.onCreateInputConnection(outAttrs), reactContext, this);
InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
if (inputConnection != null) {
inputConnection = new ReactEditTextInputConnectionWrapper(inputConnection, reactContext, this);
}
if (isMultiline() && getBlurOnSubmit()) {
// Remove IME_FLAG_NO_ENTER_ACTION to keep the original IME_OPTION
outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
}
return inputConnectionWrapper;
return inputConnection;
}
@Override
@@ -94,14 +94,15 @@ class ReactEditTextInputConnectionWrapper extends InputConnectionWrapper {
int previousSelectionEnd = mEditText.getSelectionEnd();
String key;
boolean consumed = super.setComposingText(text, newCursorPosition);
int currentSelectionStart = mEditText.getSelectionStart();
boolean noPreviousSelection = previousSelectionStart == previousSelectionEnd;
boolean cursorDidNotMove = mEditText.getSelectionStart() == previousSelectionStart;
boolean cursorMovedBackwards = mEditText.getSelectionStart() < previousSelectionStart;
if ((noPreviousSelection && cursorMovedBackwards)
|| !noPreviousSelection && cursorDidNotMove) {
boolean cursorDidNotMove = currentSelectionStart == previousSelectionStart;
boolean cursorMovedBackwardsOrAtBeginningOfInput =
(currentSelectionStart < previousSelectionStart) || currentSelectionStart <= 0;
if (cursorMovedBackwardsOrAtBeginningOfInput || (!noPreviousSelection && cursorDidNotMove)) {
key = BACKSPACE_KEY_VALUE;
} else {
key = String.valueOf(mEditText.getText().charAt(mEditText.getSelectionStart() - 1));
key = String.valueOf(mEditText.getText().charAt(currentSelectionStart - 1));
}
dispatchKeyEventOrEnqueue(key);
return consumed;
@@ -24,14 +24,19 @@ import static org.fest.assertions.api.Assertions.assertThat;
@RunWith(RobolectricTestRunner.class)
public class MultipartStreamReaderTest {
class CallCountTrackingChunkCallback implements MultipartStreamReader.ChunkCallback {
class CallCountTrackingChunkCallback implements MultipartStreamReader.ChunkListener {
private int mCount = 0;
@Override
public void execute(Map<String, String> headers, Buffer body, boolean done) throws IOException {
public void onChunkComplete(Map<String, String> headers, Buffer body, boolean done) throws IOException {
mCount++;
}
@Override
public void onChunkProgress(Map<String, String> headers, long loaded, long total) throws IOException {
}
public int getCallCount() {
return mCount;
}
@@ -41,12 +46,12 @@ public class MultipartStreamReaderTest {
public void testSimpleCase() throws IOException {
ByteString response = ByteString.encodeUtf8(
"preable, should be ignored\r\n" +
"--sample_boundary\r\n" +
"Content-Type: application/json; charset=utf-8\r\n" +
"Content-Length: 2\r\n\r\n" +
"{}\r\n" +
"--sample_boundary--\r\n" +
"epilogue, should be ignored");
"--sample_boundary\r\n" +
"Content-Type: application/json; charset=utf-8\r\n" +
"Content-Length: 2\r\n\r\n" +
"{}\r\n" +
"--sample_boundary--\r\n" +
"epilogue, should be ignored");
Buffer source = new Buffer();
source.write(response);
@@ -55,8 +60,8 @@ public class MultipartStreamReaderTest {
CallCountTrackingChunkCallback callback = new CallCountTrackingChunkCallback() {
@Override
public void execute(Map<String, String> headers, Buffer body, boolean done) throws IOException {
super.execute(headers, body, done);
public void onChunkComplete(Map<String, String> headers, Buffer body, boolean done) throws IOException {
super.onChunkComplete(headers, body, done);
assertThat(done).isTrue();
assertThat(headers.get("Content-Type")).isEqualTo("application/json; charset=utf-8");
@@ -89,8 +94,8 @@ public class MultipartStreamReaderTest {
CallCountTrackingChunkCallback callback = new CallCountTrackingChunkCallback() {
@Override
public void execute(Map<String, String> headers, Buffer body, boolean done) throws IOException {
super.execute(headers, body, done);
public void onChunkComplete(Map<String, String> headers, Buffer body, boolean done) throws IOException {
super.onChunkComplete(headers, body, done);
assertThat(done).isEqualTo(getCallCount() == 3);
assertThat(body.readUtf8()).isEqualTo(String.valueOf(getCallCount()));
@@ -122,12 +127,12 @@ public class MultipartStreamReaderTest {
public void testNoCloseDelimiter() throws IOException {
ByteString response = ByteString.encodeUtf8(
"preable, should be ignored\r\n" +
"--sample_boundary\r\n" +
"Content-Type: application/json; charset=utf-8\r\n" +
"Content-Length: 2\r\n\r\n" +
"{}\r\n" +
"--sample_boundary\r\n" +
"incomplete message...");
"--sample_boundary\r\n" +
"Content-Type: application/json; charset=utf-8\r\n" +
"Content-Length: 2\r\n\r\n" +
"{}\r\n" +
"--sample_boundary\r\n" +
"incomplete message...");
Buffer source = new Buffer();
source.write(response);
+2
View File
@@ -126,3 +126,5 @@ exports.dependencyConfig = function dependencyConfigAndroid(folder, userConfig)
return { sourceDir, folder, manifest, packageImportPath, packageInstance };
};
exports.linkConfig = require('../../link/android');
+2
View File
@@ -57,3 +57,5 @@ exports.projectConfig = function projectConfigIOS(folder, userConfig) {
};
exports.dependencyConfig = exports.projectConfig;
exports.linkConfig = require('../../link/ios');
+7 -3
View File
@@ -81,8 +81,10 @@ describe('link', () => {
it('should register native module when android/ios projects are present', (done) => {
const registerNativeModule = sinon.stub();
const dependencyConfig = {android: {}, ios: {}, assets: [], commands: {}};
const androidLinkConfig = require('../android');
const iosLinkConfig = require('../ios');
const config = {
getPlatformConfig: () => ({ios: {}, android: {}}),
getPlatformConfig: () => ({ios: { linkConfig: iosLinkConfig }, android: { linkConfig: androidLinkConfig }}),
getProjectConfig: () => ({android: {}, ios: {}, assets: []}),
getDependencyConfig: sinon.stub().returns(dependencyConfig),
};
@@ -223,8 +225,9 @@ describe('link', () => {
sinon.stub().returns(false)
);
const linkConfig = require('../ios');
const config = {
getPlatformConfig: () => ({ ios: {}}),
getPlatformConfig: () => ({ ios: { linkConfig: linkConfig }}),
getProjectConfig: () => ({ ios: {}, assets: [] }),
getDependencyConfig: sinon.stub().returns({
ios: {}, assets: [], commands: { prelink, postlink },
@@ -251,8 +254,9 @@ describe('link', () => {
copyAssets
);
const linkConfig = require('../ios');
const config = {
getPlatformConfig: () => ({ ios: {} }),
getPlatformConfig: () => ({ ios: { linkConfig: linkConfig } }),
getProjectConfig: () => ({ ios: {}, assets: projectAssets }),
getDependencyConfig: sinon.stub().returns(dependencyConfig),
};
+2 -2
View File
@@ -17,10 +17,10 @@ const groupFilesByType = require('../groupFilesByType');
* For now, the only types of files that are handled are:
* - Fonts (otf, ttf) - copied to targetPath/fonts under original name
*/
module.exports = function copyAssetsAndroid(files, targetPath) {
module.exports = function copyAssetsAndroid(files, project) {
const assets = groupFilesByType(files);
(assets.font || []).forEach(asset =>
fs.copySync(asset, path.join(targetPath, 'fonts', path.basename(asset)))
fs.copySync(asset, path.join(project.assetsPath, 'fonts', path.basename(asset)))
);
};
+9
View File
@@ -0,0 +1,9 @@
module.exports = function() {
return {
isInstalled: require('./isInstalled'),
register: require('./registerNativeModule'),
unregister: require('./unregisterNativeModule'),
copyAssets: require('./copyAssets'),
unlinkAssets: require('./unlinkAssets')
};
};
+2 -2
View File
@@ -17,11 +17,11 @@ const groupFilesByType = require('../groupFilesByType');
* For now, the only types of files that are handled are:
* - Fonts (otf, ttf) - copied to targetPath/fonts under original name
*/
module.exports = function unlinkAssetsAndroid(files, targetPath) {
module.exports = function unlinkAssetsAndroid(files, project) {
const assets = groupFilesByType(files);
(assets.font || []).forEach((file) => {
const filePath = path.join(targetPath, 'fonts', path.basename(file));
const filePath = path.join(project.assetsPath, 'fonts', path.basename(file));
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
+6
View File
@@ -0,0 +1,6 @@
const isInstalledIOS = require('../isInstalled');
const isInstalledPods = require('../../pods/isInstalled');
module.exports = function isInstalled(projectConfig, name, dependencyConfig) {
return isInstalledIOS(projectConfig, dependencyConfig) || isInstalledPods(projectConfig, dependencyConfig);
};
@@ -0,0 +1,16 @@
const registerDependencyIOS = require('../registerNativeModule');
const registerDependencyPods = require('../../pods/registerNativeModule');
module.exports = function registerNativeModule(
name,
dependencyConfig,
params,
projectConfig
) {
if (projectConfig.podfile && dependencyConfig.podspec) {
registerDependencyPods(name, dependencyConfig, projectConfig);
}
else {
registerDependencyIOS(dependencyConfig, projectConfig);
}
};
@@ -0,0 +1,22 @@
const compact = require('lodash').compact;
const isInstalledIOS = require('../isInstalled');
const isInstalledPods = require('../../pods/isInstalled');
const unregisterDependencyIOS = require('../unregisterNativeModule');
const unregisterDependencyPods = require('../../pods/unregisterNativeModule');
module.exports = function unregisterNativeModule(
name,
dependencyConfig,
projectConfig,
otherDependencies
) {
const isIosInstalled = isInstalledIOS(projectConfig, dependencyConfig);
const isPodInstalled = isInstalledPods(projectConfig, dependencyConfig);
if (isIosInstalled) {
const iOSDependencies = compact(otherDependencies.map(d => d.config.ios));
unregisterDependencyIOS(dependencyConfig, projectConfig, iOSDependencies);
}
else if (isPodInstalled) {
unregisterDependencyPods(dependencyConfig, projectConfig);
}
};
+9
View File
@@ -0,0 +1,9 @@
module.exports = function() {
return {
isInstalled: require('./common/isInstalled'),
register: require('./common/registerNativeModule'),
unregister: require('./common/unregisterNativeModule'),
copyAssets: require('./copyAssets'),
unlinkAssets: require('./unlinkAssets')
};
};
+3 -75
View File
@@ -26,14 +26,6 @@ const chalk = require('chalk');
* run Flow. */
const isEmpty = require('lodash').isEmpty;
const promiseWaterfall = require('./promiseWaterfall');
const registerDependencyAndroid = require('./android/registerNativeModule');
const registerDependencyIOS = require('./ios/registerNativeModule');
const registerDependencyPods = require('./pods/registerNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledIOS = require('./ios/isInstalled');
const isInstalledPods = require('./pods/isInstalled');
const copyAssetsAndroid = require('./android/copyAssets');
const copyAssetsIOS = require('./ios/copyAssets');
const getProjectDependencies = require('./getProjectDependencies');
const getDependencyConfig = require('./getDependencyConfig');
const pollParams = require('./pollParams');
@@ -47,37 +39,8 @@ log.heading = 'rnpm-link';
const dedupeAssets = (assets) => uniqBy(assets, asset => path.basename(asset));
const linkDependencyAndroid = (androidProject, dependency) => {
if (!androidProject || !dependency.config.android) {
return null;
}
const isInstalled = isInstalledAndroid(androidProject, dependency.name);
if (isInstalled) {
log.info(chalk.grey(`Android module ${dependency.name} is already linked`));
return null;
}
return pollParams(dependency.config.params).then(params => {
log.info(`Linking ${dependency.name} android dependency`);
registerDependencyAndroid(
dependency.name,
dependency.config.android,
params,
androidProject
);
log.info(`Android module ${dependency.name} has been successfully linked`);
});
};
const linkDependencyPlatforms = (platforms, project, dependency) => {
const ignorePlatforms = ['android', 'ios'];
const linkDependency = (platforms, project, dependency) => {
Object.keys(platforms || {})
.filter(platform => ignorePlatforms.indexOf(platform) < 0)
.forEach(platform => {
if (!project[platform] || !dependency.config[platform]) {
return null;
@@ -88,7 +51,7 @@ const linkDependencyPlatforms = (platforms, project, dependency) => {
return null;
}
const isInstalled = linkConfig.isInstalled(project[platform], dependency.config[platform]);
const isInstalled = linkConfig.isInstalled(project[platform], dependency.name, dependency.config[platform]);
if (isInstalled) {
log.info(chalk.grey(`Platform '${platform}' module ${dependency.name} is already linked`));
@@ -110,45 +73,12 @@ const linkDependencyPlatforms = (platforms, project, dependency) => {
});
};
const linkDependencyIOS = (iOSProject, dependency) => {
if (!iOSProject || !dependency.config.ios) {
return;
}
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios) || isInstalledPods(iOSProject, dependency.config.ios);
if (isInstalled) {
log.info(chalk.grey(`iOS module ${dependency.name} is already linked`));
return;
}
log.info(`Linking ${dependency.name} ios dependency`);
if (iOSProject.podfile && dependency.config.ios.podspec) {
registerDependencyPods(dependency, iOSProject);
}
else {
registerDependencyIOS(dependency.config.ios, iOSProject);
}
log.info(`iOS module ${dependency.name} has been successfully linked`);
};
const linkAssets = (platforms, project, assets) => {
if (isEmpty(assets)) {
return;
}
if (project.ios) {
log.info('Linking assets to ios project');
copyAssetsIOS(assets, project.ios);
}
if (project.android) {
log.info('Linking assets to android project');
copyAssetsAndroid(assets, project.android.assetsPath);
}
const ignorePlatforms = ['android', 'ios'];
Object.keys(platforms || {})
.filter(platform => ignorePlatforms.indexOf(platform) < 0)
.forEach(platform => {
const linkConfig = platforms[platform] && platforms[platform].linkConfig && platforms[platform].linkConfig();
if (!linkConfig || !linkConfig.copyAssets) {
@@ -212,9 +142,7 @@ function link(args: Array<string>, config: RNConfig) {
const tasks = flatten(dependencies.map(dependency => [
() => promisify(dependency.config.commands.prelink || commandStub),
() => linkDependencyAndroid(project.android, dependency),
() => linkDependencyIOS(project.ios, dependency),
() => linkDependencyPlatforms(platforms, project, dependency),
() => linkDependency(platforms, project, dependency),
() => promisify(dependency.config.commands.postlink || commandStub),
]));
+2 -2
View File
@@ -16,10 +16,10 @@ const findMarkedLinesInPodfile = require('./findMarkedLinesInPodfile');
const addPodEntry = require('./addPodEntry');
const savePodFile = require('./savePodFile');
module.exports = function registerNativeModulePods(dependency, iOSProject) {
module.exports = function registerNativeModulePods(name, dependencyConfig, iOSProject) {
const podLines = readPodfile(iOSProject.podfile);
const linesToAddEntry = getLinesToAddEntry(podLines, iOSProject);
addPodEntry(podLines, linesToAddEntry, dependency.config.ios.podspec, dependency.name);
addPodEntry(podLines, linesToAddEntry, dependencyConfig.podspec, name);
savePodFile(iOSProject.podfile, podLines);
};
+17 -72
View File
@@ -10,16 +10,7 @@
const log = require('npmlog');
const getProjectDependencies = require('./getProjectDependencies');
const unregisterDependencyAndroid = require('./android/unregisterNativeModule');
const unregisterDependencyIOS = require('./ios/unregisterNativeModule');
const unregisterDependencyPods = require('./pods/unregisterNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledIOS = require('./ios/isInstalled');
const isInstalledPods = require('./pods/isInstalled');
const unlinkAssetsAndroid = require('./android/unlinkAssets');
const unlinkAssetsIOS = require('./ios/unlinkAssets');
const getDependencyConfig = require('./getDependencyConfig');
const compact = require('lodash').compact;
const difference = require('lodash').difference;
const filter = require('lodash').filter;
const flatten = require('lodash').flatten;
@@ -30,41 +21,20 @@ const promisify = require('./promisify');
log.heading = 'rnpm-link';
const unlinkDependencyAndroid = (androidProject, dependency, packageName) => {
if (!androidProject || !dependency.android) {
return;
}
const unlinkDependency = (platforms, project, dependency, packageName, otherDependencies) => {
const isInstalled = isInstalledAndroid(androidProject, packageName);
if (!isInstalled) {
log.info(`Android module ${packageName} is not installed`);
return;
}
log.info(`Unlinking ${packageName} android dependency`);
unregisterDependencyAndroid(packageName, dependency.android, androidProject);
log.info(`Android module ${packageName} has been successfully unlinked`);
};
const unlinkDependencyPlatforms = (platforms, project, dependency, packageName) => {
const ignorePlatforms = ['android', 'ios'];
Object.keys(platforms || {})
.filter(platform => ignorePlatforms.indexOf(platform) < 0)
.forEach(platform => {
if (!project[platform] || !dependency[platform]) {
return null;
return;
}
const linkConfig = platforms[platform] && platforms[platform].linkConfig && platforms[platform].linkConfig();
if (!linkConfig || !linkConfig.isInstalled || !linkConfig.unregister) {
return null;
return;
}
const isInstalled = linkConfig.isInstalled(project[platform], dependency[platform]);
const isInstalled = linkConfig.isInstalled(project[platform], packageName, dependency[platform]);
if (!isInstalled) {
log.info(`Platform '${platform}' module ${packageName} is not installed`);
@@ -76,37 +46,14 @@ const unlinkDependencyPlatforms = (platforms, project, dependency, packageName)
linkConfig.unregister(
packageName,
dependency[platform],
project[platform]
project[platform],
otherDependencies
);
log.info(`Platform '${platform}' module ${dependency.name} has been successfully unlinked`);
});
};
const unlinkDependencyIOS = (iOSProject, dependency, packageName, iOSDependencies) => {
if (!iOSProject || !dependency.ios) {
return;
}
const isIosInstalled = isInstalledIOS(iOSProject, dependency.ios);
const isPodInstalled = isInstalledPods(iOSProject, dependency.ios);
if (!isIosInstalled && !isPodInstalled) {
log.info(`iOS module ${packageName} is not installed`);
return;
}
log.info(`Unlinking ${packageName} ios dependency`);
if (isIosInstalled) {
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
}
else if (isPodInstalled) {
unregisterDependencyPods(dependency.ios, iOSProject);
}
log.info(`iOS module ${packageName} has been successfully unlinked`);
};
/**
* Updates project and unlink specific dependency
*
@@ -143,13 +90,10 @@ function unlink(args, config) {
const allDependencies = getDependencyConfig(config, getProjectDependencies());
const otherDependencies = filter(allDependencies, d => d.name !== packageName);
const iOSDependencies = compact(otherDependencies.map(d => d.config.ios));
const tasks = [
() => promisify(dependency.commands.preunlink || commandStub),
() => unlinkDependencyAndroid(project.android, dependency, packageName),
() => unlinkDependencyIOS(project.ios, dependency, packageName, iOSDependencies),
() => unlinkDependencyPlatforms(platforms, project, dependency, packageName),
() => unlinkDependency(platforms, project, dependency, packageName, otherDependencies),
() => promisify(dependency.commands.postunlink || commandStub)
];
@@ -166,15 +110,16 @@ function unlink(args, config) {
return Promise.resolve();
}
if (project.ios) {
log.info('Unlinking assets from ios project');
unlinkAssetsIOS(assets, project.ios);
}
if (project.android) {
log.info('Unlinking assets from android project');
unlinkAssetsAndroid(assets, project.android.assetsPath);
}
Object.keys(platforms || {})
.forEach(platform => {
const linkConfig = platforms[platform] && platforms[platform].linkConfig && platforms[platform].linkConfig();
if (!linkConfig || !linkConfig.unlinkAssets) {
return;
}
log.info(`Unlinking assets from ${platform} project`);
linkConfig.unlinkAssets(assets, project[platform]);
});
log.info(
`${packageName} assets has been successfully unlinked from your project`
@@ -32,6 +32,7 @@
pre: new Map(),
post: new Map(),
modules: new Map(),
id: undefined,
};
this._initialized = false;
this._lastNumModifiedFiles = 0;
@@ -68,6 +69,7 @@
pre: new Map(),
post: new Map(),
modules: new Map(),
id: undefined,
};
}
@@ -82,9 +84,15 @@
this._patchMap(this._lastBundle.post, deltaBundle.post);
this._patchMap(this._lastBundle.modules, deltaBundle.delta);
this._lastBundle.id = deltaBundle.id;
return this;
}
getLastBundleId() {
return this._lastBundle.id;
}
/**
* Returns the number of modified files in the last received Delta. This is
* currently used to populate the `X-Metro-Files-Changed-Count` HTTP header
@@ -22,31 +22,34 @@
* whole JS bundle Blob.
*/
async function deltaUrlToBlobUrl(deltaUrl) {
let cachedBundle = cachedBundleUrls.get(deltaUrl);
const client = global.DeltaPatcher.get(deltaUrl);
const deltaBundleId = cachedBundle
? `&deltaBundleId=${cachedBundle.id}`
const deltaBundleId = client.getLastBundleId()
? `&deltaBundleId=${client.getLastBundleId()}`
: '';
const data = await fetch(deltaUrl + deltaBundleId);
const bundle = await data.json();
const deltaPatcher = global.DeltaPatcher.get(bundle.id).applyDelta({
const deltaPatcher = client.applyDelta({
id: bundle.id,
pre: new Map(bundle.pre),
post: new Map(bundle.post),
delta: new Map(bundle.delta),
reset: bundle.reset,
});
let cachedBundle = cachedBundleUrls.get(deltaUrl);
// If nothing changed, avoid recreating a bundle blob by reusing the
// previous one.
if (deltaPatcher.getLastNumModifiedFiles() === 0 && cachedBundle) {
return cachedBundle.url;
return cachedBundle;
}
// Clean up the previous bundle URL to not leak memory.
if (cachedBundle) {
URL.revokeObjectURL(cachedBundle.url);
URL.revokeObjectURL(cachedBundle);
}
// To make Source Maps work correctly, we need to add a newline between
@@ -60,13 +63,10 @@
type: 'application/javascript',
});
const bundleUrl = URL.createObjectURL(blob);
cachedBundleUrls.set(deltaUrl, {
id: bundle.id,
url: bundleUrl,
});
const bundleContents = URL.createObjectURL(blob);
cachedBundleUrls.set(deltaUrl, bundleContents);
return bundleUrl;
return bundleContents;
}
global.deltaUrlToBlobUrl = deltaUrlToBlobUrl;
+6 -1
View File
@@ -17,7 +17,7 @@ const getPolyfills = require('../../rn-get-polyfills');
const invariant = require('fbjs/lib/invariant');
const path = require('path');
const {Config: MetroConfig} = require('metro');
const {Config: MetroConfig, createBlacklist} = require('metro');
const RN_CLI_CONFIG = 'rn-cli.config.js';
@@ -56,6 +56,10 @@ const getProjectRoots = () => {
return resolveSymlinksForRoots([getProjectPath()]);
};
const getBlacklistRE = () => {
return createBlacklist([/.*\/__fixtures__\/.*/]);
};
/**
* Module capable of getting the configuration out of a given file.
*
@@ -67,6 +71,7 @@ const getProjectRoots = () => {
const Config = {
DEFAULT: ({
...MetroConfig.DEFAULT,
getBlacklistRE,
getProjectRoots,
getPolyfills,
getModulesRunBeforeMainModule: () => [
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.54.2",
"description": "A framework for building native apps using React",
"license": "BSD-3-Clause",
"repository": {
@@ -143,7 +143,7 @@
"react-native": "local-cli/wrong-react-native.js"
},
"peerDependencies": {
"react": "^16.3.0-alpha.0"
"react": "^16.3.0-alpha.1"
},
"dependencies": {
"absolute-path": "^0.0.0",
@@ -175,8 +175,8 @@
"graceful-fs": "^4.1.3",
"inquirer": "^3.0.6",
"lodash": "^4.17.5",
"metro": "^0.25.1",
"metro-core": "^0.25.1",
"metro": "^0.28.0",
"metro-core": "^0.28.0",
"mime": "^1.3.4",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
@@ -224,4 +224,4 @@
"shelljs": "^0.7.8",
"sinon": "^2.2.0"
}
}
}