Compare commits

...
19 Commits
Author SHA1 Message Date
Mike Grabowski 984cbb3524 [0.51.1] Bump version numbers 2018-02-14 15:49:08 +01:00
Eric RozellandMike Grabowski 3a5572e0e0 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-14 15:49:02 +01:00
Eric RozellandMike Grabowski 62695a1351 Enable platforms to configure CLI commands
Summary:
This change adds hooks via the `package.json` for a platform plugin to specify hooks for generating platform configurations. This change also adds hooks to allow platform plugins to participate in `link` and `unlink` commands. The goal is to move platform-specific code for platform plugins into their own repositories / node_modules.

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

-->

We need to be able to configure the CLI commands for plugin platforms (e.g., react-native-windows) in their own repositories.

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!)

- All jest tests, including new tests, pass.
- `link` and `unlink` commands are successful on iOS and Android.

(If this PR adds or changes functionality, please take some time to update the docs at https://github.com/facebook/react-native-website, and link to your PR here.)

https://github.com/Microsoft/react-native-windows/pull/1601

<!--
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/core/index.js] - Allow platform plugins to contribute to the RNConfig.
[CLI][FEATURE][local-cli/link/link.js] - Allow platform plugins to participate in the `link` command.
[CLI][FEATURE][local-cli/link/unlink.js] - Allow platform plugins to participate in the `unlink` command.
Closes https://github.com/facebook/react-native/pull/17745

Differential Revision: D6883558

Pulled By: hramos

fbshipit-source-id: ea32fe21cedd4cc02c5c0d48229f2cdb2ac8142b
2018-02-14 15:49:01 +01:00
Mike Grabowski 5f3f3d463a [0.51.0] Bump version numbers 2017-12-05 18:39:30 -08:00
Mike Grabowski ec2f97ac1a [0.51.0-rc.3] Bump version numbers 2017-11-30 18:49:14 +01:00
James ReggioandMike Grabowski 43d1cf8fd1 Fix crash with non-zero blurRadius less than 1
Summary:
There's a crash-inducing bug with `Image.blurRadius` on Android.

`blurRadius` is specified in JavaScript as a `float`, but it's cast to `int` before being passed to the `IterativeBoxBlurPostProcessor`. However, in `IterativeBoxBlurPostProcessor`, there is an argument precondition requiring the integer `blurRadius` to be non-zero.

Because the `== 0` condition is evaluated on the `float`, it's possible for a `blurRadius` in the range of `(0, 1)` (non-inclusive) to pass the conditional, and then be truncated to `0` and passed as an argument to `IterativeBoxBlurPostProcessor`, which will fail its precondition and crash the app.

This change works in our app, which was previously crashing.

[ANDROID] [BUGFIX] [Image] Fixed crash when specifying an Image.blurRadius between (0, 1)
Closes https://github.com/facebook/react-native/pull/16845

Differential Revision: D6387416

Pulled By: shergin

fbshipit-source-id: d5191aa97e949ffd41e6d68c96b3c7bcbc82a52e
2017-11-30 18:49:03 +01:00
Nicolas CharpentierandJames Ide 9faf324d30 Fix markdown in requiresMainQueueSetup warning
Summary:
nit-picking

[IOS] [MINOR] [React/Base/RCTModuleData.mm] - Fix markdown in `requiresMainQueueSetup` warning
Closes https://github.com/facebook/react-native/pull/16916

Differential Revision: D6394725

Pulled By: shergin

fbshipit-source-id: 272c5a6ee529af0b162230e8a07275043a888907
2017-11-28 12:48:22 -08:00
Nicolas CharpentierandJames Ide 8e0d54d38b Implement requiresMainQueueSetup in RCTTVNavigationEventEmitter
Summary:
Fix the following warning:

> Module RCTTVNavigationEventEmitter requires main queue setup since it overrides `init` but doesn't implement `requiresMainQueueSetup`. In a future release React Native will default to initializing all native modules on a background thread unless explicitly opted-out of.

Trivial change.

[IOS] [MINOR] [RCTTVNavigationEventEmitter] - Implement `requiresMainQueueSetup` in `RCTTVNavigationEventEmitter`
Closes https://github.com/facebook/react-native/pull/16915

Differential Revision: D6394636

Pulled By: shergin

fbshipit-source-id: 3981655832715e73e93ef917d987e25097029b84
2017-11-28 12:48:10 -08:00
Valentin SherginandJames Ide 2d88b63747 TextInput: Fixed autoscroll to cursor on Android
Summary:
This feature was disabled for multiline textinputs in D3528202 ... seems without a good reason.
The broken autoscroll-to-cursor feature is terribly harmful and counter-intuitive in ALL cases.
I also add a contentSize tracking in the example app to make sure that it is unaffected by this change.

https://pxl.cl/9RHP

https://github.com/facebook/react-native/issues/12799
https://github.com/facebook/react-native/issues/15778

Special thanks to konradkierus!

Reviewed By: sahrens

Differential Revision: D6405985

fbshipit-source-id: 337a390a9db7b3528200ef66c4a079b87608294e
2017-11-28 12:45:50 -08:00
Mike Grabowski d93de22ad9 [0.51.0-rc.2] Bump version numbers 2017-11-21 22:39:52 +01:00
Mike Grabowski f3cbd4f159 Fix CircleCI 2017-11-21 22:35:39 +01:00
Mike Grabowski a7fdca038f Revert "Automated fixups for Instagram"
This reverts commit dd016f334c.
2017-11-16 21:11:21 +01:00
Mike Grabowski 42879d21e8 Revert old Buck 2017-11-16 21:11:20 +01:00
Mike Grabowski d0bb66fbf7 Fix circle config:
One more deploy fix

Circle fixes

Fix deployment

Improve CI config

Update alias

Fix Buck and tests on master

Hardcode Buck version

Fix Buck cache keys
2017-11-16 20:54:01 +01:00
Mike Grabowski c85aa82cd0 [0.51.0-rc.1] Bump version numbers 2017-11-05 00:48:48 +01:00
Mehdi MulaniandJames Ide 91509751ef Fix memory leak in RCTSafeAreaView
Summary:
According to the retain code collector, RCTSafeAreaView is in a cycle:
-> _bridge -> RCTCxxBridge -> _moduleDataByName -> __NSDictionaryM -> RCTModuleData -> _instance -> RCTUIManager -> _viewRegistry -> __NSDictionaryM -> RCTSafeAreaView
This should break the cycle.

Reviewed By: shergin

Differential Revision: D6213668

fbshipit-source-id: efb9c1dd148b72f66fe4485b81c16cd4c2d18b17
2017-11-03 23:34:29 -07:00
James Ide 87e524b4a4 [podspec] Define pod_target_xcconfig for PrivateDatabase
We don't strictly need this since all the other subspecs that use this set up the header search path for ReactCommon anyway but do it just for correctness.
2017-11-03 17:09:10 -07:00
Joshua MayandJames Ide 6817f05f63 Adds PrivateDatabase podspec
Summary:
Resolves #16663.

Walked through the reproduction steps in #16663, but with successful build.

We can also now see the files are included in the `Pods` project:

<img width="236" alt="screen shot 2017-11-03 at 7 43 21 pm" src="https://user-images.githubusercontent.com/33126/32390596-4a06c05c-c0cf-11e7-8616-3ae547f8cca2.png">

(I noticed a subspec called `_ignore_me_subspec_for_linting_` so do we have tests for the podspec?)

