Skip to content

Commit 133f1e3

Browse files
committed
Add WebView Flow types
1 parent 13ab5d6 commit 133f1e3

8 files changed

+627
-57
lines changed

.flowconfig

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
[ignore]
2+
; This flowconfig is forked by platform - the only difference between them is which suffix is ignored.
3+
.*/*[.]android.js
4+
;.*/*[.]ios.js
5+
6+
; Ignore templates for 'react-native init'
7+
.*/local-cli/templates/.*
8+
9+
; Ignore the Dangerfile
10+
node_modules/react-native/bots/dangerfile.js
11+
12+
; Ignore "BUCK" generated dirs
13+
node_modules/react-native/\.buckd/
14+
15+
; Ignore unexpected extra "@providesModule"
16+
.*/node_modules/.*/node_modules/fbjs/.*
17+
18+
; Ignore duplicate module providers
19+
; For RN Apps installed via npm, "Libraries" folder is inside
20+
; "node_modules/react-native" but in the source repo it is in the root
21+
.*/Libraries/react-native/React.js
22+
23+
; Ignore polyfills
24+
.*/Libraries/polyfills/.*
25+
26+
; Ignore metro
27+
.*/node_modules/metro/.*
28+
29+
; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow
30+
.*/node_modules/config-chain/test/*.
31+
32+
; These should not be required directly
33+
; require from fbjs/lib instead: require('fbjs/lib/invariant')
34+
.*/node_modules/invariant/.*
35+
.*/node_modules/warning/.*
36+
37+
[include]
38+
39+
[libs]
40+
node_modules/react-native/Libraries/react-native/react-native-interface.js
41+
node_modules/react-native/flow/
42+
node_modules/react-native/flow-github/
43+
44+
[lints]
45+
46+
[options]
47+
emoji=true
48+
49+
esproposal.optional_chaining=enable
50+
esproposal.nullish_coalescing=enable
51+
52+
module.system=haste
53+
module.system.haste.use_name_reducers=true
54+
# keep the following in sync with server/haste/hasteImpl.js
55+
# get basename
56+
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
57+
# strip .js or .js.flow suffix
58+
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
59+
# strip platform suffix
60+
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
61+
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
62+
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
63+
module.system.haste.paths.blacklist=.*/__tests__/.*
64+
module.system.haste.paths.blacklist=.*/__mocks__/.*
65+
module.system.haste.paths.whitelist=<PROJECT_ROOT>/js/.*
66+
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
67+
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
68+
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
69+
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
70+
; Surpress error `Duplicate module provider` until the slimmening is done
71+
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
72+
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WKWebView/*.
73+
74+
munge_underscores=true
75+
76+
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
77+
78+
suppress_type=$FlowIssue
79+
suppress_type=$FlowFixMe
80+
suppress_type=$FlowFixMeProps
81+
suppress_type=$FlowFixMeState
82+
83+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
84+
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
85+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
86+
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
87+
88+
[strict]

.flowconfig.android

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
[ignore]
2+
; This flowconfig is forked by platform - the only difference between them is which suffix is ignored.
3+
;.*/*[.]android.js
4+
.*/*[.]ios.js
5+
6+
; Ignore templates for 'react-native init'
7+
.*/local-cli/templates/.*
8+
9+
; Ignore the Dangerfile
10+
node_modules/react-native/bots/dangerfile.js
11+
12+
; Ignore "BUCK" generated dirs
13+
node_modules/react-native/\.buckd/
14+
15+
; Ignore unexpected extra "@providesModule"
16+
.*/node_modules/.*/node_modules/fbjs/.*
17+
18+
; Ignore duplicate module providers
19+
; For RN Apps installed via npm, "Libraries" folder is inside
20+
; "node_modules/react-native" but in the source repo it is in the root
21+
.*/Libraries/react-native/React.js
22+
23+
; Ignore polyfills
24+
.*/Libraries/polyfills/.*
25+
26+
; Ignore metro
27+
.*/node_modules/metro/.*
28+
29+
; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow
30+
.*/node_modules/config-chain/test/*.
31+
32+
; These should not be required directly
33+
; require from fbjs/lib instead: require('fbjs/lib/invariant')
34+
.*/node_modules/invariant/.*
35+
.*/node_modules/warning/.*
36+
37+
[include]
38+
39+
[libs]
40+
node_modules/react-native/Libraries/react-native/react-native-interface.js
41+
node_modules/react-native/flow/
42+
node_modules/react-native/flow-github/
43+
44+
[lints]
45+
46+
[options]
47+
emoji=true
48+
49+
esproposal.optional_chaining=enable
50+
esproposal.nullish_coalescing=enable
51+
52+
module.system=haste
53+
module.system.haste.use_name_reducers=true
54+
# keep the following in sync with server/haste/hasteImpl.js
55+
# get basename
56+
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
57+
# strip .js or .js.flow suffix
58+
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
59+
# strip platform suffix
60+
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
61+
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
62+
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
63+
module.system.haste.paths.blacklist=.*/__tests__/.*
64+
module.system.haste.paths.blacklist=.*/__mocks__/.*
65+
module.system.haste.paths.whitelist=<PROJECT_ROOT>/js/.*
66+
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
67+
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
68+
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
69+
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
70+
; Surpress error `Duplicate module provider` until the slimmening is done
71+
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
72+
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WKWebView/*.
73+
74+
munge_underscores=true
75+
76+
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
77+
78+
suppress_type=$FlowIssue
79+
suppress_type=$FlowFixMe
80+
suppress_type=$FlowFixMeProps
81+
suppress_type=$FlowFixMeState
82+
83+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
84+
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
85+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
86+
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
87+
88+
[strict]

js/WKWebView.ios.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,20 @@ import { requireNativeComponent } from 'react-native';
1515

1616
const RCTWKWebView = requireNativeComponent('RCTWKWebView');
1717

18-
class WKWebView extends React.Component {
19-
componentWillReceiveProps(nextProps) {
18+
type RCTWKWebViewProps = {
19+
allowsInlineMediaPlayback?: boolean,
20+
mediaPlaybackRequiresUserAction?: boolean,
21+
dataDetectorTypes?: boolean,
22+
};
23+
24+
class WKWebView extends React.Component<RCTWKWebViewProps> {
25+
componentWillReceiveProps(nextProps: RCTWKWebViewProps) {
2026
this.showRedboxOnPropChanges(nextProps, 'allowsInlineMediaPlayback');
2127
this.showRedboxOnPropChanges(nextProps, 'mediaPlaybackRequiresUserAction');
2228
this.showRedboxOnPropChanges(nextProps, 'dataDetectorTypes');
2329
}
2430

25-
showRedboxOnPropChanges(nextProps, propName) {
31+
showRedboxOnPropChanges(nextProps: RCTWKWebViewProps, propName: string) {
2632
if (this.props[propName] !== nextProps[propName]) {
2733
console.error(`Changes to property ${propName} do nothing after the initial render.`);
2834
}

js/WebView.android.js

+66-15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*
77
* @format
8+
* @flow
89
*/
910

