mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
52
Commits
nc/react-sync
...
v0.56.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c6a916536 | ||
|
|
93379b696b | ||
|
|
48341c8bb0 | ||
|
|
ba9d41d645 | ||
|
|
30476703a8 | ||
|
|
600747ffd1 | ||
|
|
3861dbef5b | ||
|
|
ca515e99a3 | ||
|
|
537731f8a5 | ||
|
|
5ae80f91fb | ||
|
|
1b4187fc41 | ||
|
|
8bd43449f0 | ||
|
|
8aaf73b4b0 | ||
|
|
af76473c2e | ||
|
|
302699a472 | ||
|
|
8ea8dd6d48 | ||
|
|
b4b0610167 | ||
|
|
5b7a817723 | ||
|
|
b348aa14d4 | ||
|
|
5ae9799041 | ||
|
|
c159447447 | ||
|
|
6d27bd182f | ||
|
|
ce739139f3 | ||
|
|
1980dd771a | ||
|
|
3d946718c5 | ||
|
|
21d0eedd27 | ||
|
|
72d22e8828 | ||
|
|
27a497dd5a | ||
|
|
d9d324073f | ||
|
|
053f580a1d | ||
|
|
9ead7d6147 | ||
|
|
d91c9d97b8 | ||
|
|
93b722f2a0 | ||
|
|
9821241b79 | ||
|
|
541c6e9783 | ||
|
|
cf71637a65 | ||
|
|
3fc653e140 | ||
|
|
997e173f24 | ||
|
|
55eef9b58f | ||
|
|
2adcc9bc5d | ||
|
|
9b2d3092f9 | ||
|
|
2645956953 | ||
|
|
2beaa39101 | ||
|
|
c1c32276bc | ||
|
|
d047d2c227 | ||
|
|
e07bbbe24d | ||
|
|
579eb3f9fe | ||
|
|
a59e19240a | ||
|
|
204207f4c9 | ||
|
|
8bdef5d2c0 | ||
|
|
71f56fc2e3 | ||
|
|
d642e60291 |
+2
-1
@@ -1,12 +1,13 @@
|
||||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
target = android-26
|
||||
|
||||
[download]
|
||||
max_number_of_retries = 3
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
||||
google = https://maven.google.com
|
||||
|
||||
[alias]
|
||||
rntester = //RNTester/android/app:app
|
||||
|
||||
+20
-25
@@ -456,18 +456,15 @@ jobs:
|
||||
|
||||
- restore-cache: *restore-cache-android-packages
|
||||
- run: *install-android-packages
|
||||
- save-cache: *save-cache-android-packages
|
||||
|
||||
# Install Android NDK
|
||||
- run: *create-ndk-directory
|
||||
- restore-cache: *restore-cache-ndk
|
||||
- run: *install-ndk
|
||||
- save-cache: *save-cache-ndk
|
||||
|
||||
# Fetch dependencies using BUCK
|
||||
- restore-cache: *restore-cache-buck
|
||||
- run: *install-buck
|
||||
- save-cache: *save-cache-buck
|
||||
|
||||
- run: buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
|
||||
- run: buck fetch ReactAndroid/src/main/java/com/facebook/react
|
||||
@@ -477,24 +474,18 @@ jobs:
|
||||
|
||||
- restore-cache: *restore-cache-gradle-downloads
|
||||
- run: *gradle-download-deps
|
||||
- save-cache: *save-cache-gradle-downloads
|
||||
|
||||
- restore-cache: *restore-yarn-cache
|
||||
- run: *yarn
|
||||
- save-cache: *save-yarn-cache
|
||||
|
||||
- run:
|
||||
name: Publish React Native Package
|
||||
command: |
|
||||
if [ -z "$CIRCLE_PULL_REQUEST" ]; then
|
||||
echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
|
||||
git config --global user.email "reactjs-bot@users.noreply.github.com"
|
||||
git config --global user.name "npm Deployment Script"
|
||||
echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc
|
||||
node ./scripts/publish-npm.js
|
||||
else
|
||||
echo "Skipping deploy."
|
||||
fi
|
||||
echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
|
||||
git config --global user.email "reactjs-bot@users.noreply.github.com"
|
||||
git config --global user.name "npm Deployment Script"
|
||||
echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc
|
||||
node ./scripts/publish-npm.js
|
||||
|
||||
# Set up an Android environment for downstream jobs
|
||||
test_android:
|
||||
@@ -652,6 +643,21 @@ workflows:
|
||||
requires:
|
||||
- checkout_code
|
||||
|
||||
# Only runs on vX.X.X tags if all tests are green
|
||||
- publish_npm_package:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- /.*-stable/
|
||||
tags:
|
||||
only: /v[0-9]+(\.[0-9]+)*(\-rc(\.[0-9]+)?)?/
|
||||
requires:
|
||||
- test_javascript
|
||||
- test_ios
|
||||
- test_tvos
|
||||
- test_end_to_end
|
||||
- analyze
|
||||
|
||||
# Only runs on PRs
|
||||
analyze:
|
||||
jobs:
|
||||
@@ -665,17 +671,6 @@ workflows:
|
||||
requires:
|
||||
- checkout_code
|
||||
|
||||
# Only runs on NN-stable branches
|
||||
deploy:
|
||||
jobs:
|
||||
# If we are on a stable branch, wait for approval to deploy to npm
|
||||
- approve_publish_npm_package:
|
||||
filters: *filter-only-stable
|
||||
type: approval
|
||||
|
||||
- publish_npm_package:
|
||||
requires:
|
||||
- approve_publish_npm_package
|
||||
|
||||
# These tests are flaky or are yet to be fixed. They are placed on their own
|
||||
# workflow to avoid marking benign PRs as broken.
|
||||
|
||||
+1
-1
@@ -74,4 +74,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
[version]
|
||||
^0.74.0
|
||||
^0.75.0
|
||||
|
||||
@@ -86,12 +86,12 @@ export type KeyboardType =
|
||||
| 'numeric'
|
||||
| 'phone-pad'
|
||||
| 'number-pad'
|
||||
| 'decimal-pad'
|
||||
// iOS-only
|
||||
| 'ascii-capable'
|
||||
| 'numbers-and-punctuation'
|
||||
| 'url'
|
||||
| 'name-phone-pad'
|
||||
| 'decimal-pad'
|
||||
| 'twitter'
|
||||
| 'web-search'
|
||||
// Android-only
|
||||
@@ -388,6 +388,7 @@ const TextInput = createReactClass({
|
||||
* - `default`
|
||||
* - `numeric`
|
||||
* - `number-pad`
|
||||
* - `decimal-pad`
|
||||
* - `email-address`
|
||||
* - `phone-pad`
|
||||
*
|
||||
@@ -399,7 +400,6 @@ const TextInput = createReactClass({
|
||||
* - `numbers-and-punctuation`
|
||||
* - `url`
|
||||
* - `name-phone-pad`
|
||||
* - `decimal-pad`
|
||||
* - `twitter`
|
||||
* - `web-search`
|
||||
*
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
* @generated by scripts/bump-oss-version.js
|
||||
*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @generated by scripts/bump-oss-version.js
|
||||
* @flow
|
||||
*/
|
||||
|
||||
exports.version = {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
minor: 56,
|
||||
patch: 0,
|
||||
prerelease: null,
|
||||
};
|
||||
|
||||
@@ -1217,7 +1217,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
_selectOffset(metrics: $ReadOnly<{x: number, y: number}>): number {
|
||||
return !this.props.horizontal ? metrics.y : metrics.x;
|
||||
return (
|
||||
(!this.props.horizontal ? metrics.y : metrics.x) - this._headerLength
|
||||
);
|
||||
}
|
||||
|
||||
_maybeCallOnEndReached() {
|
||||
|
||||
@@ -127,4 +127,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
if (!global.__RCTProfileIsProfiling) {
|
||||
const YellowBox = require('YellowBox');
|
||||
YellowBox.install();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppContainer;
|
||||
|
||||
@@ -1,554 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('EventEmitter');
|
||||
const Platform = require('Platform');
|
||||
const React = require('React');
|
||||
const SafeAreaView = require('SafeAreaView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const RCTLog = require('RCTLog');
|
||||
|
||||
const infoLog = require('infoLog');
|
||||
const openFileInEditor = require('openFileInEditor');
|
||||
const parseErrorStack = require('parseErrorStack');
|
||||
const stringifySafe = require('stringifySafe');
|
||||
const symbolicateStackTrace = require('symbolicateStackTrace');
|
||||
|
||||
import type EmitterSubscription from 'EmitterSubscription';
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
type WarningInfo = {
|
||||
count: number,
|
||||
stacktrace: Array<StackFrame>,
|
||||
symbolicated: boolean,
|
||||
};
|
||||
|
||||
const _warningEmitter = new EventEmitter();
|
||||
const _warningMap: Map<string, WarningInfo> = new Map();
|
||||
const IGNORED_WARNINGS: Array<string> = [];
|
||||
|
||||
/**
|
||||
* YellowBox renders warnings at the bottom of the app being developed.
|
||||
*
|
||||
* Warnings help guard against subtle yet significant issues that can impact the
|
||||
* quality of the app. This "in your face" style of warning allows developers to
|
||||
* notice and correct these issues as quickly as possible.
|
||||
*
|
||||
* By default, the warning box is enabled in `__DEV__`. Set the following flag
|
||||
* to disable it (and call `console.warn` to update any rendered <YellowBox>):
|
||||
*
|
||||
* console.disableYellowBox = true;
|
||||
* console.warn('YellowBox is disabled.');
|
||||
*
|
||||
* Ignore specific warnings by calling:
|
||||
*
|
||||
* YellowBox.ignoreWarnings(['Warning: ...']);
|
||||
*
|
||||
* (DEPRECATED) Warnings can be ignored programmatically by setting the array:
|
||||
*
|
||||
* console.ignoredYellowBox = ['Warning: ...'];
|
||||
*
|
||||
* Strings in `console.ignoredYellowBox` can be a prefix of the warning that
|
||||
* should be ignored.
|
||||
*/
|
||||
|
||||
if (__DEV__) {
|
||||
const {error, warn} = console;
|
||||
|
||||
(console: any).error = function() {
|
||||
error.apply(console, arguments);
|
||||
// Show yellow box for the `warning` module.
|
||||
if (
|
||||
typeof arguments[0] === 'string' &&
|
||||
arguments[0].startsWith('Warning: ')
|
||||
) {
|
||||
updateWarningMap.apply(null, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
(console: any).warn = function() {
|
||||
warn.apply(console, arguments);
|
||||
updateWarningMap.apply(null, arguments);
|
||||
};
|
||||
|
||||
if (Platform.isTesting) {
|
||||
(console: any).disableYellowBox = true;
|
||||
}
|
||||
|
||||
RCTLog.setWarningHandler((...args) => {
|
||||
updateWarningMap.apply(null, args);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple function for formatting strings.
|
||||
*
|
||||
* Replaces placeholders with values passed as extra arguments
|
||||
*
|
||||
* @param {string} format the base string
|
||||
* @param ...args the values to insert
|
||||
* @return {string} the replaced string
|
||||
*/
|
||||
function sprintf(format, ...args) {
|
||||
let index = 0;
|
||||
return format.replace(/%s/g, match => args[index++]);
|
||||
}
|
||||
|
||||
function updateWarningMap(...args): void {
|
||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an error
|
||||
* found when Flow v0.68 was deployed. To see the error delete this comment
|
||||
* and run Flow. */
|
||||
if (console.disableYellowBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
let warning;
|
||||
if (typeof args[0] === 'string') {
|
||||
const [format, ...formatArgs] = args;
|
||||
const argCount = (format.match(/%s/g) || []).length;
|
||||
warning = [
|
||||
sprintf(format, ...formatArgs.slice(0, argCount).map(stringifySafe)),
|
||||
...formatArgs.slice(argCount).map(stringifySafe),
|
||||
].join(' ');
|
||||
} else {
|
||||
warning = args.map(stringifySafe).join(' ');
|
||||
}
|
||||
|
||||
if (warning.startsWith('(ADVICE)')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const warningInfo = _warningMap.get(warning);
|
||||
if (warningInfo) {
|
||||
warningInfo.count += 1;
|
||||
} else {
|
||||
const error: any = new Error();
|
||||
error.framesToPop = 2;
|
||||
|
||||
_warningMap.set(warning, {
|
||||
count: 1,
|
||||
stacktrace: parseErrorStack(error),
|
||||
symbolicated: false,
|
||||
});
|
||||
}
|
||||
|
||||
_warningEmitter.emit('warning', _warningMap);
|
||||
}
|
||||
|
||||
function ensureSymbolicatedWarning(warning: string): void {
|
||||
const prevWarningInfo = _warningMap.get(warning);
|
||||
if (!prevWarningInfo || prevWarningInfo.symbolicated) {
|
||||
return;
|
||||
}
|
||||
prevWarningInfo.symbolicated = true;
|
||||
|
||||
symbolicateStackTrace(prevWarningInfo.stacktrace).then(
|
||||
stack => {
|
||||
const nextWarningInfo = _warningMap.get(warning);
|
||||
if (nextWarningInfo) {
|
||||
nextWarningInfo.stacktrace = stack;
|
||||
_warningEmitter.emit('warning', _warningMap);
|
||||
}
|
||||
},
|
||||
error => {
|
||||
const nextWarningInfo = _warningMap.get(warning);
|
||||
if (nextWarningInfo) {
|
||||
infoLog('Failed to symbolicate warning, "%s":', warning, error);
|
||||
_warningEmitter.emit('warning', _warningMap);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function isWarningIgnored(warning: string): boolean {
|
||||
const isIgnored = IGNORED_WARNINGS.some((ignoredWarning: string) =>
|
||||
warning.includes(ignoredWarning),
|
||||
);
|
||||
|
||||
if (isIgnored) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
return (
|
||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.68 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
Array.isArray(console.ignoredYellowBox) &&
|
||||
console.ignoredYellowBox.some(ignorePrefix =>
|
||||
warning.startsWith(String(ignorePrefix)),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const WarningRow = ({count, warning, onPress}) => {
|
||||
const Text = require('Text');
|
||||
const TouchableHighlight = require('TouchableHighlight');
|
||||
const View = require('View');
|
||||
|
||||
const countText =
|
||||
count > 1 ? (
|
||||
<Text style={styles.listRowCount}>{'(' + count + ') '}</Text>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<View style={styles.listRow}>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.5}
|
||||
onPress={onPress}
|
||||
style={styles.listRowContent}
|
||||
underlayColor="transparent">
|
||||
<Text style={styles.listRowText} numberOfLines={2}>
|
||||
{countText}
|
||||
{warning}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
type StackRowProps = {frame: StackFrame};
|
||||
const StackRow = ({frame}: StackRowProps) => {
|
||||
const Text = require('Text');
|
||||
const TouchableHighlight = require('TouchableHighlight');
|
||||
const {file, lineNumber} = frame;
|
||||
let fileName;
|
||||
if (file) {
|
||||
const fileParts = file.split('/');
|
||||
fileName = fileParts[fileParts.length - 1];
|
||||
} else {
|
||||
fileName = '<unknown file>';
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.5}
|
||||
style={styles.openInEditorButton}
|
||||
underlayColor="transparent"
|
||||
onPress={openFileInEditor.bind(null, file, lineNumber)}>
|
||||
<Text style={styles.inspectorCountText}>
|
||||
{fileName}:{lineNumber}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
};
|
||||
|
||||
const WarningInspector = ({
|
||||
warningInfo,
|
||||
warning,
|
||||
stacktraceVisible,
|
||||
onDismiss,
|
||||
onMinimize,
|
||||
toggleStacktrace,
|
||||
}) => {
|
||||
const ScrollView = require('ScrollView');
|
||||
const Text = require('Text');
|
||||
const TouchableHighlight = require('TouchableHighlight');
|
||||
const View = require('View');
|
||||
const {count, stacktrace} = warningInfo || {};
|
||||
|
||||
const countSentence =
|
||||
'Warning encountered ' + count + ' time' + (count - 1 ? 's' : '') + '.';
|
||||
|
||||
let stacktraceList;
|
||||
if (stacktraceVisible && stacktrace) {
|
||||
stacktraceList = (
|
||||
<View style={styles.stacktraceList}>
|
||||
{stacktrace.map((frame, ii) => <StackRow frame={frame} key={ii} />)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.inspector}>
|
||||
<SafeAreaView style={styles.safeArea}>
|
||||
<View style={styles.inspectorCount}>
|
||||
<Text style={styles.inspectorCountText}>{countSentence}</Text>
|
||||
<TouchableHighlight
|
||||
onPress={toggleStacktrace}
|
||||
underlayColor="transparent">
|
||||
<Text style={styles.inspectorButtonText}>
|
||||
{stacktraceVisible ? '\u{25BC}' : '\u{25B6}'} Stacktrace
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
<ScrollView style={styles.inspectorWarning}>
|
||||
{stacktraceList}
|
||||
<Text style={styles.inspectorWarningText}>{warning}</Text>
|
||||
</ScrollView>
|
||||
<View style={styles.inspectorButtons}>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.5}
|
||||
onPress={onMinimize}
|
||||
style={styles.inspectorButton}
|
||||
underlayColor="transparent">
|
||||
<Text style={styles.inspectorButtonText}>Minimize</Text>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.5}
|
||||
onPress={onDismiss}
|
||||
style={styles.inspectorButton}
|
||||
underlayColor="transparent">
|
||||
<Text style={styles.inspectorButtonText}>Dismiss</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
class YellowBox extends React.Component<
|
||||
mixed,
|
||||
{
|
||||
stacktraceVisible: boolean,
|
||||
inspecting: ?string,
|
||||
warningMap: Map<any, any>,
|
||||
},
|
||||
> {
|
||||
_listener: ?EmitterSubscription;
|
||||
dismissWarning: (warning: ?string) => void;
|
||||
|
||||
constructor(props: mixed, context: mixed) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
inspecting: null,
|
||||
stacktraceVisible: false,
|
||||
warningMap: _warningMap,
|
||||
};
|
||||
this.dismissWarning = warning => {
|
||||
const {inspecting, warningMap} = this.state;
|
||||
if (warning) {
|
||||
warningMap.delete(warning);
|
||||
} else {
|
||||
warningMap.clear();
|
||||
}
|
||||
this.setState({
|
||||
inspecting: warning && inspecting !== warning ? inspecting : null,
|
||||
warningMap,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
static ignoreWarnings(warnings: Array<string>): void {
|
||||
warnings.forEach((warning: string) => {
|
||||
if (IGNORED_WARNINGS.indexOf(warning) === -1) {
|
||||
IGNORED_WARNINGS.push(warning);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let scheduled = null;
|
||||
this._listener = _warningEmitter.addListener('warning', warningMap => {
|
||||
// Use `setImmediate` because warnings often happen during render, but
|
||||
// state cannot be set while rendering.
|
||||
scheduled =
|
||||
scheduled ||
|
||||
setImmediate(() => {
|
||||
scheduled = null;
|
||||
this.setState({
|
||||
warningMap,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const {inspecting} = this.state;
|
||||
if (inspecting != null) {
|
||||
ensureSymbolicatedWarning(inspecting);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._listener) {
|
||||
this._listener.remove();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.68 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
if (console.disableYellowBox || this.state.warningMap.size === 0) {
|
||||
return null;
|
||||
}
|
||||
const ScrollView = require('ScrollView');
|
||||
const View = require('View');
|
||||
const Text = require('Text');
|
||||
const TouchableHighlight = require('TouchableHighlight');
|
||||
|
||||
const {inspecting, stacktraceVisible} = this.state;
|
||||
const inspector =
|
||||
inspecting !== null ? (
|
||||
<WarningInspector
|
||||
warningInfo={this.state.warningMap.get(inspecting)}
|
||||
warning={inspecting}
|
||||
stacktraceVisible={stacktraceVisible}
|
||||
onDismiss={() => this.dismissWarning(inspecting)}
|
||||
onMinimize={() => this.setState({inspecting: null})}
|
||||
toggleStacktrace={() =>
|
||||
this.setState({stacktraceVisible: !stacktraceVisible})
|
||||
}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const rows = [];
|
||||
this.state.warningMap.forEach((warningInfo, warning) => {
|
||||
if (!isWarningIgnored(warning)) {
|
||||
rows.push(
|
||||
<WarningRow
|
||||
key={warning}
|
||||
count={warningInfo.count}
|
||||
warning={warning}
|
||||
onPress={() => this.setState({inspecting: warning})}
|
||||
onDismiss={() => this.dismissWarning(warning)}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const listStyle = [
|
||||
styles.list,
|
||||
// Additional `0.4` so the 5th row can peek into view.
|
||||
{height: Math.min(rows.length, 4.4) * (rowGutter + rowHeight)},
|
||||
];
|
||||
return (
|
||||
<View style={inspector ? styles.fullScreen : listStyle}>
|
||||
{!inspector &&
|
||||
rows.length > 0 && (
|
||||
<TouchableHighlight
|
||||
style={styles.dismissAllContainer}
|
||||
onPress={() => this.dismissWarning(null)}>
|
||||
<Text style={styles.dismissAll}>Dismiss All</Text>
|
||||
</TouchableHighlight>
|
||||
)}
|
||||
<ScrollView style={listStyle} scrollsToTop={false}>
|
||||
{rows}
|
||||
</ScrollView>
|
||||
{inspector}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const backgroundColor = opacity => 'rgba(250, 186, 48, ' + opacity + ')';
|
||||
const textColor = 'white';
|
||||
const rowGutter = 1;
|
||||
const rowHeight = 46;
|
||||
|
||||
// For unknown reasons, setting elevation: Number.MAX_VALUE causes remote debugging to
|
||||
// hang on iOS (some sort of overflow maybe). Setting it to Number.MAX_SAFE_INTEGER fixes the iOS issue, but since
|
||||
// elevation is an android-only style property we might as well remove it altogether for iOS.
|
||||
// See: https://github.com/facebook/react-native/issues/12223
|
||||
const elevation =
|
||||
Platform.OS === 'android' ? Number.MAX_SAFE_INTEGER : undefined;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullScreen: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
elevation: elevation,
|
||||
position: 'absolute',
|
||||
},
|
||||
inspector: {
|
||||
backgroundColor: backgroundColor(0.95),
|
||||
height: '100%',
|
||||
paddingTop: 5,
|
||||
elevation: elevation,
|
||||
},
|
||||
inspectorButtons: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
inspectorButton: {
|
||||
flex: 1,
|
||||
paddingVertical: 22,
|
||||
backgroundColor: backgroundColor(1),
|
||||
},
|
||||
safeArea: {
|
||||
flex: 1,
|
||||
},
|
||||
stacktraceList: {
|
||||
paddingBottom: 5,
|
||||
},
|
||||
inspectorButtonText: {
|
||||
color: textColor,
|
||||
fontSize: 14,
|
||||
opacity: 0.8,
|
||||
textAlign: 'center',
|
||||
},
|
||||
openInEditorButton: {
|
||||
paddingTop: 5,
|
||||
paddingBottom: 5,
|
||||
},
|
||||
inspectorCount: {
|
||||
padding: 15,
|
||||
paddingBottom: 0,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
inspectorCountText: {
|
||||
color: textColor,
|
||||
fontSize: 14,
|
||||
},
|
||||
inspectorWarning: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 15,
|
||||
},
|
||||
inspectorWarningText: {
|
||||
color: textColor,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
list: {
|
||||
backgroundColor: 'transparent',
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
elevation: elevation,
|
||||
},
|
||||
listRow: {
|
||||
backgroundColor: backgroundColor(0.95),
|
||||
height: rowHeight,
|
||||
marginTop: rowGutter,
|
||||
},
|
||||
listRowContent: {
|
||||
flex: 1,
|
||||
},
|
||||
listRowCount: {
|
||||
color: 'rgba(255, 255, 255, 0.5)',
|
||||
},
|
||||
listRowText: {
|
||||
color: textColor,
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: Platform.OS === 'android' ? 5 : 7,
|
||||
marginLeft: 15,
|
||||
marginRight: 15,
|
||||
},
|
||||
dismissAllContainer: {
|
||||
height: 20,
|
||||
justifyContent: 'center',
|
||||
marginTop: -30,
|
||||
marginRight: 5,
|
||||
backgroundColor: backgroundColor(0.95),
|
||||
alignSelf: 'flex-end',
|
||||
paddingHorizontal: 10,
|
||||
borderRadius: 10,
|
||||
},
|
||||
dismissAll: {
|
||||
color: 'white',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBox;
|
||||
@@ -1 +1 @@
|
||||
c0fe8d6f6942f5cbc93c09825e803ba8cf950522
|
||||
ae14317d6810d5b07d01308a2116a707e5e09983
|
||||
+8320
-8611
File diff suppressed because it is too large
Load Diff
+2960
-3569
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
|
||||
} = require('ReactNative');
|
||||
|
||||
module.exports =
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactNativeComponentTree;
|
||||
@@ -89,7 +89,6 @@ export type NativeMethodsMixinType = {
|
||||
|
||||
type SecretInternalsType = {
|
||||
NativeMethodsMixin: NativeMethodsMixinType,
|
||||
ReactNativeComponentTree: any,
|
||||
computeComponentStackForErrorReporting(tag: number): string,
|
||||
// TODO (bvaughn) Decide which additional types to expose here?
|
||||
// And how much information to fill in for the above types.
|
||||
@@ -97,7 +96,6 @@ type SecretInternalsType = {
|
||||
|
||||
type SecretInternalsFabricType = {
|
||||
NativeMethodsMixin: NativeMethodsMixinType,
|
||||
ReactNativeComponentTree: any,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,10 +92,10 @@ export type ____LayoutStyle_Internal = $ReadOnly<{|
|
||||
export type ____TransformStyle_Internal = $ReadOnly<{|
|
||||
transform?: $ReadOnlyArray<
|
||||
| {|+perspective: number | AnimatedNode|}
|
||||
| {|+rotate: string|}
|
||||
| {|+rotateX: string|}
|
||||
| {|+rotateY: string|}
|
||||
| {|+rotateZ: string|}
|
||||
| {|+rotate: string | AnimatedNode|}
|
||||
| {|+rotateX: string | AnimatedNode|}
|
||||
| {|+rotateY: string | AnimatedNode|}
|
||||
| {|+rotateZ: string | AnimatedNode|}
|
||||
| {|+scale: number | AnimatedNode|}
|
||||
| {|+scaleX: number | AnimatedNode|}
|
||||
| {|+scaleY: number | AnimatedNode|}
|
||||
@@ -138,23 +138,23 @@ export type ____ViewStyle_Internal = $ReadOnly<{|
|
||||
borderRightColor?: ColorValue,
|
||||
borderStartColor?: ColorValue,
|
||||
borderTopColor?: ColorValue,
|
||||
borderRadius?: number,
|
||||
borderBottomEndRadius?: number,
|
||||
borderBottomLeftRadius?: number,
|
||||
borderBottomRightRadius?: number,
|
||||
borderBottomStartRadius?: number,
|
||||
borderTopEndRadius?: number,
|
||||
borderTopLeftRadius?: number,
|
||||
borderTopRightRadius?: number,
|
||||
borderTopStartRadius?: number,
|
||||
borderRadius?: number | AnimatedNode,
|
||||
borderBottomEndRadius?: number | AnimatedNode,
|
||||
borderBottomLeftRadius?: number | AnimatedNode,
|
||||
borderBottomRightRadius?: number | AnimatedNode,
|
||||
borderBottomStartRadius?: number | AnimatedNode,
|
||||
borderTopEndRadius?: number | AnimatedNode,
|
||||
borderTopLeftRadius?: number | AnimatedNode,
|
||||
borderTopRightRadius?: number | AnimatedNode,
|
||||
borderTopStartRadius?: number | AnimatedNode,
|
||||
borderStyle?: 'solid' | 'dotted' | 'dashed',
|
||||
borderWidth?: number,
|
||||
borderBottomWidth?: number,
|
||||
borderEndWidth?: number,
|
||||
borderLeftWidth?: number,
|
||||
borderRightWidth?: number,
|
||||
borderStartWidth?: number,
|
||||
borderTopWidth?: number,
|
||||
borderWidth?: number | AnimatedNode,
|
||||
borderBottomWidth?: number | AnimatedNode,
|
||||
borderEndWidth?: number | AnimatedNode,
|
||||
borderLeftWidth?: number | AnimatedNode,
|
||||
borderRightWidth?: number | AnimatedNode,
|
||||
borderStartWidth?: number | AnimatedNode,
|
||||
borderTopWidth?: number | AnimatedNode,
|
||||
opacity?: number | AnimatedNode,
|
||||
elevation?: number,
|
||||
|}>;
|
||||
|
||||
@@ -289,7 +289,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||
|
||||
NSString *previousText = [_predictedText substringWithRange:range] ?: @"";
|
||||
|
||||
if (_predictedText) {
|
||||
// After clearing the text by replacing it with an empty string, `_predictedText`
|
||||
// still preserves the deleted text.
|
||||
// As the first character in the TextInput always comes with the range value (0, 0),
|
||||
// we should check the range value in order to avoid appending a character to the deleted string
|
||||
// (which caused the issue #18374)
|
||||
if (!NSEqualRanges(range, NSMakeRange(0, 0)) && _predictedText) {
|
||||
_predictedText = [_predictedText stringByReplacingCharactersInRange:range withString:text];
|
||||
} else {
|
||||
_predictedText = text;
|
||||
|
||||
@@ -20,6 +20,7 @@ const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationI
|
||||
* - Less chance of typos.
|
||||
*/
|
||||
const UTFSequence = deepFreezeAndThrowOnMutationInDev({
|
||||
BOM: '\ufeff', // byte order mark
|
||||
BULLET: '\u2022', // bullet: •
|
||||
BULLET_SP: '\u00A0\u2022\u00A0', // •
|
||||
MIDDOT: '\u00B7', // normal middle dot: ·
|
||||
@@ -31,6 +32,8 @@ const UTFSequence = deepFreezeAndThrowOnMutationInDev({
|
||||
NDASH_SP: '\u00A0\u2013\u00A0', // –
|
||||
NBSP: '\u00A0', // non-breaking space:
|
||||
PIZZA: '\uD83C\uDF55',
|
||||
TRIANGLE_LEFT: '\u25c0', // black left-pointing triangle
|
||||
TRIANGLE_RIGHT: '\u25b6', // black right-pointing triangle
|
||||
});
|
||||
|
||||
module.exports = UTFSequence;
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTDefines.h>
|
||||
|
||||
#if __has_include(<React/fishhook.h>)
|
||||
#import <React/fishhook.h>
|
||||
#else
|
||||
#import <fishhook/fishhook.h>
|
||||
#endif
|
||||
|
||||
#if __has_include(<os/log.h>) && defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100300 /* __IPHONE_10_3 */
|
||||
#import <os/log.h>
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const Text = require('Text');
|
||||
const UTFSequence = require('UTFSequence');
|
||||
|
||||
const stringifySafe = require('stringifySafe');
|
||||
|
||||
import type {TextStyleProp} from 'StyleSheet';
|
||||
|
||||
export type Category = string;
|
||||
export type Message = $ReadOnly<{|
|
||||
content: string,
|
||||
substitutions: $ReadOnlyArray<
|
||||
$ReadOnly<{|
|
||||
length: number,
|
||||
offset: number,
|
||||
|}>,
|
||||
>,
|
||||
|}>;
|
||||
|
||||
const SUBSTITUTION = UTFSequence.BOM + '%s';
|
||||
|
||||
const YellowBoxCategory = {
|
||||
parse(
|
||||
args: $ReadOnlyArray<mixed>,
|
||||
): $ReadOnly<{|
|
||||
category: Category,
|
||||
message: Message,
|
||||
|}> {
|
||||
const categoryParts = [];
|
||||
const contentParts = [];
|
||||
const substitutionOffsets = [];
|
||||
|
||||
const remaining = [...args];
|
||||
|
||||
if (typeof remaining[0] === 'string') {
|
||||
const formatString = String(remaining.shift());
|
||||
const formatStringParts = formatString.split('%s');
|
||||
const substitutionCount = formatStringParts.length - 1;
|
||||
const substitutions = remaining.splice(0, substitutionCount);
|
||||
|
||||
let categoryString = '';
|
||||
let contentString = '';
|
||||
|
||||
let substitutionIndex = 0;
|
||||
for (const formatStringPart of formatStringParts) {
|
||||
categoryString += formatStringPart;
|
||||
contentString += formatStringPart;
|
||||
|
||||
if (substitutionIndex < substitutionCount) {
|
||||
if (substitutionIndex < substitutions.length) {
|
||||
const substitution = stringifySafe(
|
||||
substitutions[substitutionIndex],
|
||||
);
|
||||
substitutionOffsets.push({
|
||||
length: substitution.length,
|
||||
offset: contentString.length,
|
||||
});
|
||||
|
||||
categoryString += SUBSTITUTION;
|
||||
contentString += substitution;
|
||||
} else {
|
||||
substitutionOffsets.push({
|
||||
length: 2,
|
||||
offset: contentString.length,
|
||||
});
|
||||
|
||||
categoryString += '%s';
|
||||
contentString += '%s';
|
||||
}
|
||||
|
||||
substitutionIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
categoryParts.push(categoryString);
|
||||
contentParts.push(contentString);
|
||||
}
|
||||
|
||||
const remainingArgs = remaining.map(stringifySafe);
|
||||
categoryParts.push(...remainingArgs);
|
||||
contentParts.push(...remainingArgs);
|
||||
|
||||
return {
|
||||
category: categoryParts.join(' '),
|
||||
message: {
|
||||
content: contentParts.join(' '),
|
||||
substitutions: substitutionOffsets,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
render(
|
||||
{content, substitutions}: Message,
|
||||
substitutionStyle: TextStyleProp,
|
||||
): React.Node {
|
||||
const elements = [];
|
||||
|
||||
const lastOffset = substitutions.reduce(
|
||||
(prevOffset, substitution, index) => {
|
||||
const key = String(index);
|
||||
|
||||
if (substitution.offset > prevOffset) {
|
||||
const prevPart = content.substr(
|
||||
prevOffset,
|
||||
substitution.offset - prevOffset,
|
||||
);
|
||||
elements.push(<Text key={key}>{prevPart}</Text>);
|
||||
}
|
||||
|
||||
const substititionPart = content.substr(
|
||||
substitution.offset,
|
||||
substitution.length,
|
||||
);
|
||||
elements.push(
|
||||
<Text key={key + '.5'} style={substitutionStyle}>
|
||||
{substititionPart}
|
||||
</Text>,
|
||||
);
|
||||
|
||||
return substitution.offset + substitution.length;
|
||||
},
|
||||
0,
|
||||
);
|
||||
|
||||
if (lastOffset < content.length - 1) {
|
||||
const lastPart = content.substr(lastOffset);
|
||||
elements.push(<Text key="-1">{lastPart}</Text>);
|
||||
}
|
||||
|
||||
return elements;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = YellowBoxCategory;
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxWarning = require('YellowBoxWarning');
|
||||
|
||||
import type {Category} from 'YellowBoxCategory';
|
||||
|
||||
export type Registry = Map<Category, $ReadOnlyArray<YellowBoxWarning>>;
|
||||
|
||||
export type Observer = (registry: Registry) => void;
|
||||
|
||||
export type Subscription = $ReadOnly<{|
|
||||
unsubscribe: () => void,
|
||||
|}>;
|
||||
|
||||
const observers: Set<{observer: Observer}> = new Set();
|
||||
const ignorePatterns: Set<string> = new Set();
|
||||
const registry: Registry = new Map();
|
||||
|
||||
let disabled = false;
|
||||
let projection = new Map();
|
||||
let updateTimeout = null;
|
||||
|
||||
function isWarningIgnored(warning: YellowBoxWarning): boolean {
|
||||
for (const pattern of ignorePatterns) {
|
||||
if (warning.message.content.includes(pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleUpdate(): void {
|
||||
projection = new Map();
|
||||
if (!disabled) {
|
||||
for (const [category, warnings] of registry) {
|
||||
const filtered = warnings.filter(warning => !isWarningIgnored(warning));
|
||||
if (filtered.length > 0) {
|
||||
projection.set(category, filtered);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updateTimeout == null) {
|
||||
updateTimeout = setImmediate(() => {
|
||||
updateTimeout = null;
|
||||
for (const {observer} of observers) {
|
||||
observer(projection);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const YellowBoxRegistry = {
|
||||
add({
|
||||
args,
|
||||
framesToPop,
|
||||
}: $ReadOnly<{|
|
||||
args: $ReadOnlyArray<mixed>,
|
||||
framesToPop: number,
|
||||
|}>): void {
|
||||
if (typeof args[0] === 'string' && args[0].startsWith('(ADVICE)')) {
|
||||
return;
|
||||
}
|
||||
const {category, message, stack} = YellowBoxWarning.parse({
|
||||
args,
|
||||
framesToPop: framesToPop + 1,
|
||||
});
|
||||
|
||||
let warnings = registry.get(category);
|
||||
if (warnings == null) {
|
||||
warnings = [];
|
||||
}
|
||||
warnings = [...warnings, new YellowBoxWarning(message, stack)];
|
||||
|
||||
registry.delete(category);
|
||||
registry.set(category, warnings);
|
||||
|
||||
handleUpdate();
|
||||
},
|
||||
|
||||
delete(category: Category): void {
|
||||
if (registry.has(category)) {
|
||||
registry.delete(category);
|
||||
handleUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
clear(): void {
|
||||
if (registry.size > 0) {
|
||||
registry.clear();
|
||||
handleUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
addIgnorePatterns(patterns: $ReadOnlyArray<string>): void {
|
||||
const newPatterns = patterns.filter(
|
||||
pattern => !ignorePatterns.has(pattern),
|
||||
);
|
||||
if (newPatterns.length === 0) {
|
||||
return;
|
||||
}
|
||||
for (const pattern of newPatterns) {
|
||||
ignorePatterns.add(pattern);
|
||||
}
|
||||
handleUpdate();
|
||||
},
|
||||
|
||||
setDisabled(value: boolean): void {
|
||||
if (value === disabled) {
|
||||
return;
|
||||
}
|
||||
disabled = value;
|
||||
handleUpdate();
|
||||
},
|
||||
|
||||
isDisabled(): boolean {
|
||||
return disabled;
|
||||
},
|
||||
|
||||
observe(observer: Observer): Subscription {
|
||||
const subscription = {observer};
|
||||
observers.add(subscription);
|
||||
observer(projection);
|
||||
return {
|
||||
unsubscribe(): void {
|
||||
observers.delete(subscription);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = YellowBoxRegistry;
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const symbolicateStackTrace = require('symbolicateStackTrace');
|
||||
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
type CacheKey = string;
|
||||
|
||||
export type Stack = Array<StackFrame>;
|
||||
|
||||
const cache: Map<CacheKey, Promise<Stack>> = new Map();
|
||||
|
||||
const YellowBoxSymbolication = {
|
||||
symbolicate(stack: Stack): Promise<Stack> {
|
||||
const key = getCacheKey(stack);
|
||||
|
||||
let promise = cache.get(key);
|
||||
if (promise == null) {
|
||||
promise = symbolicateStackTrace(stack).then(sanitize);
|
||||
cache.set(key, promise);
|
||||
}
|
||||
|
||||
return promise;
|
||||
},
|
||||
};
|
||||
|
||||
const getCacheKey = (stack: Stack): CacheKey => {
|
||||
return JSON.stringify(stack);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sanitize because sometimes, `symbolicateStackTrace` gives us invalid values.
|
||||
*/
|
||||
const sanitize = (maybeStack: mixed): Stack => {
|
||||
if (!Array.isArray(maybeStack)) {
|
||||
throw new Error('Expected stack to be an array.');
|
||||
}
|
||||
const stack = [];
|
||||
for (const maybeFrame of maybeStack) {
|
||||
if (typeof maybeFrame !== 'object' || maybeFrame == null) {
|
||||
throw new Error('Expected each stack frame to be an object.');
|
||||
}
|
||||
if (typeof maybeFrame.column !== 'number' && maybeFrame.column != null) {
|
||||
throw new Error('Expected stack frame `column` to be a nullable number.');
|
||||
}
|
||||
if (typeof maybeFrame.file !== 'string') {
|
||||
throw new Error('Expected stack frame `file` to be a string.');
|
||||
}
|
||||
if (typeof maybeFrame.lineNumber !== 'number') {
|
||||
throw new Error('Expected stack frame `lineNumber` to be a number.');
|
||||
}
|
||||
if (typeof maybeFrame.methodName !== 'string') {
|
||||
throw new Error('Expected stack frame `methodName` to be a string.');
|
||||
}
|
||||
stack.push({
|
||||
column: maybeFrame.column,
|
||||
file: maybeFrame.file,
|
||||
lineNumber: maybeFrame.lineNumber,
|
||||
methodName: maybeFrame.methodName,
|
||||
});
|
||||
}
|
||||
return stack;
|
||||
};
|
||||
|
||||
module.exports = YellowBoxSymbolication;
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
const YellowBoxSymbolication = require('YellowBoxSymbolication');
|
||||
|
||||
const parseErrorStack = require('parseErrorStack');
|
||||
|
||||
import type {Category, Message} from 'YellowBoxCategory';
|
||||
import type {Stack} from 'YellowBoxSymbolication';
|
||||
|
||||
export type SymbolicationRequest = $ReadOnly<{|
|
||||
abort: () => void,
|
||||
|}>;
|
||||
|
||||
class YellowBoxWarning {
|
||||
static parse({
|
||||
args,
|
||||
framesToPop,
|
||||
}: $ReadOnly<{|
|
||||
args: $ReadOnlyArray<mixed>,
|
||||
framesToPop: number,
|
||||
|}>): {|
|
||||
category: Category,
|
||||
message: Message,
|
||||
stack: Stack,
|
||||
|} {
|
||||
return {
|
||||
...YellowBoxCategory.parse(args),
|
||||
stack: createStack({framesToPop: framesToPop + 1}),
|
||||
};
|
||||
}
|
||||
|
||||
message: Message;
|
||||
stack: Stack;
|
||||
symbolicated:
|
||||
| $ReadOnly<{|error: null, stack: null, status: 'NONE'|}>
|
||||
| $ReadOnly<{|error: null, stack: null, status: 'PENDING'|}>
|
||||
| $ReadOnly<{|error: null, stack: Stack, status: 'COMPLETE'|}>
|
||||
| $ReadOnly<{|error: Error, stack: null, status: 'FAILED'|}> = {
|
||||
error: null,
|
||||
stack: null,
|
||||
status: 'NONE',
|
||||
};
|
||||
|
||||
constructor(message: Message, stack: Stack) {
|
||||
this.message = message;
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
getAvailableStack(): Stack {
|
||||
return this.symbolicated.status === 'COMPLETE'
|
||||
? this.symbolicated.stack
|
||||
: this.stack;
|
||||
}
|
||||
|
||||
symbolicate(callback: () => void): SymbolicationRequest {
|
||||
let aborted = false;
|
||||
|
||||
if (this.symbolicated.status !== 'COMPLETE') {
|
||||
const updateStatus = (error: ?Error, stack: ?Stack): void => {
|
||||
if (error != null) {
|
||||
this.symbolicated = {error, stack: null, status: 'FAILED'};
|
||||
} else if (stack != null) {
|
||||
this.symbolicated = {error: null, stack, status: 'COMPLETE'};
|
||||
} else {
|
||||
this.symbolicated = {error: null, stack: null, status: 'PENDING'};
|
||||
}
|
||||
if (!aborted) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
updateStatus(null, null);
|
||||
YellowBoxSymbolication.symbolicate(this.stack).then(
|
||||
stack => {
|
||||
updateStatus(null, stack);
|
||||
},
|
||||
error => {
|
||||
updateStatus(error, null);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
abort(): void {
|
||||
aborted = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function createStack({framesToPop}: $ReadOnly<{|framesToPop: number|}>): Stack {
|
||||
const error: any = new Error();
|
||||
error.framesToPop = framesToPop + 1;
|
||||
return parseErrorStack(error);
|
||||
}
|
||||
|
||||
module.exports = YellowBoxWarning;
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
|
||||
describe('YellowBoxCategory', () => {
|
||||
it('parses strings', () => {
|
||||
expect(YellowBoxCategory.parse(['A'])).toEqual({
|
||||
category: 'A',
|
||||
message: {
|
||||
content: 'A',
|
||||
substitutions: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('parses strings with arguments', () => {
|
||||
expect(YellowBoxCategory.parse(['A', 'B', 'C'])).toEqual({
|
||||
category: 'A "B" "C"',
|
||||
message: {
|
||||
content: 'A "B" "C"',
|
||||
substitutions: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('parses formatted strings', () => {
|
||||
expect(YellowBoxCategory.parse(['%s', 'A'])).toEqual({
|
||||
category: '\ufeff%s',
|
||||
message: {
|
||||
content: '"A"',
|
||||
substitutions: [
|
||||
{
|
||||
length: 3,
|
||||
offset: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('parses formatted strings with insufficient arguments', () => {
|
||||
expect(YellowBoxCategory.parse(['%s %s', 'A'])).toEqual({
|
||||
category: '\ufeff%s %s',
|
||||
message: {
|
||||
content: '"A" %s',
|
||||
substitutions: [
|
||||
{
|
||||
length: 3,
|
||||
offset: 0,
|
||||
},
|
||||
{
|
||||
length: 2,
|
||||
offset: 4,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('parses formatted strings with excess arguments', () => {
|
||||
expect(YellowBoxCategory.parse(['%s', 'A', 'B'])).toEqual({
|
||||
category: '\ufeff%s "B"',
|
||||
message: {
|
||||
content: '"A" "B"',
|
||||
substitutions: [
|
||||
{
|
||||
length: 3,
|
||||
offset: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('treats "%s" in arguments as literals', () => {
|
||||
expect(YellowBoxCategory.parse(['%s', '%s', 'A'])).toEqual({
|
||||
category: '\ufeff%s "A"',
|
||||
message: {
|
||||
content: '"%s" "A"',
|
||||
substitutions: [
|
||||
{
|
||||
length: 4,
|
||||
offset: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,267 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
const YellowBoxRegistry = require('YellowBoxRegistry');
|
||||
|
||||
const registry = () => {
|
||||
const observer = jest.fn();
|
||||
YellowBoxRegistry.observe(observer).unsubscribe();
|
||||
return observer.mock.calls[0][0];
|
||||
};
|
||||
|
||||
const observe = () => {
|
||||
const observer = jest.fn();
|
||||
return {
|
||||
observer,
|
||||
subscription: YellowBoxRegistry.observe(observer),
|
||||
};
|
||||
};
|
||||
|
||||
describe('YellowBoxRegistry', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
});
|
||||
|
||||
it('adds and deletes warnings', () => {
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
const {category: categoryA} = YellowBoxCategory.parse(['A']);
|
||||
|
||||
expect(registry().size).toBe(1);
|
||||
expect(registry().get(categoryA)).not.toBe(undefined);
|
||||
|
||||
YellowBoxRegistry.delete(categoryA);
|
||||
expect(registry().size).toBe(0);
|
||||
expect(registry().get(categoryA)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('clears all warnings', () => {
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['C'], framesToPop: 0});
|
||||
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.clear();
|
||||
expect(registry().size).toBe(0);
|
||||
});
|
||||
|
||||
it('sorts warnings in chronological order', () => {
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['C'], framesToPop: 0});
|
||||
|
||||
const {category: categoryA} = YellowBoxCategory.parse(['A']);
|
||||
const {category: categoryB} = YellowBoxCategory.parse(['B']);
|
||||
const {category: categoryC} = YellowBoxCategory.parse(['C']);
|
||||
|
||||
expect(Array.from(registry().keys())).toEqual([
|
||||
categoryA,
|
||||
categoryB,
|
||||
categoryC,
|
||||
]);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
|
||||
// Expect `A` to be hoisted to the end of the registry.
|
||||
expect(Array.from(registry().keys())).toEqual([
|
||||
categoryB,
|
||||
categoryC,
|
||||
categoryA,
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignores warnings matching patterns', () => {
|
||||
YellowBoxRegistry.add({args: ['A!'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B?'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['C!'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.addIgnorePatterns(['!']);
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.addIgnorePatterns(['?']);
|
||||
expect(registry().size).toBe(0);
|
||||
});
|
||||
|
||||
it('ignores all warnings when disabled', () => {
|
||||
YellowBoxRegistry.add({args: ['A!'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B?'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['C!'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.setDisabled(true);
|
||||
expect(registry().size).toBe(0);
|
||||
|
||||
YellowBoxRegistry.setDisabled(false);
|
||||
expect(registry().size).toBe(3);
|
||||
});
|
||||
|
||||
it('groups warnings by simple categories', () => {
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(2);
|
||||
});
|
||||
|
||||
it('groups warnings by format string categories', () => {
|
||||
YellowBoxRegistry.add({args: ['%s', 'A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['%s', 'B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(2);
|
||||
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
});
|
||||
|
||||
it('groups warnings with consideration for arguments', () => {
|
||||
YellowBoxRegistry.add({args: ['A', 'B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A', 'B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A', 'C'], framesToPop: 0});
|
||||
expect(registry().size).toBe(2);
|
||||
|
||||
YellowBoxRegistry.add({args: ['%s', 'A', 'A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.add({args: ['%s', 'B', 'A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.add({args: ['%s', 'B', 'B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(4);
|
||||
});
|
||||
|
||||
it('ignores warnings starting with "(ADVICE)"', () => {
|
||||
YellowBoxRegistry.add({args: ['(ADVICE) ...'], framesToPop: 0});
|
||||
expect(registry().size).toBe(0);
|
||||
});
|
||||
|
||||
it('does not ignore warnings formatted to start with "(ADVICE)"', () => {
|
||||
YellowBoxRegistry.add({args: ['%s ...', '(ADVICE)'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
});
|
||||
|
||||
it('immediately updates new observers', () => {
|
||||
const {observer} = observe();
|
||||
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
expect(observer.mock.calls[0][0]).toBe(registry());
|
||||
});
|
||||
|
||||
it('sends batched updates asynchoronously', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it('stops sending updates to unsubscribed observers', () => {
|
||||
const {observer, subscription} = observe();
|
||||
subscription.unsubscribe();
|
||||
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
expect(observer.mock.calls[0][0]).toBe(registry());
|
||||
});
|
||||
|
||||
it('updates observers when a warning is added or deleted', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
const {category: categoryA} = YellowBoxCategory.parse(['A']);
|
||||
YellowBoxRegistry.delete(categoryA);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
|
||||
// Does nothing when category does not exist.
|
||||
YellowBoxRegistry.delete(categoryA);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
});
|
||||
|
||||
it('updates observers when cleared', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
YellowBoxRegistry.clear();
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
|
||||
// Does nothing when already empty.
|
||||
YellowBoxRegistry.clear();
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
});
|
||||
|
||||
it('updates observers when an ignore pattern is added', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.addIgnorePatterns(['?']);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
YellowBoxRegistry.addIgnorePatterns(['!']);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
|
||||
// Does nothing for an existing ignore pattern.
|
||||
YellowBoxRegistry.addIgnorePatterns(['!']);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
});
|
||||
|
||||
it('updates observers when disabled or enabled', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.setDisabled(true);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
// Does nothing when already disabled.
|
||||
YellowBoxRegistry.setDisabled(true);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
YellowBoxRegistry.setDisabled(false);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
|
||||
// Does nothing when already enabled.
|
||||
YellowBoxRegistry.setDisabled(false);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
jest.mock('symbolicateStackTrace');
|
||||
|
||||
const YellowBoxSymbolication = require('YellowBoxSymbolication');
|
||||
|
||||
const symbolicateStackTrace: JestMockFn<
|
||||
$ReadOnlyArray<Array<StackFrame>>,
|
||||
Promise<Array<StackFrame>>,
|
||||
> = (require('symbolicateStackTrace'): any);
|
||||
|
||||
const createStack = methodNames =>
|
||||
methodNames.map(methodName => ({
|
||||
column: null,
|
||||
file: 'file://path/to/file.js',
|
||||
lineNumber: 1,
|
||||
methodName,
|
||||
}));
|
||||
|
||||
describe('YellowBoxSymbolication', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
symbolicateStackTrace.mockImplementation(async stack => stack);
|
||||
});
|
||||
|
||||
it('symbolicates different stacks', () => {
|
||||
YellowBoxSymbolication.symbolicate(createStack(['A', 'B', 'C']));
|
||||
YellowBoxSymbolication.symbolicate(createStack(['D', 'E', 'F']));
|
||||
|
||||
expect(symbolicateStackTrace.mock.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it('batch symbolicates equivalent stacks', () => {
|
||||
YellowBoxSymbolication.symbolicate(createStack(['A', 'B', 'C']));
|
||||
YellowBoxSymbolication.symbolicate(createStack(['A', 'B', 'C']));
|
||||
|
||||
expect(symbolicateStackTrace.mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
jest.mock('YellowBoxSymbolication');
|
||||
|
||||
const YellowBoxSymbolication: {|
|
||||
symbolicate: JestMockFn<
|
||||
$ReadOnlyArray<Array<StackFrame>>,
|
||||
Promise<Array<StackFrame>>,
|
||||
>,
|
||||
|} = (require('YellowBoxSymbolication'): any);
|
||||
const YellowBoxWarning = require('YellowBoxWarning');
|
||||
|
||||
const createStack = methodNames =>
|
||||
methodNames.map(methodName => ({
|
||||
column: null,
|
||||
file: 'file://path/to/file.js',
|
||||
lineNumber: 1,
|
||||
methodName,
|
||||
}));
|
||||
|
||||
describe('YellowBoxWarning', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
YellowBoxSymbolication.symbolicate.mockImplementation(async stack =>
|
||||
createStack(stack.map(frame => `S(${frame.methodName})`)),
|
||||
);
|
||||
});
|
||||
|
||||
it('starts without a symbolicated stack', () => {
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
|
||||
expect(warning.symbolicated).toEqual({
|
||||
error: null,
|
||||
stack: null,
|
||||
status: 'NONE',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates when symbolication is in progress', () => {
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
const callback = jest.fn();
|
||||
warning.symbolicate(callback);
|
||||
|
||||
expect(callback.mock.calls.length).toBe(1);
|
||||
expect(warning.symbolicated).toEqual({
|
||||
error: null,
|
||||
stack: null,
|
||||
status: 'PENDING',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates when symbolication finishes', () => {
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
const callback = jest.fn();
|
||||
warning.symbolicate(callback);
|
||||
|
||||
jest.runAllTicks();
|
||||
|
||||
expect(callback.mock.calls.length).toBe(2);
|
||||
expect(warning.symbolicated).toEqual({
|
||||
error: null,
|
||||
stack: createStack(['S(A)', 'S(B)', 'S(C)']),
|
||||
status: 'COMPLETE',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates when symbolication fails', () => {
|
||||
const error = new Error('...');
|
||||
YellowBoxSymbolication.symbolicate.mockImplementation(async stack => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
const callback = jest.fn();
|
||||
warning.symbolicate(callback);
|
||||
|
||||
jest.runAllTicks();
|
||||
|
||||
expect(callback.mock.calls.length).toBe(2);
|
||||
expect(warning.symbolicated).toEqual({
|
||||
error,
|
||||
stack: null,
|
||||
status: 'FAILED',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not update aborted requests', () => {
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
const callback = jest.fn();
|
||||
const request = warning.symbolicate(callback);
|
||||
request.abort();
|
||||
|
||||
jest.runAllTicks();
|
||||
|
||||
expect(callback.mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
hitSlop?: ?EdgeInsetsProp,
|
||||
label: string,
|
||||
onPress: () => void,
|
||||
|}>;
|
||||
|
||||
const YellowBoxButton = (props: Props): React.Node => (
|
||||
<YellowBoxPressable
|
||||
hitSlop={props.hitSlop}
|
||||
onPress={props.onPress}
|
||||
style={styles.root}>
|
||||
<Text numberOfLines={1} style={styles.label}>
|
||||
{props.label}
|
||||
</Text>
|
||||
</YellowBoxPressable>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
borderRadius: 14,
|
||||
height: 28,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
label: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 12,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 16,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxButton;
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const PixelRatio = require('PixelRatio');
|
||||
|
||||
const scale = PixelRatio.get();
|
||||
|
||||
/**
|
||||
* We use inline images for YellowBox in order to avoid display latency due to
|
||||
* resource contention with symbolicating stack traces.
|
||||
*
|
||||
* The following steps were used to create these:
|
||||
*
|
||||
* 1. Download SVG files from: https://feathericons.com
|
||||
* 2. Rasterize SVG files to PNG files at 16dp, 36dp, and 48dp.
|
||||
* 3. Convert to Base64: https://www.google.com/search?q=base64+image+encoder
|
||||
*
|
||||
* @see https://github.com/feathericons/feather
|
||||
* @copyright 2013-2017 Cole Bemis
|
||||
* @license MIT
|
||||
*/
|
||||
const YellowBoxImageSource = {
|
||||
alertTriangle:
|
||||
scale > 2
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAB60lEQVRoge2Z3W3DIBSFj9oFPAIjZARGyAiMkBHuJh4hI2QEj5AR3Me+tQ91JALHmD8bKvmTkCr5Auc6/kzUACcnRXzuvL4GoAB8Afjeea9qXADcAfw4475c65orgBl++NeYl5ouUQiHt5tQTRJuwB6b5zLY49QVGn7I0bo+kuv60IQbuHf5CWCIqOkCgX93maia1MkRAUMo+OI+AvUPp7a50EzcUCBF6psJrUkYiZgnZJ7eId8mMeIyhpW5hyLw72LKCXsl86VqwgAKceKapW5e/nZpJnSsuHaTM7muyDq7C63JprJS69YxhNTpSlkpKeLGNHCo0EJChcSNaQA4SGiFtBMXJFSI3YVOPXFB6kMoUl9NaE0Wl4h5KQ0AOwqde+KmNrCL0EKCxJ64qQ0AlYVWSBfXZusgW6Oa0Dni2hiEv0qsoci+yUJrsoikLlKAkP11ygK54taiSOgb/O5b/DMqS+gBZeLWJlnoEX7XwQkBDPIktlEkz7hWrEmxZG4M5L9GXYTk0qxwcopKxa3VABN6cosM/C5LxTUof4ReMKHf1nRlaSnuGsGM7kfU4w8RF5Bz4aNlokLe/HQ/ngl9/Qih4L9k3h4hA1+S3odxu3Q77Hl4r1Hg75n6D01M2Difbp02Mi3ZTk5OLH4BUyEtOlDYuK0AAAAASUVORK5CYII='
|
||||
: scale > 1
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABVklEQVRYheWX4U3DMBBGH4gBMoJHyAgeoSNkAxjBG5QNOkJHCGzQDcoGZQP4gY3Oqe1cEscS4pNOqs9Jvqvv6ZrCf9fDhnutD4A3H810Br4mcW5l7hLmIdze5mZi+OJD5syeBYzC6CjyR5Ef9zI/CJMb0Im9zufC/qG2eQdchcGQuGYQ+9dJgZvl0B2xbJGrZW6IIevFXp9YVwcyB540syJfFcgSeJb0cVcDcg68XAFQCUhH+ShLBcBGIA158LQFqIB8zBRwEp9fgctcxQld/L2pZxZVAk/KkucjaDGQmoknrz35KEE2sABIRxm8tVIBaZgHb61UQOYmXk7aFgQVJ6QWPCnLAriYAVILnpTxD7yh/9EZiIEE4m+y29uMkGy1nQ6i9wYFRB5PwKdYP/v1msmnUe89gn695bG0iqjdXeMiRu9599csvGKZ0jlu0Ac/7d2rxX9Q37HW6QfX/ZguAAAAAElFTkSuQmCC'
|
||||
: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAvUlEQVQ4jbWT4Q3CIBCFP40DdANxg24gIzhKuwEjuIFxAkcwTtARGicoG+iPXlMCB8UfvoQc4e7ePV4A/ogWuMlqc0W7AsEo0QMNcPplugMmwMia5KwKWkNIuIkHq3wLXGQ/Sq4IC3wkLpOfmZyKeEpIEKsDYB8VN0Afkfpg30uNiycbdKcNqXEOxdBEWoEAoqta8uZ0iqqkxwGDUrSFAXAHZpOWd/+ubD5Kz335Cx1wZna4Bh54AddauVl8ARfCLO9Xq7xGAAAAAElFTkSuQmCC',
|
||||
check:
|
||||
scale > 2
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAqElEQVRoge3YTQ7CIBRF4bPUu/8JS6gTSaqilh95vuR+CaO2cGgYNAUzMzOzFgHlPhRaMkDAcRoltKaTeIxPtQHxGn+Q5AgJx8cQjo8hHB9DOP76Yiu/RcTmN18WLiQCjs3zBkYXVGOeLWd+xcIr5pgyEzDz7FIjISPP/FRPUM+9W4nvYVfuCSXeB3669ldEOzRFfCUSx1cicXwlEsdXIvEPKDMzM7PMbtugw3XTpNA2AAAAAElFTkSuQmCC'
|
||||
: scale > 1
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAf0lEQVRYhe2UvQ2AIBQGL3EBR3AESkv3bxxFN8DmWUgwvkI+En1X0cBd+IMg+DuDyDMCs413kfMiX4EMbD3l8oCaPIU85B4mYLEF5XJscrYFPRGvb/sZ4IlocubJGdH0wj1FSG77XYT0qdUi5O+8jOjyyZQRUnkZ0UUeBMF3OQC/0VsyGlxligAAAABJRU5ErkJggg=='
|
||||
: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAASElEQVQ4jWNgGJHAgIGBIYESze8ZGBjWU6L5PAMDgwBNNCdAFZJt83qoQmRDSHK2AFQhzBCy/IxsCNkBJsDAwLAfiknWPBIBAETPFeuA4fr6AAAAAElFTkSuQmCC',
|
||||
loader:
|
||||
scale > 2
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABXElEQVRoge2a3W3DMAyEr+0CHkGjaISOcKN4k6zQETpCR+gGzgbpQ10kcamIpKQ6avQBBPxg3pHwL2UDg/8LASxrcNdKnCwATmssrUyeWgnju/DmXs8tRP+Sh2kgAJga1rFlWj2rcMD5YqQh77QJLbzIORjyRIJQCJW5ngYo5AVlrsgkCGqbsDbAhFfxqZsSZibP0oDXQ43HQPsg82i7sBoR+VcJq2YxKcPo0IoJLRZXmYGC6ezQmQUdVqhPBVH/CNBTSMkLVlzjA8Bbocb7GoPBoADi+umZilYzbrG/JrnljOvy734iu4To/BQaDB6Rl4LciPPF9Lmjhgvi+s7w6tCIGw3WKS0P8fvWNjt0ZkGHFeq7CQXTbkZKGg2JOxrqPUZ3s6ziNdju38IjS/dLi0EQpDLX2gDQYHEX6Hx5/YcA+6H0NgAYPnCMj3x7Mxq4wTGx3Q1E578aDDR8AX0mOGD6BEN/AAAAAElFTkSuQmCC'
|
||||
: scale > 1
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABN0lEQVRYhe2WzU3EMBCFP34KyJEjJaQDXAIlJJ24BSow2wEdhHSwJSwd7JHbcmC0mOxMnDiWDIInWbHkN29exo4n8IvRAEFGU8OAA04yulyR60Jm7msbyIZloAMGwBfI4UWrWxM08LW/weC4iOMNTog4g0awKjBG827GxBwC3996NHizAifsSrTRmlsZm23CT9adktyXSq6ZUPdxgiXnZzW8CLcLuC3lvqA/gCt5NtjlPQL7TP0Wu1HtRRu4PO3T4TKTz2kG+AG9IN6CR/Su9iojBw69egfghWgL/pGCp+JFVPUqTjWjlsuqeAo1o6rt2C8QcNiV0UxoHPMieojmz0CfMKyhl1hN84xbI3gnz5Ftp7kH3iT5LsFdDUf6pzSJ6r2glIFDbuDNhqRH4I7Pvv4EvG/QqocP2Jh/xzzX/zUAAAAASUVORK5CYII='
|
||||
: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAsklEQVQ4jaWTYRHCMAyFP7gJmIQ6oChgEpBQKXMwC3MADpAAEiphDuBHC4QuDRu8u9ylyWtem7Rgw2X7GT1wsghb4beAVzhtsfYyJgs44AoEQzBkjrMId1HkKPwyZ6oMSnxYsnk1NqT7yMo34Fzhd9meGJvs7Hh3NhqCLXDI/rT0lKsR+KOJgc9RdaRRarkZvELogYsi8HqxjUhGYE+aQg1jzketwFTZXHbbEpjB8eU7PwAbLiJz46707gAAAABJRU5ErkJggg==',
|
||||
};
|
||||
|
||||
module.exports = YellowBoxImageSource;
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Platform = require('Platform');
|
||||
const React = require('React');
|
||||
const ScrollView = require('ScrollView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const View = require('View');
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
const YellowBoxInspectorFooter = require('YellowBoxInspectorFooter');
|
||||
const YellowBoxInspectorHeader = require('YellowBoxInspectorHeader');
|
||||
const YellowBoxInspectorSourceMapStatus = require('YellowBoxInspectorSourceMapStatus');
|
||||
const YellowBoxInspectorStackFrame = require('YellowBoxInspectorStackFrame');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
const openFileInEditor = require('openFileInEditor');
|
||||
|
||||
import type YellowBoxWarning from 'YellowBoxWarning';
|
||||
import type {SymbolicationRequest} from 'YellowBoxWarning';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onDismiss: () => void,
|
||||
onMinimize: () => void,
|
||||
warnings: $ReadOnlyArray<YellowBoxWarning>,
|
||||
|}>;
|
||||
|
||||
type State = {|
|
||||
selectedIndex: number,
|
||||
|};
|
||||
|
||||
class YellowBoxInspector extends React.Component<Props, State> {
|
||||
_symbolication: ?SymbolicationRequest;
|
||||
|
||||
state = {
|
||||
selectedIndex: 0,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const {warnings} = this.props;
|
||||
const {selectedIndex} = this.state;
|
||||
|
||||
const warning = warnings[selectedIndex];
|
||||
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
<YellowBoxInspectorHeader
|
||||
onSelectIndex={this._handleSelectIndex}
|
||||
selectedIndex={selectedIndex}
|
||||
warnings={warnings}
|
||||
/>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.bodyContent}
|
||||
key={selectedIndex}
|
||||
style={styles.body}>
|
||||
<View>
|
||||
<View style={styles.bodyHeading}>
|
||||
<Text style={styles.bodyHeadingText}>Warning</Text>
|
||||
</View>
|
||||
<Text style={styles.bodyText}>
|
||||
{YellowBoxCategory.render(
|
||||
warning.message,
|
||||
styles.substitutionText,
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.bodySection}>
|
||||
<View style={styles.bodyHeading}>
|
||||
<Text style={styles.bodyHeadingText}>Stack</Text>
|
||||
<YellowBoxInspectorSourceMapStatus
|
||||
status={warning.symbolicated.status}
|
||||
/>
|
||||
</View>
|
||||
{warning.getAvailableStack().map((frame, index) => (
|
||||
<YellowBoxInspectorStackFrame
|
||||
key={index}
|
||||
frame={frame}
|
||||
onPress={
|
||||
warning.symbolicated.status === 'COMPLETE'
|
||||
? () => {
|
||||
openFileInEditor(frame.file, frame.lineNumber);
|
||||
}
|
||||
: null
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</ScrollView>
|
||||
<YellowBoxInspectorFooter
|
||||
onDismiss={this.props.onDismiss}
|
||||
onMinimize={this.props.onMinimize}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._handleSymbolication();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State): void {
|
||||
if (
|
||||
prevProps.warnings !== this.props.warnings ||
|
||||
prevState.selectedIndex !== this.state.selectedIndex
|
||||
) {
|
||||
this._cancelSymbolication();
|
||||
this._handleSymbolication();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this._cancelSymbolication();
|
||||
}
|
||||
|
||||
_handleSymbolication(): void {
|
||||
const warning = this.props.warnings[this.state.selectedIndex];
|
||||
if (warning.symbolicated.status !== 'COMPLETE') {
|
||||
this._symbolication = warning.symbolicate(() => {
|
||||
this.forceUpdate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_cancelSymbolication(): void {
|
||||
if (this._symbolication != null) {
|
||||
this._symbolication.abort();
|
||||
this._symbolication = null;
|
||||
}
|
||||
}
|
||||
|
||||
_handleSelectIndex = (selectedIndex: number): void => {
|
||||
this.setState({selectedIndex});
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
elevation: Platform.OS === 'android' ? Number.MAX_SAFE_INTEGER : undefined,
|
||||
height: '100%',
|
||||
},
|
||||
body: {
|
||||
backgroundColor: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
borderBottomColor: YellowBoxStyle.getDividerColor(0.95),
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: YellowBoxStyle.getDividerColor(0.95),
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
flex: 1,
|
||||
},
|
||||
bodyContent: {
|
||||
paddingVertical: 12,
|
||||
},
|
||||
bodyHeading: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
marginBottom: 6,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
bodyHeadingText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
flex: 1,
|
||||
fontSize: 20,
|
||||
fontWeight: '600',
|
||||
includeFontPadding: false,
|
||||
lineHeight: 28,
|
||||
},
|
||||
bodyText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
substitutionText: {
|
||||
color: YellowBoxStyle.getTextColor(0.6),
|
||||
},
|
||||
bodySection: {
|
||||
marginTop: 20,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspector;
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const SafeAreaView = require('SafeAreaView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const View = require('View');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onDismiss: () => void,
|
||||
onMinimize: () => void,
|
||||
|}>;
|
||||
|
||||
const YellowBoxInspectorFooter = (props: Props): React.Node => (
|
||||
<View style={styles.root}>
|
||||
<YellowBoxPressable
|
||||
backgroundColor={{
|
||||
default: 'transparent',
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
}}
|
||||
onPress={props.onMinimize}
|
||||
style={styles.button}>
|
||||
<View style={styles.content}>
|
||||
<Text style={styles.label}>Minimize</Text>
|
||||
</View>
|
||||
<SafeAreaView />
|
||||
</YellowBoxPressable>
|
||||
<YellowBoxPressable
|
||||
backgroundColor={{
|
||||
default: 'transparent',
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
}}
|
||||
onPress={props.onDismiss}
|
||||
style={styles.button}>
|
||||
<View style={styles.content}>
|
||||
<Text style={styles.label}>Dismiss</Text>
|
||||
</View>
|
||||
<SafeAreaView />
|
||||
</YellowBoxPressable>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
flexDirection: 'row',
|
||||
},
|
||||
button: {
|
||||
flex: 1,
|
||||
},
|
||||
content: {
|
||||
alignItems: 'center',
|
||||
height: 48,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
label: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspectorFooter;
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Platform = require('Platform');
|
||||
const React = require('React');
|
||||
const SafeAreaView = require('SafeAreaView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const UTFSequence = require('UTFSequence');
|
||||
const View = require('View');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type YellowBoxWarning from 'YellowBoxWarning';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onSelectIndex: (selectedIndex: number) => void,
|
||||
selectedIndex: number,
|
||||
warnings: $ReadOnlyArray<YellowBoxWarning>,
|
||||
|}>;
|
||||
|
||||
const YellowBoxInspectorHeader = (props: Props): React.Node => {
|
||||
const prevIndex = props.selectedIndex - 1;
|
||||
const nextIndex = props.selectedIndex + 1;
|
||||
|
||||
const titleText =
|
||||
props.warnings.length === 1
|
||||
? 'Single Occurrence'
|
||||
: `Occurrence ${props.selectedIndex + 1} of ${props.warnings.length}`;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.root}>
|
||||
<View style={styles.header}>
|
||||
<YellowBoxInspectorHeaderButton
|
||||
disabled={props.warnings[prevIndex] == null}
|
||||
label={UTFSequence.TRIANGLE_LEFT}
|
||||
onPress={() => props.onSelectIndex(prevIndex)}
|
||||
/>
|
||||
<View style={styles.headerTitle}>
|
||||
<Text style={styles.headerTitleText}>{titleText}</Text>
|
||||
</View>
|
||||
<YellowBoxInspectorHeaderButton
|
||||
disabled={props.warnings[nextIndex] == null}
|
||||
label={UTFSequence.TRIANGLE_RIGHT}
|
||||
onPress={() => props.onSelectIndex(nextIndex)}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const YellowBoxInspectorHeaderButton = (
|
||||
props: $ReadOnly<{|
|
||||
disabled: boolean,
|
||||
label: React.Node,
|
||||
onPress?: ?() => void,
|
||||
|}>,
|
||||
): React.Node => (
|
||||
<YellowBoxPressable
|
||||
backgroundColor={{
|
||||
default: 'transparent',
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
}}
|
||||
onPress={props.disabled ? null : props.onPress}
|
||||
style={styles.headerButton}>
|
||||
{props.disabled ? null : (
|
||||
<Text style={styles.headerButtonText}>{props.label}</Text>
|
||||
)}
|
||||
</YellowBoxPressable>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
height: Platform.select({
|
||||
android: 48,
|
||||
ios: 44,
|
||||
}),
|
||||
},
|
||||
headerButton: {
|
||||
alignItems: 'center',
|
||||
aspectRatio: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerButtonText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 16,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 20,
|
||||
},
|
||||
headerTitle: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerTitleText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
includeFontPadding: false,
|
||||
lineHeight: 20,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspectorHeader;
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Animated = require('Animated');
|
||||
const Easing = require('Easing');
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const View = require('View');
|
||||
const YellowBoxImageSource = require('YellowBoxImageSource');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {CompositeAnimation} from 'AnimatedImplementation';
|
||||
import type AnimatedInterpolation from 'AnimatedInterpolation';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
status: 'COMPLETE' | 'FAILED' | 'NONE' | 'PENDING',
|
||||
|}>;
|
||||
|
||||
type State = {|
|
||||
animation: ?CompositeAnimation,
|
||||
rotate: ?AnimatedInterpolation,
|
||||
|};
|
||||
|
||||
class YellowBoxInspectorSourceMapStatus extends React.Component<Props, State> {
|
||||
state = {
|
||||
animation: null,
|
||||
rotate: null,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
let image;
|
||||
switch (this.props.status) {
|
||||
case 'COMPLETE':
|
||||
image = YellowBoxImageSource.check;
|
||||
break;
|
||||
case 'FAILED':
|
||||
image = YellowBoxImageSource.alertTriangle;
|
||||
break;
|
||||
case 'PENDING':
|
||||
image = YellowBoxImageSource.loader;
|
||||
break;
|
||||
}
|
||||
|
||||
return image == null ? null : (
|
||||
<View
|
||||
style={StyleSheet.compose(
|
||||
styles.root,
|
||||
this.props.status === 'PENDING' ? styles.pending : null,
|
||||
)}>
|
||||
<Animated.Image
|
||||
source={{height: 16, uri: image, width: 16}}
|
||||
style={StyleSheet.compose(
|
||||
styles.image,
|
||||
this.state.rotate == null
|
||||
? null
|
||||
: {transform: [{rotate: this.state.rotate}]},
|
||||
)}
|
||||
/>
|
||||
<Text style={styles.text}>Source Map</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._updateAnimation();
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
this._updateAnimation();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this.state.animation != null) {
|
||||
this.state.animation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
_updateAnimation(): void {
|
||||
if (this.props.status === 'PENDING') {
|
||||
if (this.state.animation == null) {
|
||||
const animated = new Animated.Value(0);
|
||||
const animation = Animated.loop(
|
||||
Animated.timing(animated, {
|
||||
duration: 2000,
|
||||
easing: Easing.linear,
|
||||
toValue: 1,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
);
|
||||
this.setState(
|
||||
{
|
||||
animation,
|
||||
rotate: animated.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['0deg', '360deg'],
|
||||
}),
|
||||
},
|
||||
() => {
|
||||
animation.start();
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this.state.animation != null) {
|
||||
this.state.animation.stop();
|
||||
this.setState({
|
||||
animation: null,
|
||||
rotate: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: YellowBoxStyle.getTextColor(0.8),
|
||||
borderRadius: 12,
|
||||
flexDirection: 'row',
|
||||
height: 24,
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
pending: {
|
||||
backgroundColor: YellowBoxStyle.getTextColor(0.6),
|
||||
},
|
||||
image: {
|
||||
marginEnd: 4,
|
||||
tintColor: YellowBoxStyle.getBackgroundColor(1),
|
||||
},
|
||||
text: {
|
||||
color: YellowBoxStyle.getBackgroundColor(1),
|
||||
fontSize: 12,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 16,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspectorSourceMapStatus;
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {PressEvent} from 'CoreEventTypes';
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
frame: StackFrame,
|
||||
onPress?: ?(event: PressEvent) => void,
|
||||
|}>;
|
||||
|
||||
const YellowBoxInspectorStackFrame = (props: Props): React.Node => {
|
||||
const {frame, onPress} = props;
|
||||
|
||||
return (
|
||||
<YellowBoxPressable
|
||||
backgroundColor={{
|
||||
default: YellowBoxStyle.getBackgroundColor(0),
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
}}
|
||||
onPress={onPress}
|
||||
style={styles.frame}>
|
||||
<Text style={styles.frameName}>{frame.methodName}</Text>
|
||||
<Text
|
||||
ellipsizeMode="middle"
|
||||
numberOfLines={1}
|
||||
style={styles.frameLocation}>
|
||||
{`${getFrameLocation(frame.file)}:${frame.lineNumber}${
|
||||
frame.column == null ? '' : ':' + frame.column
|
||||
}`}
|
||||
</Text>
|
||||
</YellowBoxPressable>
|
||||
);
|
||||
};
|
||||
|
||||
const getFrameLocation = (uri: string): string => {
|
||||
const queryIndex = uri.indexOf('?');
|
||||
const query = queryIndex < 0 ? '' : uri.substr(queryIndex);
|
||||
|
||||
const path = queryIndex < 0 ? uri : uri.substr(0, queryIndex);
|
||||
const file = path.substr(path.lastIndexOf('/') + 1);
|
||||
|
||||
return file + query;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
frame: {
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 4,
|
||||
},
|
||||
frameName: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
},
|
||||
frameLocation: {
|
||||
color: YellowBoxStyle.getTextColor(0.7),
|
||||
fontSize: 12,
|
||||
fontWeight: '300',
|
||||
includeFontPadding: false,
|
||||
lineHeight: 16,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspectorStackFrame;
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Dimensions = require('Dimensions');
|
||||
const React = require('React');
|
||||
const FlatList = require('FlatList');
|
||||
const SafeAreaView = require('SafeAreaView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const View = require('View');
|
||||
const YellowBoxButton = require('YellowBoxButton');
|
||||
const YellowBoxInspector = require('YellowBoxInspector');
|
||||
const YellowBoxListRow = require('YellowBoxListRow');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {Category} from 'YellowBoxCategory';
|
||||
import type {Registry} from 'YellowBoxRegistry';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onDismiss: (category: Category) => void,
|
||||
onDismissAll: () => void,
|
||||
registry: Registry,
|
||||
|}>;
|
||||
|
||||
type State = {|
|
||||
selectedCategory: ?Category,
|
||||
|};
|
||||
|
||||
const VIEWPORT_RATIO = 0.5;
|
||||
const MAX_ITEMS = Math.floor(
|
||||
(Dimensions.get('window').height * VIEWPORT_RATIO) /
|
||||
(YellowBoxListRow.GUTTER + YellowBoxListRow.HEIGHT),
|
||||
);
|
||||
|
||||
class YellowBoxList extends React.Component<Props, State> {
|
||||
state = {
|
||||
selectedCategory: null,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const selectedWarnings =
|
||||
this.state.selectedCategory == null
|
||||
? null
|
||||
: this.props.registry.get(this.state.selectedCategory);
|
||||
|
||||
if (selectedWarnings != null) {
|
||||
return (
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
<YellowBoxInspector
|
||||
onDismiss={this._handleInspectorDismiss}
|
||||
onMinimize={this._handleInspectorMinimize}
|
||||
warnings={selectedWarnings}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const items = [];
|
||||
for (const [category, warnings] of this.props.registry) {
|
||||
items.unshift({category, warnings});
|
||||
}
|
||||
|
||||
const listStyle = {
|
||||
height:
|
||||
// Additional `0.5` so the (N + 1)th row can peek into view.
|
||||
Math.min(items.length, MAX_ITEMS + 0.5) *
|
||||
(YellowBoxListRow.GUTTER + YellowBoxListRow.HEIGHT),
|
||||
};
|
||||
|
||||
return items.length === 0 ? null : (
|
||||
<View style={styles.list}>
|
||||
<View pointerEvents="box-none" style={styles.dismissAll}>
|
||||
<YellowBoxButton
|
||||
hitSlop={{bottom: 4, left: 4, right: 4, top: 4}}
|
||||
label="Dismiss All"
|
||||
onPress={this.props.onDismissAll}
|
||||
/>
|
||||
</View>
|
||||
<FlatList
|
||||
data={items}
|
||||
keyExtractor={item => item.category}
|
||||
renderItem={({item}) => (
|
||||
<YellowBoxListRow {...item} onPress={this._handleRowPress} />
|
||||
)}
|
||||
scrollEnabled={items.length > MAX_ITEMS}
|
||||
scrollsToTop={false}
|
||||
style={listStyle}
|
||||
/>
|
||||
<SafeAreaView style={styles.safeArea} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_handleInspectorDismiss = () => {
|
||||
const category = this.state.selectedCategory;
|
||||
if (category == null) {
|
||||
return;
|
||||
}
|
||||
this.setState({selectedCategory: null}, () => {
|
||||
this.props.onDismiss(category);
|
||||
});
|
||||
};
|
||||
|
||||
_handleInspectorMinimize = () => {
|
||||
this.setState({selectedCategory: null});
|
||||
};
|
||||
|
||||
_handleRowPress = (category: Category) => {
|
||||
this.setState({selectedCategory: category});
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
list: {
|
||||
bottom: 0,
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
},
|
||||
dismissAll: {
|
||||
bottom: '100%',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
paddingBottom: 4,
|
||||
paddingEnd: 4,
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
},
|
||||
safeArea: {
|
||||
backgroundColor: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
marginTop: StyleSheet.hairlineWidth,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxList;
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const View = require('View');
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
const YellowBoxWarning = require('YellowBoxWarning');
|
||||
|
||||
import type {Category} from 'YellowBoxCategory';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
category: Category,
|
||||
warnings: $ReadOnlyArray<YellowBoxWarning>,
|
||||
onPress: (category: Category) => void,
|
||||
|}>;
|
||||
|
||||
class YellowBoxListRow extends React.Component<Props> {
|
||||
static GUTTER = StyleSheet.hairlineWidth;
|
||||
static HEIGHT = 48;
|
||||
|
||||
shouldComponentUpdate(nextProps: Props): boolean {
|
||||
const prevProps = this.props;
|
||||
return (
|
||||
prevProps.category !== nextProps.category ||
|
||||
prevProps.onPress !== nextProps.onPress ||
|
||||
prevProps.warnings.length !== nextProps.warnings.length ||
|
||||
prevProps.warnings.some(
|
||||
(prevWarning, index) => prevWarning !== nextProps[index],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const {warnings} = this.props;
|
||||
|
||||
return (
|
||||
<YellowBoxPressable onPress={this._handlePress} style={styles.root}>
|
||||
<View style={styles.content}>
|
||||
{warnings.length < 2 ? null : (
|
||||
<Text style={styles.metaText}>{'(' + warnings.length + ') '}</Text>
|
||||
)}
|
||||
<Text numberOfLines={2} style={styles.bodyText}>
|
||||
{YellowBoxCategory.render(
|
||||
warnings[warnings.length - 1].message,
|
||||
styles.substitutionText,
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
</YellowBoxPressable>
|
||||
);
|
||||
}
|
||||
|
||||
_handlePress = () => {
|
||||
this.props.onPress(this.props.category);
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
height: YellowBoxListRow.HEIGHT,
|
||||
justifyContent: 'center',
|
||||
marginTop: YellowBoxListRow.GUTTER,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
content: {
|
||||
alignItems: 'flex-start',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
bodyText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
flex: 1,
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
},
|
||||
metaText: {
|
||||
color: YellowBoxStyle.getTextColor(0.5),
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
},
|
||||
substitutionText: {
|
||||
color: YellowBoxStyle.getTextColor(0.6),
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxListRow;
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const TouchableWithoutFeedback = require('TouchableWithoutFeedback');
|
||||
const View = require('View');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {PressEvent} from 'CoreEventTypes';
|
||||
import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
|
||||
import type {ViewStyleProp} from 'StyleSheet';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
backgroundColor: $ReadOnly<{|
|
||||
default: string,
|
||||
pressed: string,
|
||||
|}>,
|
||||
children?: React.Node,
|
||||
hitSlop?: ?EdgeInsetsProp,
|
||||
onPress?: ?(event: PressEvent) => void,
|
||||
style?: ViewStyleProp,
|
||||
|}>;
|
||||
|
||||
type State = {|
|
||||
pressed: boolean,
|
||||
|};
|
||||
|
||||
class YellowBoxPressable extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
backgroundColor: {
|
||||
default: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
},
|
||||
};
|
||||
|
||||
state = {
|
||||
pressed: false,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const content = (
|
||||
<View
|
||||
style={StyleSheet.compose(
|
||||
{
|
||||
backgroundColor: this.state.pressed
|
||||
? this.props.backgroundColor.pressed
|
||||
: this.props.backgroundColor.default,
|
||||
},
|
||||
this.props.style,
|
||||
)}>
|
||||
{this.props.children}
|
||||
</View>
|
||||
);
|
||||
return this.props.onPress == null ? (
|
||||
content
|
||||
) : (
|
||||
<TouchableWithoutFeedback
|
||||
hitSlop={this.props.hitSlop}
|
||||
onPress={this.props.onPress}
|
||||
onPressIn={this._handlePressIn}
|
||||
onPressOut={this._handlePressOut}>
|
||||
{content}
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
_handlePressIn = () => {
|
||||
this.setState({pressed: true});
|
||||
};
|
||||
|
||||
_handlePressOut = () => {
|
||||
this.setState({pressed: false});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = YellowBoxPressable;
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxStyle = {
|
||||
getBackgroundColor(opacity: number): string {
|
||||
return `rgba(250, 186, 48, ${opacity})`;
|
||||
},
|
||||
|
||||
getDividerColor(opacity: number): string {
|
||||
return `rgba(255, 255, 255, ${opacity})`;
|
||||
},
|
||||
|
||||
getHighlightColor(opacity: number): string {
|
||||
return `rgba(252, 176, 29, ${opacity})`;
|
||||
},
|
||||
|
||||
getTextColor(opacity: number): string {
|
||||
return `rgba(255, 255, 255, ${opacity})`;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = YellowBoxStyle;
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
|
||||
import type {Category} from 'YellowBoxCategory';
|
||||
import type {Registry, Subscription} from 'YellowBoxRegistry';
|
||||
|
||||
type Props = $ReadOnly<{||}>;
|
||||
type State = {|
|
||||
registry: ?Registry,
|
||||
|};
|
||||
|
||||
let YellowBox;
|
||||
|
||||
/**
|
||||
* YellowBox displays warnings at the bottom of the screen.
|
||||
*
|
||||
* Warnings help guard against subtle yet significant issues that can impact the
|
||||
* quality of the app. This "in your face" style of warning allows developers to
|
||||
* notice and correct these issues as quickly as possible.
|
||||
*
|
||||
* YellowBox is only enabled in `__DEV__`. Set the following flag to disable it:
|
||||
*
|
||||
* console.disableYellowBox = true;
|
||||
*
|
||||
* Ignore specific warnings by calling:
|
||||
*
|
||||
* YellowBox.ignoreWarnings(['Warning: ...']);
|
||||
*
|
||||
* Strings supplied to `YellowBox.ignoreWarnings` only need to be a substring of
|
||||
* the ignored warning messages.
|
||||
*/
|
||||
if (__DEV__) {
|
||||
const Platform = require('Platform');
|
||||
const RCTLog = require('RCTLog');
|
||||
const YellowBoxList = require('YellowBoxList');
|
||||
const YellowBoxRegistry = require('YellowBoxRegistry');
|
||||
|
||||
const {error, warn} = console;
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
YellowBox = class YellowBox extends React.Component<Props, State> {
|
||||
static ignoreWarnings(patterns: $ReadOnlyArray<string>): void {
|
||||
YellowBoxRegistry.addIgnorePatterns(patterns);
|
||||
}
|
||||
|
||||
static install(): void {
|
||||
(console: any).error = function(...args) {
|
||||
error.call(console, ...args);
|
||||
// Show YellowBox for the `warning` module.
|
||||
if (typeof args[0] === 'string' && args[0].startsWith('Warning: ')) {
|
||||
registerWarning(...args);
|
||||
}
|
||||
};
|
||||
|
||||
(console: any).warn = function(...args) {
|
||||
warn.call(console, ...args);
|
||||
registerWarning(...args);
|
||||
};
|
||||
|
||||
if ((console: any).disableYellowBox === true) {
|
||||
YellowBoxRegistry.setDisabled(true);
|
||||
}
|
||||
(Object.defineProperty: any)(console, 'disableYellowBox', {
|
||||
configurable: true,
|
||||
get: () => YellowBoxRegistry.isDisabled(),
|
||||
set: value => YellowBoxRegistry.setDisabled(value),
|
||||
});
|
||||
|
||||
if (Platform.isTesting) {
|
||||
(console: any).disableYellowBox = true;
|
||||
}
|
||||
|
||||
RCTLog.setWarningHandler((...args) => {
|
||||
registerWarning(...args);
|
||||
});
|
||||
}
|
||||
|
||||
static uninstall(): void {
|
||||
(console: any).error = error;
|
||||
(console: any).warn = error;
|
||||
delete (console: any).disableYellowBox;
|
||||
}
|
||||
|
||||
_subscription: ?Subscription;
|
||||
|
||||
state = {
|
||||
registry: null,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
// TODO: Ignore warnings that fire when rendering `YellowBox` itself.
|
||||
return this.state.registry == null ? null : (
|
||||
<YellowBoxList
|
||||
onDismiss={this._handleDismiss}
|
||||
onDismissAll={this._handleDismissAll}
|
||||
registry={this.state.registry}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._subscription = YellowBoxRegistry.observe(registry => {
|
||||
this.setState({registry});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this._subscription != null) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
_handleDismiss = (category: Category): void => {
|
||||
YellowBoxRegistry.delete(category);
|
||||
};
|
||||
|
||||
_handleDismissAll(): void {
|
||||
YellowBoxRegistry.clear();
|
||||
}
|
||||
};
|
||||
|
||||
const registerWarning = (...args): void => {
|
||||
YellowBoxRegistry.add({args, framesToPop: 2});
|
||||
};
|
||||
} else {
|
||||
YellowBox = class extends React.Component<Props> {
|
||||
static ignoreWarnings(patterns: $ReadOnlyArray<string>): void {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
static install(): void {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
static uninstall(): void {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = YellowBox;
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBox = require('YellowBox');
|
||||
const YellowBoxRegistry = require('YellowBoxRegistry');
|
||||
|
||||
describe('YellowBox', () => {
|
||||
const {error, warn} = console;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
(console: any).error = jest.fn();
|
||||
(console: any).warn = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
YellowBox.uninstall();
|
||||
(console: any).error = error;
|
||||
(console: any).warn = warn;
|
||||
});
|
||||
|
||||
it('can set `disableYellowBox` after installing', () => {
|
||||
expect((console: any).disableYellowBox).toBe(undefined);
|
||||
|
||||
YellowBox.install();
|
||||
|
||||
expect((console: any).disableYellowBox).toBe(false);
|
||||
expect(YellowBoxRegistry.isDisabled()).toBe(false);
|
||||
|
||||
(console: any).disableYellowBox = true;
|
||||
|
||||
expect((console: any).disableYellowBox).toBe(true);
|
||||
expect(YellowBoxRegistry.isDisabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('can set `disableYellowBox` before installing', () => {
|
||||
expect((console: any).disableYellowBox).toBe(undefined);
|
||||
|
||||
(console: any).disableYellowBox = true;
|
||||
YellowBox.install();
|
||||
|
||||
expect((console: any).disableYellowBox).toBe(true);
|
||||
expect(YellowBoxRegistry.isDisabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('registers warnings', () => {
|
||||
jest.mock('YellowBoxRegistry');
|
||||
|
||||
YellowBox.install();
|
||||
|
||||
expect(YellowBoxRegistry.add).not.toBeCalled();
|
||||
(console: any).warn('...');
|
||||
expect(YellowBoxRegistry.add).toBeCalled();
|
||||
});
|
||||
|
||||
it('registers errors beginning with "Warning: "', () => {
|
||||
jest.mock('YellowBoxRegistry');
|
||||
|
||||
YellowBox.install();
|
||||
|
||||
(console: any).error('...');
|
||||
expect(YellowBoxRegistry.add).not.toBeCalled();
|
||||
|
||||
(console: any).error('Warning: ...');
|
||||
expect(YellowBoxRegistry.add).toBeCalled();
|
||||
});
|
||||
});
|
||||
@@ -84,8 +84,8 @@ def enableSeparateBuildPerCPUArchitecture = false
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "26.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.facebook.react.uiapp"
|
||||
@@ -137,7 +137,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
|
||||
// Build React Native from source
|
||||
compile project(':ReactAndroid')
|
||||
|
||||
@@ -98,6 +98,9 @@ void RCTVerifyAllModulesExported(NSArray *extraModules)
|
||||
|
||||
for (unsigned int i = 0; i < classCount; i++) {
|
||||
Class cls = classes[i];
|
||||
if (strncmp(class_getName(cls), "RCTCxxModule", strlen("RCTCxxModule")) == 0) {
|
||||
continue;
|
||||
}
|
||||
Class superclass = cls;
|
||||
while (superclass) {
|
||||
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule))) {
|
||||
|
||||
@@ -21,7 +21,7 @@ static void __makeVersion()
|
||||
{
|
||||
__rnVersion = @{
|
||||
RCTVersionMajor: @(0),
|
||||
RCTVersionMinor: @(0),
|
||||
RCTVersionMinor: @(56),
|
||||
RCTVersionPatch: @(0),
|
||||
RCTVersionPrerelease: [NSNull null],
|
||||
};
|
||||
|
||||
@@ -871,6 +871,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
if (!_valid) {
|
||||
RCTLogError(@"Attempting to reload bridge before it's valid: %@. Try restarting the development server if connected.", self);
|
||||
}
|
||||
[_parentBridge reload];
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,11 @@ using namespace facebook::react;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)schedulerDidComputeMutationInstructions:(facebook::react::TreeMutationInstructionList)instructions rootTag:(ReactTag)rootTag
|
||||
{
|
||||
[_mountingManager mutateComponentViewTreeWithMutationInstructions:instructions
|
||||
|
||||
@@ -81,6 +81,8 @@
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
[self _stop];
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ def thirdPartyNdkDir = new File("$buildDir/third-party-ndk")
|
||||
// - boost_1_63_0
|
||||
// - double-conversion-1.1.1
|
||||
// - folly-deprecate-dynamic-initializer
|
||||
// - glog-0.3.3
|
||||
// - glog-0.3.5
|
||||
// - jsc-headers
|
||||
def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES")
|
||||
|
||||
@@ -89,10 +89,10 @@ task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy
|
||||
}
|
||||
|
||||
task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
|
||||
src 'https://github.com/google/glog/archive/v0.3.3.tar.gz'
|
||||
src 'https://github.com/google/glog/archive/v0.3.5.tar.gz'
|
||||
onlyIfNewer true
|
||||
overwrite false
|
||||
dest new File(downloadsDir, 'glog-0.3.3.tar.gz')
|
||||
dest new File(downloadsDir, 'glog-0.3.5.tar.gz')
|
||||
}
|
||||
|
||||
// Prepare glog sources to be compiled, this task will perform steps that normally should've been
|
||||
@@ -100,7 +100,7 @@ task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
|
||||
task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) {
|
||||
from dependenciesPath ?: tarTree(downloadGlog.dest)
|
||||
from 'src/main/jni/third-party/glog/'
|
||||
include 'glog-0.3.3/src/**/*', 'Android.mk', 'config.h'
|
||||
include 'glog-0.3.5/src/**/*', 'Android.mk', 'config.h'
|
||||
includeEmptyDirs = false
|
||||
filesMatching('**/*.h.in') {
|
||||
filter(ReplaceTokens, tokens: [
|
||||
@@ -245,8 +245,8 @@ task packageReactNdkLibsForBuck(dependsOn: packageReactNdkLibs, type: Copy) {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "26.0.3"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
@@ -295,7 +295,7 @@ android {
|
||||
dependencies {
|
||||
compile fileTree(dir: 'src/main/third-party/java/infer-annotations/', include: ['*.jar'])
|
||||
compile 'javax.inject:javax.inject:1'
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.facebook.fbui.textlayoutbuilder:textlayoutbuilder:1.0.0'
|
||||
compile 'com.facebook.fresco:fresco:1.9.0'
|
||||
compile 'com.facebook.fresco:imagepipeline-okhttp3:1.9.0'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=1000.0.0-master
|
||||
VERSION_NAME=0.56.0
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -6,8 +6,8 @@ android_prebuilt_aar(
|
||||
|
||||
remote_file(
|
||||
name = "appcompat-binary-aar",
|
||||
sha1 = "7d659f671541394a8bc2b9f909950aa2a5ec87ff",
|
||||
url = "mvn:com.android.support:appcompat-v7:aar:23.0.1",
|
||||
sha1 = "da5771a9ac6e8f3a461360eed3c6f921dc7580fd",
|
||||
url = "mvn:com.android.support:appcompat-v7:aar:26.1.0",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
|
||||
@@ -19,9 +19,11 @@ import com.facebook.react.modules.core.PermissionAwareActivity;
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
|
||||
/**
|
||||
* Base Activity for React Native applications. Like {@link ReactActivity} but extends
|
||||
* {@link FragmentActivity} instead of {@link android.app.Activity}.
|
||||
* @deprecated
|
||||
* ReactFragmentActivity will be removed in 0.57 release.
|
||||
* Use {@link ReactActivity} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class ReactFragmentActivity extends FragmentActivity implements
|
||||
DefaultHardwareBackBtnHandler, PermissionAwareActivity {
|
||||
|
||||
|
||||
+4
-4
@@ -86,9 +86,9 @@ public class PermissionsModule extends ReactContextBaseJavaModule implements Per
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the given permission. successCallback is called with true if the permission had been
|
||||
* granted, false otherwise. For devices before Android M, this instead checks if the user has
|
||||
* the permission given or not.
|
||||
* Request the given permission. successCallback is called with GRANTED if the permission had been
|
||||
* granted, DENIED or NEVER_ASK_AGAIN otherwise. For devices before Android M, this checks if the user has
|
||||
* the permission given or not and resolves with GRANTED or DENIED.
|
||||
* See {@link Activity#checkSelfPermission}.
|
||||
*/
|
||||
@ReactMethod
|
||||
@@ -96,7 +96,7 @@ public class PermissionsModule extends ReactContextBaseJavaModule implements Per
|
||||
Context context = getReactApplicationContext().getBaseContext();
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
promise.resolve(context.checkPermission(permission, Process.myPid(), Process.myUid()) ==
|
||||
PackageManager.PERMISSION_GRANTED);
|
||||
PackageManager.PERMISSION_GRANTED ? GRANTED : DENIED);
|
||||
return;
|
||||
}
|
||||
if (context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import java.util.Map;
|
||||
public class ReactNativeVersion {
|
||||
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
|
||||
"major", 0,
|
||||
"minor", 0,
|
||||
"minor", 56,
|
||||
"patch", 0,
|
||||
"prerelease", null);
|
||||
}
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ public abstract class ReactBaseTextShadowNode extends LayoutShadowNode {
|
||||
start, end, new BackgroundColorSpan(textShadowNode.mBackgroundColor)));
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (textShadowNode.mLetterSpacing != Float.NaN) {
|
||||
if (!Float.isNaN(textShadowNode.mLetterSpacing)) {
|
||||
ops.add(new SetSpanOperation(
|
||||
start,
|
||||
end,
|
||||
|
||||
+6
-2
@@ -70,8 +70,9 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
private static final int BLUR_TEXT_INPUT = 2;
|
||||
|
||||
private static final int INPUT_TYPE_KEYBOARD_NUMBER_PAD = InputType.TYPE_CLASS_NUMBER;
|
||||
private static final int INPUT_TYPE_KEYBOARD_NUMBERED = INPUT_TYPE_KEYBOARD_NUMBER_PAD |
|
||||
InputType.TYPE_NUMBER_FLAG_DECIMAL |
|
||||
private static final int INPUT_TYPE_KEYBOARD_DECIMAL_PAD = INPUT_TYPE_KEYBOARD_NUMBER_PAD |
|
||||
InputType.TYPE_NUMBER_FLAG_DECIMAL;
|
||||
private static final int INPUT_TYPE_KEYBOARD_NUMBERED = INPUT_TYPE_KEYBOARD_DECIMAL_PAD |
|
||||
InputType.TYPE_NUMBER_FLAG_SIGNED;
|
||||
private static final int PASSWORD_VISIBILITY_FLAG = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD &
|
||||
~InputType.TYPE_TEXT_VARIATION_PASSWORD;
|
||||
@@ -82,6 +83,7 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
|
||||
private static final String KEYBOARD_TYPE_EMAIL_ADDRESS = "email-address";
|
||||
private static final String KEYBOARD_TYPE_NUMERIC = "numeric";
|
||||
private static final String KEYBOARD_TYPE_DECIMAL_PAD = "decimal-pad";
|
||||
private static final String KEYBOARD_TYPE_NUMBER_PAD = "number-pad";
|
||||
private static final String KEYBOARD_TYPE_PHONE_PAD = "phone-pad";
|
||||
private static final String KEYBOARD_TYPE_VISIBLE_PASSWORD = "visible-password";
|
||||
@@ -567,6 +569,8 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
flagsToSet = INPUT_TYPE_KEYBOARD_NUMBERED;
|
||||
} else if (KEYBOARD_TYPE_NUMBER_PAD.equalsIgnoreCase(keyboardType)) {
|
||||
flagsToSet = INPUT_TYPE_KEYBOARD_NUMBER_PAD;
|
||||
} else if (KEYBOARD_TYPE_DECIMAL_PAD.equalsIgnoreCase(keyboardType)) {
|
||||
flagsToSet = INPUT_TYPE_KEYBOARD_DECIMAL_PAD;
|
||||
} else if (KEYBOARD_TYPE_EMAIL_ADDRESS.equalsIgnoreCase(keyboardType)) {
|
||||
flagsToSet = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | InputType.TYPE_CLASS_TEXT;
|
||||
} else if (KEYBOARD_TYPE_PHONE_PAD.equalsIgnoreCase(keyboardType)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
APP_BUILD_SCRIPT := Android.mk
|
||||
|
||||
APP_ABI := armeabi-v7a x86
|
||||
APP_PLATFORM := android-9
|
||||
APP_PLATFORM := android-16
|
||||
|
||||
APP_MK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
|
||||
|
||||
+9
-9
@@ -3,17 +3,17 @@ LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
glog-0.3.3/src/demangle.cc \
|
||||
glog-0.3.3/src/logging.cc \
|
||||
glog-0.3.3/src/raw_logging.cc \
|
||||
glog-0.3.3/src/signalhandler.cc \
|
||||
glog-0.3.3/src/symbolize.cc \
|
||||
glog-0.3.3/src/utilities.cc \
|
||||
glog-0.3.3/src/vlog_is_on.cc
|
||||
glog-0.3.5/src/demangle.cc \
|
||||
glog-0.3.5/src/logging.cc \
|
||||
glog-0.3.5/src/raw_logging.cc \
|
||||
glog-0.3.5/src/signalhandler.cc \
|
||||
glog-0.3.5/src/symbolize.cc \
|
||||
glog-0.3.5/src/utilities.cc \
|
||||
glog-0.3.5/src/vlog_is_on.cc
|
||||
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/.. $(LOCAL_PATH)/glog-0.3.3/src/
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/.. $(LOCAL_PATH)/glog-0.3.5/src/
|
||||
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/.. $(LOCAL_PATH)/glog-0.3.3/src/
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/.. $(LOCAL_PATH)/glog-0.3.5/src/
|
||||
|
||||
LOCAL_CFLAGS += \
|
||||
-Wall \
|
||||
|
||||
+6
-3
@@ -49,6 +49,9 @@
|
||||
/* define if the compiler implements namespaces */
|
||||
#define HAVE_NAMESPACES 1
|
||||
|
||||
/* NDK android-16 provides ssize_t pread(int, void*, size_t, off_t) */
|
||||
#define HAVE_PREAD 1
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#define HAVE_PTHREAD 1
|
||||
|
||||
@@ -132,7 +135,7 @@
|
||||
#define PACKAGE_NAME "glog"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "glog 0.3.3"
|
||||
#define PACKAGE_STRING "glog 0.3.5"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "glog"
|
||||
@@ -141,7 +144,7 @@
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "0.3.3"
|
||||
#define PACKAGE_VERSION "0.3.5"
|
||||
|
||||
/* How to access the PC from a struct ucontext */
|
||||
/* #undef PC_FROM_UCONTEXT */
|
||||
@@ -163,7 +166,7 @@
|
||||
#define TEST_SRC_DIR "."
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "0.3.3"
|
||||
#define VERSION "0.3.5"
|
||||
|
||||
/* Stops putting the code inside the Google namespace */
|
||||
#define _END_GOOGLE_NAMESPACE_ }
|
||||
|
||||
@@ -6,6 +6,6 @@ prebuilt_jar(
|
||||
|
||||
remote_file(
|
||||
name = "support-annotations-binary-aar",
|
||||
sha1 = "1fce89a6428c51467090d7f424e4c9c3dbd55f7e",
|
||||
url = "mvn:com.android.support:support-annotations:jar:23.0.1",
|
||||
sha1 = "0814258103cf26a15fcc26ecce35f5b7d24b73f8",
|
||||
url = "mvn:com.android.support:support-annotations:jar:26.1.0",
|
||||
)
|
||||
|
||||
+97
-6
@@ -1,11 +1,102 @@
|
||||
android_prebuilt_aar(
|
||||
android_library(
|
||||
name = "lib-support-v4",
|
||||
aar = ":lib-support-v4-binary-aar",
|
||||
exported_deps = [
|
||||
":lib-support-v4-support-compat",
|
||||
":lib-support-v4-support-core-utils",
|
||||
":lib-support-v4-support-core-ui",
|
||||
":lib-support-v4-support-media-compat",
|
||||
":lib-support-v4-support-fragment",
|
||||
":android-lifecycle-runtime",
|
||||
":android-lifecycle-common.jar",
|
||||
":android-lifecycle-core.jar"
|
||||
],
|
||||
visibility = ["PUBLIC"],
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-binary-aar",
|
||||
sha1 = "9e8da0e4ecf9f63258c7fbd273889252cba2d0c3",
|
||||
url = "mvn:com.android.support:support-v4:aar:23.0.1",
|
||||
prebuilt_jar(
|
||||
name = "android-lifecycle-common.jar",
|
||||
binary_jar = ":android-lifecycle-common-jar"
|
||||
)
|
||||
|
||||
prebuilt_jar(
|
||||
name = "android-lifecycle-core.jar",
|
||||
binary_jar = ":android-lifecycle-core-jar"
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "android-lifecycle-runtime",
|
||||
aar = ":android-lifecycle-runtime-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-compat",
|
||||
aar = ":lib-support-v4-support-compat-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-core-utils",
|
||||
aar = ":lib-support-v4-support-core-utils-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-core-ui",
|
||||
aar = ":lib-support-v4-support-core-ui-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-media-compat",
|
||||
aar = ":lib-support-v4-support-media-compat-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-fragment",
|
||||
aar = ":lib-support-v4-support-fragment-aar",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "android-lifecycle-runtime-aar",
|
||||
sha1 = "30c60a8a357ee1321ffd0c9f08ef54b24045cd10",
|
||||
url = "mvn:android.arch.lifecycle:runtime:aar:1.0.0"
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "android-lifecycle-common-jar",
|
||||
sha1 = "e414a4cb28434e25c4f6aa71426eb20cf4874ae9",
|
||||
url = "mvn:android.arch.lifecycle:common:jar:1.0.0"
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "android-lifecycle-core-jar",
|
||||
sha1 = "a2d487452376193fc8c103dd2b9bd5f2b1b44563",
|
||||
url = "mvn:android.arch.core:common:jar:1.0.0"
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-compat-aar",
|
||||
sha1 = "1e1c8ffc01d277d8f01dfd11d5d2ce3a2af4b98c",
|
||||
url = "mvn:com.android.support:support-compat:aar:26.1.0",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-core-utils-aar",
|
||||
sha1 = "1bfaae21c4d5c5532c5e732071e9ce234cd58cff",
|
||||
url = "mvn:com.android.support:support-core-utils:aar:26.1.0",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-core-ui-aar",
|
||||
sha1 = "e306308d54052a1ded7bf9c2f5c2fdf5152a1f22",
|
||||
url = "mvn:com.android.support:support-core-ui:aar:26.1.0",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-media-compat-aar",
|
||||
sha1 = "09fb587f27cde19aa8f2e50c5c9ee645d9aec44d",
|
||||
url = "mvn:com.android.support:support-media-compat:aar:26.1.0",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-fragment-aar",
|
||||
sha1 = "ddacf369bb98dd6558606558de8ddcd53895cf91",
|
||||
url = "mvn:com.android.support:support-fragment:aar:26.1.0",
|
||||
)
|
||||
@@ -56,6 +56,6 @@ python_binary(
|
||||
|
||||
remote_file(
|
||||
name = "appcompat-binary-aar",
|
||||
sha1 = "7d659f671541394a8bc2b9f909950aa2a5ec87ff",
|
||||
url = "mvn:com.android.support:appcompat-v7:aar:23.0.1",
|
||||
sha1 = "da5771a9ac6e8f3a461360eed3c6f921dc7580fd",
|
||||
url = "mvn:com.android.support:appcompat-v7:aar:26.1.0",
|
||||
)
|
||||
|
||||
+8
@@ -231,6 +231,8 @@ public class ReactTextInputPropertyTest {
|
||||
@Test
|
||||
public void testKeyboardType() {
|
||||
ReactEditText view = mManager.createViewInstance(mThemedContext);
|
||||
int numberPadTypeFlags = InputType.TYPE_CLASS_NUMBER;
|
||||
int decimalPadTypeFlags = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL;
|
||||
int numericTypeFlags =
|
||||
InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL |
|
||||
InputType.TYPE_NUMBER_FLAG_SIGNED;
|
||||
@@ -249,6 +251,12 @@ public class ReactTextInputPropertyTest {
|
||||
mManager.updateProperties(view, buildStyles("keyboardType", "text"));
|
||||
assertThat(view.getInputType() & generalKeyboardTypeFlags).isEqualTo(InputType.TYPE_CLASS_TEXT);
|
||||
|
||||
mManager.updateProperties(view, buildStyles("keyboardType", "number-pad"));
|
||||
assertThat(view.getInputType() & generalKeyboardTypeFlags).isEqualTo(numberPadTypeFlags);
|
||||
|
||||
mManager.updateProperties(view, buildStyles("keyboardType", "decimal-pad"));
|
||||
assertThat(view.getInputType() & generalKeyboardTypeFlags).isEqualTo(decimalPadTypeFlags);
|
||||
|
||||
mManager.updateProperties(view, buildStyles("keyboardType", "numeric"));
|
||||
assertThat(view.getInputType() & generalKeyboardTypeFlags).isEqualTo(numericTypeFlags);
|
||||
|
||||
|
||||
+31
-55
@@ -14,14 +14,10 @@ React Native follows a monthly release train. Every month, a new branch created
|
||||
| 0.40.0 | 1st of December | 1st of January |
|
||||
| 0.41.0 | 1st of January | 1st of February |
|
||||
| 0.42.0 | 1st of February | 1st of March |
|
||||
| 0.43.0 | 1st of March | 1st of April |
|
||||
| 0.44.0 | 1st of April | 1st of May |
|
||||
| 0.45.0 | 1st of May | 1st of June |
|
||||
| 0.46.0 | 1st of June | 1st of July |
|
||||
| 0.47.0 | 1st of July | 1st of August |
|
||||
| 0.48.0 | 1st of August | 1st of September |
|
||||
| 0.49.0 | 1st of September | 1st of October |
|
||||
| 0.50.0 | 1st of October | 1st of November |
|
||||
| ... | ... | ... |
|
||||
| 0.56.0 | 1st of June | 1st of July |
|
||||
| 0.57.0 | 1st of July | 1st of August |
|
||||
| 0.58.0 | 1st of August | 1st of October |
|
||||
| ... | ... | ... |
|
||||
|
||||
-------------------
|
||||
@@ -31,15 +27,16 @@ React Native follows a monthly release train. Every month, a new branch created
|
||||
### Prerequisites
|
||||
|
||||
The following are required for the local test suite to run:
|
||||
|
||||
- macOS with [Android dev environment set up](https://github.com/facebook/react-native/blob/master/ReactAndroid/README.md)
|
||||
- At least 0.2.0 [react-native-cli](https://www.npmjs.com/package/react-native-cli) installed globally
|
||||
- [react-native-cli](https://www.npmjs.com/package/react-native-cli) installed globally (v0.2.0 or newer)
|
||||
|
||||
### Step 1: Check everything works
|
||||
|
||||
Before cutting a release branch, make sure [Circle](https://circleci.com/gh/facebook/react-native) CI system is green.
|
||||
|
||||
|
||||
Before executing the following script, make sure you have:
|
||||
|
||||
- An Android emulator / Genymotion device running
|
||||
- No packager running in any of the projects
|
||||
|
||||
@@ -57,18 +54,14 @@ Run:
|
||||
|
||||
```bash
|
||||
git checkout -b <version_you_are_releasing>-stable
|
||||
# e.g. git checkout -b 0.50-stable
|
||||
# e.g. git checkout -b 0.57-stable
|
||||
|
||||
./scripts/bump-oss-version.js <exact-version_you_are_releasing>
|
||||
# e.g. ./scripts/bump-oss-version.js 0.50.0-rc
|
||||
# You can use the --remote option to specify a Git remote other than the default "origin"
|
||||
git push --tags
|
||||
git push
|
||||
./scripts/bump-oss-version.js <exact_version_you_are_releasing>
|
||||
# e.g. ./scripts/bump-oss-version.js 0.57.0-rc.0
|
||||
# or ./scripts/bump-oss-version.js 0.58.0
|
||||
```
|
||||
|
||||
Circle CI will automatically run the tests and publish to npm with the version you have specified (e.g `0.50.0-rc`) and tag `next` meaning that this version will not be installed for users by default.
|
||||
|
||||
Go to [Circle CI](https://circleci.com/gh/facebook/react-native) and look for the build triggered by your push (e.g. _0.50-stable, [0.50.0] Bump version numbers_), then scroll down to the npm publish step to verify the package was published successfully (the build will be red if not).
|
||||
Circle CI will automatically run the tests and publish to npm with the version you have specified (e.g `0.57.0-rc.0`) and tag `next` meaning that this version will not be installed for users by default.
|
||||
|
||||
### Step 3: Write the release notes
|
||||
|
||||
@@ -89,7 +82,9 @@ Sometimes commit messages might be really short / confusing - try rewording them
|
||||
- `Fix logging reported by RUN_JS_BUNDLE` -> `Fix systrace logging of RUN_JS_BUNDLE event`
|
||||
- `Fixes hot code reloading issue` -> `Fix an edge case in hot module reloading`
|
||||
|
||||
Before posting the list of changes, consider asking one of contributors for their opinion. Once everything is ready, post the release notes: https://github.com/facebook/react-native/releases
|
||||
Open a pull request against CHANGELOG.md at https://github.com/react-native-community/react-native-releases and ask for feedback.
|
||||
|
||||
Once everything is ready, create a new release at https://github.com/facebook/react-native/releases and link to the release notes.
|
||||
|
||||
**Important**: For release candidate releases, make sure to check "This is a pre-release".
|
||||
|
||||
@@ -109,7 +104,7 @@ Now that the release is out in the open, go ahead and create a GitHub issue titl
|
||||
|
||||
-------------------
|
||||
|
||||
## How to release an RC update (e.g. 0.50.0-rc.1, 0.50.0-rc.2)
|
||||
## How to release an RC update (e.g. 0.57.0-rc.1, 0.57.0-rc.2)
|
||||
|
||||
The release is now in the open, people are finding bugs, and fixes have landed in master. People have been nominating fixes in the issue you created above. Use your best judgment to decide which commits merit an RC update. It's a good idea to do a new RC release when several small and non-risky bugs have been fixed. Having a few RC releases can also help people bisect in case we cherry-pick a bad commit by mistake.
|
||||
|
||||
@@ -122,10 +117,10 @@ Follow these steps to check out the release branch:
|
||||
|
||||
```bash
|
||||
git checkout <version_you_are_patching>-stable
|
||||
# e.g. git checkout 0.50-stable
|
||||
# e.g. git checkout 0.57-stable
|
||||
|
||||
git pull origin <version_you_are_patching>-stable
|
||||
# e.g. git pull origin 0.50-stable
|
||||
# e.g. git pull origin 0.57-stable
|
||||
```
|
||||
|
||||
> If you don't have a local checkout of the release branch, you can run the following instead:
|
||||
@@ -153,40 +148,31 @@ If everything worked, run the following to bump the version number:
|
||||
|
||||
```bash
|
||||
./scripts/bump-oss-version.js <exact_version_you_are_releasing>
|
||||
# e.g. ./scripts/bump-oss-version.js 0.50.0-rc.1
|
||||
# e.g. ./scripts/bump-oss-version.js 0.57.0-rc.1
|
||||
```
|
||||
|
||||
### Step 5: Push to GitHub
|
||||
Again, Circle CI will automatically run the tests and publish to npm with the version you have specified (e.g `0.57.0-rc.1`).
|
||||
|
||||
Finally, push the new tags and commits to GitHub:
|
||||
### Step 5: Update the release notes
|
||||
|
||||
```
|
||||
git push --tags
|
||||
git push
|
||||
````
|
||||
|
||||
Again, Circle CI will automatically run the tests and publish to npm with the version you have specified (e.g `0.50.0-rc.1`).
|
||||
|
||||
### Step 6: Update the release notes
|
||||
|
||||
Go to https://github.com/facebook/react-native/releases and find the release notes for this release candidate. Edit them so that they now point to the tag that you've just created. We want single release notes per version. For example, if there is v0.50.0-rc and you just released v0.50.0-rc.1, the release notes should live on the v0.50.0-rc.1 tag at https://github.com/facebook/react-native/tags
|
||||
Go to https://github.com/facebook/react-native/releases and find the release notes for this release candidate. Edit them so that they now point to the tag that you've just created. We want single release notes per version. For example, if there is v0.57.0-rc and you just released v0.57.0-rc.1, the release notes should live on the v0.57.0-rc.1 tag at https://github.com/facebook/react-native/tags
|
||||
|
||||
-------------------
|
||||
|
||||
## How to do the final stable release (e.g. 0.50.0, 0.50.1)
|
||||
## How to do the final stable release (e.g. 0.57.0, 0.57.1)
|
||||
|
||||
A stable release is promoted roughly a month after the release branch is cut (refer to the schedule above). The release may be delayed for several reasons, including major issues in the release candidate. Make sure that all bug fixes that have been nominated in your tracking issue have been addressed as needed. Avoid cherry-picking commits that have not been vetted in the release candidate phase at this point.
|
||||
|
||||
Once you are sure that the release is solid, perform the following steps. Note that they're similar to the steps you may have followed earlier when patching the release candidate, but we're not cherry-picking any additional commits at this point.
|
||||
Once you are sure that the release is solid, perform the following steps. Note that they're similar to the steps you may have followed earlier when patching the release candidate, but we're not cherry-picking any additional commits at this point.
|
||||
|
||||
### Step 1: Check out the release branch
|
||||
|
||||
```bash
|
||||
git checkout <version_you_are_patching>-stable
|
||||
# e.g. git checkout 0.50-stable
|
||||
# e.g. git checkout 0.57-stable
|
||||
|
||||
git pull origin <version_you_are_patching>-stable
|
||||
# e.g. git pull origin 0.50-stable
|
||||
# e.g. git pull origin 0.57-stable
|
||||
```
|
||||
|
||||
> If you don't have a local checkout of the release branch, you can run the following instead:
|
||||
@@ -206,28 +192,18 @@ If everything worked:
|
||||
|
||||
```bash
|
||||
./scripts/bump-oss-version.js <exact_version_you_are_releasing>
|
||||
# e.g. ./scripts/bump-oss-version.js 0.50.0
|
||||
# e.g. ./scripts/bump-oss-version.js 0.57.0
|
||||
```
|
||||
|
||||
### Step 4: Push to GitHub
|
||||
As with the release candidate, Circle CI will automatically run the tests and publish to npm with the version you have specified (e.g `0.57.0`).
|
||||
|
||||
Finally, push the new tags and commits to GitHub:
|
||||
|
||||
```
|
||||
git push --tags
|
||||
git push
|
||||
```
|
||||
|
||||
As with the release candidate, Circle CI will automatically run the tests and publish to npm with the version you have specified (e.g `0.50.0`).
|
||||
|
||||
Go to [Circle CI](https://circleci.com/gh/facebook/react-native) and look for the build triggered by your push (e.g. _0.50-stable, [0.50.0] Bump version numbers_), then scroll down to the npm publish step to verify the package was published successfully (the build will be red if not).
|
||||
Go to [Circle CI](https://circleci.com/gh/facebook/react-native) and look for the build triggered by your push (e.g. _0.57-stable, [0.57.0] Bump version numbers_), then scroll down to the npm publish step to verify the package was published successfully (the build will be red if not).
|
||||
|
||||
This will now become the latest release, and will be installed by users by default. At this point, the website will be updated and the docs for this release will be displayed by default.
|
||||
|
||||
### Step 5: Update the release notes
|
||||
### Step 4: Update the release notes
|
||||
|
||||
Once you see that the website is displaying the version that you have just created, you will need to update the release notes. Move the release notes from the release candidate, to the tag that you've just created. We want single release notes per version. For example, if there is v0.50.0-rc and later we release v0.50.0, the release notes should live on v0.50.0:
|
||||
https://github.com/facebook/react-native/tags
|
||||
Go to https://github.com/facebook/react-native/releases and find the release notes for this release candidate. Edit them so that they now point to the tag that you've just created. We want single release notes per version. For example, if there is v0.57.0 and you just released v0.57.1, the release notes should live on the v0.57.1 tag at https://github.com/facebook/react-native/tags
|
||||
|
||||
For non-RC releases: Uncheck the box "This is a pre-release" and publish the notes.
|
||||
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@ buildscript {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.2.2'
|
||||
classpath 'de.undercouch:gradle-download-task:3.1.2'
|
||||
classpath 'com.android.tools.build:gradle:2.3.3'
|
||||
classpath 'de.undercouch:gradle-download-task:3.4.3'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
+529
-65
@@ -4,30 +4,124 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* Modified from https://raw.githubusercontent.com/flowtype/flow-typed/e3b0f3034929e0f0fb85c790450a201b380ac2fd/definitions/npm/jest_v17.x.x/flow_v0.33.x-/jest_v17.x.x.js
|
||||
* Modified from https://raw.githubusercontent.com/flowtype/flow-typed/b43dff3e0ed5ccf7033839045f4819e8db40faa9/definitions/npm/jest_v20.x.x/flow_v0.22.x-/jest_v20.x.x.js
|
||||
* Duplicated from www/flow/shared/jest.js
|
||||
* List of modifications:
|
||||
* - added function fail
|
||||
* - added jest.requireActual
|
||||
* - added jest.requireMock
|
||||
* - added JestMockFn.mockName
|
||||
* - added JestMockFn.mockRejectedValue
|
||||
* - added JestMockFn.mockRejectedValueOnce
|
||||
* - added JestMockFn.mockResolvedValue
|
||||
* - added JestMockFn.mockResolvedValueOnce
|
||||
* - added JestMockFn.mock.thrownErrors
|
||||
* - added JestMockFn.mock.returnValues
|
||||
* - added jest.setTimeout
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* eslint-disable lint/no-unclear-flowtypes */
|
||||
|
||||
type JestMockFn = {
|
||||
(...args: Array<any>): any,
|
||||
type JestMockFn<TArguments: $ReadOnlyArray<*>, TReturn> = {
|
||||
(...args: TArguments): TReturn,
|
||||
/**
|
||||
* An object for introspecting mock calls
|
||||
*/
|
||||
mock: {
|
||||
calls: Array<Array<any>>,
|
||||
instances: mixed,
|
||||
/**
|
||||
* An array that represents all calls that have been made into this mock
|
||||
* function. Each call is represented by an array of arguments that were
|
||||
* passed during the call.
|
||||
*/
|
||||
calls: Array<TArguments>,
|
||||
/**
|
||||
* An array that contains all the object instances that have been
|
||||
* instantiated from this mock function.
|
||||
*/
|
||||
instances: Array<TReturn>,
|
||||
/**
|
||||
* An array containing the results of a method, and whether they were
|
||||
* returned or thrown.
|
||||
*/
|
||||
results: Array<{isThrown: boolean}>,
|
||||
},
|
||||
mockClear(): Function,
|
||||
mockReset(): Function,
|
||||
mockImplementation(fn: Function): JestMockFn,
|
||||
mockImplementationOnce(fn: Function): JestMockFn,
|
||||
/**
|
||||
* Resets all information stored in the mockFn.mock.calls and
|
||||
* mockFn.mock.instances arrays. Often this is useful when you want to clean
|
||||
* up a mock's usage data between two assertions.
|
||||
*/
|
||||
mockClear(): void,
|
||||
/**
|
||||
* Resets all information stored in the mock. This is useful when you want to
|
||||
* completely restore a mock back to its initial state.
|
||||
*/
|
||||
mockReset(): void,
|
||||
/**
|
||||
* Removes the mock and restores the initial implementation. This is useful
|
||||
* when you want to mock functions in certain test cases and restore the
|
||||
* original implementation in others. Beware that mockFn.mockRestore only
|
||||
* works when mock was created with jest.spyOn. Thus you have to take care of
|
||||
* restoration yourself when manually assigning jest.fn().
|
||||
*/
|
||||
mockRestore(): void,
|
||||
/**
|
||||
* Accepts a function that should be used as the implementation of the mock.
|
||||
* The mock itself will still record all calls that go into and instances
|
||||
* that come from itself -- the only difference is that the implementation
|
||||
* will also be executed when the mock is called.
|
||||
*/
|
||||
mockImplementation(
|
||||
fn: (...args: TArguments) => TReturn,
|
||||
): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Accepts a function that will be used as an implementation of the mock for
|
||||
* one call to the mocked function. Can be chained so that multiple function
|
||||
* calls produce different results.
|
||||
*/
|
||||
mockImplementationOnce(
|
||||
fn: (...args: TArguments) => TReturn,
|
||||
): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Accepts a string to use in test result output in place of "jest.fn()" to
|
||||
* indicate which mock function is being referenced.
|
||||
*/
|
||||
mockName(name: string): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for: jest.fn().mockReturnValue(Promise.reject(value));
|
||||
*/
|
||||
mockRejectedValue(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for: jest.fn().mockReturnValueOnce(Promise.reject(value));
|
||||
*/
|
||||
mockRejectedValueOnce(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for: jest.fn().mockReturnValue(Promise.resolve(value));
|
||||
*/
|
||||
mockResolvedValue(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for: jest.fn().mockReturnValueOnce(Promise.resolve(value));
|
||||
*/
|
||||
mockResolvedValueOnce(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Just a simple sugar function for returning `this`
|
||||
*/
|
||||
mockReturnThis(): void,
|
||||
mockReturnValue(value: any): JestMockFn,
|
||||
mockReturnValueOnce(value: any): JestMockFn,
|
||||
/**
|
||||
* Deprecated: use jest.fn(() => value) instead
|
||||
*/
|
||||
mockReturnValue(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for only returning a value once inside your mock
|
||||
*/
|
||||
mockReturnValueOnce(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
};
|
||||
|
||||
type JestAsymmetricEqualityType = {
|
||||
/**
|
||||
* A custom Jasmine equality tester
|
||||
*/
|
||||
asymmetricMatch(value: mixed): boolean,
|
||||
};
|
||||
|
||||
@@ -44,7 +138,7 @@ type JestCallsType = {
|
||||
type JestClockType = {
|
||||
install(): void,
|
||||
mockDate(date: Date): void,
|
||||
tick(): void,
|
||||
tick(milliseconds?: number): void,
|
||||
uninstall(): void,
|
||||
};
|
||||
|
||||
@@ -68,105 +162,471 @@ type JestPromiseType = {
|
||||
resolves: JestExpectType,
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugin: jest-enzyme
|
||||
*/
|
||||
type EnzymeMatchersType = {
|
||||
toBeChecked(): void,
|
||||
toBeDisabled(): void,
|
||||
toBeEmpty(): void,
|
||||
toBePresent(): void,
|
||||
toContainReact(element: React$Element<any>): void,
|
||||
toHaveClassName(className: string): void,
|
||||
toHaveHTML(html: string): void,
|
||||
toHaveProp(propKey: string, propValue?: any): void,
|
||||
toHaveRef(refName: string): void,
|
||||
toHaveState(stateKey: string, stateValue?: any): void,
|
||||
toHaveStyle(styleKey: string, styleValue?: any): void,
|
||||
toHaveTagName(tagName: string): void,
|
||||
toHaveText(text: string): void,
|
||||
toIncludeText(text: string): void,
|
||||
toHaveValue(value: any): void,
|
||||
toMatchElement(element: React$Element<any>): void,
|
||||
toMatchSelector(selector: string): void,
|
||||
};
|
||||
|
||||
type JestExpectType = {
|
||||
not: JestExpectType,
|
||||
not: JestExpectType & EnzymeMatchersType,
|
||||
/**
|
||||
* If you have a mock function, you can use .lastCalledWith to test what
|
||||
* arguments it was last called with.
|
||||
*/
|
||||
lastCalledWith(...args: Array<any>): void,
|
||||
/**
|
||||
* toBe just checks that a value is what you expect. It uses === to check
|
||||
* strict equality.
|
||||
*/
|
||||
toBe(value: any): void,
|
||||
/**
|
||||
* Use .toHaveBeenCalled to ensure that a mock function got called.
|
||||
*/
|
||||
toBeCalled(): void,
|
||||
/**
|
||||
* Use .toBeCalledWith to ensure that a mock function was called with
|
||||
* specific arguments.
|
||||
*/
|
||||
toBeCalledWith(...args: Array<any>): void,
|
||||
/**
|
||||
* Using exact equality with floating point numbers is a bad idea. Rounding
|
||||
* means that intuitive things fail.
|
||||
*/
|
||||
toBeCloseTo(num: number, delta: any): void,
|
||||
/**
|
||||
* Use .toBeDefined to check that a variable is not undefined.
|
||||
*/
|
||||
toBeDefined(): void,
|
||||
/**
|
||||
* Use .toBeFalsy when you don't care what a value is, you just want to
|
||||
* ensure a value is false in a boolean context.
|
||||
*/
|
||||
toBeFalsy(): void,
|
||||
/**
|
||||
* To compare floating point numbers, you can use toBeGreaterThan.
|
||||
*/
|
||||
toBeGreaterThan(number: number): void,
|
||||
/**
|
||||
* To compare floating point numbers, you can use toBeGreaterThanOrEqual.
|
||||
*/
|
||||
toBeGreaterThanOrEqual(number: number): void,
|
||||
/**
|
||||
* To compare floating point numbers, you can use toBeLessThan.
|
||||
*/
|
||||
toBeLessThan(number: number): void,
|
||||
/**
|
||||
* To compare floating point numbers, you can use toBeLessThanOrEqual.
|
||||
*/
|
||||
toBeLessThanOrEqual(number: number): void,
|
||||
/**
|
||||
* Use .toBeInstanceOf(Class) to check that an object is an instance of a
|
||||
* class.
|
||||
*/
|
||||
toBeInstanceOf(cls: Class<*>): void,
|
||||
/**
|
||||
* .toBeNull() is the same as .toBe(null) but the error messages are a bit
|
||||
* nicer.
|
||||
*/
|
||||
toBeNull(): void,
|
||||
/**
|
||||
* Use .toBeTruthy when you don't care what a value is, you just want to
|
||||
* ensure a value is true in a boolean context.
|
||||
*/
|
||||
toBeTruthy(): void,
|
||||
/**
|
||||
* Use .toBeUndefined to check that a variable is undefined.
|
||||
*/
|
||||
toBeUndefined(): void,
|
||||
/**
|
||||
* Use .toContain when you want to check that an item is in a list. For
|
||||
* testing the items in the list, this uses ===, a strict equality check.
|
||||
*/
|
||||
toContain(item: any): void,
|
||||
/**
|
||||
* Use .toContainEqual when you want to check that an item is in a list. For
|
||||
* testing the items in the list, this matcher recursively checks the
|
||||
* equality of all fields, rather than checking for object identity.
|
||||
*/
|
||||
toContainEqual(item: any): void,
|
||||
/**
|
||||
* Use .toEqual when you want to check that two objects have the same value.
|
||||
* This matcher recursively checks the equality of all fields, rather than
|
||||
* checking for object identity.
|
||||
*/
|
||||
toEqual(value: any): void,
|
||||
/**
|
||||
* Use .toHaveBeenCalled to ensure that a mock function got called.
|
||||
*/
|
||||
toHaveBeenCalled(): void,
|
||||
/**
|
||||
* Use .toHaveBeenCalledTimes to ensure that a mock function got called exact
|
||||
* number of times.
|
||||
*/
|
||||
toHaveBeenCalledTimes(number: number): void,
|
||||
/**
|
||||
* Use .toHaveBeenCalledWith to ensure that a mock function was called with
|
||||
* specific arguments.
|
||||
*/
|
||||
toHaveBeenCalledWith(...args: Array<any>): void,
|
||||
toHaveProperty(path: string, value?: any): void,
|
||||
toMatch(regexp: RegExp): void,
|
||||
/**
|
||||
* Use .toHaveBeenLastCalledWith to ensure that a mock function was last called
|
||||
* with specific arguments.
|
||||
*/
|
||||
toHaveBeenLastCalledWith(...args: Array<any>): void,
|
||||
/**
|
||||
* Check that an object has a .length property and it is set to a certain
|
||||
* numeric value.
|
||||
*/
|
||||
toHaveLength(number: number): void,
|
||||
/**
|
||||
*
|
||||
*/
|
||||
toHaveProperty(propPath: string, value?: any): void,
|
||||
/**
|
||||
* Use .toMatch to check that a string matches a regular expression or string.
|
||||
*/
|
||||
toMatch(regexpOrString: RegExp | string): void,
|
||||
/**
|
||||
* Use .toMatchObject to check that a javascript object matches a subset of the properties of an object.
|
||||
*/
|
||||
toMatchObject(object: Object): void,
|
||||
toMatchSnapshot(): void,
|
||||
toThrow(message?: string | Error | Class<Error>): void,
|
||||
toThrowError(message?: string | Error | Class<Error> | RegExp): void,
|
||||
/**
|
||||
* This ensures that a React component matches the most recent snapshot.
|
||||
*/
|
||||
toMatchSnapshot(name?: string): void,
|
||||
/**
|
||||
* Use .toThrow to test that a function throws when it is called.
|
||||
* If you want to test that a specific error gets thrown, you can provide an
|
||||
* argument to toThrow. The argument can be a string for the error message,
|
||||
* a class for the error, or a regex that should match the error.
|
||||
*
|
||||
* Alias: .toThrowError
|
||||
*/
|
||||
toThrow(message?: string | Error | RegExp): void,
|
||||
toThrowError(message?: string | Error | RegExp): void,
|
||||
/**
|
||||
* Use .toThrowErrorMatchingSnapshot to test that a function throws a error
|
||||
* matching the most recent snapshot when it is called.
|
||||
*/
|
||||
toThrowErrorMatchingSnapshot(): void,
|
||||
};
|
||||
|
||||
type JestObjectType = {
|
||||
/**
|
||||
* Disables automatic mocking in the module loader.
|
||||
*
|
||||
* After this method is called, all `require()`s will return the real
|
||||
* versions of each module (rather than a mocked version).
|
||||
*/
|
||||
disableAutomock(): JestObjectType,
|
||||
/**
|
||||
* An un-hoisted version of disableAutomock
|
||||
*/
|
||||
autoMockOff(): JestObjectType,
|
||||
/**
|
||||
* Enables automatic mocking in the module loader.
|
||||
*/
|
||||
enableAutomock(): JestObjectType,
|
||||
/**
|
||||
* An un-hoisted version of enableAutomock
|
||||
*/
|
||||
autoMockOn(): JestObjectType,
|
||||
/**
|
||||
* Clears the mock.calls and mock.instances properties of all mocks.
|
||||
* Equivalent to calling .mockClear() on every mocked function.
|
||||
*/
|
||||
clearAllMocks(): JestObjectType,
|
||||
/**
|
||||
* Resets the state of all mocks. Equivalent to calling .mockReset() on every
|
||||
* mocked function.
|
||||
*/
|
||||
resetAllMocks(): JestObjectType,
|
||||
/**
|
||||
* Restores all mocks back to their original value.
|
||||
*/
|
||||
restoreAllMocks(): JestObjectType,
|
||||
/**
|
||||
* Removes any pending timers from the timer system.
|
||||
*/
|
||||
clearAllTimers(): void,
|
||||
/**
|
||||
* The same as `mock` but not moved to the top of the expectation by
|
||||
* babel-jest.
|
||||
*/
|
||||
doMock(moduleName: string, moduleFactory?: any): JestObjectType,
|
||||
/**
|
||||
* The same as `unmock` but not moved to the top of the expectation by
|
||||
* babel-jest.
|
||||
*/
|
||||
dontMock(moduleName: string): JestObjectType,
|
||||
/**
|
||||
* Returns a new, unused mock function. Optionally takes a mock
|
||||
* implementation.
|
||||
*/
|
||||
fn<TArguments: $ReadOnlyArray<*>, TReturn>(
|
||||
implementation?: (...args: TArguments) => TReturn,
|
||||
): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Determines if the given function is a mocked function.
|
||||
*/
|
||||
isMockFunction(fn: Function): boolean,
|
||||
/**
|
||||
* Given the name of a module, use the automatic mocking system to generate a
|
||||
* mocked version of the module for you.
|
||||
*/
|
||||
genMockFromModule(moduleName: string): any,
|
||||
/**
|
||||
* Mocks a module with an auto-mocked version when it is being required.
|
||||
*
|
||||
* The second argument can be used to specify an explicit module factory that
|
||||
* is being run instead of using Jest's automocking feature.
|
||||
*
|
||||
* The third argument can be used to create virtual mocks -- mocks of modules
|
||||
* that don't exist anywhere in the system.
|
||||
*/
|
||||
mock(
|
||||
moduleName: string,
|
||||
moduleFactory?: any,
|
||||
options?: Object,
|
||||
): JestObjectType,
|
||||
/**
|
||||
* Resets the module registry - the cache of all required modules. This is
|
||||
* useful to isolate modules where local state might conflict between tests.
|
||||
*/
|
||||
resetModules(): JestObjectType,
|
||||
/**
|
||||
* Exhausts the micro-task queue (usually interfaced in node via
|
||||
* process.nextTick).
|
||||
*/
|
||||
runAllTicks(): void,
|
||||
/**
|
||||
* Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(),
|
||||
* setInterval(), and setImmediate()).
|
||||
*/
|
||||
runAllTimers(): void,
|
||||
/**
|
||||
* Exhausts all tasks queued by setImmediate().
|
||||
*/
|
||||
runAllImmediates(): void,
|
||||
/**
|
||||
* Executes only the macro task queue (i.e. all tasks queued by setTimeout()
|
||||
* or setInterval() and setImmediate()).
|
||||
*/
|
||||
advanceTimersByTime(msToRun: number): void,
|
||||
/**
|
||||
* Executes only the macro task queue (i.e. all tasks queued by setTimeout()
|
||||
* or setInterval() and setImmediate()).
|
||||
*
|
||||
* Renamed to `advanceTimersByTime`.
|
||||
*/
|
||||
runTimersToTime(msToRun: number): void,
|
||||
/**
|
||||
* Executes only the macro-tasks that are currently pending (i.e., only the
|
||||
* tasks that have been queued by setTimeout() or setInterval() up to this
|
||||
* point)
|
||||
*/
|
||||
runOnlyPendingTimers(): void,
|
||||
/**
|
||||
* Explicitly supplies the mock object that the module system should return
|
||||
* for the specified module. Note: It is recommended to use jest.mock()
|
||||
* instead.
|
||||
*/
|
||||
setMock(moduleName: string, moduleExports: any): JestObjectType,
|
||||
/**
|
||||
* Indicates that the module system should never return a mocked version of
|
||||
* the specified module from require() (e.g. that it should always return the
|
||||
* real module).
|
||||
*/
|
||||
unmock(moduleName: string): JestObjectType,
|
||||
/**
|
||||
* Instructs Jest to use fake versions of the standard timer functions
|
||||
* (setTimeout, setInterval, clearTimeout, clearInterval, nextTick,
|
||||
* setImmediate and clearImmediate).
|
||||
*/
|
||||
useFakeTimers(): JestObjectType,
|
||||
/**
|
||||
* Instructs Jest to use the real versions of the standard timer functions.
|
||||
*/
|
||||
useRealTimers(): JestObjectType,
|
||||
/**
|
||||
* Creates a mock function similar to jest.fn but also tracks calls to
|
||||
* object[methodName].
|
||||
*/
|
||||
spyOn(object: Object, methodName: string): JestMockFn<any, any>,
|
||||
/**
|
||||
* Set the default timeout interval for tests and before/after hooks in milliseconds.
|
||||
* Note: The default timeout interval is 5 seconds if this method is not called.
|
||||
*/
|
||||
setTimeout(timeout: number): JestObjectType,
|
||||
|
||||
// These methods are added separately and not a part of flow-typed OSS
|
||||
// version of this file. They were added in jest@21.0.0.alpha-2 which is not
|
||||
// yet publicly released yet.
|
||||
// TODO T21262347 Add them to OSS version of flow-typed
|
||||
requireActual(module: string): any,
|
||||
requireMock(module: string): any,
|
||||
};
|
||||
|
||||
type JestSpyType = {
|
||||
calls: JestCallsType,
|
||||
};
|
||||
|
||||
declare function afterEach(fn: Function): void;
|
||||
declare function beforeEach(fn: Function): void;
|
||||
declare function afterAll(fn: Function): void;
|
||||
declare function beforeAll(fn: Function): void;
|
||||
declare function describe(name: string, fn: Function): void;
|
||||
declare var it: {
|
||||
(name: string, fn: Function): ?Promise<void>,
|
||||
only(name: string, fn: Function): ?Promise<void>,
|
||||
skip(name: string, fn: Function): ?Promise<void>,
|
||||
/** Runs this function after every test inside this context */
|
||||
declare function afterEach(
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
/** Runs this function before every test inside this context */
|
||||
declare function beforeEach(
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
/** Runs this function after all tests have finished inside this context */
|
||||
declare function afterAll(
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
/** Runs this function before any tests have started inside this context */
|
||||
declare function beforeAll(
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
|
||||
/** A context for grouping tests together */
|
||||
declare var describe: {
|
||||
/**
|
||||
* Creates a block that groups together several related tests in one "test suite"
|
||||
*/
|
||||
(name: string, fn: () => void): void,
|
||||
|
||||
/**
|
||||
* Only run this describe block
|
||||
*/
|
||||
only(name: string, fn: () => void): void,
|
||||
|
||||
/**
|
||||
* Skip running this describe block
|
||||
*/
|
||||
skip(name: string, fn: () => void): void,
|
||||
};
|
||||
declare function fit(name: string, fn: Function): ?Promise<void>;
|
||||
declare function pit(name: string, fn: () => Promise<any>): Promise<void>;
|
||||
|
||||
/** An individual test unit */
|
||||
declare var it: {
|
||||
/**
|
||||
* An individual test unit
|
||||
*
|
||||
* @param {string} Name of Test
|
||||
* @param {Function} Test
|
||||
* @param {number} Timeout for the test, in milliseconds.
|
||||
*/
|
||||
(
|
||||
name: string,
|
||||
fn?: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void,
|
||||
/**
|
||||
* Only run this test
|
||||
*
|
||||
* @param {string} Name of Test
|
||||
* @param {Function} Test
|
||||
* @param {number} Timeout for the test, in milliseconds.
|
||||
*/
|
||||
only(
|
||||
name: string,
|
||||
fn?: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void,
|
||||
/**
|
||||
* Skip running this test
|
||||
*
|
||||
* @param {string} Name of Test
|
||||
* @param {Function} Test
|
||||
* @param {number} Timeout for the test, in milliseconds.
|
||||
*/
|
||||
skip(
|
||||
name: string,
|
||||
fn?: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void,
|
||||
/**
|
||||
* Run the test concurrently
|
||||
*
|
||||
* @param {string} Name of Test
|
||||
* @param {Function} Test
|
||||
* @param {number} Timeout for the test, in milliseconds.
|
||||
*/
|
||||
concurrent(
|
||||
name: string,
|
||||
fn?: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void,
|
||||
};
|
||||
declare function fit(
|
||||
name: string,
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
/** An individual test unit */
|
||||
declare var test: typeof it;
|
||||
/** A disabled group of tests */
|
||||
declare var xdescribe: typeof describe;
|
||||
/** A focused group of tests */
|
||||
declare var fdescribe: typeof describe;
|
||||
/** A disabled individual test */
|
||||
declare var xit: typeof it;
|
||||
/** A disabled individual test */
|
||||
declare var xtest: typeof it;
|
||||
|
||||
/** The expect function is used every time you want to test a value */
|
||||
declare var expect: {
|
||||
(value: any): JestExpectType & JestPromiseType,
|
||||
any: any,
|
||||
/** The object that you want to make assertions against */
|
||||
(value: any): JestExpectType & JestPromiseType & EnzymeMatchersType,
|
||||
/** Add additional Jasmine matchers to Jest's roster */
|
||||
extend(matchers: {[name: string]: JestMatcher}): void,
|
||||
objectContaining(any): void,
|
||||
/** Add a module that formats application-specific data structures. */
|
||||
addSnapshotSerializer(serializer: (input: Object) => string): void,
|
||||
assertions(expectedAssertions: number): void,
|
||||
hasAssertions(): void,
|
||||
any(value: mixed): JestAsymmetricEqualityType,
|
||||
anything(): void,
|
||||
arrayContaining(value: Array<mixed>): void,
|
||||
objectContaining(value: Object): void,
|
||||
/** Matches any received string that contains the exact expected string. */
|
||||
stringContaining(value: string): void,
|
||||
stringMatching(value: string | RegExp): void,
|
||||
};
|
||||
|
||||
declare function fail(message?: string): void;
|
||||
|
||||
// TODO handle return type
|
||||
// http://jasmine.github.io/2.4/introduction.html#section-Spies
|
||||
declare function spyOn(value: mixed, method: string): Object;
|
||||
|
||||
type Jest = {
|
||||
autoMockOff(): Jest,
|
||||
autoMockOn(): Jest,
|
||||
resetAllMocks(): Jest,
|
||||
clearAllTimers(): void,
|
||||
currentTestPath(): void,
|
||||
disableAutomock(): Jest,
|
||||
doMock(moduleName: string, moduleFactory?: any): void,
|
||||
dontMock(moduleName: string): Jest,
|
||||
enableAutomock(): Jest,
|
||||
fn(implementation?: Function): JestMockFn,
|
||||
genMockFromModule(moduleName: string): any,
|
||||
isMockFunction(fn: Function): boolean,
|
||||
mock(
|
||||
moduleName: string,
|
||||
moduleFactory?: any,
|
||||
options?: {virtual?: boolean},
|
||||
): Jest,
|
||||
resetModuleRegistry(): Jest, // undocumented alias for resetModuleRegistry
|
||||
resetModules(): Jest,
|
||||
restoreAllMocks(): Jest,
|
||||
runAllTicks(): Jest,
|
||||
runAllTimers(): Jest,
|
||||
runTimersToTime(msToRun: number): Jest,
|
||||
runOnlyPendingTimers(): Jest,
|
||||
setMock(moduleName: string, moduleExports: any): Jest,
|
||||
unmock(moduleName: string): Jest,
|
||||
useFakeTimers(): Jest,
|
||||
useRealTimers(): Jest,
|
||||
};
|
||||
|
||||
declare var jest: Jest;
|
||||
/** Holds all functions related to manipulating test runner */
|
||||
declare var jest: JestObjectType;
|
||||
|
||||
/**
|
||||
* The global Jamine object, this is generally not exposed as the public API,
|
||||
* using features inside here could break in later versions of Jest.
|
||||
*/
|
||||
declare var jasmine: {
|
||||
DEFAULT_TIMEOUT_INTERVAL: number,
|
||||
any(value: mixed): JestAsymmetricEqualityType,
|
||||
@@ -174,6 +634,10 @@ declare var jasmine: {
|
||||
arrayContaining(value: Array<mixed>): void,
|
||||
clock(): JestClockType,
|
||||
createSpy(name: string): JestSpyType,
|
||||
createSpyObj(
|
||||
baseName: string,
|
||||
methodNames: Array<string>,
|
||||
): {[methodName: string]: JestSpyType},
|
||||
objectContaining(value: Object): void,
|
||||
stringMatching(value: string): void,
|
||||
};
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip
|
||||
|
||||
+8
-4
@@ -12,7 +12,10 @@
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const ROOTS = [
|
||||
path.resolve(__dirname, '..') + path.sep,
|
||||
path.resolve(__dirname, '../../react-native-windows') + path.sep,
|
||||
];
|
||||
|
||||
const BLACKLISTED_PATTERNS /*: Array<RegExp> */ = [
|
||||
/.*\/__(mocks|tests)__\/.*/,
|
||||
@@ -33,7 +36,7 @@ const NAME_REDUCERS /*: Array<[RegExp, string]> */ = [
|
||||
// strip .js/.js.flow suffix
|
||||
[/^(.*)\.js(\.flow)?$/, '$1'],
|
||||
// strip .android/.ios/.native/.web suffix
|
||||
[/^(.*)\.(android|ios|native|web)$/, '$1'],
|
||||
[/^(.*)\.(android|ios|native|web|windows)$/, '$1'],
|
||||
];
|
||||
|
||||
const haste = {
|
||||
@@ -63,11 +66,12 @@ function isHastePath(filePath /*: string */) /*: boolean */ {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!filePath.startsWith(ROOT)) {
|
||||
const root = ROOTS.find(r => filePath.startsWith(r));
|
||||
if (!root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
filePath = filePath.substr(ROOT.length + 1);
|
||||
filePath = filePath.substr(root.length);
|
||||
if (BLACKLISTED_PATTERNS.some(pattern => pattern.test(filePath))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -72,6 +72,14 @@ const pluginPlatforms = plugins.platforms.reduce((acc, pathToPlatforms) => {
|
||||
const defaultRNConfig = {
|
||||
hasteImplModulePath: require.resolve('../../jest/hasteImpl'),
|
||||
|
||||
getPlatforms(): Array<string> {
|
||||
return ['ios', 'android', 'windows', 'web'];
|
||||
},
|
||||
|
||||
getProvidesModuleNodeModules(): Array<string> {
|
||||
return ['react-native', 'react-native-windows'];
|
||||
},
|
||||
|
||||
getProjectCommands(): Array<CommandT> {
|
||||
const commands = plugins.commands.map(pathToCommands => {
|
||||
const name = pathToCommands.split(path.sep)[0];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
dependencies {
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.facebook.react:react-native:0.18.+"
|
||||
compile "com.android.support:appcompat-v7:26.1.0"
|
||||
compile "com.facebook.react:react-native:+"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ dependencies {
|
||||
exclude(group: 'org.unwanted', module: 'test10')
|
||||
}
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.facebook.react:react-native:0.18.+"
|
||||
compile "com.android.support:appcompat-v7:26.1.0"
|
||||
compile "com.facebook.react:react-native:+"
|
||||
}
|
||||
|
||||
@@ -160,8 +160,11 @@ function runOnSimulator(xcodeProject, args, scheme) {
|
||||
* it will not boot the "default" device, but the one we set. If the app is already running,
|
||||
* this flag has no effect.
|
||||
*/
|
||||
const activeDeveloperDir = child_process
|
||||
.execFileSync('xcode-select', ['-p'], {encoding: 'utf8'})
|
||||
.trim();
|
||||
child_process.execFileSync('open', [
|
||||
'/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app',
|
||||
`${activeDeveloperDir}/Applications/Simulator.app`,
|
||||
'--args',
|
||||
'-CurrentDeviceUDID',
|
||||
selectedSimulator.udid,
|
||||
|
||||
@@ -30,6 +30,19 @@ node_modules/react-native/flow-github/
|
||||
emoji=true
|
||||
|
||||
module.system=haste
|
||||
module.system.haste.use_name_reducers=true
|
||||
# get basename
|
||||
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
|
||||
# strip .js or .js.flow suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
|
||||
# strip .ios suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
|
||||
module.system.haste.paths.blacklist=.*/__tests__/.*
|
||||
module.system.haste.paths.blacklist=.*/__mocks__/.*
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
@@ -51,4 +64,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
[version]
|
||||
^0.74.0
|
||||
^0.75.0
|
||||
|
||||
@@ -94,13 +94,13 @@ def enableSeparateBuildPerCPUArchitecture = false
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.helloworld"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
@@ -138,7 +138,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven {
|
||||
url 'https://maven.google.com/'
|
||||
name 'Google'
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
classpath 'com.android.tools.build:gradle:2.3.3'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@@ -20,5 +24,17 @@ allprojects {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
}
|
||||
maven {
|
||||
url 'https://maven.google.com/'
|
||||
name 'Google'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
buildToolsVersion = "26.0.3"
|
||||
minSdkVersion = 16
|
||||
compileSdkVersion = 26
|
||||
targetSdkVersion = 26
|
||||
supportLibVersion = "26.1.0"
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "1000.0.0",
|
||||
"version": "0.56.0",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -143,7 +143,7 @@
|
||||
"react-native": "local-cli/wrong-react-native.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "16.3.2"
|
||||
"react": "16.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"absolute-path": "^0.0.0",
|
||||
@@ -212,13 +212,13 @@
|
||||
"eslint-plugin-prettier": "2.6.0",
|
||||
"eslint-plugin-react": "7.8.2",
|
||||
"eslint-plugin-react-native": "^3.2.1",
|
||||
"flow-bin": "^0.74.0",
|
||||
"jest": "23.1.0",
|
||||
"jest-junit": "4.0.0",
|
||||
"prettier": "1.13.4",
|
||||
"react": "16.3.2",
|
||||
"react-test-renderer": "16.3.2",
|
||||
"flow-bin": "^0.75.0",
|
||||
"jest": "23.2.0",
|
||||
"jest-junit": "5.1.0",
|
||||
"prettier": "1.13.6",
|
||||
"react": "16.4.1",
|
||||
"react-test-renderer": "16.4.1",
|
||||
"shelljs": "^0.7.8",
|
||||
"sinon": "^2.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -4,9 +4,9 @@
|
||||
|
||||
## ANDROID ##
|
||||
# Android SDK Build Tools revision
|
||||
export ANDROID_SDK_BUILD_TOOLS_REVISION=23.0.1
|
||||
export ANDROID_SDK_BUILD_TOOLS_REVISION=26.0.3
|
||||
# Android API Level we build with
|
||||
export ANDROID_SDK_BUILD_API_LEVEL="23"
|
||||
export ANDROID_SDK_BUILD_API_LEVEL="26"
|
||||
# Google APIs for Android level
|
||||
export ANDROID_GOOGLE_API_LEVEL="23"
|
||||
# Minimum Android API Level we target
|
||||
|
||||
@@ -2,7 +2,18 @@
|
||||
set -e
|
||||
|
||||
PLATFORM_NAME="${PLATFORM_NAME:-iphoneos}"
|
||||
CURRENT_ARCH="${CURRENT_ARCH:-armv7}"
|
||||
CURRENT_ARCH="${CURRENT_ARCH}"
|
||||
|
||||
if [ -z "$CURRENT_ARCH" ] || [ "$CURRENT_ARCH" == "undefined_arch" ]; then
|
||||
# Xcode 10 beta sets CURRENT_ARCH to "undefined_arch", this leads to incorrect linker arg.
|
||||
# it's better to rely on platform name as fallback because architecture differs between simulator and device
|
||||
|
||||
if [[ "$PLATFORM_NAME" == *"simulator"* ]]; then
|
||||
CURRENT_ARCH="x86_64"
|
||||
else
|
||||
CURRENT_ARCH="armv7"
|
||||
fi
|
||||
fi
|
||||
|
||||
export CC="$(xcrun -find -sdk $PLATFORM_NAME cc) -arch $CURRENT_ARCH -isysroot $(xcrun -sdk $PLATFORM_NAME --show-sdk-path)"
|
||||
export CXX="$CC"
|
||||
|
||||
Reference in New Issue
Block a user