Compare commits

...
Author SHA1 Message Date
Mike Grabowski 6ea6684ce2 [0.50.0] Bump version numbers 2017-11-03 17:51:20 +01:00
Mike Grabowski 1f6cd2d37d [0.50.0-rc.2] Bump version numbers 2017-10-30 10:31:12 +01:00
David Vacca b12213d017 Fixing RTL HorizontalScrolling in Android
Reviewed By: astreet

Differential Revision: D6170631

fbshipit-source-id: 254e6ed9a4d6e42b6d1215de1ff63aedb2c07a0a
2017-10-30 10:30:44 +01:00
Mike Grabowski 2da00fe871 [0.50.0-rc.1] Bump version numbers 2017-10-19 16:51:18 +02:00
Mike Grabowski e8d30c67d2 Fix test suite 2017-10-19 16:50:52 +02:00
Miron Pawlik 4c196aec90 Make react-native link play nicely with CocoaPods-based iOS projects.
Summary:
The core React Native codebase already has full support for CocoaPods. However, `react-native link` doesn’t play nicely with CocoaPods, so installing third-party libs from the RN ecosystem is really hard.

This change will allow to link projects that contains its own `.podspec` file to CocoaPods-based projects. In case `link` detect `Podfile` in `iOS` directory, it will look for related `.podspec` file in linked project directory, and add it to `Podfile`. If `Podfile` and `.podspec` files are not present, it will fall back to previous implementation.