1011
'use strict';
@@ -21,9 +22,16 @@ import {
2122
requireNativeComponent
2223
} from 'react-native';
2324

25+
import invariant from 'fbjs/lib/invariant';
2426
import keyMirror from 'fbjs/lib/keyMirror';
2527

2628
import WebViewShared from './WebViewShared';
29+
import type {
30+
WebViewErrorEvent,
31+
WebViewEvent,
32+
WebViewSharedProps,
33+
WebViewSource,
34+
} from './WebViewTypes';
2735

2836
const resolveAssetSource = Image.resolveAssetSource;
2937

@@ -41,10 +49,52 @@ const defaultRenderLoading = () => (
4149
</View>
4250
);
4351

52+
type State = {|
53+
viewState: WebViewState,
54+
lastErrorEvent: ?WebViewErrorEvent,
55+
startInLoadingState: boolean,
56+
|};
57+
58+
type WebViewPropsAndroid = $ReadOnly<{|
59+
...WebViewSharedProps,
60+
onNavigationStateChange?: (event: WebViewEvent) => any,
61+
onContentSizeChange?: (event: WebViewEvent) => any,
62+
63+
/**
64+
* Sets whether Geolocation is enabled. The default is false.
65+
* @platform android
66+
*/
67+
geolocationEnabled?: ?boolean,
68+
69+
/**
70+
* Boolean that sets whether JavaScript running in the context of a file
71+
* scheme URL should be allowed to access content from any origin.
72+
* Including accessing content from other file scheme URLs
73+
* @platform android
74+
*/
75+
allowUniversalAccessFromFileURLs?: ?boolean,
76+
77+
/**
78+
* Used on Android only, controls whether form autocomplete data should be saved
79+
* @platform android
80+
*/
81+
saveFormDataDisabled?: ?boolean,
82+
83+
/*
84+
* Used on Android only, controls whether the given list of URL prefixes should
85+
* make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a
86+
* default activity intent for those URL instead of loading it within the webview.
87+
* Use this to list URLs that WebView cannot handle, e.g. a PDF url.
88+
* @platform android
89+
*/
90+
urlPrefixesForDefaultIntent?: $ReadOnlyArray<string>,
91+
92+
|}>;
93+
4494
/**
4595
* Renders a native WebView.
4696
*/
47-
class WebView extends React.Component {
97+
class WebView extends React.Component<WebViewPropsAndroid, State> {
4898
static defaultProps = {
4999
javaScriptEnabled: true,
50100
thirdPartyCookiesEnabled: true,
@@ -55,7 +105,7 @@ class WebView extends React.Component {
55105

56106
state = {
57107
viewState: WebViewState.IDLE,
58-
lastErrorEvent: null,
108+
lastErrorEvent: (null: ?WebViewErrorEvent),
59109
startInLoadingState: true,
60110
};
61111

@@ -72,6 +122,7 @@ class WebView extends React.Component {
72122
otherView = (this.props.renderLoading || defaultRenderLoading)();
73123
} else if (this.state.viewState === WebViewState.ERROR) {
74124
const errorEvent = this.state.lastErrorEvent;
125+
invariant(errorEvent != null, 'lastErrorEvent expected to be non-null');
75126
otherView =
76127
this.props.renderError &&
77128
this.props.renderError(
@@ -81,7 +132,7 @@ class WebView extends React.Component {
81132
);
82133
} else if (this.state.viewState !== WebViewState.IDLE) {
83134
console.error(
84-
'RCTWebView invalid state encountered: ' + this.state.loading,
135+
'RCTWebView invalid state encountered: ' + this.state.viewState,
85136
);
86137
}
87138

@@ -94,11 +145,11 @@ class WebView extends React.Component {
94145
webViewStyles.push(styles.hidden);
95146
}
96147

97-
const source = this.props.source || {};
98-
if (this.props.html) {
99-
source.html = this.props.html;
100-
} else if (this.props.url) {
101-
source.uri = this.props.url;
148+
let source = this.props.source || ({}: WebViewSource);
149+
if (!this.props.source && this.props.html) {
150+
source = { html: this.props.html };
151+
} else if (!this.props.source && this.props.url) {
152+
source = { uri: this.props.url };
102153
}
103154

104155
if (source.method === 'POST' && source.headers) {
@@ -198,7 +249,7 @@ class WebView extends React.Component {
198249
);
199250
};
200251

201-
postMessage = data => {
252+
postMessage = (data: string) => {
202253
UIManager.dispatchViewManagerCommand(
203254
this.getWebViewHandle(),
204255
UIManager.RCTWebView.Commands.postMessage,
@@ -212,7 +263,7 @@ class WebView extends React.Component {
212263
* on pages with a Content Security Policy that disallows eval(). If you need that
213264
* functionality, look into postMessage/onMessage.
214265
*/
215-
injectJavaScript = data => {
266+
injectJavaScript = (data: string) => {
216267
UIManager.dispatchViewManagerCommand(
217268
this.getWebViewHandle(),
218269
UIManager.RCTWebView.Commands.injectJavaScript,
@@ -224,7 +275,7 @@ class WebView extends React.Component {
224275
* We return an event with a bunch of fields including:
225276
* url, title, loading, canGoBack, canGoForward
226277
*/
227-
updateNavigationState = event => {
278+
updateNavigationState = (event: WebViewEvent) => {
228279
if (this.props.onNavigationStateChange) {
229280
this.props.onNavigationStateChange(event.nativeEvent);
230281
}
@@ -234,13 +285,13 @@ class WebView extends React.Component {
234285
return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
235286
};
236287

237-
onLoadingStart = event => {
288+
onLoadingStart = (event: WebViewEvent) => {
238289
const onLoadStart = this.props.onLoadStart;
239290
onLoadStart && onLoadStart(event);
240291
this.updateNavigationState(event);
241292
};
242293

243-
onLoadingError = event => {
294+
onLoadingError = (event: WebViewEvent) => {
244295
event.persist(); // persist this event because we need to store it
245296
const { onError, onLoadEnd } = this.props;
246297
onError && onError(event);
@@ -253,7 +304,7 @@ class WebView extends React.Component {
253304
});
254305
};
255306

256-
onLoadingFinish = event => {
307+
onLoadingFinish = (event: WebViewEvent) => {
257308
const { onLoad, onLoadEnd } = this.props;
258309
onLoad && onLoad(event);
259310
onLoadEnd && onLoadEnd(event);
@@ -263,7 +314,7 @@ class WebView extends React.Component {
263314
this.updateNavigationState(event);
264315
};
265316

266-
onMessage = (event) => {
317+
onMessage = (event: WebViewEvent) => {
267318
const { onMessage } = this.props;
268319
onMessage && onMessage(event);
269320
};

0 commit comments

Comments
 (0)