[IOS] [BUGFIX] [React.podspec] - Resolves build error about missing symbols for PrivateDatabase.{cpp,h}
Closes https://github.com/facebook/react-native/pull/16664

Differential Revision: D6238178

Pulled By: hramos

fbshipit-source-id: b57007ab8c7c8bd0faf1f2f11269a7a2c28183e5
2017-11-03 17:09:06 -07:00
Mike Grabowski 7f4fe158a2 [0.51.0-rc.0] Bump version numbers 2017-11-03 17:50:06 +01:00
58 changed files with 552 additions and 829 deletions
+156 -148
View File
@@ -52,6 +52,10 @@ aliases:
keys:
- v1-android-ndk-{{ arch }}-r10e-32-64
- &install-ndk
|
source scripts/circle-ci-android-setup.sh && getAndroidNDK
- &save-cache-ndk
paths:
- /opt/ndk
@@ -87,6 +91,19 @@ aliases:
|
npm install --no-package-lock --no-spin --no-progress
- &install-buck
|
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
- &install-node
|
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
- &run-node-tests
|
npm test -- --maxWorkers=2
@@ -99,6 +116,11 @@ aliases:
- /.*-stable/
- master
- &filter-only-stable
branches:
only:
- /.*-stable/
- &filter-ignore-gh-pages
branches:
ignore: gh-pages
@@ -110,9 +132,54 @@ aliases:
- /.*-stable/
- gh-pages
- &create-ndk-directory
|
if [[ ! -e /opt/ndk ]]; then
sudo mkdir /opt/ndk
fi
sudo chown ${USER:=$(/usr/bin/id -run)}:$USER /opt/ndk
# CircleCI does not support interpolating env variables in the environment
# https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables
- &configure-android-path
|
echo 'export PATH=${ANDROID_NDK}:~/react-native/gradle-2.9/bin:~/buck/bin:$PATH' >> $BASH_ENV
source $BASH_ENV
- &install-android-packages
|
source scripts/circle-ci-android-setup.sh && getAndroidSDK
- &install-build-dependencies
|
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 file -y
- &install-android-app-dependencies
|
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
defaults: &defaults
working_directory: ~/react-native
android_defaults: &android_defaults
<<: *defaults
docker:
- image: circleci/android:api-26-alpha
resource_class: "large"
environment:
- TERM: "dumb"
- ADB_INSTALL_TIMEOUT: 10
- _JAVA_OPTIONS: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
- GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-XX:+HeapDumpOnOutOfMemoryError"'
- ANDROID_NDK: '/opt/ndk/android-ndk-r10e'
- BUILD_THREADS: 2
version: 2
jobs:
# Runs JavaScript tests on Node 8
@@ -208,6 +275,7 @@ jobs:
- save-cache: *save-node-cache
- run: ./scripts/process-podspecs.sh
# Tests website
test-website:
<<: *defaults
docker:
@@ -228,6 +296,7 @@ jobs:
name: Test Build Static Website
command: cd website && node ./server/generate.js
# Deploys website
deploy-website:
<<: *defaults
docker:
@@ -252,6 +321,46 @@ jobs:
echo "Skipping deploy."
fi
# Publishes new version onto npm
deploy:
<<: *android_defaults
steps:
- checkout
# Configure Android dependencies
- run: *configure-android-path
- run: *install-build-dependencies
- restore-cache: *restore-cache-android-packages
- run: *install-android-packages
- save-cache: *save-cache-android-packages
- run: *create-ndk-directory
- restore-cache: *restore-cache-ndk
- run: *install-ndk
- save-cache: *save-cache-ndk
- restore-cache: *restore-cache-buck
- run: *install-buck
- save-cache: *save-cache-buck
- run: *install-node
- restore-cache: *restore-node-cache
- run: *install-node-dependencies
- save-cache: *save-node-cache
- restore-cache: *restore-cache-buck-downloads
- run: *install-android-app-dependencies
- save-cache: *save-cache-buck-downloads
- run:
name: Publish React Native Package
command: |
if [ -z "$CIRCLE_PULL_REQUEST" ]; then
echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
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
node ./scripts/publish-npm.js
else
echo "Skipping deploy."
fi
# Build JavaScript bundle for Android tests
build-js-bundle:
<<: *defaults
@@ -274,116 +383,55 @@ jobs:
# Runs unit tests tests on Android
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
<<: *android_defaults
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
# Configure Android dependencies
- run: *configure-android-path
- run: *install-build-dependencies
- restore-cache: *restore-cache-android-packages
- run:
name: Install Android Packages
command: source scripts/circle-ci-android-setup.sh && getAndroidSDK
- run: *install-android-packages
- save-cache: *save-cache-android-packages
# Starting emulator in advance as it takes some time to boot.
- 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
# Keep configuring Android dependencies while AVD boots up
- run: *create-ndk-directory
- restore-cache: *restore-cache-ndk
- run:
name: Install Android NDK
command: source scripts/circle-ci-android-setup.sh && getAndroidNDK
- run: *install-ndk
- 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
- run: *install-buck
- 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: *install-node
- restore-cache: *restore-node-cache
- run: *install-node-dependencies
- save-cache: *save-node-cache
# TODO: Install and use watchman to speed up builds
# - 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
- run: *install-android-app-dependencies
- 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
# 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.
# 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:
@@ -394,7 +442,8 @@ jobs:
else
echo "JavaScript bundle found.";
fi
# Tests
# Tests
- run:
name: Compile Native Libs for Unit and Integration Tests
command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS -Pcom.android.build.threadPoolSize=1
@@ -402,28 +451,19 @@ jobs:
- run:
name: Unit Tests
command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS
# Integration Tests
# 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
# TODO: Uncomment, test was already failing on Circle 1.0
# - 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
# TODO: 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)
# 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
@@ -449,7 +489,6 @@ jobs:
echo "Skipping dependency installation."
fi
- save-cache: *save-cache-analysis
# Run Danger
- run:
name: Analyze Pull Request
command: |
@@ -459,7 +498,6 @@ jobs:
echo "Skipping pull request analysis."
fi
when: always
# Run eslint
- run:
name: Analyze Code
command: |
@@ -469,81 +507,51 @@ jobs:
echo "Skipping code analysis."
fi
publish-npm:
<<: *defaults
docker:
- image: circleci/node:8
steps:
- checkout
- run: *install-node-dependencies
- run:
name: Publish React Native Package
command: |
if [ -z "$CIRCLE_PULL_REQUEST" ]; then
echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
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
node ./scripts/publish-npm.js
else
echo "Skipping publication."
fi
# Workflows enables us to run multiple jobs in parallel
workflows:
version: 2
test_node:
build:
jobs:
# Test Javascript on Node 8 and 6
- test-js-node-8:
filters: *filter-ignore-gh-pages
- test-js-node-6:
filters: *filter-ignore-gh-pages
# TODO: Re-enable. Node 4 tests are already failing on Circle 1.0
# - test-js-node-4:
# filters: *filter-ignore-gh-pages
test_android:
jobs:
# Test Android
- build-js-bundle:
filters: *filter-ignore-gh-pages
- test-android:
requires:
- build-js-bundle
test_ios:
jobs:
# Test iOS & tvOS
- test-objc-ios:
filters: *filter-ignore-gh-pages
- test-objc-tvos:
filters: *filter-ignore-gh-pages
- test-objc-e2e:
filters: *filter-ignore-gh-pages
# TODO: Re-enable. Podspec tests are already failing on Travis
# - test-podspec:
# filters: *filter-ignore-gh-pages
website:
jobs:
# Test website
- test-website:
filters: *filter-ignore-gh-pages
- deploy-website:
requires:
- test-website
filters: *filter-only-master-stable
analyze:
jobs:
- analyze-pull-request:
filters: *filter-ignore-master-stable
deploy:
jobs:
# If we are on a stable branch, deploy to `npm`
- hold:
type: approval
- publish-npm:
requires:
- deploy:
filters: *filter-only-stable
requires:
- hold
# If we are on a master / stable branch, deploy docs
- deploy-website:
filters: *filter-only-master-stable
requires:
- test-website
- analyze-pull-request:
filters: *filter-ignore-master-stable
+2 -2
View File
@@ -14,7 +14,7 @@
exports.version = {
major: 0,
minor: 0,
patch: 0,
minor: 51,
patch: 1,
prerelease: null,
};
@@ -427,6 +427,20 @@
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTSettings;
};
AD9FBECB1FD7597F00CADA08 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 9936F3131F5F2E4B0010BF04;
remoteInfo = privatedata;
};
AD9FBECD1FD7597F00CADA08 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04;
remoteInfo = "privatedata-tvOS";
};
D85B829B1AB6D5CE003F4FE2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */;
@@ -806,6 +820,8 @@
2D66FF8C1ECA405900F0A767 /* libthird-party.a */,
3D507F441EBC88B700B56834 /* libdouble-conversion.a */,
2D66FF8E1ECA405900F0A767 /* libdouble-conversion.a */,
AD9FBECC1FD7597F00CADA08 /* libprivatedata.a */,
AD9FBECE1FD7597F00CADA08 /* libprivatedata-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@@ -1478,6 +1494,20 @@
remoteRef = 834C36D11AF8DA610019C93C /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
AD9FBECC1FD7597F00CADA08 /* libprivatedata.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libprivatedata.a;
remoteRef = AD9FBECB1FD7597F00CADA08 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
AD9FBECE1FD7597F00CADA08 /* libprivatedata-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libprivatedata-tvOS.a";
remoteRef = AD9FBECD1FD7597F00CADA08 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@@ -44,13 +44,6 @@ RCT_TEST(LayoutExample)
RCT_TEST(ARTExample)
RCT_TEST(ScrollViewExample)
RCT_TEST(TextExample)
#if !TARGET_OS_TV
// No switch or slider available on tvOS
RCT_TEST(SwitchExample)
RCT_TEST(SliderExample)
// TabBarExample on tvOS passes locally but not on Travis
RCT_TEST(TabBarExample)
#endif
- (void)testZZZNotInRecordMode
{
+2
View File
@@ -11,6 +11,8 @@
*/
'use strict';
require('MessageQueue').spy(true);
const AsyncStorage = require('AsyncStorage');
const BackHandler = require('BackHandler');
const Linking = require('Linking');
+7 -1
View File
@@ -320,10 +320,14 @@ class AutogrowingTextInputExample extends React.Component<{}> {
width: 100,
multiline: true,
text: '',
contentSize: {
width: 0,
height: 0,
},
};
}
componentDidReceiveProps(props) {
componentWillReceiveProps(props) {
this.setState({
multiline: props.multiline,
});
@@ -351,10 +355,12 @@ class AutogrowingTextInputExample extends React.Component<{}> {
multiline={this.state.multiline}
style={[style, {width: this.state.width + '%'}]}
onChangeText={(value) => this.setState({text: value})}
onContentSizeChange={(event) => this.setState({contentSize: event.nativeEvent.contentSize})}
{...props}
/>
<Text>Plain text value representation:</Text>
<Text>{this.state.text}</Text>
<Text>Content Size: {JSON.stringify(this.state.contentSize)}</Text>
</View>
);
}
+7
View File
@@ -109,6 +109,7 @@ Pod::Spec.new do |s|
s.subspec "jschelpers" do |ss|
ss.dependency "Folly", "2016.09.26.00"
ss.dependency "React/PrivateDatabase"
ss.compiler_flags = folly_compiler_flags
ss.source_files = "ReactCommon/jschelpers/*.{cpp,h}"
ss.private_header_files = "ReactCommon/jschelpers/*.h"
@@ -116,6 +117,12 @@ Pod::Spec.new do |s|
ss.framework = "JavaScriptCore"
end
s.subspec "PrivateDatabase" do |ss|
ss.source_files = "ReactCommon/privatedata/*.{cpp,h}"
ss.private_header_files = "ReactCommon/privatedata/*.h"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"" }
end
s.subspec "cxxreact" do |ss|
ss.dependency "React/jschelpers"
ss.dependency "boost"
+1 -1
View File
@@ -65,7 +65,7 @@
methodName = "init";
}
RCTLogWarn(@"Module %@ requires main queue setup since it overrides `%s` but doesn't implement "
"`requiresMainQueueSetup. In a future release React Native will default to initializing all native modules "
"`requiresMainQueueSetup`. In a future release React Native will default to initializing all native modules "
"on a background thread unless explicitly opted-out of.", _moduleClass, methodName);
}
}
+2 -2
View File
@@ -11,7 +11,7 @@
#define RCT_REACT_NATIVE_VERSION @{ \
@"major": @(0), \
@"minor": @(0), \
@"patch": @(0), \
@"minor": @(51), \
@"patch": @(1), \
@"prerelease": [NSNull null], \
}
@@ -17,6 +17,11 @@ static NSString *const TVNavigationEventName = @"onTVNavEvent";
RCT_EXPORT_MODULE()
+ (BOOL)requiresMainQueueSetup
{
return NO;
}
- (instancetype)init
{
if (self = [super init]) {
+1 -1
View File
@@ -15,7 +15,7 @@
#import "RCTSafeAreaViewLocalData.h"
@implementation RCTSafeAreaView {
RCTBridge *_bridge;
__weak RCTBridge *_bridge;
UIEdgeInsets _currentSafeAreaInsets;
}
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0-master
VERSION_NAME=0.51.1
GROUP=com.facebook.react
POM_NAME=ReactNative
@@ -12,7 +12,6 @@ android_library(
provided_deps = [
react_native_dep("third-party/android/support/v4:lib-support-v4"),
],
required_for_source_only_abi = True,
visibility = [
"PUBLIC",
],
@@ -16,7 +16,6 @@ android_library(
provided_deps = [
react_native_dep("third-party/android/support/v4:lib-support-v4"),
],
required_for_source_only_abi = True,
visibility = [
"PUBLIC",
],
@@ -9,14 +9,17 @@
package com.facebook.react.devsupport;
import android.os.Handler;
import android.os.Looper;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.JavaJSExecutor;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import android.os.Handler;
import android.os.Looper;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.JavaJSExecutor;
/**
* Executes JS remotely via the react nodejs server as a proxy to a browser on the host machine.
@@ -157,7 +160,8 @@ public class WebsocketJavaScriptExecutor implements JavaJSExecutor {
}
@Override
public void loadApplicationScript(String sourceURL) throws JavaJSExecutor.ProxyExecutorException {
public void loadApplicationScript(String sourceURL)
throws ProxyExecutorException {
JSExecutorCallbackFuture callback = new JSExecutorCallbackFuture();
Assertions.assertNotNull(mWebSocketClient).loadApplicationScript(
sourceURL,
@@ -172,7 +176,7 @@ public class WebsocketJavaScriptExecutor implements JavaJSExecutor {
@Override
public @Nullable String executeJSCall(String methodName, String jsonArgsArray)
throws JavaJSExecutor.ProxyExecutorException {
throws ProxyExecutorException {
JSExecutorCallbackFuture callback = new JSExecutorCallbackFuture();
Assertions.assertNotNull(mWebSocketClient).executeJSCall(
methodName,
@@ -3,7 +3,6 @@ include_defs("//ReactAndroid/DEFS")
android_library(
name = "annotations",
srcs = glob(["**/*.java"]),
required_for_source_only_abi = True,
visibility = [
"PUBLIC",
],
@@ -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", 51,
"patch", 1,
"prerelease", null);
}
@@ -14,7 +14,6 @@ android_library(
provided_deps = [
react_native_dep("third-party/android/support/v4:lib-support-v4"),
],
required_for_source_only_abi = True,
visibility = [
"PUBLIC",
],
@@ -3,7 +3,6 @@ include_defs("//ReactAndroid/DEFS")
android_library(
name = "annotations",
srcs = glob(["*.java"]),
required_for_source_only_abi = True,
visibility = [
"PUBLIC",
],
@@ -12,7 +12,7 @@ import android.view.animation.Transformation;
*/
/* package */ class OpacityAnimation extends Animation {
static class OpacityAnimationListener implements Animation.AnimationListener {
static class OpacityAnimationListener implements AnimationListener {
private final View mView;
private boolean mLayerTypeChanged = false;
@@ -11,7 +11,6 @@ android_library(
react_native_dep("third-party/android/support-annotations:android-support-annotations"),
react_native_dep("third-party/android/support/v4:lib-support-v4"),
],
required_for_source_only_abi = True,
visibility = ["PUBLIC"],
deps = [
react_native_dep("third-party/java/jsr-305:jsr-305"),
@@ -229,11 +229,11 @@ public class ReactImageView extends GenericDraweeView {
}
public void setBlurRadius(float blurRadius) {
if (blurRadius == 0) {
int pixelBlurRadius = (int) PixelUtil.toPixelFromDIP(blurRadius);
if (pixelBlurRadius == 0) {
mIterativeBoxBlurPostProcessor = null;
} else {
mIterativeBoxBlurPostProcessor =
new IterativeBoxBlurPostProcessor((int) PixelUtil.toPixelFromDIP(blurRadius));
mIterativeBoxBlurPostProcessor = new IterativeBoxBlurPostProcessor(pixelBlurRadius);
}
mIsDirty = true;
}
@@ -117,16 +117,7 @@ public class ReactEditText extends EditText {
// TODO: t6408636 verify if we should schedule a layout after a View does a requestLayout()
@Override
public boolean isLayoutRequested() {
// If we are watching and updating container height based on content size
// then we don't want to scroll right away. This isn't perfect -- you might
// want to limit the height the text input can grow to. Possible solution
// is to add another prop that determines whether we should scroll to end
// of text.
if (mContentSizeWatcher != null) {
return isMultiline();
} else {
return false;
}
return false;
}
@Override
@@ -54,13 +54,13 @@ public class ReactViewGroup extends ViewGroup implements
/**
* This listener will be set for child views when removeClippedSubview property is enabled. When
* children layout is updated, it will call {@link #updateSubviewClipStatus} to notify parent view
* about that fact so that view can be attached/detached if necessary.
* children layout is updated, it will call {@link #updateSubviewClipStatus} to notify parent
* view about that fact so that view can be attached/detached if necessary.
*
* <p>TODO(7728005): Attach/detach views in batch - once per frame in case when multiple children
* TODO(7728005): Attach/detach views in batch - once per frame in case when multiple children
* update their layout.
*/
private static final class ChildrenLayoutChangeListener implements View.OnLayoutChangeListener {
private static final class ChildrenLayoutChangeListener implements OnLayoutChangeListener {
private final ReactViewGroup mParent;
@@ -396,7 +396,7 @@ public class ReactViewGroup extends ViewGroup implements
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
public void addView(View child, int index, LayoutParams params) {
// This will get called for every overload of addView so there is not need to override every method.
mDrawingOrderHelper.handleAddView(child);
setChildrenDrawingOrderEnabled(mDrawingOrderHelper.shouldEnableCustomDrawingOrder());
@@ -468,8 +468,7 @@ public class ReactViewGroup extends ViewGroup implements
addViewWithSubviewClippingEnabled(child, index, sDefaultLayoutParam);
}
/*package*/ void addViewWithSubviewClippingEnabled(
View child, int index, ViewGroup.LayoutParams params) {
/*package*/ void addViewWithSubviewClippingEnabled(View child, int index, LayoutParams params) {
Assertions.assertCondition(mRemoveClippedSubviews);
Assertions.assertNotNull(mClippingRect);
Assertions.assertNotNull(mAllChildren);
@@ -9,17 +9,19 @@
package com.facebook.react.views.viewpager;
import java.util.ArrayList;
import java.util.List;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.events.NativeGestureUtil;
import java.util.ArrayList;
import java.util.List;
/**
* Wrapper view for {@link ViewPager}. It's forwarding calls to {@link ViewGroup#addView} to add
@@ -116,7 +118,7 @@ public class ReactViewPager extends ViewPager {
}
}
private class PageChangeListener implements ViewPager.OnPageChangeListener {
private class PageChangeListener implements OnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+20 -8
View File
@@ -26,18 +26,27 @@ describe('findPlugins', () => {
jest.mock(pjsonPath, () => ({
dependencies: {'rnpm-plugin-test': '*'},
}));
expect(findPlugins([ROOT])).toHaveLength(1);
expect(findPlugins([ROOT])[0]).toBe('rnpm-plugin-test');
expect(findPlugins([ROOT])).toHaveProperty('commands');
expect(findPlugins([ROOT])).toHaveProperty('platforms');
expect(findPlugins([ROOT]).commands).toHaveLength(1);
expect(findPlugins([ROOT]).commands[0]).toBe('rnpm-plugin-test');
expect(findPlugins([ROOT]).platforms).toHaveLength(0);
});
it('returns an empty array if there are no plugins in this folder', () => {
jest.mock(pjsonPath, () => ({}));
expect(findPlugins([ROOT])).toHaveLength(0);
expect(findPlugins([ROOT])).toHaveProperty('commands');
expect(findPlugins([ROOT])).toHaveProperty('platforms');
expect(findPlugins([ROOT]).commands).toHaveLength(0);
expect(findPlugins([ROOT]).platforms).toHaveLength(0);
});
it('returns an empty array if there is no package.json in the supplied folder', () => {
expect(Array.isArray(findPlugins(['fake-path']))).toBeTruthy();
expect(findPlugins(['fake-path'])).toHaveLength(0);
it('returns an object with empty arrays if there is no package.json in the supplied folder', () => {
expect(findPlugins(['fake-path'])).toHaveProperty('commands');
expect(findPlugins(['fake-path'])).toHaveProperty('platforms');
expect(findPlugins(['fake-path']).commands).toHaveLength(0);
expect(findPlugins(['fake-path']).platforms).toHaveLength(0);
});
it('returns plugins from both dependencies and dev dependencies', () => {
@@ -45,7 +54,10 @@ describe('findPlugins', () => {
dependencies: {'rnpm-plugin-test': '*'},
devDependencies: {'rnpm-plugin-test-2': '*'},
}));
expect(findPlugins([ROOT])).toHaveLength(2);
expect(findPlugins([ROOT])).toHaveProperty('commands');
expect(findPlugins([ROOT])).toHaveProperty('platforms');
expect(findPlugins([ROOT]).commands).toHaveLength(2);
expect(findPlugins([ROOT]).platforms).toHaveLength(0);
});
it('returns unique list of plugins', () => {
@@ -53,6 +65,6 @@ describe('findPlugins', () => {
dependencies: {'rnpm-plugin-test': '*'},
devDependencies: {'rnpm-plugin-test': '*'},
}));
expect(findPlugins([ROOT])).toHaveLength(1);
expect(findPlugins([ROOT]).commands).toHaveLength(1);
});
});
+2
View File
@@ -126,3 +126,5 @@ exports.dependencyConfig = function dependencyConfigAndroid(folder, userConfig)
return { sourceDir, folder, manifest, packageImportPath, packageInstance };
};
exports.linkConfig = require('../../link/android');
+23 -9
View File
@@ -37,11 +37,19 @@ const findPluginsInReactNativePackage = (pjson) => {
return path.join(pjson.name, pjson.rnpm.plugin);
};
const findPlatformsInPackage = (pjson) => {
if (!pjson.rnpm || !pjson.rnpm.platform) {
return [];
}
return path.join(pjson.name, pjson.rnpm.platform);
};
const findPluginInFolder = (folder) => {
const pjson = readPackage(folder);
if (!pjson) {
return [];
return {commands: [], platforms: []};
}
const deps = union(
@@ -51,27 +59,33 @@ const findPluginInFolder = (folder) => {
return deps.reduce(
(acc, pkg) => {
let commands = acc.commands;
let platforms = acc.platforms;
if (isRNPMPlugin(pkg)) {
return acc.concat(pkg);
commands = commands.concat(pkg);
}
if (isReactNativePlugin(pkg)) {
const pkgJson = readPackage(path.join(folder, 'node_modules', pkg));
if (!pkgJson) {
return acc;
if (pkgJson) {
commands = commands.concat(findPluginsInReactNativePackage(pkgJson));
platforms = platforms.concat(findPlatformsInPackage(pkgJson));
}
return acc.concat(findPluginsInReactNativePackage(pkgJson));
}
return acc;
return {commands: commands, platforms: platforms};
},
[]
{commands: [], platforms: []}
);
};
/**
* Find plugins in package.json of the given folder
* @param {String} folder Path to the folder to get the package.json from
* @type {Array} Array of plugins or an empty array if no package.json found
* @type {Object} Object of commands and platform plugins
*/
module.exports = function findPlugins(folders) {
return uniq(flatten(folders.map(findPluginInFolder)));
const plugins = folders.map(findPluginInFolder);
return {
commands: uniq(flatten(plugins.map(p => p.commands))),
platforms: uniq(flatten(plugins.map(p => p.platforms)))
};
};
+42 -12
View File
@@ -15,7 +15,6 @@ const Config = require('../util/Config');
const findPlugins = require('./findPlugins');
const findAssets = require('./findAssets');
const ios = require('./ios');
const windows = require('./windows');
const wrapCommands = require('./wrapCommands');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
@@ -33,6 +32,10 @@ import type {ConfigT} from 'metro-bundler';
export type RNConfig = {
...ConfigT,
/**
* Returns an object with all platform configurations.
*/
getPlatformConfig(): Object,
/**
* Returns an array of project commands used by the CLI to load
*/
@@ -55,10 +58,21 @@ const attachPackage = (command, pkg) => Array.isArray(command)
? command.map(cmd => attachPackage(cmd, pkg))
: { ...command, pkg };
const appRoot = process.cwd();
const plugins = findPlugins([appRoot]);
const pluginPlatforms = plugins
.platforms
.reduce((acc, pathToPlatforms) => {
// $FlowFixMe non-literal require
return Object.assign(acc, require(path.join(appRoot, 'node_modules', pathToPlatforms)));
},
{});
const defaultRNConfig = {
getProjectCommands(): Array<CommandT> {
const appRoot = process.cwd();
const plugins = findPlugins([appRoot])
const commands = plugins
.commands
.map(pathToCommands => {
const name = pathToCommands.split(path.sep)[0];
@@ -70,35 +84,51 @@ const defaultRNConfig = {
);
});
return flatten(plugins);
return flatten(commands);
},
getPlatformConfig(): Object {
return {
ios,
android,
...pluginPlatforms
};
},
getProjectConfig(): Object {
const platforms = this.getPlatformConfig();
const folder = process.cwd();
const rnpm = getRNPMConfig(folder);
return Object.assign({}, rnpm, {
ios: ios.projectConfig(folder, rnpm.ios || {}),
android: android.projectConfig(folder, rnpm.android || {}),
windows: windows.projectConfig(folder, rnpm.windows || {}),
let config = Object.assign({}, rnpm, {
assets: findAssets(folder, rnpm.assets),
});
Object.keys(platforms).forEach(key => {
config[key] = platforms[key].projectConfig(folder, rnpm[key] || {});
});
return config;
},
getDependencyConfig(packageName: string) {
const platforms = this.getPlatformConfig();
const folder = path.join(process.cwd(), 'node_modules', packageName);
const rnpm = getRNPMConfig(
path.join(process.cwd(), 'node_modules', packageName)
);
return Object.assign({}, rnpm, {
ios: ios.dependencyConfig(folder, rnpm.ios || {}),
android: android.dependencyConfig(folder, rnpm.android || {}),
windows: windows.dependencyConfig(folder, rnpm.windows || {}),
let config = Object.assign({}, rnpm, {
assets: findAssets(folder, rnpm.assets),
commands: wrapCommands(rnpm.commands),
params: rnpm.params || [],
});
Object.keys(platforms).forEach(key => {
config[key] = platforms[key].dependencyConfig(folder, rnpm[key] || {});
});
return config;
},
};
+2
View File
@@ -57,3 +57,5 @@ exports.projectConfig = function projectConfigIOS(folder, userConfig) {
};
exports.dependencyConfig = exports.projectConfig;
exports.linkConfig = require('../../link/ios');
-30
View File
@@ -1,30 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
const fs = require('fs');
const glob = require('glob');
const path = require('path');
/**
* Gets package's namespace
* by searching for its declaration in all C# files present in the folder
*
* @param {String} folder Folder to find C# files
*/
module.exports = function getNamespace(folder) {
const files = glob.sync('**/*.cs', { cwd: folder });
const packages = files
.map(filePath => fs.readFileSync(path.join(folder, filePath), 'utf8'))
.map(file => file.match(/namespace (.*)[\s\S]+IReactPackage/))
.filter(match => match);
return packages.length ? packages[0][1] : null;
};
@@ -1,30 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
const fs = require('fs');
const glob = require('glob');
const path = require('path');
/**
* Gets package's class name (class that implements IReactPackage)
* by searching for its declaration in all C# files present in the folder
*
* @param {String} folder Folder to find C# files
*/
module.exports = function getPackageClassName(folder) {
const files = glob.sync('**/*.cs', { cwd: folder });
const packages = files
.map(filePath => fs.readFileSync(path.join(folder, filePath), 'utf8'))
.map(file => file.match(/class (.*) : IReactPackage/))
.filter(match => match);
return packages.length ? packages[0][1] : null;
};
-27
View File
@@ -1,27 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
const glob = require('glob');
const path = require('path');
/**
* Find an C# project file
*
* @param {String} folder Name of the folder where to seek
* @return {String}
*/
module.exports = function findManifest(folder) {
const csprojPath = glob.sync(path.join('**', '*.csproj'), {
cwd: folder,
ignore: ['node_modules/**', '**/build/**', 'Examples/**', 'examples/**'],
})[0];
return csprojPath ? path.join(folder, csprojPath) : null;
};
@@ -1,60 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
const glob = require('glob');
const path = require('path');
/**
* Glob pattern to look for solution file
*/
const GLOB_PATTERN = '**/*.sln';
/**
* Regexp matching all test projects
*/
const TEST_PROJECTS = /test|example|sample/i;
/**
* Base windows folder
*/
const WINDOWS_BASE = 'windows';
/**
* These folders will be excluded from search to speed it up
*/
const GLOB_EXCLUDE_PATTERN = ['**/@(node_modules)/**'];
/**
* Finds windows project by looking for all .sln files
* in given folder.
*
* Returns first match if files are found or null
*
* Note: `./windows/*.sln` are returned regardless of the name
*/
module.exports = function findSolution(folder) {
const projects = glob
.sync(GLOB_PATTERN, {
cwd: folder,
ignore: GLOB_EXCLUDE_PATTERN,
})
.filter(project => {
return path.dirname(project) === WINDOWS_BASE || !TEST_PROJECTS.test(project);
})
.sort((projectA, projectB) => {
return path.dirname(projectA) === WINDOWS_BASE ? -1 : 1;
});
if (projects.length === 0) {
return null;
}
return projects[0];
};
-10
View File
@@ -1,10 +0,0 @@
const s4 = () => {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
};
module.exports = function generateGUID() {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
};
-114
View File
@@ -1,114 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
const findWindowsSolution = require('./findWindowsSolution');
const findNamespace = require('./findNamespace');
const findProject = require('./findProject');
const findPackageClassName = require('./findPackageClassName');
const path = require('path');
const generateGUID = require('./generateGUID');
const relativeProjectPath = (fullProjPath) => {
const windowsPath = fullProjPath
.substring(fullProjPath.lastIndexOf('node_modules') - 1, fullProjPath.length)
.replace(/\//g, '\\');
return '..' + windowsPath;
};
const getProjectName = (fullProjPath) => {
return fullProjPath.split('/').slice(-1)[0].replace(/\.csproj/i, '');
};
/**
* Gets windows project config by analyzing given folder and taking some
* defaults specified by user into consideration
*/
exports.projectConfig = function projectConfigWindows(folder, userConfig) {
const csSolution = userConfig.csSolution || findWindowsSolution(folder);
if (!csSolution) {
return null;
}
// expects solutions to be named the same as project folders
const solutionPath = path.join(folder, csSolution);
const windowsAppFolder = csSolution.substring(0, csSolution.lastIndexOf('.sln'));
const src = userConfig.sourceDir || windowsAppFolder;
const sourceDir = path.join(folder, src);
const mainPage = path.join(sourceDir, 'MainPage.cs');
const projectPath = userConfig.projectPath || findProject(folder);
return {
sourceDir,
solutionPath,
projectPath,
mainPage,
folder,
userConfig,
};
};
/**
* Same as projectConfigWindows except it returns
* different config that applies to packages only
*/
exports.dependencyConfig = function dependencyConfigWindows(folder, userConfig) {
const csSolution = userConfig.csSolution || findWindowsSolution(folder);
if (!csSolution) {
return null;
}
// expects solutions to be named the same as project folders
const windowsAppFolder = csSolution.substring(0, csSolution.lastIndexOf('.sln'));
const src = userConfig.sourceDir || windowsAppFolder;
if (!src) {
return null;
}
const sourceDir = path.join(folder, src);
const packageClassName = findPackageClassName(sourceDir);
const namespace = userConfig.namespace || findNamespace(sourceDir);
const csProj = userConfig.csProj || findProject(folder);
/**
* This module has no package to export or no namespace
*/
if (!packageClassName || !namespace) {
return null;
}
const packageUsingPath = userConfig.packageUsingPath ||
`using ${namespace};`;
const packageInstance = userConfig.packageInstance ||
`new ${packageClassName}()`;
const projectGUID = generateGUID();
const pathGUID = generateGUID();
const projectName = getProjectName(csProj);
const relativeProjPath = relativeProjectPath(csProj);
return {
sourceDir,
packageUsingPath,
packageInstance,
projectName,
csProj,
folder,
projectGUID,
pathGUID,
relativeProjPath,
};
};
+67 -1
View File
@@ -37,6 +37,7 @@ describe('link', () => {
it('should accept a name of a dependency to link', (done) => {
const config = {
getPlatformConfig: () => ({ios: {}, android: {}}),
getProjectConfig: () => ({ assets: [] }),
getDependencyConfig: sinon.stub().returns({ assets: [], commands: {} }),
};
@@ -52,6 +53,7 @@ describe('link', () => {
it('should read dependencies from package.json when name not provided', (done) => {
const config = {
getPlatformConfig: () => ({ios: {}, android: {}}),
getProjectConfig: () => ({ assets: [] }),
getDependencyConfig: sinon.stub().returns({ assets: [], commands: {} }),
};
@@ -77,7 +79,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: { linkConfig: iosLinkConfig }, android: { linkConfig: androidLinkConfig }}),
getProjectConfig: () => ({android: {}, ios: {}, assets: []}),
getDependencyConfig: sinon.stub().returns(dependencyConfig),
};
@@ -114,6 +119,7 @@ describe('link', () => {
const registerNativeModule = sinon.stub();
const dependencyConfig = {ios: {}, android: {}, assets: [], commands: {}};
const config = {
getPlatformConfig: () => ({ios: {}, android: {}}),
getProjectConfig: () => ({ ios: {}, android: {}, assets: [] }),
getDependencyConfig: sinon.stub().returns(dependencyConfig),
};
@@ -146,6 +152,62 @@ describe('link', () => {
});
});
it('should register native modules for plugins', (done) => {
const registerNativeModule = sinon.stub();
const dependencyConfig = {ios: {}, android: {}, test: {}, assets: [], commands: {}};
const linkPluginConfig = { isInstalled: () => false, register: registerNativeModule };
const config = {
getPlatformConfig: () => ({ ios: {}, android: {}, test: { linkConfig: () => linkPluginConfig }}),
getProjectConfig: () => ({ ios: {}, android: {}, test: {}, assets: [] }),
getDependencyConfig: sinon.stub().returns(dependencyConfig),
};
jest.setMock(
'../ios/isInstalled.js',
sinon.stub().returns(true)
);
jest.setMock(
'../android/isInstalled.js',
sinon.stub().returns(true)
);
const link = require('../link').func;
link(['react-native-blur'], config).then(() => {
expect(registerNativeModule.calledOnce).toBeTruthy();
done();
});
});
it('should not register native modules for plugins when already installed', (done) => {
const registerNativeModule = sinon.stub();
const dependencyConfig = {ios: {}, android: {}, test: {}, assets: [], commands: {}};
const linkPluginConfig = { isInstalled: () => true, register: registerNativeModule};
const config = {
getPlatformConfig: () => ({ ios: {}, android: {}, test: { linkConfig: () => linkPluginConfig }}),
getProjectConfig: () => ({ ios: {}, android: {}, test: {}, assets: [] }),
getDependencyConfig: sinon.stub().returns(dependencyConfig),
};
jest.setMock(
'../ios/isInstalled.js',
sinon.stub().returns(true)
);
jest.setMock(
'../android/isInstalled.js',
sinon.stub().returns(true)
);
const link = require('../link').func;
link(['react-native-blur'], config).then(() => {
expect(registerNativeModule.callCount).toEqual(0);
done();
});
});
it('should run prelink and postlink commands at the appropriate times', (done) => {
const registerNativeModule = sinon.stub();
const prelink = sinon.stub().yieldsAsync();
@@ -161,7 +223,9 @@ describe('link', () => {
sinon.stub().returns(false)
);
const linkConfig = require('../ios');
const config = {
getPlatformConfig: () => ({ ios: { linkConfig: linkConfig }}),
getProjectConfig: () => ({ ios: {}, assets: [] }),
getDependencyConfig: sinon.stub().returns({
ios: {}, assets: [], commands: { prelink, postlink },
@@ -179,7 +243,7 @@ describe('link', () => {
it('should copy assets from both project and dependencies projects', (done) => {
const dependencyAssets = ['Fonts/Font.ttf'];
const dependencyConfig = {assets: dependencyAssets, commands: {}};
const dependencyConfig = {assets: dependencyAssets, ios: {}, commands: {}};
const projectAssets = ['Fonts/FontC.ttf'];
const copyAssets = sinon.stub();
@@ -188,7 +252,9 @@ describe('link', () => {
copyAssets
);
const linkConfig = require('../ios');
const config = {
getPlatformConfig: () => ({ ios: { linkConfig: linkConfig } }),
getProjectConfig: () => ({ ios: {}, assets: projectAssets }),
getDependencyConfig: sinon.stub().returns(dependencyConfig),
};
+2 -2
View File
@@ -8,10 +8,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
@@ -8,11 +8,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(config, name) {
return isInstalledIOS(config, name) || isInstalledPods(config, name);
};
@@ -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('../registerNativeModule');
const unregisterDependencyPods = require('../../pods/registerNativeModule');
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')
};
};
+44 -93
View File
@@ -26,16 +26,6 @@ const chalk = require('chalk');
* run Flow. */
const isEmpty = require('lodash').isEmpty;
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');
const getDependencyConfig = require('./getDependencyConfig');
const pollParams = require('./pollParams');
@@ -49,95 +39,55 @@ log.heading = 'rnpm-link';
const dedupeAssets = (assets) => uniqBy(assets, asset => path.basename(asset));
const linkDependency = (platforms, project, dependency) => {
Object.keys(platforms || {})
.forEach(platform => {
if (!project[platform] || !dependency.config[platform]) {
return null;
}
const linkDependencyAndroid = (androidProject, dependency) => {
if (!androidProject || !dependency.config.android) {
return null;
}
const linkConfig = platforms[platform] && platforms[platform].linkConfig && platforms[platform].linkConfig();
if (!linkConfig || !linkConfig.isInstalled || !linkConfig.register) {
return null;
}
const isInstalled = isInstalledAndroid(androidProject, dependency.name);
const isInstalled = linkConfig.isInstalled(project[platform], dependency.config[platform]);
if (isInstalled) {
log.info(chalk.grey(`Android module ${dependency.name} is already linked`));
return null;
}
if (isInstalled) {
log.info(chalk.grey(`Platform '${platform}' module ${dependency.name} is already linked`));
return null;
}
return pollParams(dependency.config.params).then(params => {
log.info(`Linking ${dependency.name} android dependency`);
return pollParams(dependency.config.params).then(params => {
log.info(`Linking ${dependency.name} ${platform} dependency`);
registerDependencyAndroid(
dependency.name,
dependency.config.android,
params,
androidProject
);
linkConfig.register(
dependency.name,
dependency.config[platform],
params,
project[platform]
);
log.info(`Android module ${dependency.name} has been successfully linked`);
});
log.info(`Platform '${platform}' module ${dependency.name} has been successfully linked`);
});
});
};
const linkDependencyWindows = (windowsProject, dependency) => {
if (!windowsProject || !dependency.config.windows) {
return null;
}
const isInstalled = isInstalledWindows(windowsProject, dependency.config.windows);
if (isInstalled) {
log.info(chalk.grey(`Windows module ${dependency.name} is already linked`));
return null;
}
return pollParams(dependency.config.params).then(params => {
log.info(`Linking ${dependency.name} windows dependency`);
registerDependencyWindows(
dependency.name,
dependency.config.windows,
params,
windowsProject
);
log.info(`Windows module ${dependency.name} has been successfully linked`);
});
};
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 = (project, assets) => {
const linkAssets = (platforms, project, assets) => {
if (isEmpty(assets)) {
return;
}
if (project.ios) {
log.info('Linking assets to ios project');
copyAssetsIOS(assets, project.ios);
}
Object.keys(platforms || {})
.forEach(platform => {
const linkConfig = platforms[platform] && platforms[platform].linkConfig && platforms[platform].linkConfig();
if (!linkConfig || !linkConfig.copyAssets) {
return;
}
if (project.android) {
log.info('Linking assets to android project');
copyAssetsAndroid(assets, project.android.assetsPath);
}
log.info(`Linking assets to ${platform} project`);
linkConfig.copyAssets(assets, project[platform]);
});
log.info('Assets have been successfully linked to your project');
};
@@ -150,9 +100,11 @@ const linkAssets = (project, assets) => {
* @param config CLI config, see local-cli/core/index.js
*/
function link(args: Array<string>, config: RNConfig) {
var project;
let project;
let platforms;
try {
project = config.getProjectConfig();
platforms = config.getPlatformConfig();
} catch (err) {
log.error(
'ERRPACKAGEJSON',
@@ -161,7 +113,8 @@ function link(args: Array<string>, config: RNConfig) {
return Promise.reject(err);
}
if (!project.android && !project.ios && !project.windows && findReactNativeScripts()) {
const hasProjectConfig = Object.keys(platforms).reduce((acc, key) => acc || key in project, false);
if (!hasProjectConfig && findReactNativeScripts()) {
throw new Error(
'`react-native link` can not be used in Create React Native App projects. ' +
'If you need to include a library that relies on custom native code, ' +
@@ -183,19 +136,17 @@ function link(args: Array<string>, config: RNConfig) {
);
const assets = dedupeAssets(dependencies.reduce(
(assets, dependency) => assets.concat(dependency.config.assets),
(acc, dependency) => acc.concat(dependency.config.assets),
project.assets
));
const tasks = flatten(dependencies.map(dependency => [
() => promisify(dependency.config.commands.prelink || commandStub),
() => linkDependencyAndroid(project.android, dependency),
() => linkDependencyIOS(project.ios, dependency),
() => linkDependencyWindows(project.windows, dependency),
() => linkDependency(platforms, project, dependency),
() => promisify(dependency.config.commands.postlink || commandStub),
]));
tasks.push(() => linkAssets(project, assets));
tasks.push(() => linkAssets(platforms, project, assets));
return promiseWaterfall(tasks).catch(err => {
log.error(
+2 -2
View File
@@ -7,10 +7,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);
};
+39 -79
View File
@@ -1,18 +1,7 @@
const log = require('npmlog');
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 flatten = require('lodash').flatten;
@@ -23,66 +12,37 @@ 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);
Object.keys(platforms || {})
.forEach(platform => {
if (!project[platform] || !dependency[platform]) {
return;
}
if (!isInstalled) {
log.info(`Android module ${packageName} is not installed`);
return;
}
const linkConfig = platforms[platform] && platforms[platform].linkConfig && platforms[platform].linkConfig();
if (!linkConfig || !linkConfig.isInstalled || !linkConfig.unregister) {
return;
}
log.info(`Unlinking ${packageName} android dependency`);
const isInstalled = linkConfig.isInstalled(project[platform], dependency[platform]);
unregisterDependencyAndroid(packageName, dependency.android, androidProject);
if (!isInstalled) {
log.info(`Platform '${platform}' module ${packageName} is not installed`);
return;
}
log.info(`Android module ${packageName} has been successfully unlinked`);
};
log.info(`Unlinking ${packageName} ${platform} dependency`);
const unlinkDependencyWindows = (windowsProject, dependency, packageName) => {
if (!windowsProject || !dependency.windows) {
return;
}
linkConfig.unregister(
packageName,
dependency[platform],
project[platform],
otherDependencies
);
const isInstalled = isInstalledWindows(windowsProject, dependency.windows);
if (!isInstalled) {
log.info(`Windows module ${packageName} is not installed`);
return;
}
log.info(`Unlinking ${packageName} windows dependency`);
unregisterDependencyWindows(packageName, dependency.windows, windowsProject);
log.info(`Windows module ${packageName} 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`);
log.info(`Platform '${platform}' module ${dependency.name} has been successfully unlinked`);
});
};
/**
@@ -94,10 +54,12 @@ const unlinkDependencyIOS = (iOSProject, dependency, packageName, iOSDependencie
function unlink(args, config) {
const packageName = args[0];
var project;
var dependency;
let platforms;
let project;
let dependency;
try {
platforms = config.getPlatformConfig();
project = config.getProjectConfig();
} catch (err) {
log.error(
@@ -119,13 +81,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),
() => unlinkDependencyWindows(project.windows, dependency, packageName),
() => unlinkDependency(platforms, project, dependency, packageName, otherDependencies),
() => promisify(dependency.commands.postunlink || commandStub)
];
@@ -142,15 +101,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`
-8
View File
@@ -1,8 +0,0 @@
const fs = require('fs');
const makeUsingPatch = require('./patches/makeUsingPatch');
module.exports = function isInstalled(config, dependencyConfig) {
return fs
.readFileSync(config.mainPage)
.indexOf(makeUsingPatch(dependencyConfig.packageUsingPath).patch) > -1;
};
@@ -1,23 +0,0 @@
/**
* Copyright (c) 2013-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.
*/
const toCamelCase = require('lodash').camelCase;
module.exports = function applyParams(str, params, prefix) {
return str.replace(
/\$\{(\w+)\}/g,
(pattern, param) => {
const name = toCamelCase(prefix) + '_' + param;
return params[param]
? `getResources().getString(R.string.${name})`
: null;
}
);
};
@@ -1,11 +0,0 @@
const fs = require('fs');
module.exports = function applyPatch(file, patch, flip = false) {
fs.writeFileSync(file, fs
.readFileSync(file, 'utf8')
.replace(patch.pattern, match => {
return flip ? `${patch.patch}${match}` : `${match}${patch.patch}`;
})
);
};
@@ -1,10 +0,0 @@
const applyParams = require('./applyParams');
module.exports = function makePackagePatch(packageInstance, params, prefix) {
const processedInstance = applyParams(packageInstance, params, prefix);
return {
pattern: 'new MainReactPackage()',
patch: ',\n ' + processedInstance,
};
};
@@ -1,14 +0,0 @@
module.exports = function makeProjectPatch(windowsConfig) {
const projectInsert = `<ProjectReference Include="..\\${windowsConfig.relativeProjPath}">
<Project>{${windowsConfig.pathGUID}}</Project>
<Name>${windowsConfig.projectName}</Name>
</ProjectReference>
`;
return {
pattern: '<ProjectReference Include="..\\..\\node_modules\\react-native-windows\\ReactWindows\\ReactNative\\ReactNative.csproj">',
patch: projectInsert,
unpatch: new RegExp(`<ProjectReference.+\\s+.+\\s+.+${windowsConfig.projectName}.+\\s+.+\\s`),
};
};
@@ -1,12 +0,0 @@
module.exports = function makeSolutionPatch(windowsConfig) {
const solutionInsert = `Project("{${windowsConfig.projectGUID.toUpperCase()}}") = "${windowsConfig.projectName}", "${windowsConfig.relativeProjPath}", "{${windowsConfig.pathGUID.toUpperCase()}}"
EndProject
`;
return {
pattern: 'Global',
patch: solutionInsert,
unpatch: new RegExp(`Project.+${windowsConfig.projectName}.+\\s+EndProject\\s+`),
};
};
@@ -1,6 +0,0 @@
module.exports = function makeUsingPatch(packageImportPath) {
return {
pattern: 'using ReactNative.Modules.Core;',
patch: '\n' + packageImportPath,
};
};
@@ -1,9 +0,0 @@
const fs = require('fs');
module.exports = function revokePatch(file, patch) {
const unpatch = patch.unpatch || patch.patch;
fs.writeFileSync(file, fs
.readFileSync(file, 'utf8')
.replace(unpatch, '')
);
};
@@ -1,26 +0,0 @@
const applyPatch = require('./patches/applyPatch');
const makeProjectPatch = require('./patches/makeProjectPatch');
const makeSolutionPatch = require('./patches/makeSolutionPatch');
const makeUsingPatch = require('./patches/makeUsingPatch');
const makePackagePatch = require('./patches/makePackagePatch');
module.exports = function registerNativeWindowsModule(
name,
windowsConfig,
params,
projectConfig
) {
applyPatch(projectConfig.projectPath, makeProjectPatch(windowsConfig), true);
applyPatch(projectConfig.solutionPath, makeSolutionPatch(windowsConfig), true);
applyPatch(
projectConfig.mainPage,
makePackagePatch(windowsConfig.packageInstance, params, name)
);
applyPatch(
projectConfig.mainPage,
makeUsingPatch(windowsConfig.packageUsingPath)
);
};
@@ -1,27 +0,0 @@
const fs = require('fs');
const toCamelCase = require('lodash').camelCase;
const revokePatch = require('./patches/revokePatch');
const makeProjectPatch = require('./patches/makeProjectPatch');
const makeSolutionPatch = require('./patches/makeSolutionPatch');
const makeUsingPatch = require('./patches/makeUsingPatch');
const makePackagePatch = require('./patches/makePackagePatch');
module.exports = function unregisterNativeWindowsModule(
name,
windowsConfig,
projectConfig
) {
revokePatch(projectConfig.projectPath, makeProjectPatch(windowsConfig));
revokePatch(projectConfig.solutionPath, makeSolutionPatch(windowsConfig));
revokePatch(
projectConfig.mainPage,
makePackagePatch(windowsConfig.packageInstance, {}, name)
);
revokePatch(
projectConfig.mainPage,
makeUsingPatch(windowsConfig.packageUsingPath)
);
};
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.51.1",
"description": "A framework for building native apps using React",
"license": "BSD-3-Clause",
"repository": {
@@ -207,4 +207,4 @@
"shelljs": "^0.7.8",
"sinon": "^2.2.0"
}
}
}