mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a5b6a5a09 | ||
|
|
35b8519f15 | ||
|
|
0e9ed6f790 | ||
|
|
4080141a93 | ||
|
|
a7524c27cc | ||
|
|
94c5a3412f | ||
|
|
468309bbae | ||
|
|
7b861cf336 | ||
|
|
f6f8d7b158 | ||
|
|
9a8784c241 | ||
|
|
6bab7356a6 | ||
|
|
9954499df1 | ||
|
|
f465017fe8 | ||
|
|
02a61a8d8e | ||
|
|
33650db050 | ||
|
|
cc1499e124 | ||
|
|
7cb831d164 | ||
|
|
e6c1fb7212 | ||
|
|
e56089af1d | ||
|
|
e258e5f214 | ||
|
|
3259353fce |
@@ -62,11 +62,11 @@ aliases:
|
||||
|
||||
- &restore-cache-buck
|
||||
keys:
|
||||
- v1-buck-{{ arch }}-v2017.11.16.01
|
||||
- v1-buck-{{ arch }}-v2017.09.04.02
|
||||
- &save-cache-buck
|
||||
paths:
|
||||
- ~/buck
|
||||
key: v1-buck-{{ arch }}-v2017.11.16.01
|
||||
key: v1-buck-{{ arch }}-v2017.09.04.02
|
||||
|
||||
- &restore-cache-watchman
|
||||
keys:
|
||||
|
||||
@@ -528,14 +528,14 @@ module.exports = {
|
||||
ValueXY: AnimatedValueXY,
|
||||
/**
|
||||
* Exported to use the Interpolation type in flow.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#interpolation
|
||||
*/
|
||||
Interpolation: AnimatedInterpolation,
|
||||
/**
|
||||
* Exported for ease of type checking. All animated values derive from this
|
||||
* class.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#node
|
||||
*/
|
||||
Node: AnimatedNode,
|
||||
@@ -557,7 +557,7 @@ module.exports = {
|
||||
/**
|
||||
* Animates a value according to an analytical spring model based on
|
||||
* damped harmonic oscillation.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#spring
|
||||
*/
|
||||
spring,
|
||||
@@ -565,7 +565,7 @@ module.exports = {
|
||||
/**
|
||||
* Creates a new Animated value composed from two Animated values added
|
||||
* together.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#add
|
||||
*/
|
||||
add,
|
||||
@@ -573,7 +573,7 @@ module.exports = {
|
||||
/**
|
||||
* Creates a new Animated value composed by dividing the first Animated value
|
||||
* by the second Animated value.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#divide
|
||||
*/
|
||||
divide,
|
||||
@@ -581,7 +581,7 @@ module.exports = {
|
||||
/**
|
||||
* Creates a new Animated value composed from two Animated values multiplied
|
||||
* together.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#multiply
|
||||
*/
|
||||
multiply,
|
||||
@@ -589,7 +589,7 @@ module.exports = {
|
||||
/**
|
||||
* Creates a new Animated value that is the (non-negative) modulo of the
|
||||
* provided Animated value.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#modulo
|
||||
*/
|
||||
modulo,
|
||||
@@ -598,14 +598,14 @@ module.exports = {
|
||||
* Create a new Animated value that is limited between 2 values. It uses the
|
||||
* difference between the last value so even if the value is far from the
|
||||
* bounds it will start changing when the value starts getting closer again.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#diffclamp
|
||||
*/
|
||||
diffClamp,
|
||||
|
||||
/**
|
||||
* Starts an animation after the given delay.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#delay
|
||||
*/
|
||||
delay,
|
||||
@@ -613,7 +613,7 @@ module.exports = {
|
||||
* Starts an array of animations in order, waiting for each to complete
|
||||
* before starting the next. If the current running animation is stopped, no
|
||||
* following animations will be started.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#sequence
|
||||
*/
|
||||
sequence,
|
||||
@@ -621,44 +621,44 @@ module.exports = {
|
||||
* Starts an array of animations all at the same time. By default, if one
|
||||
* of the animations is stopped, they will all be stopped. You can override
|
||||
* this with the `stopTogether` flag.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#parallel
|
||||
*/
|
||||
parallel,
|
||||
/**
|
||||
* Array of animations may run in parallel (overlap), but are started in
|
||||
* sequence with successive delays. Nice for doing trailing effects.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#stagger
|
||||
*/
|
||||
stagger,
|
||||
/**
|
||||
* Loops a given animation continuously, so that each time it reaches the
|
||||
* end, it resets and begins again from the start.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#loop
|
||||
*/
|
||||
*/
|
||||
loop,
|
||||
|
||||
/**
|
||||
* Takes an array of mappings and extracts values from each arg accordingly,
|
||||
* then calls `setValue` on the mapped outputs.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#event
|
||||
*/
|
||||
event,
|
||||
|
||||
/**
|
||||
* Make any React component Animatable. Used to create `Animated.View`, etc.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#createanimatedcomponent
|
||||
*/
|
||||
createAnimatedComponent,
|
||||
|
||||
/**
|
||||
* Imperative API to attach an animated value to an event on a view. Prefer
|
||||
* Imperative API to attach an animated value to an event on a view. Prefer
|
||||
* using `Animated.event` with `useNativeDrive: true` if possible.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#attachnativeevent
|
||||
*/
|
||||
attachNativeEvent,
|
||||
@@ -666,7 +666,7 @@ module.exports = {
|
||||
/**
|
||||
* Advanced imperative API for snooping on animated events that are passed in
|
||||
* through props. Use values directly where possible.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#forkevent
|
||||
*/
|
||||
forkEvent,
|
||||
|
||||
@@ -193,9 +193,7 @@ function validateTransform(configs: Array<Object>): void {
|
||||
configs.forEach(config => {
|
||||
if (!TRANSFORM_WHITELIST.hasOwnProperty(config.property)) {
|
||||
throw new Error(
|
||||
`Property '${
|
||||
config.property
|
||||
}' is not supported by native animated module`,
|
||||
`Property '${config.property}' is not supported by native animated module`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -68,7 +68,7 @@ function _flush(rootNode: AnimatedValue): void {
|
||||
* multiple properties in a synchronized fashion, but can only be driven by one
|
||||
* mechanism at a time. Using a new mechanism (e.g. starting a new animation,
|
||||
* or calling `setValue`) will stop any previous ones.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html
|
||||
*/
|
||||
class AnimatedValue extends AnimatedWithChildren {
|
||||
@@ -108,7 +108,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
/**
|
||||
* Directly set the value. This will stop any animations running on the value
|
||||
* and update all the bound properties.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#setvalue
|
||||
*/
|
||||
setValue(value: number): void {
|
||||
@@ -129,7 +129,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
* Sets an offset that is applied on top of whatever value is set, whether via
|
||||
* `setValue`, an animation, or `Animated.event`. Useful for compensating
|
||||
* things like the start of a pan gesture.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#setoffset
|
||||
*/
|
||||
setOffset(offset: number): void {
|
||||
@@ -142,7 +142,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
/**
|
||||
* Merges the offset value into the base value and resets the offset to zero.
|
||||
* The final output of the value is unchanged.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#flattenoffset
|
||||
*/
|
||||
flattenOffset(): void {
|
||||
@@ -156,7 +156,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
/**
|
||||
* Sets the offset value to the base value, and resets the base value to zero.
|
||||
* The final output of the value is unchanged.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#extractoffset
|
||||
*/
|
||||
extractOffset(): void {
|
||||
@@ -171,7 +171,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
* Adds an asynchronous listener to the value so you can observe updates from
|
||||
* animations. This is useful because there is no way to
|
||||
* synchronously read the value because it might be driven natively.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#addlistener
|
||||
*/
|
||||
addListener(callback: ValueListenerCallback): string {
|
||||
@@ -184,9 +184,9 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a listener. The `id` param shall match the identifier
|
||||
* previously returned by `addListener()`.
|
||||
*
|
||||
* Unregister a listener. The `id` param shall match the identifier
|
||||
* previously returned by `addListener()`.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#removelistener
|
||||
*/
|
||||
removeListener(id: string): void {
|
||||
@@ -198,7 +198,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
|
||||
/**
|
||||
* Remove all registered listeners.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#removealllisteners
|
||||
*/
|
||||
removeAllListeners(): void {
|
||||
@@ -239,7 +239,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
* Stops any running animation or tracking. `callback` is invoked with the
|
||||
* final value after stopping the animation, which is useful for updating
|
||||
* state to match the animation position with layout.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#stopanimation
|
||||
*/
|
||||
stopAnimation(callback?: ?(value: number) => void): void {
|
||||
@@ -251,7 +251,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
|
||||
/**
|
||||
* Stops any animation and resets the value to its original.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#resetanimation
|
||||
*/
|
||||
resetAnimation(callback?: ?(value: number) => void): void {
|
||||
@@ -270,7 +270,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
/**
|
||||
* Typically only used internally, but could be used by a custom Animation
|
||||
* class.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#animate
|
||||
*/
|
||||
animate(animation: Animation, callback: ?EndCallback): void {
|
||||
|
||||
@@ -22,9 +22,9 @@ type ValueXYListenerCallback = (value: {x: number, y: number}) => void;
|
||||
let _uniqueId = 1;
|
||||
|
||||
/**
|
||||
* 2D Value for driving 2D animations, such as pan gestures. Almost identical
|
||||
* 2D Value for driving 2D animations, such as pan gestures. Almost identical
|
||||
* API to normal `Animated.Value`, but multiplexed.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html
|
||||
*/
|
||||
class AnimatedValueXY extends AnimatedWithChildren {
|
||||
@@ -55,7 +55,7 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
/**
|
||||
* Directly set the value. This will stop any animations running on the value
|
||||
* and update all the bound properties.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#setvalue
|
||||
*/
|
||||
setValue(value: {x: number, y: number}) {
|
||||
@@ -64,10 +64,10 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an offset that is applied on top of whatever value is set, whether
|
||||
* via `setValue`, an animation, or `Animated.event`. Useful for compensating
|
||||
* Sets an offset that is applied on top of whatever value is set, whether
|
||||
* via `setValue`, an animation, or `Animated.event`. Useful for compensating
|
||||
* things like the start of a pan gesture.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#setoffset
|
||||
*/
|
||||
setOffset(offset: {x: number, y: number}) {
|
||||
@@ -76,9 +76,9 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the offset value into the base value and resets the offset to zero.
|
||||
* Merges the offset value into the base value and resets the offset to zero.
|
||||
* The final output of the value is unchanged.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#flattenoffset
|
||||
*/
|
||||
flattenOffset(): void {
|
||||
@@ -87,9 +87,9 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the offset value to the base value, and resets the base value to
|
||||
* Sets the offset value to the base value, and resets the base value to
|
||||
* zero. The final output of the value is unchanged.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#extractoffset
|
||||
*/
|
||||
extractOffset(): void {
|
||||
@@ -106,7 +106,7 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
|
||||
/**
|
||||
* Stops any animation and resets the value to its original.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#resetanimation
|
||||
*/
|
||||
resetAnimation(callback?: (value: {x: number, y: number}) => void): void {
|
||||
@@ -116,10 +116,10 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any running animation or tracking. `callback` is invoked with the
|
||||
* final value after stopping the animation, which is useful for updating
|
||||
* Stops any running animation or tracking. `callback` is invoked with the
|
||||
* final value after stopping the animation, which is useful for updating
|
||||
* state to match the animation position with layout.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#stopanimation
|
||||
*/
|
||||
stopAnimation(callback?: (value: {x: number, y: number}) => void): void {
|
||||
@@ -129,12 +129,12 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an asynchronous listener to the value so you can observe updates from
|
||||
* animations. This is useful because there is no way to synchronously read
|
||||
* Adds an asynchronous listener to the value so you can observe updates from
|
||||
* animations. This is useful because there is no way to synchronously read
|
||||
* the value because it might be driven natively.
|
||||
*
|
||||
*
|
||||
* Returns a string that serves as an identifier for the listener.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#addlistener
|
||||
*/
|
||||
addListener(callback: ValueXYListenerCallback): string {
|
||||
@@ -150,9 +150,9 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a listener. The `id` param shall match the identifier
|
||||
* previously returned by `addListener()`.
|
||||
*
|
||||
* Unregister a listener. The `id` param shall match the identifier
|
||||
* previously returned by `addListener()`.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#removelistener
|
||||
*/
|
||||
removeListener(id: string): void {
|
||||
@@ -163,7 +163,7 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
|
||||
/**
|
||||
* Remove all registered listeners.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#removealllisteners
|
||||
*/
|
||||
removeAllListeners(): void {
|
||||
@@ -174,7 +174,7 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
|
||||
/**
|
||||
* Converts `{x, y}` into `{left, top}` for use in style.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#getlayout
|
||||
*/
|
||||
getLayout(): {[key: string]: AnimatedValue} {
|
||||
@@ -186,7 +186,7 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
|
||||
/**
|
||||
* Converts `{x, y}` into a useable translation transform.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#gettranslatetransform
|
||||
*/
|
||||
getTranslateTransform(): Array<{[key: string]: AnimatedValue}> {
|
||||
|
||||
@@ -59,9 +59,7 @@ class URL {
|
||||
if (BLOB_URL_PREFIX === null) {
|
||||
throw new Error('Cannot create URL for blob!');
|
||||
}
|
||||
return `${BLOB_URL_PREFIX}${blob.blobId}?offset=${blob.offset}&size=${
|
||||
blob.size
|
||||
}`;
|
||||
return `${BLOB_URL_PREFIX}${blob.blobId}?offset=${blob.offset}&size=${blob.size}`;
|
||||
}
|
||||
|
||||
static revokeObjectURL(url: string) {
|
||||
|
||||
@@ -35,7 +35,6 @@ class BugReporting {
|
||||
static _extraSources: Map<string, SourceCallback> = new Map();
|
||||
static _fileSources: Map<string, SourceCallback> = new Map();
|
||||
static _subscription: ?EmitterSubscription = null;
|
||||
static _redboxSubscription: ?EmitterSubscription = null;
|
||||
|
||||
static _maybeInit() {
|
||||
if (!BugReporting._subscription) {
|
||||
@@ -43,11 +42,6 @@ class BugReporting {
|
||||
.addListener('collectBugExtraData', BugReporting.collectExtraData, null);
|
||||
defaultExtras();
|
||||
}
|
||||
|
||||
if (!BugReporting._redboxSubscription) {
|
||||
BugReporting._redboxSubscription = RCTDeviceEventEmitter
|
||||
.addListener('collectRedBoxExtraData', BugReporting.collectExtraData, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,11 +98,6 @@ class BugReporting {
|
||||
BugReportingNativeModule.setExtraData &&
|
||||
BugReportingNativeModule.setExtraData(extraData, fileData);
|
||||
|
||||
const RedBoxNativeModule = require('NativeModules').RedBox;
|
||||
RedBoxNativeModule &&
|
||||
RedBoxNativeModule.setExtraData &&
|
||||
RedBoxNativeModule.setExtraData(extraData, 'From BugReporting.js');
|
||||
|
||||
return { extras: extraData, files: fileData };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ const getPhotosParamChecker = createStrictShapeTypeChecker({
|
||||
groupName: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Specifies filter on asset type
|
||||
*/
|
||||
* Specifies filter on asset type
|
||||
*/
|
||||
assetType: PropTypes.oneOf(Object.keys(ASSET_TYPE_OPTIONS)),
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
exports.version = {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
minor: 52,
|
||||
patch: 3,
|
||||
prerelease: null,
|
||||
};
|
||||
|
||||
@@ -60,17 +60,17 @@ export type ViewabilityConfig = {|
|
||||
|};
|
||||
|
||||
/**
|
||||
* A Utility class for calculating viewable items based on current metrics like scroll position and
|
||||
* layout.
|
||||
*
|
||||
* An item is said to be in a "viewable" state when any of the following
|
||||
* is true for longer than `minimumViewTime` milliseconds (after an interaction if `waitForInteraction`
|
||||
* is true):
|
||||
*
|
||||
* - Occupying >= `viewAreaCoveragePercentThreshold` of the view area XOR fraction of the item
|
||||
* visible in the view area >= `itemVisiblePercentThreshold`.
|
||||
* - Entirely visible on screen
|
||||
*/
|
||||
* A Utility class for calculating viewable items based on current metrics like scroll position and
|
||||
* layout.
|
||||
*
|
||||
* An item is said to be in a "viewable" state when any of the following
|
||||
* is true for longer than `minimumViewTime` milliseconds (after an interaction if `waitForInteraction`
|
||||
* is true):
|
||||
*
|
||||
* - Occupying >= `viewAreaCoveragePercentThreshold` of the view area XOR fraction of the item
|
||||
* visible in the view area >= `itemVisiblePercentThreshold`.
|
||||
* - Entirely visible on screen
|
||||
*/
|
||||
class ViewabilityHelper {
|
||||
_config: ViewabilityConfig;
|
||||
_hasInteracted: boolean = false;
|
||||
|
||||
@@ -59,7 +59,7 @@ let wrapperComponentProvider: ?WrapperComponentProvider;
|
||||
|
||||
/**
|
||||
* `AppRegistry` is the JavaScript entry point to running all React Native apps.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/appregistry.html
|
||||
*/
|
||||
const AppRegistry = {
|
||||
@@ -89,7 +89,7 @@ const AppRegistry = {
|
||||
|
||||
/**
|
||||
* Registers an app's root component.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/appregistry.html#registercomponent
|
||||
*/
|
||||
registerComponent(
|
||||
@@ -155,7 +155,7 @@ const AppRegistry = {
|
||||
|
||||
/**
|
||||
* Loads the JavaScript bundle and runs the app.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/appregistry.html#runapplication
|
||||
*/
|
||||
runApplication(appKey: string, appParameters: any): void {
|
||||
@@ -197,8 +197,8 @@ const AppRegistry = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops an application when a view should be destroyed.
|
||||
*
|
||||
* Stops an application when a view should be destroyed.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/appregistry.html#unmountapplicationcomponentatroottag
|
||||
*/
|
||||
unmountApplicationComponentAtRootTag(rootTag: number): void {
|
||||
@@ -207,7 +207,7 @@ const AppRegistry = {
|
||||
|
||||
/**
|
||||
* Register a headless task. A headless task is a bit of code that runs without a UI.
|
||||
*
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/appregistry.html#registerheadlesstask
|
||||
*/
|
||||
registerHeadlessTask(taskKey: string, task: TaskProvider): void {
|
||||
|
||||
@@ -398,8 +398,7 @@ class YellowBox extends React.Component<
|
||||
onDismissAll={() => this.dismissWarning(null)}
|
||||
onMinimize={() => this.setState({inspecting: null})}
|
||||
toggleStacktrace={() =>
|
||||
this.setState({stacktraceVisible: !stacktraceVisible})
|
||||
}
|
||||
this.setState({stacktraceVisible: !stacktraceVisible})}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ Error: ${e.message}`
|
||||
}
|
||||
case 'error': {
|
||||
HMRLoadingView.hide();
|
||||
throw new Error(data.body.type + ' ' + data.body.description);
|
||||
throw new Error(`${data.body.type}: ${data.body.message}`);
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unexpected message: ${data}`);
|
||||
|
||||
@@ -75,15 +75,13 @@ class EventCheckBoxExample extends React.Component<{}, $FlowFixMeState> {
|
||||
<View>
|
||||
<CheckBox
|
||||
onValueChange={value =>
|
||||
this.setState({eventCheckBoxRegressionIsOn: value})
|
||||
}
|
||||
this.setState({eventCheckBoxRegressionIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventCheckBoxRegressionIsOn}
|
||||
/>
|
||||
<CheckBox
|
||||
onValueChange={value =>
|
||||
this.setState({eventCheckBoxRegressionIsOn: value})
|
||||
}
|
||||
this.setState({eventCheckBoxRegressionIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventCheckBoxRegressionIsOn}
|
||||
/>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#define RCT_REACT_NATIVE_VERSION @{ \
|
||||
@"major": @(0), \
|
||||
@"minor": @(0), \
|
||||
@"patch": @(0), \
|
||||
@"minor": @(52), \
|
||||
@"patch": @(3), \
|
||||
@"prerelease": [NSNull null], \
|
||||
}
|
||||
|
||||
+264
-316
@@ -13,9 +13,7 @@
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTErrorInfo.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTJSStackFrame.h"
|
||||
#import "RCTRedBoxExtraDataViewController.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#if RCT_DEBUG
|
||||
@@ -26,7 +24,6 @@
|
||||
|
||||
- (void)redBoxWindow:(RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(RCTJSStackFrame *)stackFrame;
|
||||
- (void)reloadFromRedBoxWindow:(RCTRedBoxWindow *)redBoxWindow;
|
||||
- (void)loadExtraDataViewController;
|
||||
|
||||
@end
|
||||
|
||||
@@ -36,332 +33,308 @@
|
||||
|
||||
@implementation RCTRedBoxWindow
|
||||
{
|
||||
UITableView *_stackTraceTableView;
|
||||
NSString *_lastErrorMessage;
|
||||
NSArray<RCTJSStackFrame *> *_lastStackTrace;
|
||||
UITableView *_stackTraceTableView;
|
||||
NSString *_lastErrorMessage;
|
||||
NSArray<RCTJSStackFrame *> *_lastStackTrace;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
#if TARGET_OS_TV
|
||||
self.windowLevel = UIWindowLevelAlert + 1000;
|
||||
self.windowLevel = UIWindowLevelAlert + 1000;
|
||||
#else
|
||||
self.windowLevel = UIWindowLevelStatusBar - 1;
|
||||
self.windowLevel = UIWindowLevelStatusBar - 1;
|
||||
#endif
|
||||
self.backgroundColor = [UIColor colorWithRed:0.8 green:0 blue:0 alpha:1];
|
||||
self.hidden = YES;
|
||||
self.backgroundColor = [UIColor colorWithRed:0.8 green:0 blue:0 alpha:1];
|
||||
self.hidden = YES;
|
||||
|
||||
UIViewController *rootController = [UIViewController new];
|
||||
self.rootViewController = rootController;
|
||||
UIView *rootView = rootController.view;
|
||||
rootView.backgroundColor = [UIColor clearColor];
|
||||
UIViewController *rootController = [UIViewController new];
|
||||
self.rootViewController = rootController;
|
||||
UIView *rootView = rootController.view;
|
||||
rootView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
const CGFloat buttonHeight = 60;
|
||||
const CGFloat buttonHeight = 60;
|
||||
|
||||
CGRect detailsFrame = rootView.bounds;
|
||||
detailsFrame.size.height -= buttonHeight;
|
||||
CGRect detailsFrame = rootView.bounds;
|
||||
detailsFrame.size.height -= buttonHeight;
|
||||
|
||||
_stackTraceTableView = [[UITableView alloc] initWithFrame:detailsFrame style:UITableViewStylePlain];
|
||||
_stackTraceTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_stackTraceTableView.delegate = self;
|
||||
_stackTraceTableView.dataSource = self;
|
||||
_stackTraceTableView.backgroundColor = [UIColor clearColor];
|
||||
_stackTraceTableView = [[UITableView alloc] initWithFrame:detailsFrame style:UITableViewStylePlain];
|
||||
_stackTraceTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_stackTraceTableView.delegate = self;
|
||||
_stackTraceTableView.dataSource = self;
|
||||
_stackTraceTableView.backgroundColor = [UIColor clearColor];
|
||||
#if !TARGET_OS_TV
|
||||
_stackTraceTableView.separatorColor = [UIColor colorWithWhite:1 alpha:0.3];
|
||||
_stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
_stackTraceTableView.separatorColor = [UIColor colorWithWhite:1 alpha:0.3];
|
||||
_stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
#endif
|
||||
_stackTraceTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
|
||||
[rootView addSubview:_stackTraceTableView];
|
||||
_stackTraceTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
|
||||
[rootView addSubview:_stackTraceTableView];
|
||||
|
||||
#if TARGET_OS_SIMULATOR
|
||||
NSString *reloadText = @"Reload JS (\u2318R)";
|
||||
NSString *dismissText = @"Dismiss (ESC)";
|
||||
NSString *copyText = @"Copy (\u2325\u2318C)";
|
||||
NSString *extraText = @"Extra Info (\u2318E)";
|
||||
#else
|
||||
NSString *reloadText = @"Reload JS";
|
||||
NSString *dismissText = @"Dismiss";
|
||||
NSString *copyText = @"Copy";
|
||||
NSString *extraText = @"Extra Info";
|
||||
#endif
|
||||
#if TARGET_OS_SIMULATOR
|
||||
NSString *reloadText = @"Reload JS (\u2318R)";
|
||||
NSString *dismissText = @"Dismiss (ESC)";
|
||||
NSString *copyText = @"Copy (\u2325\u2318C)";
|
||||
#else
|
||||
NSString *reloadText = @"Reload JS";
|
||||
NSString *dismissText = @"Dismiss";
|
||||
NSString *copyText = @"Copy";
|
||||
#endif
|
||||
|
||||
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
dismissButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
|
||||
dismissButton.accessibilityIdentifier = @"redbox-dismiss";
|
||||
dismissButton.titleLabel.font = [UIFont systemFontOfSize:13];
|
||||
[dismissButton setTitle:dismissText forState:UIControlStateNormal];
|
||||
[dismissButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateNormal];
|
||||
[dismissButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
[dismissButton addTarget:self action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside];
|
||||
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
dismissButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
|
||||
dismissButton.accessibilityIdentifier = @"redbox-dismiss";
|
||||
dismissButton.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||
[dismissButton setTitle:dismissText forState:UIControlStateNormal];
|
||||
[dismissButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateNormal];
|
||||
[dismissButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
[dismissButton addTarget:self action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
UIButton *reloadButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
reloadButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;
|
||||
reloadButton.accessibilityIdentifier = @"redbox-reload";
|
||||
reloadButton.titleLabel.font = [UIFont systemFontOfSize:13];
|
||||
UIButton *reloadButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
reloadButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;
|
||||
reloadButton.accessibilityIdentifier = @"redbox-reload";
|
||||
reloadButton.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||
|
||||
[reloadButton setTitle:reloadText forState:UIControlStateNormal];
|
||||
[reloadButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateNormal];
|
||||
[reloadButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
[reloadButton addTarget:self action:@selector(reload) forControlEvents:UIControlEventTouchUpInside];
|
||||
[reloadButton setTitle:reloadText forState:UIControlStateNormal];
|
||||
[reloadButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateNormal];
|
||||
[reloadButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
[reloadButton addTarget:self action:@selector(reload) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
UIButton *copyButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
copyButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;
|
||||
copyButton.accessibilityIdentifier = @"redbox-copy";
|
||||
copyButton.titleLabel.font = [UIFont systemFontOfSize:13];
|
||||
[copyButton setTitle:copyText forState:UIControlStateNormal];
|
||||
[copyButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateNormal];
|
||||
[copyButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
[copyButton addTarget:self action:@selector(copyStack) forControlEvents:UIControlEventTouchUpInside];
|
||||
UIButton *copyButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
copyButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;
|
||||
copyButton.accessibilityIdentifier = @"redbox-copy";
|
||||
copyButton.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||
[copyButton setTitle:copyText forState:UIControlStateNormal];
|
||||
[copyButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateNormal];
|
||||
[copyButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
[copyButton addTarget:self action:@selector(copyStack) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
UIButton *extraButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
extraButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;
|
||||
extraButton.accessibilityIdentifier = @"redbox-extra";
|
||||
extraButton.titleLabel.font = [UIFont systemFontOfSize:13];
|
||||
[extraButton setTitle:extraText forState:UIControlStateNormal];
|
||||
[extraButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateNormal];
|
||||
[extraButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
[extraButton addTarget:self action:@selector(showExtraDataViewController) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
CGFloat buttonWidth = self.bounds.size.width / 4;
|
||||
dismissButton.frame = CGRectMake(0, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||
reloadButton.frame = CGRectMake(buttonWidth, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||
copyButton.frame = CGRectMake(buttonWidth * 2, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||
extraButton.frame = CGRectMake(buttonWidth * 3, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||
|
||||
[rootView addSubview:dismissButton];
|
||||
[rootView addSubview:reloadButton];
|
||||
[rootView addSubview:copyButton];
|
||||
[rootView addSubview:extraButton];
|
||||
}
|
||||
return self;
|
||||
CGFloat buttonWidth = self.bounds.size.width / 3;
|
||||
dismissButton.frame = CGRectMake(0, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||
reloadButton.frame = CGRectMake(buttonWidth, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||
copyButton.frame = CGRectMake(buttonWidth * 2, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||
[rootView addSubview:dismissButton];
|
||||
[rootView addSubview:reloadButton];
|
||||
[rootView addSubview:copyButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_stackTraceTableView.dataSource = nil;
|
||||
_stackTraceTableView.delegate = nil;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
_stackTraceTableView.dataSource = nil;
|
||||
_stackTraceTableView.delegate = nil;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message withStack:(NSArray<RCTJSStackFrame *> *)stack isUpdate:(BOOL)isUpdate
|
||||
{
|
||||
// Show if this is a new message, or if we're updating the previous message
|
||||
if ((self.hidden && !isUpdate) || (!self.hidden && isUpdate && [_lastErrorMessage isEqualToString:message])) {
|
||||
_lastStackTrace = stack;
|
||||
// message is displayed using UILabel, which is unable to render text of
|
||||
// unlimited length, so we truncate it
|
||||
_lastErrorMessage = [message substringToIndex:MIN((NSUInteger)10000, message.length)];
|
||||
// Show if this is a new message, or if we're updating the previous message
|
||||
if ((self.hidden && !isUpdate) || (!self.hidden && isUpdate && [_lastErrorMessage isEqualToString:message])) {
|
||||
_lastStackTrace = stack;
|
||||
// message is displayed using UILabel, which is unable to render text of
|
||||
// unlimited length, so we truncate it
|
||||
_lastErrorMessage = [message substringToIndex:MIN((NSUInteger)10000, message.length)];
|
||||
|
||||
[_stackTraceTableView reloadData];
|
||||
[_stackTraceTableView reloadData];
|
||||
|
||||
if (self.hidden) {
|
||||
[_stackTraceTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
|
||||
atScrollPosition:UITableViewScrollPositionTop
|
||||
animated:NO];
|
||||
}
|
||||
|
||||
[self makeKeyAndVisible];
|
||||
[self becomeFirstResponder];
|
||||
if (self.hidden) {
|
||||
[_stackTraceTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
|
||||
atScrollPosition:UITableViewScrollPositionTop
|
||||
animated:NO];
|
||||
}
|
||||
|
||||
[self makeKeyAndVisible];
|
||||
[self becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismiss
|
||||
{
|
||||
self.hidden = YES;
|
||||
[self resignFirstResponder];
|
||||
[RCTSharedApplication().delegate.window makeKeyWindow];
|
||||
self.hidden = YES;
|
||||
[self resignFirstResponder];
|
||||
[RCTSharedApplication().delegate.window makeKeyWindow];
|
||||
}
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
[_actionDelegate reloadFromRedBoxWindow:self];
|
||||
}
|
||||
|
||||
- (void)showExtraDataViewController
|
||||
{
|
||||
[_actionDelegate loadExtraDataViewController];
|
||||
[_actionDelegate reloadFromRedBoxWindow:self];
|
||||
}
|
||||
|
||||
- (void)copyStack
|
||||
{
|
||||
NSMutableString *fullStackTrace;
|
||||
NSMutableString *fullStackTrace;
|
||||
|
||||
if (_lastErrorMessage != nil) {
|
||||
fullStackTrace = [_lastErrorMessage mutableCopy];
|
||||
[fullStackTrace appendString:@"\n\n"];
|
||||
}
|
||||
else {
|
||||
fullStackTrace = [NSMutableString string];
|
||||
}
|
||||
if (_lastErrorMessage != nil) {
|
||||
fullStackTrace = [_lastErrorMessage mutableCopy];
|
||||
[fullStackTrace appendString:@"\n\n"];
|
||||
}
|
||||
else {
|
||||
fullStackTrace = [NSMutableString string];
|
||||
}
|
||||
|
||||
for (RCTJSStackFrame *stackFrame in _lastStackTrace) {
|
||||
[fullStackTrace appendString:[NSString stringWithFormat:@"%@\n", stackFrame.methodName]];
|
||||
if (stackFrame.file) {
|
||||
[fullStackTrace appendFormat:@" %@\n", [self formatFrameSource:stackFrame]];
|
||||
}
|
||||
for (RCTJSStackFrame *stackFrame in _lastStackTrace) {
|
||||
[fullStackTrace appendString:[NSString stringWithFormat:@"%@\n", stackFrame.methodName]];
|
||||
if (stackFrame.file) {
|
||||
[fullStackTrace appendFormat:@" %@\n", [self formatFrameSource:stackFrame]];
|
||||
}
|
||||
}
|
||||
#if !TARGET_OS_TV
|
||||
UIPasteboard *pb = [UIPasteboard generalPasteboard];
|
||||
[pb setString:fullStackTrace];
|
||||
UIPasteboard *pb = [UIPasteboard generalPasteboard];
|
||||
[pb setString:fullStackTrace];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSString *)formatFrameSource:(RCTJSStackFrame *)stackFrame
|
||||
{
|
||||
NSString *fileName = RCTNilIfNull(stackFrame.file) ? [stackFrame.file lastPathComponent] : @"<unknown file>";
|
||||
NSString *lineInfo = [NSString stringWithFormat:@"%@:%lld",
|
||||
fileName,
|
||||
(long long)stackFrame.lineNumber];
|
||||
NSString *fileName = RCTNilIfNull(stackFrame.file) ? [stackFrame.file lastPathComponent] : @"<unknown file>";
|
||||
NSString *lineInfo = [NSString stringWithFormat:@"%@:%lld",
|
||||
fileName,
|
||||
(long long)stackFrame.lineNumber];
|
||||
|
||||
if (stackFrame.column != 0) {
|
||||
lineInfo = [lineInfo stringByAppendingFormat:@":%lld", (long long)stackFrame.column];
|
||||
}
|
||||
return lineInfo;
|
||||
if (stackFrame.column != 0) {
|
||||
lineInfo = [lineInfo stringByAppendingFormat:@":%lld", (long long)stackFrame.column];
|
||||
}
|
||||
return lineInfo;
|
||||
}
|
||||
|
||||
#pragma mark - TableView
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(__unused UITableView *)tableView
|
||||
{
|
||||
return 2;
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(__unused UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return section == 0 ? 1 : _lastStackTrace.count;
|
||||
return section == 0 ? 1 : _lastStackTrace.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == 0) {
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"msg-cell"];
|
||||
return [self reuseCell:cell forErrorMessage:_lastErrorMessage];
|
||||
}
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
|
||||
NSUInteger index = indexPath.row;
|
||||
RCTJSStackFrame *stackFrame = _lastStackTrace[index];
|
||||
return [self reuseCell:cell forStackFrame:stackFrame];
|
||||
if (indexPath.section == 0) {
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"msg-cell"];
|
||||
return [self reuseCell:cell forErrorMessage:_lastErrorMessage];
|
||||
}
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
|
||||
NSUInteger index = indexPath.row;
|
||||
RCTJSStackFrame *stackFrame = _lastStackTrace[index];
|
||||
return [self reuseCell:cell forStackFrame:stackFrame];
|
||||
}
|
||||
|
||||
- (UITableViewCell *)reuseCell:(UITableViewCell *)cell forErrorMessage:(NSString *)message
|
||||
{
|
||||
if (!cell) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"msg-cell"];
|
||||
cell.textLabel.accessibilityIdentifier = @"redbox-error";
|
||||
cell.textLabel.textColor = [UIColor whiteColor];
|
||||
cell.textLabel.font = [UIFont boldSystemFontOfSize:16];
|
||||
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
cell.textLabel.numberOfLines = 0;
|
||||
cell.detailTextLabel.textColor = [UIColor whiteColor];
|
||||
cell.backgroundColor = [UIColor clearColor];
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
}
|
||||
if (!cell) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"msg-cell"];
|
||||
cell.textLabel.accessibilityIdentifier = @"redbox-error";
|
||||
cell.textLabel.textColor = [UIColor whiteColor];
|
||||
cell.textLabel.font = [UIFont boldSystemFontOfSize:16];
|
||||
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
cell.textLabel.numberOfLines = 0;
|
||||
cell.detailTextLabel.textColor = [UIColor whiteColor];
|
||||
cell.backgroundColor = [UIColor clearColor];
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
}
|
||||
|
||||
cell.textLabel.text = message;
|
||||
cell.textLabel.text = message;
|
||||
|
||||
return cell;
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)reuseCell:(UITableViewCell *)cell forStackFrame:(RCTJSStackFrame *)stackFrame
|
||||
{
|
||||
if (!cell) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
|
||||
cell.textLabel.textColor = [UIColor colorWithWhite:1 alpha:0.9];
|
||||
cell.textLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:14];
|
||||
cell.textLabel.lineBreakMode = NSLineBreakByCharWrapping;
|
||||
cell.textLabel.numberOfLines = 2;
|
||||
cell.detailTextLabel.textColor = [UIColor colorWithWhite:1 alpha:0.7];
|
||||
cell.detailTextLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:11];
|
||||
cell.detailTextLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
||||
cell.backgroundColor = [UIColor clearColor];
|
||||
cell.selectedBackgroundView = [UIView new];
|
||||
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.2];
|
||||
}
|
||||
if (!cell) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
|
||||
cell.textLabel.textColor = [UIColor colorWithWhite:1 alpha:0.9];
|
||||
cell.textLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:14];
|
||||
cell.textLabel.lineBreakMode = NSLineBreakByCharWrapping;
|
||||
cell.textLabel.numberOfLines = 2;
|
||||
cell.detailTextLabel.textColor = [UIColor colorWithWhite:1 alpha:0.7];
|
||||
cell.detailTextLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:11];
|
||||
cell.detailTextLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
||||
cell.backgroundColor = [UIColor clearColor];
|
||||
cell.selectedBackgroundView = [UIView new];
|
||||
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.2];
|
||||
}
|
||||
|
||||
cell.textLabel.text = stackFrame.methodName ?: @"(unnamed method)";
|
||||
if (stackFrame.file) {
|
||||
cell.detailTextLabel.text = [self formatFrameSource:stackFrame];
|
||||
} else {
|
||||
cell.detailTextLabel.text = @"";
|
||||
}
|
||||
return cell;
|
||||
cell.textLabel.text = stackFrame.methodName ?: @"(unnamed method)";
|
||||
if (stackFrame.file) {
|
||||
cell.detailTextLabel.text = [self formatFrameSource:stackFrame];
|
||||
} else {
|
||||
cell.detailTextLabel.text = @"";
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == 0) {
|
||||
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
||||
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
if (indexPath.section == 0) {
|
||||
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
||||
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
|
||||
NSDictionary *attributes = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:16],
|
||||
NSParagraphStyleAttributeName: paragraphStyle};
|
||||
CGRect boundingRect = [_lastErrorMessage boundingRectWithSize:CGSizeMake(tableView.frame.size.width - 30, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
|
||||
return ceil(boundingRect.size.height) + 40;
|
||||
} else {
|
||||
return 50;
|
||||
}
|
||||
NSDictionary *attributes = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:16],
|
||||
NSParagraphStyleAttributeName: paragraphStyle};
|
||||
CGRect boundingRect = [_lastErrorMessage boundingRectWithSize:CGSizeMake(tableView.frame.size.width - 30, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
|
||||
return ceil(boundingRect.size.height) + 40;
|
||||
} else {
|
||||
return 50;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == 1) {
|
||||
NSUInteger row = indexPath.row;
|
||||
RCTJSStackFrame *stackFrame = _lastStackTrace[row];
|
||||
[_actionDelegate redBoxWindow:self openStackFrameInEditor:stackFrame];
|
||||
}
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
if (indexPath.section == 1) {
|
||||
NSUInteger row = indexPath.row;
|
||||
RCTJSStackFrame *stackFrame = _lastStackTrace[row];
|
||||
[_actionDelegate redBoxWindow:self openStackFrameInEditor:stackFrame];
|
||||
}
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
|
||||
#pragma mark - Key commands
|
||||
|
||||
- (NSArray<UIKeyCommand *> *)keyCommands
|
||||
{
|
||||
// NOTE: We could use RCTKeyCommands for this, but since
|
||||
// we control this window, we can use the standard, non-hacky
|
||||
// mechanism instead
|
||||
// NOTE: We could use RCTKeyCommands for this, but since
|
||||
// we control this window, we can use the standard, non-hacky
|
||||
// mechanism instead
|
||||
|
||||
return @[
|
||||
// Dismiss red box
|
||||
[UIKeyCommand keyCommandWithInput:UIKeyInputEscape
|
||||
modifierFlags:0
|
||||
action:@selector(dismiss)],
|
||||
return @[
|
||||
// Dismiss red box
|
||||
[UIKeyCommand keyCommandWithInput:UIKeyInputEscape
|
||||
modifierFlags:0
|
||||
action:@selector(dismiss)],
|
||||
|
||||
// Reload
|
||||
[UIKeyCommand keyCommandWithInput:@"r"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:@selector(reload)],
|
||||
// Reload
|
||||
[UIKeyCommand keyCommandWithInput:@"r"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:@selector(reload)],
|
||||
|
||||
// Copy = Cmd-Option C since Cmd-C in the simulator copies the pasteboard from
|
||||
// the simulator to the desktop pasteboard.
|
||||
[UIKeyCommand keyCommandWithInput:@"c"
|
||||
modifierFlags:UIKeyModifierCommand | UIKeyModifierAlternate
|
||||
action:@selector(copyStack)],
|
||||
// Copy = Cmd-Option C since Cmd-C in the simulator copies the pasteboard from
|
||||
// the simulator to the desktop pasteboard.
|
||||
[UIKeyCommand keyCommandWithInput:@"c"
|
||||
modifierFlags:UIKeyModifierCommand | UIKeyModifierAlternate
|
||||
action:@selector(copyStack)]
|
||||
|
||||
// Extra data
|
||||
[UIKeyCommand keyCommandWithInput:@"e"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:@selector(showExtraDataViewController)]
|
||||
];
|
||||
];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
return YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTRedBox () <RCTInvalidating, RCTRedBoxWindowActionDelegate, RCTRedBoxExtraDataActionDelegate>
|
||||
@interface RCTRedBox () <RCTInvalidating, RCTRedBoxWindowActionDelegate>
|
||||
@end
|
||||
|
||||
@implementation RCTRedBox
|
||||
{
|
||||
RCTRedBoxWindow *_window;
|
||||
NSMutableArray<id<RCTErrorCustomizer>> *_errorCustomizers;
|
||||
RCTRedBoxExtraDataViewController *_extraDataViewController;
|
||||
RCTRedBoxWindow *_window;
|
||||
NSMutableArray<id<RCTErrorCustomizer>> *_errorCustomizers;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
@@ -370,165 +343,140 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
- (void)registerErrorCustomizer:(id<RCTErrorCustomizer>)errorCustomizer
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (!self->_errorCustomizers) {
|
||||
self->_errorCustomizers = [NSMutableArray array];
|
||||
}
|
||||
if (![self->_errorCustomizers containsObject:errorCustomizer]) {
|
||||
[self->_errorCustomizers addObject:errorCustomizer];
|
||||
}
|
||||
});
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (!self->_errorCustomizers) {
|
||||
self->_errorCustomizers = [NSMutableArray array];
|
||||
}
|
||||
if (![self->_errorCustomizers containsObject:errorCustomizer]) {
|
||||
[self->_errorCustomizers addObject:errorCustomizer];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// WARNING: Should only be called from the main thread/dispatch queue.
|
||||
- (RCTErrorInfo *)_customizeError:(RCTErrorInfo *)error
|
||||
{
|
||||
RCTAssertMainQueue();
|
||||
if (!self->_errorCustomizers) {
|
||||
return error;
|
||||
}
|
||||
for (id<RCTErrorCustomizer> customizer in self->_errorCustomizers) {
|
||||
RCTErrorInfo *newInfo = [customizer customizeErrorInfo:error];
|
||||
if (newInfo) {
|
||||
error = newInfo;
|
||||
}
|
||||
}
|
||||
RCTAssertMainQueue();
|
||||
|
||||
if (!self->_errorCustomizers) {
|
||||
return error;
|
||||
}
|
||||
for (id<RCTErrorCustomizer> customizer in self->_errorCustomizers) {
|
||||
RCTErrorInfo *newInfo = [customizer customizeErrorInfo:error];
|
||||
if (newInfo) {
|
||||
error = newInfo;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
- (void)showError:(NSError *)error
|
||||
{
|
||||
[self showErrorMessage:error.localizedDescription
|
||||
withDetails:error.localizedFailureReason
|
||||
stack:error.userInfo[RCTJSStackTraceKey]];
|
||||
[self showErrorMessage:error.localizedDescription
|
||||
withDetails:error.localizedFailureReason
|
||||
stack:error.userInfo[RCTJSStackTraceKey]];
|
||||
}
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message
|
||||
{
|
||||
[self showErrorMessage:message withParsedStack:nil isUpdate:NO];
|
||||
[self showErrorMessage:message withParsedStack:nil isUpdate:NO];
|
||||
}
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message withDetails:(NSString *)details
|
||||
{
|
||||
[self showErrorMessage:message withDetails:details stack:nil];
|
||||
[self showErrorMessage:message withDetails:details stack:nil];
|
||||
}
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message withDetails:(NSString *)details stack:(NSArray<RCTJSStackFrame *> *)stack {
|
||||
NSString *combinedMessage = message;
|
||||
if (details) {
|
||||
combinedMessage = [NSString stringWithFormat:@"%@\n\n%@", message, details];
|
||||
}
|
||||
[self showErrorMessage:combinedMessage withParsedStack:stack isUpdate:NO];
|
||||
NSString *combinedMessage = message;
|
||||
if (details) {
|
||||
combinedMessage = [NSString stringWithFormat:@"%@\n\n%@", message, details];
|
||||
}
|
||||
[self showErrorMessage:combinedMessage withParsedStack:stack isUpdate:NO];
|
||||
}
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message withRawStack:(NSString *)rawStack
|
||||
{
|
||||
NSArray<RCTJSStackFrame *> *stack = [RCTJSStackFrame stackFramesWithLines:rawStack];
|
||||
[self showErrorMessage:message withParsedStack:stack isUpdate:NO];
|
||||
NSArray<RCTJSStackFrame *> *stack = [RCTJSStackFrame stackFramesWithLines:rawStack];
|
||||
[self showErrorMessage:message withParsedStack:stack isUpdate:NO];
|
||||
}
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message withStack:(NSArray<NSDictionary *> *)stack
|
||||
{
|
||||
[self showErrorMessage:message withParsedStack:[RCTJSStackFrame stackFramesWithDictionaries:stack] isUpdate:NO];
|
||||
[self showErrorMessage:message withParsedStack:[RCTJSStackFrame stackFramesWithDictionaries:stack] isUpdate:NO];
|
||||
}
|
||||
|
||||
- (void)updateErrorMessage:(NSString *)message withStack:(NSArray<NSDictionary *> *)stack
|
||||
{
|
||||
[self showErrorMessage:message withParsedStack:[RCTJSStackFrame stackFramesWithDictionaries:stack] isUpdate:YES];
|
||||
[self showErrorMessage:message withParsedStack:[RCTJSStackFrame stackFramesWithDictionaries:stack] isUpdate:YES];
|
||||
}
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message withParsedStack:(NSArray<RCTJSStackFrame *> *)stack
|
||||
{
|
||||
[self showErrorMessage:message withParsedStack:stack isUpdate:NO];
|
||||
[self showErrorMessage:message withParsedStack:stack isUpdate:NO];
|
||||
}
|
||||
|
||||
- (void)updateErrorMessage:(NSString *)message withParsedStack:(NSArray<RCTJSStackFrame *> *)stack
|
||||
{
|
||||
[self showErrorMessage:message withParsedStack:stack isUpdate:YES];
|
||||
[self showErrorMessage:message withParsedStack:stack isUpdate:YES];
|
||||
}
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message withParsedStack:(NSArray<RCTJSStackFrame *> *)stack isUpdate:(BOOL)isUpdate
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self->_extraDataViewController == nil) {
|
||||
self->_extraDataViewController = [RCTRedBoxExtraDataViewController new];
|
||||
self->_extraDataViewController.actionDelegate = self;
|
||||
}
|
||||
[self->_bridge.eventDispatcher sendDeviceEventWithName:@"collectRedBoxExtraData" body:nil];
|
||||
|
||||
if (!self->_window) {
|
||||
self->_window = [[RCTRedBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
self->_window.actionDelegate = self;
|
||||
}
|
||||
|
||||
RCTErrorInfo *errorInfo = [[RCTErrorInfo alloc] initWithErrorMessage:message
|
||||
stack:stack];
|
||||
errorInfo = [self _customizeError:errorInfo];
|
||||
[self->_window showErrorMessage:errorInfo.errorMessage
|
||||
withStack:errorInfo.stack
|
||||
isUpdate:isUpdate];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)loadExtraDataViewController {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Make sure the CMD+E shortcut doesn't call this twice
|
||||
if (self->_extraDataViewController != nil && ![self->_window.rootViewController presentedViewController]) {
|
||||
[self->_window.rootViewController presentViewController:self->_extraDataViewController animated:YES completion:nil];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setExtraData:(NSDictionary *)extraData forIdentifier:(NSString *)identifier) {
|
||||
[_extraDataViewController addExtraData:extraData forIdentifier:identifier];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (!self->_window) {
|
||||
self->_window = [[RCTRedBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
self->_window.actionDelegate = self;
|
||||
}
|
||||
RCTErrorInfo *errorInfo = [[RCTErrorInfo alloc] initWithErrorMessage:message
|
||||
stack:stack];
|
||||
errorInfo = [self _customizeError:errorInfo];
|
||||
[self->_window showErrorMessage:errorInfo.errorMessage
|
||||
withStack:errorInfo.stack
|
||||
isUpdate:isUpdate];
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(dismiss)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self->_window dismiss];
|
||||
});
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self->_window dismiss];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[self dismiss];
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
- (void)redBoxWindow:(__unused RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(RCTJSStackFrame *)stackFrame
|
||||
{
|
||||
NSURL *const bundleURL = _overrideBundleURL ?: _bridge.bundleURL;
|
||||
if (![bundleURL.scheme hasPrefix:@"http"]) {
|
||||
RCTLogWarn(@"Cannot open stack frame in editor because you're not connected to the packager.");
|
||||
return;
|
||||
}
|
||||
NSURL *const bundleURL = _overrideBundleURL ?: _bridge.bundleURL;
|
||||
if (![bundleURL.scheme hasPrefix:@"http"]) {
|
||||
RCTLogWarn(@"Cannot open stack frame in editor because you're not connected to the packager.");
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *stackFrameJSON = [RCTJSONStringify([stackFrame toDictionary], NULL) dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *postLength = [NSString stringWithFormat:@"%tu", stackFrameJSON.length];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
||||
request.URL = [NSURL URLWithString:@"/open-stack-frame" relativeToURL:bundleURL];
|
||||
request.HTTPMethod = @"POST";
|
||||
request.HTTPBody = stackFrameJSON;
|
||||
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
NSData *stackFrameJSON = [RCTJSONStringify([stackFrame toDictionary], NULL) dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *postLength = [NSString stringWithFormat:@"%tu", stackFrameJSON.length];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
||||
request.URL = [NSURL URLWithString:@"/open-stack-frame" relativeToURL:bundleURL];
|
||||
request.HTTPMethod = @"POST";
|
||||
request.HTTPBody = stackFrameJSON;
|
||||
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
|
||||
[[[NSURLSession sharedSession] dataTaskWithRequest:request] resume];
|
||||
}
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
// Window is not used and can be nil
|
||||
[self reloadFromRedBoxWindow:nil];
|
||||
[[[NSURLSession sharedSession] dataTaskWithRequest:request] resume];
|
||||
}
|
||||
|
||||
- (void)reloadFromRedBoxWindow:(__unused RCTRedBoxWindow *)redBoxWindow
|
||||
{
|
||||
if (_overrideReloadAction) {
|
||||
_overrideReloadAction();
|
||||
} else {
|
||||
[_bridge reload];
|
||||
}
|
||||
[self dismiss];
|
||||
if (_overrideReloadAction) {
|
||||
_overrideReloadAction();
|
||||
} else {
|
||||
[_bridge reload];
|
||||
}
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -537,7 +485,7 @@ RCT_EXPORT_METHOD(dismiss)
|
||||
|
||||
- (RCTRedBox *)redBox
|
||||
{
|
||||
return [self moduleForClass:[RCTRedBox class]];
|
||||
return [self moduleForClass:[RCTRedBox class]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,22 +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.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol RCTRedBoxExtraDataActionDelegate <NSObject>
|
||||
- (void)reload;
|
||||
@end
|
||||
|
||||
@interface RCTRedBoxExtraDataViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@property (nonatomic, weak) id<RCTRedBoxExtraDataActionDelegate> actionDelegate;
|
||||
|
||||
- (void)addExtraData:(NSDictionary *)data forIdentifier:(NSString *)identifier;
|
||||
|
||||
@end
|
||||
@@ -1,288 +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.
|
||||
*/
|
||||
|
||||
#import "RCTRedBoxExtraDataViewController.h"
|
||||
|
||||
@interface RCTRedBoxExtraDataCell : UITableViewCell
|
||||
|
||||
@property (nonatomic, strong) UILabel *keyLabel;
|
||||
@property (nonatomic, strong) UILabel *valueLabel;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTRedBoxExtraDataCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style
|
||||
reuseIdentifier:(NSString *)reuseIdentifier
|
||||
{
|
||||
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
|
||||
self.backgroundColor = [UIColor colorWithRed:0.8
|
||||
green:0 blue:0
|
||||
alpha:1];
|
||||
UILayoutGuide *contentLayout = self.contentView.layoutMarginsGuide;
|
||||
|
||||
self.keyLabel = [UILabel new];
|
||||
[self.contentView addSubview:self.keyLabel];
|
||||
|
||||
self.keyLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.keyLabel.leadingAnchor
|
||||
constraintEqualToAnchor:contentLayout.leadingAnchor].active = YES;
|
||||
[self.keyLabel.topAnchor
|
||||
constraintEqualToAnchor:contentLayout.topAnchor].active = YES;
|
||||
[self.keyLabel.bottomAnchor
|
||||
constraintEqualToAnchor:contentLayout.bottomAnchor].active = YES;
|
||||
[self.keyLabel.widthAnchor
|
||||
constraintEqualToAnchor:contentLayout.widthAnchor
|
||||
multiplier:0.3].active = YES;
|
||||
|
||||
self.keyLabel.textColor = [UIColor whiteColor];
|
||||
self.keyLabel.numberOfLines = 0;
|
||||
self.keyLabel.lineBreakMode = UILineBreakModeWordWrap;
|
||||
self.keyLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:12.0f];
|
||||
|
||||
self.valueLabel = [UILabel new];
|
||||
[self.contentView addSubview:self.valueLabel];
|
||||
|
||||
self.valueLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.valueLabel.leadingAnchor
|
||||
constraintEqualToAnchor:self.keyLabel.trailingAnchor
|
||||
constant:10.f].active = YES;
|
||||
[self.valueLabel.trailingAnchor
|
||||
constraintEqualToAnchor:contentLayout.trailingAnchor].active = YES;
|
||||
[self.valueLabel.topAnchor
|
||||
constraintEqualToAnchor:contentLayout.topAnchor].active = YES;
|
||||
[self.valueLabel.bottomAnchor
|
||||
constraintEqualToAnchor:contentLayout.bottomAnchor].active = YES;
|
||||
|
||||
self.valueLabel.textColor = [UIColor whiteColor];
|
||||
self.valueLabel.numberOfLines = 0;
|
||||
self.valueLabel.lineBreakMode = UILineBreakModeWordWrap;
|
||||
self.valueLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:12.0f];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTRedBoxExtraDataViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTRedBoxExtraDataViewController
|
||||
{
|
||||
UITableView *_tableView;
|
||||
NSMutableArray *_extraDataTitle;
|
||||
NSMutableArray *_extraData;
|
||||
}
|
||||
|
||||
@synthesize actionDelegate = _actionDelegate;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_extraData = [NSMutableArray new];
|
||||
_extraDataTitle = [NSMutableArray new];
|
||||
self.view.backgroundColor = [UIColor colorWithRed:0.8
|
||||
green:0
|
||||
blue:0
|
||||
alpha:1];
|
||||
|
||||
_tableView = [UITableView new];
|
||||
_tableView.delegate = self;
|
||||
_tableView.dataSource = self;
|
||||
_tableView.backgroundColor = [UIColor clearColor];
|
||||
_tableView.estimatedRowHeight = 200;
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
_tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
_tableView.allowsSelection = NO;
|
||||
|
||||
#if TARGET_OS_SIMULATOR
|
||||
NSString *reloadText = @"Reload JS (\u2318R)";
|
||||
NSString *dismissText = @"Dismiss (ESC)";
|
||||
#else
|
||||
NSString *reloadText = @"Reload JS";
|
||||
NSString *dismissText = @"Dismiss";
|
||||
#endif
|
||||
|
||||
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
dismissButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
dismissButton.accessibilityIdentifier = @"redbox-extra-data-dismiss";
|
||||
dismissButton.titleLabel.font = [UIFont systemFontOfSize:13];
|
||||
[dismissButton setTitle:dismissText forState:UIControlStateNormal];
|
||||
[dismissButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5]
|
||||
forState:UIControlStateNormal];
|
||||
[dismissButton setTitleColor:[UIColor whiteColor]
|
||||
forState:UIControlStateHighlighted];
|
||||
[dismissButton addTarget:self
|
||||
action:@selector(dismiss)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
UIButton *reloadButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
reloadButton.accessibilityIdentifier = @"redbox-reload";
|
||||
reloadButton.titleLabel.font = [UIFont systemFontOfSize:13];
|
||||
[reloadButton setTitle:reloadText forState:UIControlStateNormal];
|
||||
[reloadButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5]
|
||||
forState:UIControlStateNormal];
|
||||
[reloadButton setTitleColor:[UIColor whiteColor]
|
||||
forState:UIControlStateHighlighted];
|
||||
[reloadButton addTarget:self
|
||||
action:@selector(reload)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
UIStackView *buttonStackView = [UIStackView new];
|
||||
buttonStackView.axis = UILayoutConstraintAxisHorizontal;
|
||||
buttonStackView.distribution = UIStackViewDistributionEqualSpacing;
|
||||
buttonStackView.alignment = UIStackViewAlignmentFill;
|
||||
buttonStackView.spacing = 20;
|
||||
|
||||
[buttonStackView addArrangedSubview:dismissButton];
|
||||
[buttonStackView addArrangedSubview:reloadButton];
|
||||
buttonStackView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
UIStackView *mainStackView = [UIStackView new];
|
||||
mainStackView.axis = UILayoutConstraintAxisVertical;
|
||||
mainStackView.backgroundColor = [UIColor colorWithRed:0.8
|
||||
green:0 blue:0
|
||||
alpha:1];
|
||||
[mainStackView addArrangedSubview:_tableView];
|
||||
[mainStackView addArrangedSubview:buttonStackView];
|
||||
mainStackView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
[self.view addSubview:mainStackView];
|
||||
|
||||
CGFloat tableHeight = self.view.bounds.size.height - 60.f;
|
||||
[_tableView.heightAnchor constraintEqualToConstant:tableHeight].active = YES;
|
||||
[_tableView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor].active = YES;
|
||||
|
||||
CGFloat buttonWidth = self.view.bounds.size.width / 4;
|
||||
[dismissButton.heightAnchor constraintEqualToConstant:60].active = YES;
|
||||
[dismissButton.widthAnchor
|
||||
constraintEqualToConstant:buttonWidth].active = YES;
|
||||
[reloadButton.heightAnchor constraintEqualToConstant:60].active = YES;
|
||||
[reloadButton.widthAnchor
|
||||
constraintEqualToConstant:buttonWidth].active = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[_tableView reloadData];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return [[_extraData objectAtIndex:section] count];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
return 40;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
UIView *view = [UIView new];
|
||||
view.backgroundColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:1];
|
||||
|
||||
UILabel *header = [UILabel new];
|
||||
[view addSubview:header];
|
||||
|
||||
header.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[header.leadingAnchor
|
||||
constraintEqualToAnchor:view.leadingAnchor constant:5].active = YES;
|
||||
[header.trailingAnchor
|
||||
constraintEqualToAnchor:view.trailingAnchor].active = YES;
|
||||
[header.topAnchor
|
||||
constraintEqualToAnchor:view.topAnchor].active = YES;
|
||||
[header.bottomAnchor
|
||||
constraintEqualToAnchor:view.bottomAnchor].active = YES;
|
||||
|
||||
header.textColor = [UIColor whiteColor];
|
||||
header.font = [UIFont fontWithName:@"Menlo-Bold" size:14.0f];
|
||||
header.text = [_extraDataTitle[section] uppercaseString];
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
static NSString *reuseIdentifier = @"RedBoxExtraData";
|
||||
|
||||
RCTRedBoxExtraDataCell *cell =
|
||||
(RCTRedBoxExtraDataCell *)[tableView
|
||||
dequeueReusableCellWithIdentifier:reuseIdentifier];
|
||||
|
||||
if (cell == nil) {
|
||||
cell = [[RCTRedBoxExtraDataCell alloc]
|
||||
initWithStyle:UITableViewCellStyleDefault
|
||||
reuseIdentifier:reuseIdentifier];
|
||||
}
|
||||
|
||||
NSArray *dataKVPair = _extraData[indexPath.section][indexPath.row];
|
||||
cell.keyLabel.text = dataKVPair[0];
|
||||
cell.valueLabel.text = dataKVPair[1];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return _extraDataTitle.count;
|
||||
}
|
||||
|
||||
- (void)addExtraData:(NSDictionary *)data forIdentifier:(NSString *)identifier
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSMutableArray *newData = [NSMutableArray new];
|
||||
for (id key in data) {
|
||||
[newData addObject:@[[NSString stringWithFormat:@"%@", key],
|
||||
[NSString stringWithFormat:@"%@", [data objectForKey:key]]]];
|
||||
}
|
||||
|
||||
NSInteger idx = [self->_extraDataTitle indexOfObject:identifier];
|
||||
if (idx == NSNotFound) {
|
||||
[self->_extraDataTitle addObject:identifier];
|
||||
[self->_extraData addObject:newData];
|
||||
} else {
|
||||
[self->_extraData replaceObjectAtIndex:idx withObject:newData];
|
||||
}
|
||||
|
||||
[self->_tableView reloadData];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dismiss
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
[_actionDelegate reload];
|
||||
}
|
||||
|
||||
#pragma mark - Key commands
|
||||
|
||||
- (NSArray<UIKeyCommand *> *)keyCommands
|
||||
{
|
||||
return @[
|
||||
// Dismiss
|
||||
[UIKeyCommand keyCommandWithInput:UIKeyInputEscape
|
||||
modifierFlags:0
|
||||
action:@selector(dismiss)],
|
||||
// Reload
|
||||
[UIKeyCommand keyCommandWithInput:@"r"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:@selector(reload)]
|
||||
];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1192,8 +1192,6 @@
|
||||
EBF21BFE1FC499840052F4D5 /* InspectorInterfaces.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = EBF21BBA1FC498270052F4D5 /* InspectorInterfaces.h */; };
|
||||
EBF21BFF1FC4998E0052F4D5 /* InspectorInterfaces.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EBF21BBB1FC498270052F4D5 /* InspectorInterfaces.cpp */; };
|
||||
EBF21C001FC499A80052F4D5 /* InspectorInterfaces.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EBF21BBB1FC498270052F4D5 /* InspectorInterfaces.cpp */; };
|
||||
FEFAAC9E1FDB89B50057BBE0 /* RCTRedBoxExtraDataViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FEFAAC9C1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.m */; };
|
||||
FEFAAC9F1FDB89B50057BBE0 /* RCTRedBoxExtraDataViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = FEFAAC9D1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -2270,8 +2268,6 @@
|
||||
EBF21BBB1FC498270052F4D5 /* InspectorInterfaces.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorInterfaces.cpp; sourceTree = "<group>"; };
|
||||
EBF21BDC1FC498900052F4D5 /* libjsinspector.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjsinspector.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EBF21BFA1FC4989A0052F4D5 /* libjsinspector-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libjsinspector-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
FEFAAC9C1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRedBoxExtraDataViewController.m; sourceTree = "<group>"; };
|
||||
FEFAAC9D1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRedBoxExtraDataViewController.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -2480,8 +2476,6 @@
|
||||
5960C1B41F0804A00066FD5B /* RCTLayoutAnimationGroup.m */,
|
||||
13F17A831B8493E5007D4C75 /* RCTRedBox.h */,
|
||||
13F17A841B8493E5007D4C75 /* RCTRedBox.m */,
|
||||
FEFAAC9D1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.h */,
|
||||
FEFAAC9C1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.m */,
|
||||
000E6CE91AB0E97F000CDF4D /* RCTSourceCode.h */,
|
||||
000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */,
|
||||
13723B4E1A82FD3C00F88898 /* RCTStatusBarManager.h */,
|
||||
@@ -3470,7 +3464,6 @@
|
||||
3D80DA591DF820620028D040 /* RCTTiming.h in Headers */,
|
||||
3D80DA5A1DF820620028D040 /* RCTUIManager.h in Headers */,
|
||||
3D80DA5B1DF820620028D040 /* RCTFPSGraph.h in Headers */,
|
||||
FEFAAC9F1FDB89B50057BBE0 /* RCTRedBoxExtraDataViewController.h in Headers */,
|
||||
3D80DA5D1DF820620028D040 /* RCTMacros.h in Headers */,
|
||||
3D80DA5E1DF820620028D040 /* RCTProfile.h in Headers */,
|
||||
3D80DA5F1DF820620028D040 /* RCTActivityIndicatorView.h in Headers */,
|
||||
@@ -4417,7 +4410,6 @@
|
||||
1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */,
|
||||
59A7B9FE1E577DBF0068EDBF /* RCTRootContentView.m in Sources */,
|
||||
13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */,
|
||||
FEFAAC9E1FDB89B50057BBE0 /* RCTRedBoxExtraDataViewController.m in Sources */,
|
||||
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */,
|
||||
CF2731C11E7B8DE40044CA4F /* RCTDeviceInfo.m in Sources */,
|
||||
3D7AA9C41E548CD5001955CF /* NSDataBigString.mm in Sources */,
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "UIView+React.h"
|
||||
#if TARGET_OS_TV
|
||||
#import "RCTTVRemoteHandler.h"
|
||||
#endif
|
||||
|
||||
@implementation RCTModalHostView
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=1000.0.0-master
|
||||
VERSION_NAME=0.52.3
|
||||
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",
|
||||
],
|
||||
|
||||
+11
-7
@@ -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,
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
package com.facebook.react.flat;
|
||||
|
||||
import com.facebook.react.uimanager.UIViewOperationQueue;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -35,7 +34,7 @@ import javax.annotation.Nullable;
|
||||
private final FlatNativeViewHierarchyManager mNativeViewHierarchyManager;
|
||||
private final ProcessLayoutRequests mProcessLayoutRequests = new ProcessLayoutRequests();
|
||||
|
||||
private final class ProcessLayoutRequests implements UIViewOperationQueue.UIOperation {
|
||||
private final class ProcessLayoutRequests implements UIOperation {
|
||||
@Override
|
||||
public void execute() {
|
||||
FlatViewGroup.processLayoutRequests();
|
||||
@@ -45,7 +44,7 @@ import javax.annotation.Nullable;
|
||||
/**
|
||||
* UIOperation that updates DrawCommands for a View defined by reactTag.
|
||||
*/
|
||||
private final class UpdateMountState implements UIViewOperationQueue.UIOperation {
|
||||
private final class UpdateMountState implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final @Nullable DrawCommand[] mDrawCommands;
|
||||
@@ -76,7 +75,7 @@ import javax.annotation.Nullable;
|
||||
/**
|
||||
* UIOperation that updates DrawCommands for a View defined by reactTag.
|
||||
*/
|
||||
private final class UpdateClippingMountState implements UIViewOperationQueue.UIOperation {
|
||||
private final class UpdateClippingMountState implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final @Nullable DrawCommand[] mDrawCommands;
|
||||
@@ -128,7 +127,7 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
}
|
||||
|
||||
private final class UpdateViewGroup implements UIViewOperationQueue.UIOperation {
|
||||
private final class UpdateViewGroup implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final int[] mViewsToAdd;
|
||||
@@ -149,7 +148,7 @@ import javax.annotation.Nullable;
|
||||
/**
|
||||
* UIOperation that updates View bounds for a View defined by reactTag.
|
||||
*/
|
||||
public final class UpdateViewBounds implements UIViewOperationQueue.UIOperation {
|
||||
public final class UpdateViewBounds implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final int mLeft;
|
||||
@@ -171,7 +170,7 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
}
|
||||
|
||||
private final class SetPadding implements UIViewOperationQueue.UIOperation {
|
||||
private final class SetPadding implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final int mPaddingLeft;
|
||||
@@ -203,7 +202,7 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
}
|
||||
|
||||
private final class DropViews implements UIViewOperationQueue.UIOperation {
|
||||
private final class DropViews implements UIOperation {
|
||||
|
||||
private final SparseIntArray mViewsToDrop;
|
||||
|
||||
@@ -221,7 +220,7 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
}
|
||||
|
||||
private final class MeasureVirtualView implements UIViewOperationQueue.UIOperation {
|
||||
private final class MeasureVirtualView implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final float mScaledX;
|
||||
@@ -285,7 +284,7 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
}
|
||||
|
||||
public final class DetachAllChildrenFromViews implements UIViewOperationQueue.UIOperation {
|
||||
public final class DetachAllChildrenFromViews implements UIOperation {
|
||||
private @Nullable int[] mViewsToDetachAllChildrenFrom;
|
||||
|
||||
public void setViewsToDetachAllChildrenFrom(int[] viewsToDetachAllChildrenFrom) {
|
||||
@@ -298,7 +297,7 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
}
|
||||
|
||||
private final class FindTargetForTouchOperation implements UIViewOperationQueue.UIOperation {
|
||||
private final class FindTargetForTouchOperation implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final float mTargetX;
|
||||
@@ -374,7 +373,7 @@ import javax.annotation.Nullable;
|
||||
* Used to delay view manager command dispatch until after the view hierarchy is updated.
|
||||
* Mirrors command operation dispatch, but is only used in Nodes for view manager commands.
|
||||
*/
|
||||
public final class ViewManagerCommand implements UIViewOperationQueue.UIOperation {
|
||||
public final class ViewManagerCommand implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final int mCommand;
|
||||
@@ -469,7 +468,7 @@ import javax.annotation.Nullable;
|
||||
return new ViewManagerCommand(reactTag, command, args);
|
||||
}
|
||||
|
||||
/* package */ void enqueueFlatUIOperation(UIViewOperationQueue.UIOperation operation) {
|
||||
/* package */ void enqueueFlatUIOperation(UIOperation operation) {
|
||||
enqueueUIOperation(operation);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ include_defs("//ReactAndroid/DEFS")
|
||||
android_library(
|
||||
name = "annotations",
|
||||
srcs = glob(["**/*.java"]),
|
||||
required_for_source_only_abi = True,
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
|
||||
+2
-2
@@ -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", 52,
|
||||
"patch", 3,
|
||||
"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",
|
||||
],
|
||||
|
||||
+1
-1
@@ -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"),
|
||||
|
||||
@@ -3,7 +3,6 @@ include_defs("//ReactAndroid/DEFS")
|
||||
android_library(
|
||||
name = "modal",
|
||||
srcs = glob(["*.java"]),
|
||||
required_for_source_only_abi = True,
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
|
||||
@@ -3,7 +3,6 @@ include_defs("//ReactAndroid/DEFS")
|
||||
android_library(
|
||||
name = "text",
|
||||
srcs = glob(["*.java"]),
|
||||
required_for_source_only_abi = True,
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
|
||||
@@ -6,7 +6,6 @@ android_library(
|
||||
provided_deps = [
|
||||
react_native_dep("third-party/android/support/v4:lib-support-v4"),
|
||||
],
|
||||
required_for_source_only_abi = True,
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -422,7 +422,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());
|
||||
@@ -494,8 +494,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);
|
||||
|
||||
@@ -6,7 +6,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,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) {
|
||||
|
||||
@@ -205,7 +205,7 @@ void YGNodeToString(
|
||||
}
|
||||
appendFormatedString(str, ">");
|
||||
|
||||
const uint32_t childCount = node->children.size();
|
||||
const uint32_t childCount = static_cast<uint32_t>(node->children.size());
|
||||
if (options & YGPrintOptionsChildren && childCount > 0) {
|
||||
for (uint32_t i = 0; i < childCount; i++) {
|
||||
appendFormatedString(str, "\n");
|
||||
|
||||
@@ -288,7 +288,9 @@ YGConfigRef YGConfigGetDefault() {
|
||||
YGConfigRef YGConfigNew(void) {
|
||||
const YGConfigRef config = (const YGConfigRef)malloc(sizeof(YGConfig));
|
||||
YGAssert(config != nullptr, "Could not allocate memory for config");
|
||||
|
||||
if (config == nullptr) {
|
||||
abort();
|
||||
}
|
||||
gConfigInstanceCount++;
|
||||
memcpy(config, &gYGConfigDefaults, sizeof(YGConfig));
|
||||
return config;
|
||||
@@ -469,7 +471,7 @@ YGNodeRef YGNodeGetParent(const YGNodeRef node) {
|
||||
}
|
||||
|
||||
uint32_t YGNodeGetChildCount(const YGNodeRef node) {
|
||||
return node->children.size();
|
||||
return static_cast<uint32_t>(node->children.size());
|
||||
}
|
||||
|
||||
void YGNodeMarkDirty(const YGNodeRef node) {
|
||||
@@ -1853,7 +1855,7 @@ static void YGNodelayoutImpl(const YGNodeRef node,
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t childCount = node->children.size();
|
||||
const uint32_t childCount = YGNodeGetChildCount(node);
|
||||
if (childCount == 0) {
|
||||
YGNodeEmptyContainerSetMeasuredDimensions(node,
|
||||
availableWidth,
|
||||
@@ -3378,7 +3380,7 @@ static void YGRoundToPixelGrid(const YGNodeRef node,
|
||||
(textRounding && !hasFractionalHeight)) -
|
||||
YGRoundValueToPixelGrid(absoluteNodeTop, pointScaleFactor, false, textRounding);
|
||||
|
||||
const uint32_t childCount = node->children.size();
|
||||
const uint32_t childCount = YGNodeGetChildCount(node);
|
||||
for (uint32_t i = 0; i < childCount; i++) {
|
||||
YGRoundToPixelGrid(YGNodeGetChild(node, i), pointScaleFactor, absoluteNodeLeft, absoluteNodeTop);
|
||||
}
|
||||
|
||||
@@ -347,9 +347,11 @@ fs.createWriteStream.mockImplementation(filePath => {
|
||||
const writeStream = new stream.Writable({
|
||||
write(chunk, encoding, callback) {
|
||||
this.__chunks.push(chunk);
|
||||
callback();
|
||||
},
|
||||
final(callback) {
|
||||
/**
|
||||
* We can't use `final` as that has been added to Node 8.x.
|
||||
* For backwards compatibility and minimal code paths,
|
||||
* we update the content of a file immediately
|
||||
*/
|
||||
node[path.basename(filePath)] = this.__chunks.join('');
|
||||
callback();
|
||||
},
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
declare var jest: any;
|
||||
declare var describe: any;
|
||||
declare var beforeEach: any;
|
||||
declare var expect: any;
|
||||
declare var it: any;
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
@@ -27,18 +27,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', () => {
|
||||
@@ -46,7 +55,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', () => {
|
||||
@@ -54,6 +66,6 @@ describe('findPlugins', () => {
|
||||
dependencies: {'rnpm-plugin-test': '*'},
|
||||
devDependencies: {'rnpm-plugin-test': '*'},
|
||||
}));
|
||||
expect(findPlugins([ROOT])).toHaveLength(1);
|
||||
expect(findPlugins([ROOT]).commands).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -126,3 +126,5 @@ exports.dependencyConfig = function dependencyConfigAndroid(folder, userConfig)
|
||||
|
||||
return { sourceDir, folder, manifest, packageImportPath, packageInstance };
|
||||
};
|
||||
|
||||
exports.linkConfig = require('../../link/android');
|
||||
|
||||
@@ -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
@@ -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';
|
||||
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -57,3 +57,5 @@ exports.projectConfig = function projectConfigIOS(folder, userConfig) {
|
||||
};
|
||||
|
||||
exports.dependencyConfig = exports.projectConfig;
|
||||
|
||||
exports.linkConfig = require('../../link/ios');
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -39,6 +39,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: {} }),
|
||||
};
|
||||
@@ -54,6 +55,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: {} }),
|
||||
};
|
||||
@@ -79,7 +81,10 @@ describe('link', () => {
|
||||
it('should register native module when android/ios projects are present', (done) => {
|
||||
const registerNativeModule = sinon.stub();
|
||||
const dependencyConfig = {android: {}, ios: {}, assets: [], commands: {}};
|
||||
const androidLinkConfig = require('../android');
|
||||
const iosLinkConfig = require('../ios');
|
||||
const config = {
|
||||
getPlatformConfig: () => ({ios: { linkConfig: iosLinkConfig }, android: { linkConfig: androidLinkConfig }}),
|
||||
getProjectConfig: () => ({android: {}, ios: {}, assets: []}),
|
||||
getDependencyConfig: sinon.stub().returns(dependencyConfig),
|
||||
};
|
||||
@@ -116,6 +121,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),
|
||||
};
|
||||
@@ -148,6 +154,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();
|
||||
@@ -163,7 +225,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 },
|
||||
@@ -181,7 +245,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();
|
||||
|
||||
@@ -190,7 +254,9 @@ describe('link', () => {
|
||||
copyAssets
|
||||
);
|
||||
|
||||
const linkConfig = require('../ios');
|
||||
const config = {
|
||||
getPlatformConfig: () => ({ ios: { linkConfig: linkConfig } }),
|
||||
getProjectConfig: () => ({ ios: {}, assets: projectAssets }),
|
||||
getDependencyConfig: sinon.stub().returns(dependencyConfig),
|
||||
};
|
||||
|
||||
@@ -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)))
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
module.exports = function() {
|
||||
return {
|
||||
isInstalled: require('./isInstalled'),
|
||||
register: require('./registerNativeModule'),
|
||||
unregister: require('./unregisterNativeModule'),
|
||||
copyAssets: require('./copyAssets'),
|
||||
unlinkAssets: require('./unlinkAssets')
|
||||
};
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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
@@ -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(
|
||||
|
||||
@@ -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
@@ -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`
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
};
|
||||
@@ -10,14 +10,14 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is a copy of the reference `DeltaPatcher`, located in
|
||||
* metro. The reason to not reuse that file is that in this context
|
||||
* we cannot have flow annotations or CJS syntax (since this file is directly)
|
||||
* injected into a static HTML page.
|
||||
*
|
||||
* TODO: Find a simple and lightweight way to compile `DeltaPatcher` to avoid
|
||||
* having this duplicated file.
|
||||
*/
|
||||
* This file is a copy of the reference `DeltaPatcher`, located in
|
||||
* metro. The reason to not reuse that file is that in this context
|
||||
* we cannot have flow annotations or CJS syntax (since this file is directly)
|
||||
* injected into a static HTML page.
|
||||
*
|
||||
* TODO: Find a simple and lightweight way to compile `DeltaPatcher` to avoid
|
||||
* having this duplicated file.
|
||||
*/
|
||||
(function(global) {
|
||||
'use strict';
|
||||
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "1000.0.0",
|
||||
"version": "0.52.3",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
@@ -209,10 +209,10 @@
|
||||
"eslint-plugin-react": "^7.2.1",
|
||||
"flow-bin": "^0.61.0",
|
||||
"jest": "21.3.0-beta.13",
|
||||
"prettier": "1.9.1",
|
||||
"prettier": "1.7.0",
|
||||
"react": "16.2.0",
|
||||
"react-test-renderer": "16.2.0",
|
||||
"shelljs": "^0.7.8",
|
||||
"sinon": "^2.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user