**Test Plan**
1. Build a React Native project where the iOS part uses CocoaPods to manage its dependencies. The most common scenario here is to have React Native be a Pod dependency, among others.
2. Install a RN-related library, that contains `.podspec` file, with `react-native link` (as an example it could be: [react-native-maps](https://github.com/airbnb/react-native-maps)
3. Building the resulting iOS workspace should succeed (and there should be new entry in `Podfile`)
Closes https://github.com/facebook/react-native/pull/15460

Differential Revision: D6078649

Pulled By: hramos

fbshipit-source-id: 9651085875892fd66299563ca0e42fb2bcc00825
2017-10-18 17:38:04 -07:00
Brian Vaughn 87bc732ca7 Removed ProgressBarAndroid.android deprecation warning
Reviewed By: hramos

Differential Revision: D6092598

fbshipit-source-id: 24282a923cf45e749cf33f8a5b7a49995e7fadfe
2017-10-18 17:38:04 -07:00
James Ide 4f709f7b93 Move JS-native version check to its own module + unit tests + prefix Obj-C macro w/RCT
Summary:
- The version check that ensures the JS and native versions match is now in its own module for two reasons: it is easier to test and it allows react-native-windows to override just this module to implement its own version check (ex: more advanced checks for RNW-specific code).
- Added unit tests for the version checking to specify its behavior more clearly, including parity between dev and prod to avoid prod-only behavior and mitigate SEVs.
- Prefixed the Obj-C `#define` with `RCT_` to conform with other RN globals.
Closes https://github.com/facebook/react-native/pull/16403

Differential Revision: D6068491

Pulled By: hramos

fbshipit-source-id: 2b255b93982fb9d1b655fc62cb17b126bd5a939a
2017-10-16 22:25:14 -07:00
Mike Grabowski 63238f6add [0.50.0-rc.0] Bump version numbers
Fix eslint

Fix flow

Revert "Improve Flow Types"

This reverts commit bee33a4400.

[0.50.0-rc.1] Bump version numbers

Fix Analyze step

Remove mockFs dependency and unblock tests

Revert "[0.50.0-rc.1] Bump version numbers"

This reverts commit 5d0bc1d29bfdce40bbcc403190b06324557e5a58.

Revert "Migrate to Circle 2.0"

This reverts commit 8fa9984b11.

Remove old circle

Comment out danger for now
2017-10-11 19:36:23 +02:00
68 changed files with 1121 additions and 663 deletions
-373
View File
@@ -1,373 +0,0 @@
aliases:
- &restore-cache
keys:
- v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }}
# Fallback in case checksum fails
- v1-dependencies-{{ .Branch }}-
- &save-cache
paths:
- node_modules
key: v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }}
- &restore-cache-website
keys:
- v1-website-dependencies-{{ .Branch }}-{{ checksum "website/package.json" }}
# Fallback in case checksum fails
- v1-website-dependencies-{{ .Branch }}-
- &save-cache-website
paths:
- website/node_modules
key: v1-website-dependencies-{{ .Branch }}-{{ checksum "website/package.json" }}
- &restore-cache-danger
keys:
- v1-danger-dependencies-{{ .Branch }}-{{ checksum "danger/package.json" }}
# Fallback in case checksum fails
- v1-danger-dependencies-{{ .Branch }}-
- &save-cache-danger
paths:
- danger/node_modules
key: v1-danger-dependencies-{{ .Branch }}-{{ checksum "danger/package.json" }}
- &restore-cache-android-packages
keys:
- v1-android-sdkmanager-packages-{{ checksum "scripts/circle-ci-android-setup.sh" }}
# Fallback in case checksum fails
- v1-android-sdkmanager-packages-
- &save-cache-android-packages
paths:
- /opt/android/sdk/system-images/android-23
- /opt/android/sdk/system-images/android-19
- /opt/android/sdk/platforms/android-26
- /opt/android/sdk/platforms/android-23
- /opt/android/sdk/platforms/android-19
- /opt/android/sdk/build-tools/23.0.1
- /opt/android/sdk/add-ons/addon-google_apis-google-23
key: v1-android-sdkmanager-packages-{{ checksum "scripts/circle-ci-android-setup.sh" }}
- &restore-cache-ndk
keys:
- v1-android-ndk-r10e-32-64
- &save-cache-ndk
paths:
- /opt/ndk
key: v1-android-ndk-r10e-32-64
- &restore-cache-buck-downloads
keys:
- v1-buck-downloads-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }}
# Fallback in case checksum fails
- v1-buck-downloads-{{ .Branch }}-
- &save-cache-buck-downloads
paths:
- "ReactAndroid/build/downloads"
key: v1-buck-downloads-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }}
- &restore-cache-buck
keys:
- v1-buck-v2017.09.04.02
- &save-cache-buck
paths:
- ~/buck
key: v1-buck-v2017.09.04.02
- &restore-cache-watchman
keys:
- v1-watchman-v4.9.0
- &save-cache-watchman
paths:
- ~/watchman
key: v1-watchman-v4.9.0
defaults: &defaults
working_directory: ~/react-native
version: 2
jobs:
test-node-8:
<<: *defaults
docker:
- image: circleci/node:8
steps:
- checkout
- run: npm install --no-package-lock
- run: |
npm run lint
npm run flow -- check
npm test -- --maxWorkers=2
# eslint
- run:
name: Analyze Code
command: |
npm install github@0.2.4
cat <(echo eslint; npm run lint --silent -- --format=json; echo flow; npm run flow --silent -- check --json) | GITHUB_TOKEN="af6ef0d15709bc91d""06a6217a5a826a226fb57b7" CI_USER=$CIRCLE_PROJECT_USERNAME CI_REPO=$CIRCLE_PROJECT_REPONAME PULL_REQUEST_NUMBER=$CIRCLE_PR_NUMBER node bots/code-analysis-bot.js
test-node-6:
<<: *defaults
docker:
- image: circleci/node:6.11.0
steps:
- checkout
- run: npm install
- run: |
npm run lint
npm run flow -- check
npm test -- --maxWorkers=2
test-node-4:
<<: *defaults
docker:
- image: circleci/node:4.8.4
steps:
- checkout
- run: npm install
- run: |
npm run lint
npm run flow -- check
npm test -- --maxWorkers=2
test-website:
<<: *defaults
docker:
- image: circleci/node:8
steps:
- checkout
- run:
name: Install Dependencies
command: |
cd website
npm install --no-package-lock
- run:
name: Test Website
command: |
cd website
npm test
- run:
name: Test Build Static Website
command: cd website && node ./server/generate.js
deploy-website:
<<: *defaults
docker:
- image: circleci/node:8
steps:
- checkout
- run:
name: Install Dependencies
command: |
cd website
npm install --no-package-lock
- run:
name: Build and Deploy Static Website
command: |
if [[ $CIRCLE_PROJECT_USERNAME == "facebook" && -z $CI_PULL_REQUEST && -z $CIRCLE_PR_USERNAME ]]; then
git config --global user.email "reactjs-bot@users.noreply.github.com"
git config --global user.name "Website Deployment Script"
echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc
echo "Deploying website..."
cd website && GIT_USER=reactjs-bot npm run gh-pages
else
echo "Skipping deploy."
fi
build-js-bundle:
<<: *defaults
docker:
- image: circleci/node:8
steps:
- checkout
- run: npm install --no-package-lock
- run:
name: Build JavaScript Bundle
command: node local-cli/cli.js bundle --max-workers 2 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
- persist_to_workspace:
root: ReactAndroid/src/androidTest/assets/
paths:
- AndroidTestBundle.js
- store_artifacts:
path: ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
test-android:
<<: *defaults
docker:
- image: circleci/android:api-26-alpha
environment:
- TERM: "dumb"
- ADB_INSTALL_TIMEOUT: 10
- GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError"'
- ANDROID_NDK: '/opt/ndk/android-ndk-r10e'
- BUILD_THREADS: 2
steps:
- checkout
# CircleCI does not support interpolating env variables in the environment: step above.
# https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables
- run:
name: Configure PATH
command: |
echo 'export PATH=${ANDROID_NDK}:~/react-native/gradle-2.9/bin:~/buck/bin:$PATH' >> $BASH_ENV
source $BASH_ENV
# Configure dependencies
- run:
name: Install Build Dependencies
command: |
sudo apt-get update -y
sudo apt-get install ant autoconf automake g++ gcc libqt5widgets5 lib32z1 lib32stdc++6 make maven python-dev python3-dev qml-module-qtquick-controls qtdeclarative5-dev -y
- restore-cache: *restore-cache-android-packages
- run:
name: Install Android Packages
command: source scripts/circle-ci-android-setup.sh && getAndroidSDK
- save-cache: *save-cache-android-packages
- run:
name: Create Android Virtual Device
command: source scripts/circle-ci-android-setup.sh && createAVD
# Starting emulator in advance as it takes some time to boot.
- run:
name: Launch Android Virtual Device in Background
command: source scripts/circle-ci-android-setup.sh && launchAVD
background: true
# Continue configuring dependencies while AVD boots.
- run:
name: Create Android NDK Directory
command: |
if [[ ! -e /opt/ndk ]]; then
sudo mkdir /opt/ndk
fi
sudo chown ${USER:=$(/usr/bin/id -run)}:$USER /opt/ndk
- restore-cache: *restore-cache-ndk
- run:
name: Install Android NDK
command: source scripts/circle-ci-android-setup.sh && getAndroidNDK
- save-cache: *save-cache-ndk
- restore-cache: *restore-cache-buck
- run:
name: Install Buck
command: |
if [[ ! -e ~/buck ]]; then
git clone https://github.com/facebook/buck.git ~/buck --branch v2017.09.04.02 --depth=1
fi
cd ~/buck && ant
buck --version
- save-cache: *save-cache-buck
- run:
name: Install Node
command: |
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
- run: npm install
# - restore-cache: *restore-cache-watchman
# - run:
# name: Install Watchman Dependencies
# command: |
# sudo apt-get update -y
# sudo apt-get install libtool pkg-config -y
# - run:
# name: Install Watchman
# command: |
# if [[ ! -e ~/watchman ]]; then
# mkdir ~/watchman
# git clone https://github.com/facebook/watchman.git ~/watchman --branch v4.9.0 --depth=1
# cd ~/watchman
# ./autogen.sh
# ./configure
# make
# fi
# cd ~/watchman
# sudo make install
# - save-cache: *save-cache-watchman
- restore-cache: *restore-cache-buck-downloads
- run:
name: Download Android App Dependencies
command: |
buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
buck fetch ReactAndroid/src/main/java/com/facebook/react
buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
buck fetch ReactAndroid/src/test/...
buck fetch ReactAndroid/src/androidTest/...
./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
- save-cache: *save-cache-buck-downloads
- run:
name: Build Android App
command: |
buck build ReactAndroid/src/main/java/com/facebook/react
buck build ReactAndroid/src/main/java/com/facebook/react/shell
# Wait for AVD to finish booting before running tests
- run:
name: Wait for Android Virtual Device
command: source scripts/circle-ci-android-setup.sh && waitForAVD
# The JavaScript Bundle is built as part of the build-js-bundle workflow, and is required for instrumentation tests.
- attach_workspace:
at: ReactAndroid/src/androidTest/assets/
- run:
name: Check for JavaScript Bundle
command: |
if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then
echo "JavaScript bundle missing, verify build-js-bundle step"; exit 1;
else
echo "JavaScript bundle found.";
fi
# Tests
- run:
name: Compile Native Libs for Unit and Integration Tests
command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS -Pcom.android.build.threadPoolSize=1
no_output_timeout: 6m
- run:
name: Unit Tests
command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS
# Integration Tests
- run:
name: Build and Install Test APK
command: source scripts/circle-ci-android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS
# Failing test is expected
# - run:
# name: Run Installed APK with Tests
# command: node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests
# Should be disabled pending on https://our.intern.facebook.com/intern/tasks?t=16912142
# - run:
# name: Run Android End to End Tests
# command: source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2
# post (always runs)
- run:
name: Collect Test Results
command: |
mkdir -p ~/junit/
find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ~/junit/ \;
find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ~/junit/ \;
# TODO circle does not understand Buck's report, maybe need to transform xml slightly
# find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} ~/junit/ \;
when: always
- store_test_results:
path: ~/junit
- store_artifacts:
path: ~/junit
# Workflows enables us to run multiple jobs in parallel
workflows:
version: 2
test_node:
jobs:
- test-node-8
- test-node-6
# Node 4 tests are already failing on Circle 1.0
# - test-node-4
website:
jobs:
- test-website
- deploy-website:
requires:
- test-website
filters:
branches:
only:
- /.*-stable/
- master
test_android:
jobs:
- build-js-bundle:
filters:
branches:
ignore: gh-pages
- test-android:
requires:
- build-js-bundle
+2 -2
View File
@@ -46,8 +46,8 @@ suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
@@ -14,8 +14,9 @@
const ColorPropType = require('ColorPropType');
const NativeMethodsMixin = require('NativeMethodsMixin');
const Platform = require('Platform');
const React = require('React');
const ProgressBarAndroid = require('ProgressBarAndroid');
const PropTypes = require('prop-types');
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');
const ViewPropTypes = require('ViewPropTypes');
@@ -135,16 +136,20 @@ const ActivityIndicator = createReactClass({
break;
}
const nativeProps = {
...props,
style: sizeStyle,
styleAttr: 'Normal',
indeterminate: true,
};
return (
<View
onLayout={onLayout}
style={[styles.container, style]}>
<RCTActivityIndicator
{...props}
style={sizeStyle}
styleAttr="Normal"
indeterminate
/>
<View onLayout={onLayout} style={[styles.container, style]}>
{Platform.OS === 'ios' ? (
<RCTActivityIndicator {...nativeProps} />
) : (
<ProgressBarAndroid {...nativeProps} />
)}
</View>
);
}
@@ -169,18 +174,7 @@ if (Platform.OS === 'ios') {
var RCTActivityIndicator = requireNativeComponent(
'RCTActivityIndicatorView',
ActivityIndicator,
{nativeOnly: {activityIndicatorViewStyle: true}},
);
} else if (Platform.OS === 'android') {
var RCTActivityIndicator = requireNativeComponent(
'AndroidProgressBar',
ActivityIndicator,
// Ignore props that are specific to non inderterminate ProgressBar.
{nativeOnly: {
indeterminate: true,
progress: true,
styleAttr: true,
}},
{ nativeOnly: { activityIndicatorViewStyle: true } }
);
}
@@ -10,13 +10,14 @@
*/
'use strict';
const ActivityIndicator = require('ActivityIndicator');
const ColorPropType = require('ColorPropType');
const PropTypes = require('prop-types');
const React = require('React');
const ReactNative = require('ReactNative');
const ViewPropTypes = require('ViewPropTypes');
const requireNativeComponent = require('requireNativeComponent');
const STYLE_ATTRIBUTES = [
'Horizontal',
'Normal',
@@ -78,6 +79,10 @@ class ProgressBarAndroid extends ReactNative.NativeComponent {
* - LargeInverse
*/
styleAttr: PropTypes.oneOf(STYLE_ATTRIBUTES),
/**
* Whether to show the ProgressBar (true, the default) or hide it (false).
*/
animating: PropTypes.bool,
/**
* If the progress bar will show indeterminate progress. Note that this
* can only be false if styleAttr is Horizontal.
@@ -99,21 +104,23 @@ class ProgressBarAndroid extends ReactNative.NativeComponent {
static defaultProps = {
styleAttr: 'Normal',
indeterminate: true
indeterminate: true,
animating: true,
};
componentDidMount() {
if (this.props.indeterminate && this.props.styleAttr !== 'Horizontal') {
console.warn(
'Circular indeterminate `ProgressBarAndroid`' +
'is deprecated. Use `ActivityIndicator` instead.'
);
}
}
render() {
return <ActivityIndicator {...this.props} animating={true} />;
return <AndroidProgressBar {...this.props} />;
}
}
const AndroidProgressBar = requireNativeComponent(
'AndroidProgressBar',
ProgressBarAndroid,
{
nativeOnly: {
animating: true,
},
}
);
module.exports = ProgressBarAndroid;
@@ -659,10 +659,11 @@ const ScrollView = createReactClass({
} else if (Platform.OS === 'android') {
if (this.props.horizontal) {
ScrollViewClass = AndroidHorizontalScrollView;
ScrollContentContainerViewClass = AndroidHorizontalScrollContentView;
} else {
ScrollViewClass = AndroidScrollView;
ScrollContentContainerViewClass = View;
}
ScrollContentContainerViewClass = View;
}
invariant(
@@ -879,6 +880,7 @@ const styles = StyleSheet.create({
let nativeOnlyProps,
AndroidScrollView,
AndroidHorizontalScrollContentView,
AndroidHorizontalScrollView,
RCTScrollView,
RCTScrollContentView;
@@ -898,6 +900,9 @@ if (Platform.OS === 'android') {
(ScrollView: React.ComponentType<any>),
nativeOnlyProps
);
AndroidHorizontalScrollContentView = requireNativeComponent(
'AndroidHorizontalScrollContentView'
);
} else if (Platform.OS === 'ios') {
nativeOnlyProps = {
nativeOnly: {
+3 -19
View File
@@ -116,25 +116,9 @@ if (!global.__fbDisableExceptionsManager) {
ErrorUtils.setGlobalHandler(handleError);
}
const {PlatformConstants} = require('NativeModules');
if (PlatformConstants) {
const formatVersion = version =>
`${version.major}.${version.minor}.${version.patch}` +
(version.prerelease !== null ? `-${version.prerelease}` : '');
const ReactNativeVersion = require('ReactNativeVersion');
const nativeVersion = PlatformConstants.reactNativeVersion;
if (ReactNativeVersion.version.major !== nativeVersion.major ||
ReactNativeVersion.version.minor !== nativeVersion.minor) {
throw new Error(
`React Native version mismatch.\n\nJavaScript version: ${formatVersion(ReactNativeVersion.version)}\n` +
`Native version: ${formatVersion(nativeVersion)}\n\n` +
'Make sure that you have rebuilt the native code. If the problem persists ' +
'try clearing the watchman and packager caches with `watchman watch-del-all ' +
'&& react-native start --reset-cache`.'
);
}
}
// Check for compatibility between the JS and native code
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
ReactNativeVersionCheck.checkVersions();
// Set up collections
const _shouldPolyfillCollection = require('_shouldPolyfillES6Collection');
+1 -1
View File
@@ -14,7 +14,7 @@
exports.version = {
major: 0,
minor: 0,
minor: 50,
patch: 0,
prerelease: null,
};
+54
View File
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2017-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 ReactNativeVersionCheck
* @flow
* @format
*/
'use strict';
const {PlatformConstants} = require('NativeModules');
const ReactNativeVersion = require('ReactNativeVersion');
/**
* Checks that the version of this React Native JS is compatible with the native
* code, throwing an error if it isn't.
*
* The existence of this module is part of the public interface of React Native
* even though it is used only internally within React Native. React Native
* implementations for other platforms (ex: Windows) may override this module
* and rely on its existence as a separate module.
*/
exports.checkVersions = function checkVersions(): void {
if (!PlatformConstants) {
return;
}
const nativeVersion = PlatformConstants.reactNativeVersion;
if (
ReactNativeVersion.version.major !== nativeVersion.major ||
ReactNativeVersion.version.minor !== nativeVersion.minor
) {
throw new Error(
`React Native version mismatch.\n\nJavaScript version: ${_formatVersion(
ReactNativeVersion.version,
)}\n` +
`Native version: ${_formatVersion(nativeVersion)}\n\n` +
'Make sure that you have rebuilt the native code. If the problem ' +
'persists try clearing the Watchman and packager caches with ' +
'`watchman watch-del-all && react-native start --reset-cache`.',
);
}
};
function _formatVersion(version): string {
return (
`${version.major}.${version.minor}.${version.patch}` +
(version.prerelease !== null ? `-${version.prerelease}` : '')
);
}
@@ -0,0 +1,117 @@
/**
* Copyright (c) 2017-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.
*
* @format
*/
'use strict';
describe('checkVersion', () => {
describe('in development', () => {
_setDevelopmentModeForTests(true);
_defineCheckVersionTests();
});
describe('in production', () => {
_setDevelopmentModeForTests(false);
_defineCheckVersionTests();
});
});
function _setDevelopmentModeForTests(dev) {
let originalDev;
beforeAll(() => {
originalDev = global.__DEV__;
global.__DEV__ = dev;
});
afterAll(() => {
global.__DEV__ = originalDev;
});
}
function _defineCheckVersionTests() {
afterEach(() => {
jest.resetModules();
});
it('passes when all the versions are zero', () => {
_mockJsVersion(0, 0, 0);
_mockNativeVersion(0, 0, 0);
const ReactNativeVersion = require('ReactNativeVersion');
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(ReactNativeVersion).toMatchObject({
version: {major: 0, minor: 0, patch: 0, prerelease: null},
});
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
});
it('passes when the minor matches when the major is zero', () => {
_mockJsVersion(0, 1, 0);
_mockNativeVersion(0, 1, 0);
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
});
it("throws when the minor doesn't match when the major is zero", () => {
_mockJsVersion(0, 1, 0);
_mockNativeVersion(0, 2, 0);
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).toThrowError(
/React Native version mismatch/,
);
});
it("throws when the major doesn't match", () => {
_mockJsVersion(1, 0, 0);
_mockNativeVersion(2, 0, 0);
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).toThrowError(
/React Native version mismatch/,
);
});
it("doesn't throw if the patch doesn't match", () => {
_mockJsVersion(0, 1, 0);
_mockNativeVersion(0, 1, 2);
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
});
it("doesn't throw if the prerelease doesn't match", () => {
_mockJsVersion(0, 1, 0, 'beta.0');
_mockNativeVersion(0, 1, 0, 'alpha.1');
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
});
}
function _mockJsVersion(major = 0, minor = 0, patch = 0, prerelease = null) {
jest.doMock('ReactNativeVersion', () => ({
version: {major, minor, patch, prerelease},
}));
}
function _mockNativeVersion(
major = 0,
minor = 0,
patch = 0,
prerelease = null,
) {
jest.doMock('NativeModules', () => ({
PlatformConstants: {
reactNativeVersion: {major, minor, patch, prerelease},
},
}));
}
+5 -6
View File
@@ -23,7 +23,7 @@ import type {Props as VirtualizedSectionListProps} from 'VirtualizedSectionList'
type Item = any;
export type SectionBase<SectionItemT> = {
type SectionBase<SectionItemT> = {
/**
* The data for rendering items in this section.
*/
@@ -187,11 +187,10 @@ type OptionalProps<SectionT: SectionBase<any>> = {
legacyImplementation?: ?boolean,
};
export type Props<SectionT> = {
...$Exact<RequiredProps<SectionT>>,
...$Exact<OptionalProps<SectionT>>,
...$Exact<VirtualizedSectionListProps<SectionT>>,
};
export type Props<SectionT> = RequiredProps<SectionT> &
OptionalProps<SectionT> &
VirtualizedSectionListProps<SectionT>;
const defaultProps = {
...VirtualizedSectionList.defaultProps,
stickySectionHeadersEnabled: Platform.OS === 'ios',
+1 -3
View File
@@ -74,9 +74,7 @@ const SwipeableFlatListExample = createReactClass({
<View style={styles.row}>
<Image style={styles.rowIcon} source={item.icon} />
<View style={styles.rowData}>
<Text style={styles.rowDataText}>
{item.data}
</Text>
<Text style={styles.rowDataText}>{item.data}</Text>
</View>
</View>
);
+1 -1
View File
@@ -47,7 +47,7 @@ RCT_EXPORT_MODULE(PlatformConstants)
@"systemName": [device systemName],
@"interfaceIdiom": interfaceIdiom([device userInterfaceIdiom]),
@"isTesting": @(RCTRunningInTestEnvironment()),
@"reactNativeVersion": REACT_NATIVE_VERSION,
@"reactNativeVersion": RCT_REACT_NATIVE_VERSION,
};
}
+2 -2
View File
@@ -9,9 +9,9 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#define REACT_NATIVE_VERSION @{ \
#define RCT_REACT_NATIVE_VERSION @{ \
@"major": @(0), \
@"minor": @(0), \
@"minor": @(50), \
@"patch": @(0), \
@"prerelease": [NSNull null], \
}
+2 -2
View File
@@ -86,7 +86,7 @@ def rn_robolectric_test(name, srcs, vm_args = None, *args, **kwargs):
'-XX:MaxPermSize=620m',
'-Drobolectric.offline=true',
]
if os.path.isdir("/dev/shm") and not os.environ['CIRCLECI']:
if os.path.isdir("/dev/shm"):
extra_vm_args.append('-Djava.io.tmpdir=/dev/shm')
else:
extra_vm_args.append(
@@ -110,4 +110,4 @@ def rn_robolectric_test(name, srcs, vm_args = None, *args, **kwargs):
original_cxx_library = cxx_library
def cxx_library(allow_jni_merging=None, **kwargs):
original_cxx_library(**kwargs)
original_cxx_library(**kwargs)
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0-master
VERSION_NAME=0.50.0
GROUP=com.facebook.react
POM_NAME=ReactNative
@@ -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,
"minor", 50,
"patch", 0,
"prerelease", null);
}
@@ -61,6 +61,7 @@ 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;
import com.facebook.react.views.scroll.ReactHorizontalScrollContainerViewManager;
import com.facebook.react.views.scroll.ReactHorizontalScrollViewManager;
import com.facebook.react.views.scroll.ReactScrollViewManager;
import com.facebook.react.views.slider.ReactSliderManager;
@@ -315,6 +316,7 @@ public class MainReactPackage extends LazyReactPackage {
viewManagers.add(new ReactDrawerLayoutManager());
viewManagers.add(new ReactDropdownPickerManager());
viewManagers.add(new ReactHorizontalScrollViewManager());
viewManagers.add(new ReactHorizontalScrollContainerViewManager());
viewManagers.add(new ReactProgressBarViewManager());
viewManagers.add(new ReactScrollViewManager());
viewManagers.add(new ReactSliderManager());
@@ -16,6 +16,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/module/annotations:annotations"),
react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"),
react_native_target("java/com/facebook/react/touch:touch"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
@@ -0,0 +1,40 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.views.scroll;
import android.content.Context;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import com.facebook.react.modules.i18nmanager.I18nUtil;
/** Container of Horizontal scrollViews that supports RTL scrolling. */
public class ReactHorizontalScrollContainerView extends ViewGroup {
private int mLayoutDirection;
public ReactHorizontalScrollContainerView(Context context) {
super(context);
mLayoutDirection =
I18nUtil.getInstance().isRTL(context) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mLayoutDirection == LAYOUT_DIRECTION_RTL) {
// When the layout direction is RTL, we expect Yoga to give us a layout
// that extends off the screen to the left so we re-center it with left=0
int newLeft = 0;
int width = right - left;
int newRight = newLeft + width;
setLeft(newLeft);
setRight(newRight);
// Fix the ScrollX position when using RTL language
int offsetX = computeHorizontalScrollRange() - getScrollX();
// Call with the present values in order to re-layout if necessary
HorizontalScrollView parent = (HorizontalScrollView) getParent();
parent.scrollTo(offsetX, parent.getScrollY());
}
}
}
@@ -0,0 +1,27 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.views.scroll;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
/** View manager for {@link ReactHorizontalScrollContainerView} components. */
@ReactModule(name = ReactHorizontalScrollContainerViewManager.REACT_CLASS)
public class ReactHorizontalScrollContainerViewManager
extends ViewGroupManager<ReactHorizontalScrollContainerView> {
protected static final String REACT_CLASS = "AndroidHorizontalScrollContentView";
public ReactHorizontalScrollContainerViewManager() {}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public ReactHorizontalScrollContainerView createViewInstance(ThemedReactContext context) {
return new ReactHorizontalScrollContainerView(context);
}
}
+122
View File
@@ -0,0 +1,122 @@
general:
branches:
ignore:
- gh-pages # list of branches to ignore
machine:
node:
version: 6.2.0
environment:
PATH: "~/$CIRCLE_PROJECT_REPONAME/gradle-2.9/bin:/home/ubuntu/buck/bin:$PATH"
TERM: "dumb"
ADB_INSTALL_TIMEOUT: 10
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError"'
java:
version: 'oraclejdk8'
dependencies:
override:
# BUCK and android
- if [[ ! -e /home/ubuntu/buck ]]; then git clone https://github.com/facebook/buck.git /home/ubuntu/buck; fi
- cd /home/ubuntu/buck && ant
- buck --version
- source scripts/circle-ci-android-setup.sh && getAndroidSDK
- buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
- buck fetch ReactAndroid/src/main/java/com/facebook/react
- buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
- buck fetch ReactAndroid/src/test/...
- buck fetch ReactAndroid/src/androidTest/...
- ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
# CIRCLE_NPM_TOKEN is in React Native project settings in Circle CI.
# It was generated for bestander user, easy to replace with anyone's else
- echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
- npm config set spin=false
- npm config set progress=false
- npm install
# for eslint bot
- npm install github@0.2.4
# for website, danger
- cd website && npm install
- cd danger && npm install
cache_directories:
- "ReactAndroid/build/downloads"
- "/home/ubuntu/buck"
- "website/node_modules"
- "node_modules"
- "danger/node_modules"
test:
pre:
# starting emulator in advance because it takes very long to boot
- $ANDROID_HOME/tools/emulator -avd testAVD -no-skin -no-audio -no-window:
background: true
- source scripts/circle-ci-android-setup.sh && waitForAVD
override:
# Run Danger against PRs. This GitHub token grants public_repo access scope. The associated account has no privileged access to the React Native repo. The token must be split in this manner to avoid revocation by GitHub.
#- cd danger && DANGER_GITHUB_API_TOKEN="e622517d9f1136ea8900""07c6373666312cdfaa69" npm # run danger
# eslint bot. This GitHub token grants public_repo access scope. The token must be split in this manner to avoid revocation by GitHub.
- cat <(echo eslint; npm run lint --silent -- --format=json; echo flow; npm run flow --silent -- check --json) | GITHUB_TOKEN="af6ef0d15709bc91d""06a6217a5a826a226fb57b7" CI_USER=$CIRCLE_PROJECT_USERNAME CI_REPO=$CIRCLE_PROJECT_REPONAME PULL_REQUEST_NUMBER=$CIRCLE_PR_NUMBER node bots/code-analysis-bot.js
- npm run lint
# JS tests for dependencies installed with npm3
# Commenting out Flow tests
# - npm run flow -- check
- npm test -- --maxWorkers=1
# build app
- buck build ReactAndroid/src/main/java/com/facebook/react
- buck build ReactAndroid/src/main/java/com/facebook/react/shell
# compile native libs with Gradle script, we need bridge for unit and
# integration tests
- ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=1 -Pcom.android.build.threadPoolSize=1:
timeout: 360
# unit tests
- buck test ReactAndroid/src/test/... --config build.threads=1
# integration tests
# build JS bundle for instrumentation tests
- node local-cli/cli.js bundle --max-workers 1 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
# build test APK
# Commented out due to test failures. Please uncomment the next line once these have been fixed. See Issue #15726.
# - buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
# run installed apk with tests
# - node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests
# Android e2e test
# disabled pending on https://our.intern.facebook.com/intern/tasks?t=16912142
# - source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2
# testing docs generation
- cd website && npm test
- cd website && node ./server/generate.js
# if website can be built, deploy regardless of test failures
- if [[ ($CIRCLE_BRANCH =~ .*-stable || $CIRCLE_BRANCH == "master") && $CIRCLE_PROJECT_USERNAME == "facebook" && -z $CI_PULL_REQUEST && -z $CIRCLE_PR_USERNAME ]]; then git config --global user.email "reactjs-bot@users.noreply.github.com" && git config --global user.name "Website Deployment Script" && echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc && cd website && GIT_USER=reactjs-bot npm run gh-pages; fi
post:
# copy test report for Circle CI to display
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
- find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
- find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
# TODO circle does not understand Buck's report, maybe need to transform xml slightly
#- find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
deployment:
stable:
branch: [/.*-stable/, /master/]
owner: facebook
commands:
- git config --global user.email "reactjs-bot@users.noreply.github.com"
- git config --global user.name "Website Deployment Script"
- echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc
# publish to npm
- node ./scripts/publish-npm.js
experimental:
notify:
branches:
only:
- master
- /.*-stable/
+6 -2
View File
@@ -35,8 +35,8 @@ Install a library with native dependencies:
$ npm install <library-with-native-dependencies> --save
```
**Note:** _`--save` or `--save-dev` flag is very important for this step. React Native will link
your libs based on `dependencies` and `devDependencies` in your `package.json` file._
> ***Note:*** `--save` or `--save-dev` flag is very important for this step. React Native will link
your libs based on `dependencies` and `devDependencies` in your `package.json` file.
#### Step 2
@@ -47,6 +47,10 @@ $ react-native link
Done! All libraries with native dependencies should be successfully linked to your iOS/Android project.
> ***Note:*** If your iOS project is using CocoaPods (contains `Podfile`) and linked library has `podspec` file,
then `react-native link` will link library using Podfile. To support non-trivial Podfiles
add `# Add new pods below this line` comment to places where you expect pods to be added.
### Manual linking
#### Step 1
@@ -94,6 +94,9 @@ fs.readFileSync.mockImplementation(function(filepath, encoding) {
if (isDirNode(node)) {
throw new Error('Error readFileSync a dir: ' + filepath);
}
if (Buffer.isBuffer(node) && typeof encoding !== 'undefined') {
return node.toString();
}
return node;
});
@@ -133,7 +136,12 @@ function fsError(code, message) {
}
function isDirNode(node) {
return node && typeof node === 'object' && node.SYMLINK == null;
return (
node &&
typeof node === 'object' &&
node.SYMLINK == null &&
Buffer.isBuffer(node) === false
);
}
function readlinkSync(filepath) {
+1 -1
View File
@@ -1,4 +1,4 @@
const fs = require('fs');
const fs = require.requireActual('fs');
const path = require('path');
const manifest = fs.readFileSync(path.join(__dirname, './files/AndroidManifest.xml'));
+1 -1
View File
@@ -1,4 +1,4 @@
const fs = require('fs');
const fs = require.requireActual('fs');
const path = require('path');
const android = require('./android');
+6 -1
View File
@@ -1,10 +1,11 @@
const fs = require('fs');
const fs = require.requireActual('fs');
const path = require('path');
exports.valid = {
'demoProject.xcodeproj': {
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
},
'TestPod.podspec': 'empty'
};
exports.validTestName = {
@@ -12,3 +13,7 @@ exports.validTestName = {
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
},
};
exports.pod = {
'TestPod.podspec': 'empty'
};
+7 -3
View File
@@ -1,11 +1,10 @@
const fs = require('fs');
const path = require('path');
const android = require('./android');
const ios = require('./ios');
const flat = {
android: android.valid,
ios: ios.valid,
Podfile: 'empty'
};
const nested = {
@@ -21,4 +20,9 @@ const withExamples = {
android: android.valid,
};
module.exports = { flat, nested, withExamples };
const withPods = {
Podfile: 'content',
ios: ios.pod
};
module.exports = { flat, nested, withExamples, withPods };
@@ -11,13 +11,15 @@
'use strict';
jest.mock('fs');
const fs = require('fs');
const findAndroidAppFolder = require('../../android/findAndroidAppFolder');
const mockFS = require('mock-fs');
const mocks = require('../../__fixtures__/android');
describe('android::findAndroidAppFolder', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
nested: {
android: {
@@ -31,15 +33,11 @@ describe('android::findAndroidAppFolder', () => {
});
it('returns an android app folder if it exists in the given folder', () => {
expect(findAndroidAppFolder('flat')).toBe('android');
expect(findAndroidAppFolder('nested')).toBe('android/app');
expect(findAndroidAppFolder('/flat')).toBe('android');
expect(findAndroidAppFolder('/nested')).toBe('android/app');
});
it('returns `null` if there is no android app folder', () => {
expect(findAndroidAppFolder('empty')).toBeNull();
});
afterAll(() => {
mockFS.restore();
expect(findAndroidAppFolder('/empty')).toBeNull();
});
});
@@ -11,13 +11,15 @@
'use strict';
jest.mock('fs');
const findManifest = require('../../android/findManifest');
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
describe('android::findManifest', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
flat: {
android: mocks.valid,
@@ -26,14 +28,10 @@ describe('android::findManifest', () => {
});
it('returns a manifest path if file exists in the folder', () => {
expect(typeof findManifest('flat')).toBe('string');
expect(typeof findManifest('/flat')).toBe('string');
});
it('returns `null` if there is no manifest in the folder', () => {
expect(findManifest('empty')).toBeNull();
});
afterAll(() => {
mockFS.restore();
expect(findManifest('/empty')).toBeNull();
});
});
@@ -11,13 +11,15 @@
'use strict';
jest.mock('fs');
const findPackageClassName = require('../../android/findPackageClassName');
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
describe('android::findPackageClassName', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
flatJava: {
android: mocks.valid,
@@ -29,22 +31,20 @@ describe('android::findPackageClassName', () => {
});
it('returns manifest content if file exists in the folder', () => {
expect(typeof findPackageClassName('flatJava')).toBe('string');
expect(typeof findPackageClassName('/flatJava')).toBe('string');
});
it('returns the name of the java class implementing ReactPackage', () => {
expect(findPackageClassName('flatJava')).toBe('SomeExampleJavaPackage');
expect(findPackageClassName('/flatJava')).toBe('SomeExampleJavaPackage');
});
it('returns the name of the kotlin class implementing ReactPackage', () => {
expect(findPackageClassName('flatKotlin')).toBe('SomeExampleKotlinPackage');
expect(findPackageClassName('/flatKotlin')).toBe(
'SomeExampleKotlinPackage',
);
});
it('returns `null` if there are no matches', () => {
expect(findPackageClassName('empty')).toBeNull();
});
afterAll(() => {
mockFS.restore();
expect(findPackageClassName('/empty')).toBeNull();
});
});
@@ -11,15 +11,17 @@
'use strict';
jest.mock('fs');
const getDependencyConfig = require('../../android').dependencyConfig;
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
const userConfig = {};
describe('android::getDependencyConfig', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
nested: {
android: {
@@ -38,27 +40,23 @@ describe('android::getDependencyConfig', () => {
});
it('returns an object with android project configuration', () => {
expect(getDependencyConfig('nested', userConfig)).not.toBeNull();
expect(typeof getDependencyConfig('nested', userConfig)).toBe('object');
expect(getDependencyConfig('/nested', userConfig)).not.toBeNull();
expect(typeof getDependencyConfig('/nested', userConfig)).toBe('object');
});
it('returns `null` if manifest file has not been found', () => {
expect(getDependencyConfig('empty', userConfig)).toBeNull();
expect(getDependencyConfig('/empty', userConfig)).toBeNull();
});
it('returns `null` if android project was not found', () => {
expect(getDependencyConfig('empty', userConfig)).toBeNull();
expect(getDependencyConfig('/empty', userConfig)).toBeNull();
});
it('returns `null` if android project does not contain ReactPackage', () => {
expect(getDependencyConfig('noPackage', userConfig)).toBeNull();
expect(getDependencyConfig('/noPackage', userConfig)).toBeNull();
});
it('returns `null` if it cannot find a packageClassName', () => {
expect(getDependencyConfig('corrupted', userConfig)).toBeNull();
});
afterAll(() => {
mockFS.restore();
expect(getDependencyConfig('/corrupted', userConfig)).toBeNull();
});
});
@@ -11,13 +11,15 @@
'use strict';
jest.mock('fs');
const getProjectConfig = require('../../android').projectConfig;
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
describe('android::getProjectConfig', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
nested: {
android: {
@@ -38,7 +40,7 @@ describe('android::getProjectConfig', () => {
it("returns `null` if manifest file hasn't been found", () => {
const userConfig = {};
const folder = 'noManifest';
const folder = '/noManifest';
expect(getProjectConfig(folder, userConfig)).toBeNull();
});
@@ -46,7 +48,7 @@ describe('android::getProjectConfig', () => {
describe('returns an object with android project configuration for', () => {
it('nested structure', () => {
const userConfig = {};
const folder = 'nested';
const folder = '/nested';
expect(getProjectConfig(folder, userConfig)).not.toBeNull();
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
@@ -54,7 +56,7 @@ describe('android::getProjectConfig', () => {
it('flat structure', () => {
const userConfig = {};
const folder = 'flat';
const folder = '/flat';
expect(getProjectConfig(folder, userConfig)).not.toBeNull();
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
@@ -64,7 +66,7 @@ describe('android::getProjectConfig', () => {
const userConfig = {
manifestPath: 'src/main/AndroidManifest.xml',
};
const folder = 'multiple';
const folder = '/multiple';
expect(getProjectConfig(folder, userConfig)).not.toBeNull();
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
@@ -73,12 +75,8 @@ describe('android::getProjectConfig', () => {
it('should return `null` if android project was not found', () => {
const userConfig = {};
const folder = 'empty';
const folder = '/empty';
expect(getProjectConfig(folder, userConfig)).toBeNull();
});
afterAll(() => {
mockFS.restore();
});
});
@@ -11,14 +11,16 @@
'use strict';
jest.mock('fs');
const findManifest = require('../../android/findManifest');
const readManifest = require('../../android/readManifest');
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
describe('android::readManifest', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
nested: {
android: {
@@ -29,19 +31,15 @@ describe('android::readManifest', () => {
});
it('returns manifest content if file exists in the folder', () => {
const manifestPath = findManifest('nested');
const manifestPath = findManifest('/nested');
expect(readManifest(manifestPath)).not.toBeNull();
expect(typeof readManifest(manifestPath)).toBe('object');
});
it('throws an error if there is no manifest in the folder', () => {
const fakeManifestPath = findManifest('empty');
const fakeManifestPath = findManifest('/empty');
expect(() => {
readManifest(fakeManifestPath);
}).toThrow();
});
afterAll(() => {
mockFS.restore();
});
});
+7 -9
View File
@@ -11,24 +11,26 @@
'use strict';
jest.mock('fs');
const findAssets = require('../findAssets');
const dependencies = require('../__fixtures__/dependencies');
const mockFs = require('mock-fs');
const fs = require('fs');
describe('findAssets', () => {
beforeEach(() => {
mockFs({testDir: dependencies.withAssets});
fs.__setMockFilesystem({testDir: dependencies.withAssets});
});
it('returns an array of all files in given folders', () => {
const assets = findAssets('testDir', ['fonts', 'images']);
const assets = findAssets('/testDir', ['fonts', 'images']);
expect(Array.isArray(assets)).toBeTruthy();
expect(assets).toHaveLength(3);
});
it('prepends assets paths with the folder path', () => {
const assets = findAssets('testDir', ['fonts', 'images']);
const assets = findAssets('/testDir', ['fonts', 'images']);
assets.forEach(assetPath => {
expect(assetPath).toContain('testDir');
@@ -36,10 +38,6 @@ describe('findAssets', () => {
});
it('returns an empty array if given assets are null', () => {
expect(findAssets('testDir', null)).toHaveLength(0);
});
afterEach(() => {
mockFs.restore();
expect(findAssets('/testDir', null)).toHaveLength(0);
});
});
@@ -0,0 +1,20 @@
'use strict';
jest.mock('fs');
const findPodfilePath = require('../../ios/findPodfilePath');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
const ios = require('../../__fixtures__/ios');
describe('ios::findPodfilePath', () => {
it('returns null if there is no Podfile', () => {
fs.__setMockFilesystem(ios.valid);
expect(findPodfilePath('')).toBeNull();
});
it('returns Podfile path if it exists', () => {
fs.__setMockFilesystem(projects.withPods);
expect(findPodfilePath('/ios')).toContain('Podfile');
});
});
@@ -0,0 +1,44 @@
'use strict';
jest.mock('fs');
const findPodspecName = require('../../ios/findPodspecName');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
const ios = require('../../__fixtures__/ios');
describe('ios::findPodspecName', () => {
it('returns null if there is not podspec file', () => {
fs.__setMockFilesystem(projects.flat);
expect(findPodspecName('')).toBeNull();
});
it('returns podspec name if only one exists', () => {
fs.__setMockFilesystem(ios.pod);
expect(findPodspecName('/')).toBe('TestPod');
});
it('returns podspec name that match packet directory', () => {
fs.__setMockFilesystem({
user: {
PacketName: {
'Another.podspec': 'empty',
'PacketName.podspec': 'empty'
}
}
});
expect(findPodspecName('/user/PacketName')).toBe('PacketName');
});
it('returns first podspec name if not match in directory', () => {
fs.__setMockFilesystem({
user: {
packet: {
'Another.podspec': 'empty',
'PacketName.podspec': 'empty'
}
}
});
expect(findPodspecName('/user/packet')).toBe('Another');
});
});
@@ -11,49 +11,51 @@
'use strict';
jest.mock('fs');
const findProject = require('../../ios/findProject');
const mockFS = require('mock-fs');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
const ios = require('../../__fixtures__/ios');
describe('ios::findProject', () => {
it('returns path to xcodeproj if found', () => {
mockFS(projects.flat);
expect(findProject('')).not.toBeNull();
fs.__setMockFilesystem(projects.flat);
expect(findProject('/')).not.toBeNull();
});
it('returns null if there are no projects', () => {
mockFS({testDir: projects});
expect(findProject('')).toBeNull();
fs.__setMockFilesystem({testDir: projects});
expect(findProject('/')).toBeNull();
});
it('returns ios project regardless of its name', () => {
mockFS({ios: ios.validTestName});
expect(findProject('')).not.toBeNull();
fs.__setMockFilesystem({ios: ios.validTestName});
expect(findProject('/')).not.toBeNull();
});
it('ignores node_modules', () => {
mockFS({node_modules: projects.flat});
expect(findProject('')).toBeNull();
fs.__setMockFilesystem({node_modules: projects.flat});
expect(findProject('/')).toBeNull();
});
it('ignores Pods', () => {
mockFS({Pods: projects.flat});
expect(findProject('')).toBeNull();
fs.__setMockFilesystem({Pods: projects.flat});
expect(findProject('/')).toBeNull();
});
it('ignores Pods inside `ios` folder', () => {
mockFS({
fs.__setMockFilesystem({
ios: {
Pods: projects.flat,
DemoApp: projects.flat.ios,
},
});
expect(findProject('')).toBe('ios/DemoApp/demoProject.xcodeproj');
expect(findProject('/')).toBe('ios/DemoApp/demoProject.xcodeproj');
});
it('ignores xcodeproj from example folders', () => {
mockFS({
fs.__setMockFilesystem({
examples: projects.flat,
Examples: projects.flat,
example: projects.flat,
@@ -61,11 +63,11 @@ describe('ios::findProject', () => {
Zpp: projects.flat,
});
expect(findProject('').toLowerCase()).not.toContain('example');
expect(findProject('/').toLowerCase()).not.toContain('example');
});
it('ignores xcodeproj from sample folders', () => {
mockFS({
fs.__setMockFilesystem({
samples: projects.flat,
Samples: projects.flat,
sample: projects.flat,
@@ -73,11 +75,11 @@ describe('ios::findProject', () => {
Zpp: projects.flat,
});
expect(findProject('').toLowerCase()).not.toContain('sample');
expect(findProject('/').toLowerCase()).not.toContain('sample');
});
it('ignores xcodeproj from test folders at any level', () => {
mockFS({
fs.__setMockFilesystem({
test: projects.flat,
IntegrationTests: projects.flat,
tests: projects.flat,
@@ -87,10 +89,6 @@ describe('ios::findProject', () => {
},
});
expect(findProject('').toLowerCase()).not.toContain('test');
});
afterEach(() => {
mockFS.restore();
expect(findProject('/').toLowerCase()).not.toContain('test');
});
});
@@ -11,32 +11,34 @@
'use strict';
jest.mock('fs');
const getProjectConfig = require('../../ios').projectConfig;
const mockFS = require('mock-fs');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
describe('ios::getProjectConfig', () => {
const userConfig = {};
beforeEach(() => {
mockFS({testDir: projects});
fs.__setMockFilesystem({testDir: projects});
});
it('returns an object with ios project configuration', () => {
const folder = 'testDir/nested';
const folder = '/testDir/nested';
expect(getProjectConfig(folder, userConfig)).not.toBeNull();
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
});
it('returns `null` if ios project was not found', () => {
const folder = 'testDir/empty';
const folder = '/testDir/empty';
expect(getProjectConfig(folder, userConfig)).toBeNull();
});
it('returns normalized shared library names', () => {
const projectConfig = getProjectConfig('testDir/nested', {
const projectConfig = getProjectConfig('/testDir/nested', {
sharedLibraries: ['libc++', 'libz.tbd', 'HealthKit', 'HomeKit.framework'],
});
@@ -47,8 +49,4 @@ describe('ios::getProjectConfig', () => {
'HomeKit.framework',
]);
});
afterEach(() => {
mockFS.restore();
});
});
+11
View File
@@ -0,0 +1,11 @@
'use strict';
const fs = require('fs');
const path = require('path');
module.exports = function findPodfilePath(projectFolder) {
const podFilePath = path.join(projectFolder, '..', 'Podfile');
const podFileExists = fs.existsSync(podFilePath);
return podFileExists ? podFilePath : null;
};
+28
View File
@@ -0,0 +1,28 @@
'use strict';
const glob = require('glob');
const path = require('path');
module.exports = function findPodspecName(folder) {
const podspecs = glob.sync('*.podspec', { cwd: folder });
let podspecFile = null;
if (podspecs.length === 0) {
return null;
}
else if (podspecs.length === 1) {
podspecFile = podspecs[0];
}
else {
const folderParts = folder.split(path.sep);
const currentFolder = folderParts[folderParts.length - 1];
const toSelect = podspecs.indexOf(currentFolder + '.podspec');
if (toSelect === -1) {
podspecFile = podspecs[0];
}
else {
podspecFile = podspecs[toSelect];
}
}
return podspecFile.replace('.podspec', '');
};
+4
View File
@@ -9,6 +9,8 @@
'use strict';
const findProject = require('./findProject');
const findPodfilePath = require('./findPodfilePath');
const findPodspecName = require('./findPodspecName');
const path = require('path');
/**
@@ -44,6 +46,8 @@ exports.projectConfig = function projectConfigIOS(folder, userConfig) {
sourceDir: path.dirname(projectPath),
folder: folder,
pbxprojPath: path.join(projectPath, 'project.pbxproj'),
podfile: findPodfilePath(projectPath),
podspec: findPodspecName(folder),
projectPath: projectPath,
projectName: path.basename(projectPath),
libraryFolder: userConfig.libraryFolder || 'Libraries',
@@ -0,0 +1,8 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'Testing' do
pod 'TestPod', '~> 3.1'
# test should point to this line
end
@@ -0,0 +1,30 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'none' do
pod 'React',
:path => "../node_modules/react-native",
:subspecs => [
"Core",
"ART",
"RCTActionSheet",
"RCTAnimation",
"RCTCameraRoll",
"RCTGeolocation",
"RCTImage",
"RCTNetwork",
"RCTText",
"RCTVibration",
"RCTWebSocket",
"DevSupport",
"BatchedBridge"
]
pod 'Yoga',
:path => "../node_modules/react-native/ReactCommon/yoga"
# test should point to this line
post_install do |installer|
end
end
@@ -0,0 +1,34 @@
source 'https://github.com/CocoaPods/Specs.git'
# platform :ios, '9.0'
target 'None' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Your 'node_modules' directory is probably in the root of your project, # but if not, adjust the `:path` accordingly
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'BatchedBridge',
'RCTImage',
'RCTWebSocket', # needed for debugging
# Add any other subspecs you want to use in your project
]
# Add new pods below this line
# test should point to this line
target 'NoneTests' do
inherit! :search_paths
# Pods for testing
end
end
target 'Second' do
target 'NoneUITests' do
inherit! :search_paths
# Add new pods below this line
end
end
@@ -0,0 +1,32 @@
source 'https://github.com/CocoaPods/Specs.git'
# platform :ios, '9.0'
target 'None' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Your 'node_modules' directory is probably in the root of your project, # but if not, adjust the `:path` accordingly
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'BatchedBridge',
'RCTImage',
'RCTWebSocket', # needed for debugging
# Add any other subspecs you want to use in your project
]
# Explicitly include Yoga if you are using RN >= 0.42.0
pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga"
# test should point to this line
target 'NoneTests' do
inherit! :search_paths
# Pods for testing
end
target 'NoneUITests' do
inherit! :search_paths
# Pods for testing
end
end
@@ -0,0 +1,30 @@
'use strict';
const path = require('path');
const findLineToAddPod = require('../../pods/findLineToAddPod');
const readPodfile = require('../../pods/readPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
const LINE_AFTER_TARGET_IN_TEST_PODFILE = 4;
describe('pods::findLineToAddPod', () => {
it('returns null if file is not Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toBeNull();
});
it('returns correct line number for Simple Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 7, indentation: 2 });
});
it('returns correct line number for Podfile with target', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithTarget'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 21, indentation: 2 });
});
it('returns correct line number for Podfile with function', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithFunction'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 26, indentation: 2 });
});
});
@@ -0,0 +1,26 @@
'use strict';
const path = require('path');
const readPodfile = require('../../pods/readPodfile');
const findMarkedLinesInPodfile = require('../../pods/findMarkedLinesInPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
const LINE_AFTER_TARGET_IN_TEST_PODFILE = 4;
describe('pods::findMarkedLinesInPodfile', () => {
it('returns empty array if file is not Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
expect(findMarkedLinesInPodfile(podfile)).toEqual([]);
});
it('returns empty array for Simple Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findMarkedLinesInPodfile(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual([]);
});
it('returns correct line numbers for Podfile with marker', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithMarkers'));
const expectedObject = [{ line: 18, indentation: 2 }, { line: 31, indentation: 4 }];
expect(findMarkedLinesInPodfile(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual(expectedObject);
});
});
@@ -0,0 +1,24 @@
'use strict';
const path = require('path');
const findPodTargetLine = require('../../pods/findPodTargetLine');
const readPodfile = require('../../pods/readPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
describe('pods::findPodTargetLine', () => {
it('returns null if file is not Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
expect(findPodTargetLine(podfile, 'name')).toBeNull();
});
it('returns null if there is not matching project name', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findPodTargetLine(podfile, 'invalidName')).toBeNull();
});
it('returns null if there is not matching project name', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findPodTargetLine(podfile, 'Testing')).toBe(4);
});
});
@@ -0,0 +1,32 @@
'use strict';
const path = require('path');
const isInstalled = require('../../pods/isInstalled');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
describe('pods::isInstalled', () => {
it('returns false if pod is missing', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileSimple') };
const podspecName = { podspec: 'NotExisting' };
expect(isInstalled(project, podspecName)).toBe(false);
});
it('returns true for existing pod with version number', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileSimple') };
const podspecName = { podspec: 'TestPod' };
expect(isInstalled(project, podspecName)).toBe(true);
});
it('returns true for existing pod with path', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileWithTarget') };
const podspecName = { podspec: 'Yoga' };
expect(isInstalled(project, podspecName)).toBe(true);
});
it('returns true for existing pod with multiline definition', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileWithFunction') };
const podspecName = { podspec: 'React' };
expect(isInstalled(project, podspecName)).toBe(true);
});
});
@@ -0,0 +1,35 @@
'use strict';
const path = require('path');
const removePodEntry = require('../../pods/removePodEntry');
const readPodfile = require('../../pods/readPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
describe('pods::removePodEntry', () => {
it('should remove one line from Podfile with TestPod', () => {
const { podfileContent, podLinesCount } = readTestPodFile('PodfileSimple');
const podFileWithRemoved = removePodEntry(podfileContent, 'TestPod');
const newLineCount = podFileWithRemoved.split('\n').length;
expect(newLineCount).toBe(podLinesCount - 1);
});
it('should remove one line from Podfile with Yoga', () => {
const { podfileContent, podLinesCount } = readTestPodFile('PodfileWithTarget');
const podFileWithRemoved = removePodEntry(podfileContent, 'Yoga');
const newLineCount = podFileWithRemoved.split('\n').length;
expect(newLineCount).toBe(podLinesCount - 1);
});
it('should remove whole reference to React pod from Podfile', () => {
const { podfileContent, podLinesCount } = readTestPodFile('PodfileWithTarget');
const podFileWithRemoved = removePodEntry(podfileContent, 'React');
const newLineCount = podFileWithRemoved.split('\n').length;
expect(newLineCount).toBe(podLinesCount - 9);
});
});
function readTestPodFile(fileName) {
const podfileLines = readPodfile(path.join(PODFILES_PATH, fileName));
return { podfileContent: podfileLines.join('\n'), podLinesCount: podfileLines.length };
}
+9 -5
View File
@@ -29,9 +29,11 @@ const promiseWaterfall = require('./promiseWaterfall');
const registerDependencyAndroid = require('./android/registerNativeModule');
const registerDependencyWindows = require('./windows/registerNativeModule');
const registerDependencyIOS = require('./ios/registerNativeModule');
const registerDependencyPods = require('./pods/registerNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledWindows = require('./windows/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');
@@ -106,17 +108,19 @@ const linkDependencyIOS = (iOSProject, dependency) => {
return;
}
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios);
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`);
registerDependencyIOS(dependency.config.ios, iOSProject);
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`);
};
+21
View File
@@ -0,0 +1,21 @@
'use strict';
module.exports = function addPodEntry(podLines, linesToAddEntry, podName, nodePath) {
const newEntry = `pod '${podName}', :path => '../node_modules/${nodePath}'\n`;
if (!linesToAddEntry) {
return;
} else if (Array.isArray(linesToAddEntry)) {
linesToAddEntry.map(({ line, indentation }, idx) =>
podLines.splice(line + idx, 0, getLineToAdd(newEntry, indentation))
);
} else {
const { line, indentation } = linesToAddEntry;
podLines.splice(line, 0, getLineToAdd(newEntry, indentation));
}
};
function getLineToAdd(newEntry, indentation) {
const spaces = Array(indentation + 1).join(' ');
return spaces + newEntry;
}
+24
View File
@@ -0,0 +1,24 @@
'use strict';
module.exports = function findLineToAddPod(podLines, firstTargetLine) {
// match line with new target: target 'project_name' do (most likely target inside podfile main target)
const nextTarget = /target (\'|\")\w+(\'|\") do/g;
// match line that has only 'end' (if we don't catch new target or function, this would mean this is end of current target)
const endOfCurrentTarget = /^\s*end\s*$/g;
// match function definition, like: post_install do |installer| (some Podfiles have function defined inside main target
const functionDefinition = /^\s*[a-z_]+\s+do(\s+\|[a-z]+\|)?/g;
for (let i = firstTargetLine, len = podLines.length; i < len; i++) {
const matchNextConstruct = podLines[i].match(nextTarget) || podLines[i].match(functionDefinition);
const matchEnd = podLines[i].match(endOfCurrentTarget);
if (matchNextConstruct || matchEnd) {
const firstNonSpaceCharacter = podLines[i].search(/\S/);
return {
indentation: firstNonSpaceCharacter + (matchEnd ? 2 : 0),
line: i
};
}
}
return null;
};
@@ -0,0 +1,12 @@
'use strict';
const MARKER_TEXT = '# Add new pods below this line';
module.exports = function findMarkedLinesInPodfile(podLines) {
const result = [];
for (let i = 0, len = podLines.length; i < len; i++) {
if (podLines[i].includes(MARKER_TEXT)) {
result.push({ line: i + 1, indentation: podLines[i].indexOf('#') });
}
}
return result;
};
+14
View File
@@ -0,0 +1,14 @@
'use strict';
module.exports = function findPodTargetLine(podLines, projectName) {
const targetName = projectName.replace('.xcodeproj', '');
//match first target definition in file: target 'target_name' do
const targetRegex = new RegExp('target (\'|\")' + targetName + '(\'|\") do', 'g');
for (let i = 0, len = podLines.length; i < len; i++) {
const match = podLines[i].match(targetRegex);
if (match) {
return i + 1;
}
}
return null;
};
+19
View File
@@ -0,0 +1,19 @@
'use strict';
const readPodfile = require('./readPodfile');
module.exports = function isInstalled(iOSProject, dependencyConfig) {
if (!iOSProject.podfile) {
return false;
}
// match line with pod declaration: pod 'dependencyPodName' (other possible parameters of pod are ignored)
const dependencyRegExp = new RegExp('pod\\s+(\'|\")' + dependencyConfig.podspec + '(\'|\")', 'g');
const podLines = readPodfile(iOSProject.podfile);
for (let i = 0, len = podLines.length; i < len; i++) {
const match = podLines[i].match(dependencyRegExp);
if (match) {
return true;
}
}
return false;
};
+8
View File
@@ -0,0 +1,8 @@
'use strict';
const fs = require('fs');
module.exports = function readPodfile(podfilePath) {
const podContent = fs.readFileSync(podfilePath, 'utf8');
return podContent.split(/\r?\n/g);
};
@@ -0,0 +1,25 @@
'use strict';
const readPodfile = require('./readPodfile');
const findPodTargetLine = require('./findPodTargetLine');
const findLineToAddPod = require('./findLineToAddPod');
const findMarkedLinesInPodfile = require('./findMarkedLinesInPodfile');
const addPodEntry = require('./addPodEntry');
const savePodFile = require('./savePodFile');
module.exports = function registerNativeModulePods(dependency, iOSProject) {
const podLines = readPodfile(iOSProject.podfile);
const linesToAddEntry = getLinesToAddEntry(podLines, iOSProject);
addPodEntry(podLines, linesToAddEntry, dependency.config.ios.podspec, dependency.name);
savePodFile(iOSProject.podfile, podLines);
};
function getLinesToAddEntry(podLines, { projectName }) {
const linesToAddPodWithMarker = findMarkedLinesInPodfile(podLines);
if (linesToAddPodWithMarker.length > 0) {
return linesToAddPodWithMarker;
} else {
const firstTargetLined = findPodTargetLine(podLines, projectName);
return findLineToAddPod(podLines, firstTargetLined);
}
}
+7
View File
@@ -0,0 +1,7 @@
'use strict';
module.exports = function removePodEntry(podfileContent, podName) {
// this regex should catch line(s) with full pod definition, like: pod 'podname', :path => '../node_modules/podname', :subspecs => ['Sub2', 'Sub1']
const podRegex = new RegExp("\\n( |\\t)*pod\\s+(\"|')" + podName + "(\"|')(,\\s*(:[a-z]+\\s*=>)?\\s*((\"|').*?(\"|')|\\[[\\s\\S]*?\\]))*\\n", 'g');
return podfileContent.replace(podRegex, '\n');
};
+8
View File
@@ -0,0 +1,8 @@
'use strict';
const fs = require('fs');
module.exports = function savePodFile(podfilePath, podLines) {
const newPodfile = podLines.join('\n');
fs.writeFileSync(podfilePath, newPodfile);
};
@@ -0,0 +1,13 @@
'use strict';
const fs = require('fs');
const removePodEntry = require('./removePodEntry');
/**
* Unregister native module IOS with CocoaPods
*/
module.exports = function unregisterNativeModule(dependencyConfig, iOSProject) {
const podContent = fs.readFileSync(iOSProject.podfile, 'utf8');
const removed = removePodEntry(podContent, dependencyConfig.podspec);
fs.writeFileSync(iOSProject.podfile, removed);
};
+11 -5
View File
@@ -4,16 +4,17 @@ const getProjectDependencies = require('./getProjectDependencies');
const unregisterDependencyAndroid = require('./android/unregisterNativeModule');
const unregisterDependencyWindows = require('./windows/unregisterNativeModule');
const unregisterDependencyIOS = require('./ios/unregisterNativeModule');
const unregisterDependencyPods = require('./pods/unregisterNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledWindows = require('./windows/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 find = require('lodash').find;
const flatten = require('lodash').flatten;
const isEmpty = require('lodash').isEmpty;
const promiseWaterfall = require('./promiseWaterfall');
@@ -65,16 +66,21 @@ const unlinkDependencyIOS = (iOSProject, dependency, packageName, iOSDependencie
return;
}
const isInstalled = isInstalledIOS(iOSProject, dependency.ios);
if (!isInstalled) {
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`);
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
if (isIosInstalled) {
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
}
else if (isPodInstalled) {
unregisterDependencyPods(dependency.ios, iOSProject);
}
log.info(`iOS module ${packageName} has been successfully unlinked`);
};
+3 -4
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.50.0",
"description": "A framework for building native apps using React",
"license": "BSD-3-Clause",
"repository": {
@@ -162,7 +162,7 @@
"graceful-fs": "^4.1.3",
"inquirer": "^3.0.6",
"lodash": "^4.16.6",
"metro-bundler": "^0.19.0",
"metro-bundler": "^0.20.0",
"mime": "^1.3.4",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
@@ -200,11 +200,10 @@
"eslint-plugin-react": "^7.2.1",
"flow-bin": "^0.56.0",
"jest": "^21",
"mock-fs": "^4.4.1",
"prettier": "1.7.0",
"react": "16.0.0",
"react-test-renderer": "16.0.0",
"shelljs": "^0.7.8",
"sinon": "^2.2.0"
}
}
}
+45 -36
View File
@@ -15,7 +15,7 @@
* 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.
*/
const fs = require('fs');
const {
cat,
echo,
@@ -32,15 +32,15 @@ let argv = minimist(process.argv.slice(2), {
});
// - check we are in release branch, e.g. 0.33-stable
let branch = exec(`git symbolic-ref --short HEAD`, {silent: true}).stdout.trim();
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`);
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`));
let versionMajor = branch.slice(0, branch.indexOf('-stable'));
// - check that argument version matches branch
// e.g. 0.33.1 or 0.33.0-rc4
@@ -58,34 +58,43 @@ if (!match) {
}
let [, major, minor, patch, prerelease] = match;
cat('scripts/versiontemplates/ReactNativeVersion.java.template')
.replace('${major}', major)
.replace('${minor}', minor)
.replace('${patch}', patch)
.replace('${prerelease}', prerelease !== undefined ? `"${prerelease}"` : 'null')
.to('ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java');
fs.writeFileSync(
'ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java',
cat('scripts/versiontemplates/ReactNativeVersion.java.template')
.replace('${major}', major)
.replace('${minor}', minor)
.replace('${patch}', patch)
.replace('${prerelease}', prerelease !== undefined ? `"${prerelease}"` : 'null'),
'utf-8'
);
cat('scripts/versiontemplates/RCTVersion.h.template')
.replace('${major}', `@(${major})`)
.replace('${minor}', `@(${minor})`)
.replace('${patch}', `@(${patch})`)
.replace('${prerelease}', prerelease !== undefined ? `@"${prerelease}"` : '[NSNull null]')
.to('React/Base/RCTVersion.h');
fs.writeFileSync(
'React/Base/RCTVersion.h',
cat('scripts/versiontemplates/RCTVersion.h.template')
.replace('${major}', `@(${major})`)
.replace('${minor}', `@(${minor})`)
.replace('${patch}', `@(${patch})`)
.replace('${prerelease}', prerelease !== undefined ? `@"${prerelease}"` : '[NSNull null]'),
'utf-8'
);
cat('scripts/versiontemplates/ReactNativeVersion.js.template')
.replace('${major}', major)
.replace('${minor}', minor)
.replace('${patch}', patch)
.replace('${prerelease}', prerelease !== undefined ? `'${prerelease}'` : 'null')
.to('Libraries/Core/ReactNativeVersion.js');
fs.writeFileSync(
'Libraries/Core/ReactNativeVersion.js',
cat('scripts/versiontemplates/ReactNativeVersion.js.template')
.replace('${major}', major)
.replace('${minor}', minor)
.replace('${patch}', patch)
.replace('${prerelease}', prerelease !== undefined ? `'${prerelease}'` : 'null'),
'utf-8'
);
let packageJson = JSON.parse(cat(`package.json`));
let packageJson = JSON.parse(cat('package.json'));
packageJson.version = version;
JSON.stringify(packageJson, null, 2).to(`package.json`);
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2), 'utf-8');
// - change ReactAndroid/gradle.properties
if (sed(`-i`, /^VERSION_NAME=.*/, `VERSION_NAME=${version}`, `ReactAndroid/gradle.properties`).code) {
echo(`Couldn't update version for Gradle`);
if (sed('-i', /^VERSION_NAME=.*/, `VERSION_NAME=${version}`, 'ReactAndroid/gradle.properties').code) {
echo('Couldn\'t update version for Gradle');
exit(1);
}
@@ -93,23 +102,23 @@ if (sed(`-i`, /^VERSION_NAME=.*/, `VERSION_NAME=${version}`, `ReactAndroid/gradl
let numberOfChangedLinesWithNewVersion = exec(`git diff -U0 | grep '^[+]' | grep -c ${version} `, {silent: true})
.stdout.trim();
if (+numberOfChangedLinesWithNewVersion !== 2) {
echo(`Failed to update all the files. package.json and gradle.properties must have versions in them`);
echo(`Fix the issue, revert and try again`);
exec(`git diff`);
echo('Failed to update all the files. 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`);
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`);
echo('You may want to rollback the last commit');
echo('git reset --hard HEAD~1');
exit(1);
}
@@ -118,10 +127,10 @@ let remote = argv.remote;
exec(`git push ${remote} v${version}`);
// Tag latest if doing stable release
if (version.indexOf(`rc`) === -1) {
exec(`git tag -d latest`);
if (version.indexOf('rc') === -1) {
exec('git tag -d latest');
exec(`git push ${remote} :latest`);
exec(`git tag latest`);
exec('git tag latest');
exec(`git push ${remote} latest`);
}
+3 -55
View File
@@ -1,77 +1,25 @@
# inspired by https://github.com/Originate/guide/blob/master/android/guide/Continuous%20Integration.md
# SDK Built Tools revision, per http://facebook.github.io/react-native/docs/getting-started.html
ANDROID_SDK_BUILD_TOOLS_REVISION=23.0.1
# API Level we build with
ANDROID_SDK_BUILD_API_LEVEL="23"
# Minimum API Level we target, used for emulator image
ANDROID_SDK_TARGET_API_LEVEL="19"
# Emulator name
AVD_NAME="testAVD"
function getAndroidSDK {
export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH"
DEPS="$ANDROID_HOME/installed-dependencies"
if [ ! -e $DEPS ]; then
echo "Installing Android API level $ANDROID_SDK_TARGET_API_LEVEL, Google APIs, ARM EABI v7a system image..."
sdkmanager "system-images;android-$ANDROID_SDK_TARGET_API_LEVEL;google_apis;armeabi-v7a"
# x86 image requires hardware acceleration, which is not supported when running within the CircleCI Docker image
# echo "Installing Android API level $ANDROID_SDK_TARGET_API_LEVEL, Google APIs, Intel x86 Atom system image..."
# sdkmanager "system-images;android-$ANDROID_SDK_TARGET_API_LEVEL;google_apis;x86"
echo "Installing build SDK for Android API level $ANDROID_SDK_BUILD_API_LEVEL..."
sdkmanager "platforms;android-$ANDROID_SDK_BUILD_API_LEVEL"
echo "Installing target SDK for Android API level $ANDROID_SDK_TARGET_API_LEVEL..."
sdkmanager "platforms;android-$ANDROID_SDK_TARGET_API_LEVEL"
echo "Installing SDK build tools, revision $ANDROID_SDK_BUILD_TOOLS_REVISION..."
sdkmanager "build-tools;$ANDROID_SDK_BUILD_TOOLS_REVISION"
echo "Installing Google APIs for Android API level $ANDROID_SDK_BUILD_API_LEVEL..."
sdkmanager "add-ons;addon-google_apis-google-$ANDROID_SDK_BUILD_API_LEVEL"
echo "Installing Android Support Repository"
sdkmanager "extras;android;m2repository"
echo y | android update sdk --no-ui --all --filter extra-android-m2repository
echo no | android create avd -n testAVD -f -t android-19 --abi default/armeabi-v7a &&
touch $DEPS
fi
}
function getAndroidNDK {
NDK_HOME="/opt/ndk"
DEPS="$NDK_HOME/installed-dependencies"
if [ ! -e $DEPS ]; then
cd $NDK_HOME
echo "Downloading NDK..."
curl -o ndk.zip https://dl.google.com/android/repository/android-ndk-r10e-linux-x86.zip
curl -o ndk_64.zip https://dl.google.com/android/repository/android-ndk-r10e-linux-x86_64.zip
unzip -o -q ndk.zip
unzip -o -q ndk_64.zip
echo "Installed Android NDK at $NDK_HOME"
touch $DEPS
rm ndk.zip
rm ndk_64.zip
fi
}
function createAVD {
echo no | avdmanager create avd --name $AVD_NAME --force --package "system-images;android-$ANDROID_SDK_TARGET_API_LEVEL;google_apis;armeabi-v7a" --tag google_apis --abi armeabi-v7a
}
function launchAVD {
# The AVD name here should match the one created in createAVD
# emulator64-arm -avd $AVD_NAME -no-audio -no-window -no-boot-anim -gpu off
emulator -avd $AVD_NAME -no-audio -no-window
}
function waitForAVD {
echo "Waiting for Android Virtual Device to finish booting..."
local bootanim=""
export PATH=$(dirname $(dirname $(which android)))/platform-tools:$PATH
until [[ "$bootanim" =~ "stopped" ]]; do
sleep 5
bootanim=$(adb -e shell getprop init.svc.bootanim 2>&1)
echo "boot animation status=$bootanim"
echo "emulator status=$bootanim"
done
echo "Android Virtual Device is ready."
}
function retry3 {
+4 -3
View File
@@ -48,10 +48,11 @@ try {
const CLI_PACKAGE = path.join(ROOT, 'react-native-cli', 'react-native-cli-*.tgz');
cd('..');
// can skip cli install for non sudo mode
if (!argv['skip-cli-install']) {
if (exec(`sudo npm install -g ${CLI_PACKAGE}`).code) {
echo('Could not install react-native-cli globally.');
echo('Run with --skip-cli-install to skip this step');
if (exec(`npm install -g ${CLI_PACKAGE}`).code) {
echo('Could not install react-native-cli globally, please run in su mode');
echo('Or with --skip-cli-install to skip this step');
exitCode = 1;
throw Error(exitCode);
}
@@ -9,7 +9,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#define REACT_NATIVE_VERSION @{ \
#define RCT_REACT_NATIVE_VERSION @{ \
@"major": ${major}, \
@"minor": ${minor}, \
@"patch": ${patch}, \