Compare commits

...
Author SHA1 Message Date
Hector Ramos e07bbbe24d [0.56.0-rc.1] Bump version numbers 2018-06-13 13:03:08 -07:00
DulmandakhandHector Ramos 579eb3f9fe use android build-tools 26.0.2 and set compileSdk to 26 (#19662)
Summary:
Android Target API Level 26 will be required starting from August 2018, it's so soon 😄.Read https://android-developers.googleblog.com/2017/12/improving-app-security-and-performance.html

This PR uses android build tools 26.0.2, support library 26.1.0 (with android lifecycle) and setting compileSdkVersion to 26, but leaving minSdkVersion and targetSdkVersion intact, which will make targeting 26 easy.

Circle CI: https://circleci.com/gh/dulmandakh/react-native/209

Everything will build and work just fine.

[ANDROID] [ENHANCEMENT] [TOOLS] - Use android build-tools 26.0.2 and set compileSdk to 26, and use support library version 26.1.0.
Closes https://github.com/facebook/react-native/pull/19662

Differential Revision: D8398855

Pulled By: hramos

fbshipit-source-id: a4066eb04cb5f947efe1f3202b638c1092b79aae
2018-06-13 12:46:58 -07:00
Héctor Ramos a59e19240a Bump soloader to 0.5.0 (#19676)
Summary:
Closes https://github.com/facebook/react-native/pull/19676

0.5.0 adds compatibility for Android P.

Reviewed By: mdvacca

Differential Revision: D8379610

fbshipit-source-id: cbef6c2e5e82e2d9e17756b00d210fecb04e8a40
2018-06-13 10:07:45 -07:00
Tim YungandHector Ramos 204207f4c9 YellowBox: Apply SafeAreaView in Header
Summary: Uses `SafeAreaView` in the YellowBox inspector header so that it does not collide with the status bar.

Reviewed By: sahrens

Differential Revision: D8374861

fbshipit-source-id: e67358ac9268db8291cacf79df402f3bd5a2173d
2018-06-13 10:03:08 -07:00
Tim YungandHector Ramos 8bdef5d2c0 RN: A wild YellowBox has appeared!
Summary:
Replaces the existing `YellowBox` with a modern one.

Here are the notable changes:

- Sort warnings by recency (with most recent on top).
- Group warnings by format string if present.
- Present stack traces similar to RedBox.
- Show status of loading source maps.
- Support inspecting each occurrence of a warning.
- Fixed a bunch of edge cases and race conditions.

Reviewed By: TheSavior

Differential Revision: D8345180

fbshipit-source-id: b9e10d526b262c3985bbea639ba2ea0e7cad5081
2018-06-13 10:00:16 -07:00
Tim YungandHector Ramos 71f56fc2e3 Jest: Upgrade Flow Definition in RN + Metro
Reviewed By: sahrens

Differential Revision: D8371076

fbshipit-source-id: 12d03c545ca190a5fda1ff319e5ea906173d2241
2018-06-13 09:58:43 -07:00
Hector Ramos d642e60291 [0.56.0-rc] Bump version numbers 2018-06-11 09:46:55 -07:00
42 changed files with 2976 additions and 663 deletions
+2 -1
View File
@@ -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
+4 -4
View File
@@ -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,
prerelease: 'rc.1',
};
+7
View File
@@ -127,4 +127,11 @@ const styles = StyleSheet.create({
},
});
if (__DEV__) {
if (!global.__RCTProfileIsProfiling) {
const YellowBox = require('YellowBox');
YellowBox.install();
}
}
module.exports = AppContainer;
-554
View File
@@ -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;
+4 -4
View File
@@ -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|}
+3
View File
@@ -20,6 +20,7 @@ const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationI
* - Less chance of typos.
*/
const UTFSequence = deepFreezeAndThrowOnMutationInDev({
BOM: '\ufeff', // byte order mark
BULLET: '\u2022', // bullet: &#8226;
BULLET_SP: '\u00A0\u2022\u00A0', // &nbsp;&#8226;&nbsp;
MIDDOT: '\u00B7', // normal middle dot: &middot;
@@ -31,6 +32,8 @@ const UTFSequence = deepFreezeAndThrowOnMutationInDev({
NDASH_SP: '\u00A0\u2013\u00A0', // &nbsp;&ndash;&nbsp;
NBSP: '\u00A0', // non-breaking space: &nbsp;
PIZZA: '\uD83C\uDF55',
TRIANGLE_LEFT: '\u25c0', // black left-pointing triangle
TRIANGLE_RIGHT: '\u25b6', // black right-pointing triangle
});
module.exports = UTFSequence;
@@ -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);
});
});
+53
View File
@@ -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;
+142
View File
@@ -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;
+100
View File
@@ -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;
+31
View File
@@ -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;
+132
View File
@@ -0,0 +1,132 @@
/**
* 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 Platform = require('Platform');
const RCTLog = require('RCTLog');
const React = require('React');
const YellowBoxList = require('YellowBoxList');
const YellowBoxRegistry = require('YellowBoxRegistry');
import type {Category} from 'YellowBoxCategory';
import type {Registry, Subscription} from 'YellowBoxRegistry';
type Props = $ReadOnly<{||}>;
type State = {|
registry: ?Registry,
|};
const {error, warn} = console;
/**
* 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.
*/
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() {
// 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();
}
}
function registerWarning(...args): void {
YellowBoxRegistry.add({args, framesToPop: 2});
}
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();
});
});
+3 -3
View File
@@ -84,8 +84,8 @@ def enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
compileSdkVersion 26
buildToolsVersion "26.0.2"
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')
+2 -2
View File
@@ -21,9 +21,9 @@ static void __makeVersion()
{
__rnVersion = @{
RCTVersionMajor: @(0),
RCTVersionMinor: @(0),
RCTVersionMinor: @(56),
RCTVersionPatch: @(0),
RCTVersionPrerelease: [NSNull null],
RCTVersionPrerelease: @"rc.1",
};
}
+4 -4
View File
@@ -245,8 +245,8 @@ task packageReactNdkLibsForBuck(dependsOn: packageReactNdkLibs, type: Copy) {
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
compileSdkVersion 26
buildToolsVersion "26.0.2"
defaultConfig {
minSdkVersion 16
@@ -295,11 +295,11 @@ 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'
compile 'com.facebook.soloader:soloader:0.3.0'
compile 'com.facebook.soloader:soloader:0.5.0'
compile 'com.google.code.findbugs:jsr305:3.0.0'
compile 'com.squareup.okhttp3:okhttp:3.10.0'
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.10.0'
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0-master
VERSION_NAME=0.56.0-rc.1
GROUP=com.facebook.react
POM_NAME=ReactNative
+2 -2
View File
@@ -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(
@@ -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);
"prerelease", "rc.1");
}
@@ -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
View File
@@ -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",
)
+529 -65
View File
@@ -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,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:+"
}
@@ -94,8 +94,8 @@ def enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
compileSdkVersion 26
buildToolsVersion "26.0.2"
defaultConfig {
applicationId "com.helloworld"
@@ -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:26.1.0"
compile "com.facebook.react:react-native:+" // From node_modules
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.56.0-rc.1",
"description": "A framework for building native apps using React",
"license": "MIT",
"repository": {
@@ -221,4 +221,4 @@
"shelljs": "^0.7.8",
"sinon": "^2.2.0"
}
}
}
+2 -2
View File
@@ -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.2
# 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