Quellcode durchsuchen

Merge pull request #32 from empyrical/flow-check

Add WebView Flow types
Thibault Malbranche vor 6 Jahren
Ursprung
Commit
3f2dd70b94
Es ist kein Benutzerkonto mit dieser Commiter-Email verbunden
9 geänderte Dateien mit 680 neuen und 55 gelöschten Zeilen
  1. 88
    0
      .flowconfig
  2. 88
    0
      .flowconfig.android
  3. 7
    1
      README.md
  4. 13
    3
      js/WKWebView.ios.js
  5. 33
    14
      js/WebView.android.js
  6. 34
    34
      js/WebView.ios.js
  7. 3
    3
      js/WebViewShared.js
  8. 406
    0
      js/WebViewTypes.js
  9. 8
    0
      package.json

+ 88
- 0
.flowconfig Datei anzeigen

@@ -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]

+ 88
- 0
.flowconfig.android Datei anzeigen

@@ -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]

+ 7
- 1
README.md Datei anzeigen

@@ -51,8 +51,14 @@ Simply install React Native WebView and then use it in place of the core WebView
51 51
 
52 52
 ### Contributor Notes
53 53
 
54
-* I've removed all PropTypes for now. Instead, we'll be moving toward Flow or TypeScript at a later date
54
+* I've removed all PropTypes for now. Instead, we'll be using Flow types. TypeScript types will be added at a later date.
55 55
 * UIWebView is not tested fully and you will encounter some yellow warning boxes. Since it is deprecated, we don't intend to put a lot of time into supporting it, but feel free to submit PRs if you have a special use case. Note that you will need to specify `useWebKit={false}` to use UIWebView
56
+* After pulling this repo and installing all dependencies, you can run flow on iOS and Android-specific files using the commands:
57
+  * `yarn flow` or `npm run flow` for iOS
58
+  * `yarn flow-android` or `npm run flow-android` for Android
59
+* If you want to add another React Native platform to this repository, you will need to create another `.flowconfig` for it. If your platform is `example`, copy the main flowconfig and rename it to `.flowconfig.example`. Then edit the config to ignore other platforms, and add `.*/*[.]example.js` to the ignore lists of the other platforms. Then add an entry to `package.json` like this:
60
+  * `    "flow-example": "flow check --flowconfig-name .flowconfig.example"`
61
+* Currently you need to install React Native 0.57 to be able to test these types - `flow check` will not pass aganst 0.56.
56 62
 
57 63
 ## Maintainers
58 64
 

+ 13
- 3
js/WKWebView.ios.js Datei anzeigen

@@ -13,16 +13,26 @@ import React from 'react';
13 13
 
14 14
 import { requireNativeComponent } from 'react-native';
15 15
 
16
+import type {DataDetectorTypes} from './WebViewTypes';
17
+
16 18
 const RCTWKWebView = requireNativeComponent('RCTWKWebView');
17 19
 
18
-class WKWebView extends React.Component {
19
-  componentWillReceiveProps(nextProps) {
20
+type RCTWKWebViewProps = $ReadOnly<{|
21
+  allowsInlineMediaPlayback?: ?boolean,
22
+  mediaPlaybackRequiresUserAction?: ?boolean,
23
+  dataDetectorTypes?:
24
+    | ?DataDetectorTypes
25
+    | $ReadOnlyArray<DataDetectorTypes>,
26
+|}>;
27
+
28
+class WKWebView extends React.Component<RCTWKWebViewProps> {
29
+  componentWillReceiveProps(nextProps: RCTWKWebViewProps) {
20 30
     this.showRedboxOnPropChanges(nextProps, 'allowsInlineMediaPlayback');
21 31
     this.showRedboxOnPropChanges(nextProps, 'mediaPlaybackRequiresUserAction');
22 32
     this.showRedboxOnPropChanges(nextProps, 'dataDetectorTypes');
23 33
   }
24 34
 
25
-  showRedboxOnPropChanges(nextProps, propName) {
35
+  showRedboxOnPropChanges(nextProps: RCTWKWebViewProps, propName: string) {
26 36
     if (this.props[propName] !== nextProps[propName]) {
27 37
       console.error(`Changes to property ${propName} do nothing after the initial render.`);
28 38
     }

+ 33
- 14
js/WebView.android.js Datei anzeigen

@@ -5,6 +5,7 @@
5 5
  * LICENSE file in the root directory of this source tree.
6 6
  *
7 7
  * @format
8
+ * @flow
8 9
  */
9 10
 
10 11
 'use strict';
@@ -21,9 +22,20 @@ import {
21 22
   requireNativeComponent
22 23
 } from 'react-native';
23 24
 
25
+import invariant from 'fbjs/lib/invariant';
24 26
 import keyMirror from 'fbjs/lib/keyMirror';
25 27
 
26 28
 import WebViewShared from './WebViewShared';
29
+import type {
30
+  WebViewEvent,
31
+  WebViewError,
32
+  WebViewErrorEvent,
33
+  WebViewMessageEvent,
34
+  WebViewNavigation,
35
+  WebViewNavigationEvent,
36
+  WebViewSharedProps,
37
+  WebViewSource,
38
+} from './WebViewTypes';
27 39
 
28 40
 const resolveAssetSource = Image.resolveAssetSource;
29 41
 
@@ -41,10 +53,16 @@ const defaultRenderLoading = () => (
41 53
   </View>
42 54
 );
43 55
 
56
+type State = {|
57
+  viewState: WebViewState,
58
+  lastErrorEvent: ?WebViewError,
59
+  startInLoadingState: boolean,
60
+|};
61
+
44 62
 /**
45 63
  * Renders a native WebView.
46 64
  */
47
-class WebView extends React.Component {
65
+class WebView extends React.Component<WebViewSharedProps, State> {
48 66
   static defaultProps = {
49 67
     javaScriptEnabled: true,
50 68
     thirdPartyCookiesEnabled: true,
@@ -72,6 +90,7 @@ class WebView extends React.Component {
72 90
       otherView = (this.props.renderLoading || defaultRenderLoading)();
73 91
     } else if (this.state.viewState === WebViewState.ERROR) {
74 92
       const errorEvent = this.state.lastErrorEvent;
93
+      invariant(errorEvent != null, 'lastErrorEvent expected to be non-null');
75 94
       otherView =
76 95
         this.props.renderError &&
77 96
         this.props.renderError(
@@ -81,7 +100,7 @@ class WebView extends React.Component {
81 100
         );
82 101
     } else if (this.state.viewState !== WebViewState.IDLE) {
83 102
       console.error(
84
-        'RCTWebView invalid state encountered: ' + this.state.loading,
103
+        'RCTWebView invalid state encountered: ' + this.state.viewState,
85 104
       );
86 105
     }
87 106
 
@@ -94,11 +113,11 @@ class WebView extends React.Component {
94 113
       webViewStyles.push(styles.hidden);
95 114
     }
96 115
 
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;
116
+    let source: WebViewSource = this.props.source || {};
117
+    if (!this.props.source && this.props.html) {
118
+      source = { html: this.props.html };
119
+    } else if (!this.props.source && this.props.url) {
120
+      source = { uri: this.props.url };
102 121
     }
103 122
 
104 123
     if (source.method === 'POST' && source.headers) {
@@ -198,7 +217,7 @@ class WebView extends React.Component {
198 217
     );
199 218
   };
200 219
 
201
-  postMessage = data => {
220
+  postMessage = (data: string) => {
202 221
     UIManager.dispatchViewManagerCommand(
203 222
       this.getWebViewHandle(),
204 223
       UIManager.RCTWebView.Commands.postMessage,
@@ -212,7 +231,7 @@ class WebView extends React.Component {
212 231
    * on pages with a Content Security Policy that disallows eval(). If you need that
213 232
    * functionality, look into postMessage/onMessage.
214 233
    */
215
-  injectJavaScript = data => {
234
+  injectJavaScript = (data: string) => {
216 235
     UIManager.dispatchViewManagerCommand(
217 236
       this.getWebViewHandle(),
218 237
       UIManager.RCTWebView.Commands.injectJavaScript,
@@ -224,7 +243,7 @@ class WebView extends React.Component {
224 243
    * We return an event with a bunch of fields including:
225 244
    *  url, title, loading, canGoBack, canGoForward
226 245
    */
227
-  updateNavigationState = event => {
246
+  updateNavigationState = (event: WebViewNavigationEvent) => {
228 247
     if (this.props.onNavigationStateChange) {
229 248
       this.props.onNavigationStateChange(event.nativeEvent);
230 249
     }
@@ -234,13 +253,13 @@ class WebView extends React.Component {
234 253
     return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
235 254
   };
236 255
 
237
-  onLoadingStart = event => {
256
+  onLoadingStart = (event: WebViewNavigationEvent) => {
238 257
     const onLoadStart = this.props.onLoadStart;
239 258
     onLoadStart && onLoadStart(event);
240 259
     this.updateNavigationState(event);
241 260
   };
242 261
 
243
-  onLoadingError = event => {
262
+  onLoadingError = (event: WebViewErrorEvent) => {
244 263
     event.persist(); // persist this event because we need to store it
245 264
     const { onError, onLoadEnd } = this.props;
246 265
     onError && onError(event);
@@ -253,7 +272,7 @@ class WebView extends React.Component {
253 272
     });
254 273
   };
255 274
 
256
-  onLoadingFinish = event => {
275
+  onLoadingFinish = (event: WebViewNavigationEvent) => {
257 276
     const { onLoad, onLoadEnd } = this.props;
258 277
     onLoad && onLoad(event);
259 278
     onLoadEnd && onLoadEnd(event);
@@ -263,7 +282,7 @@ class WebView extends React.Component {
263 282
     this.updateNavigationState(event);
264 283
   };
265 284
 
266
-  onMessage = (event) => {
285
+  onMessage = (event: WebViewMessageEvent) => {
267 286
     const { onMessage } = this.props;
268 287
     onMessage && onMessage(event);
269 288
   };

+ 34
- 34
js/WebView.ios.js Datei anzeigen

@@ -5,7 +5,7 @@
5 5
  * LICENSE file in the root directory of this source tree.
6 6
  *
7 7
  * @format
8
- * @noflow
8
+ * @flow
9 9
  */
10 10
 
11 11
 'use strict';
@@ -29,6 +29,15 @@ import invariant from 'fbjs/lib/invariant';
29 29
 import keyMirror from 'fbjs/lib/keyMirror';
30 30
 
31 31
 import WebViewShared from './WebViewShared';
32
+import type {
33
+  WebViewEvent,
34
+  WebViewError,
35
+  WebViewErrorEvent,
36
+  WebViewMessageEvent,
37
+  WebViewNavigationEvent,
38
+  WebViewSharedProps,
39
+  WebViewSource,
40
+} from './WebViewTypes';
32 41
 
33 42
 const resolveAssetSource = Image.resolveAssetSource;
34 43
 
@@ -65,13 +74,11 @@ const NavigationType = keyMirror({
65 74
 
66 75
 const JSNavigationScheme = 'react-js-navigation';
67 76
 
68
-// type ErrorEvent = {
69
-//   domain: any,
70
-//   code: any,
71
-//   description: any,
72
-// };
73
-
74
-// type Event = Object;
77
+type State = {|
78
+  viewState: WebViewState,
79
+  lastErrorEvent: ?WebViewError,
80
+  startInLoadingState: boolean,
81
+|};
75 82
 
76 83
 const DataDetectorTypes = [
77 84
   'phoneNumber',
@@ -121,7 +128,7 @@ const defaultRenderError = (errorDomain, errorCode, errorDesc) => (
121 128
  * You can use this component to navigate back and forth in the web view's
122 129
  * history and configure various properties for the web content.
123 130
  */
124
-class WebView extends React.Component {
131
+class WebView extends React.Component<WebViewSharedProps, State> {
125 132
   static JSNavigationScheme = JSNavigationScheme;
126 133
   static NavigationType = NavigationType;
127 134
 
@@ -174,7 +181,7 @@ class WebView extends React.Component {
174 181
       );
175 182
     } else if (this.state.viewState !== WebViewState.IDLE) {
176 183
       console.error(
177
-        'RCTWebView invalid state encountered: ' + this.state.loading,
184
+        'RCTWebView invalid state encountered: ' + this.state.viewState,
178 185
       );
179 186
     }
180 187
 
@@ -217,6 +224,7 @@ class WebView extends React.Component {
217 224
           shouldStart &&
218 225
           this.props.onShouldStartLoadWithRequest(event.nativeEvent);
219 226
       }
227
+      invariant(viewManager != null, 'viewManager expected to be non-null');
220 228
       viewManager.startLoadWithResult(
221 229
         !!shouldStart,
222 230
         event.nativeEvent.lockIdentifier,
@@ -227,11 +235,11 @@ class WebView extends React.Component {
227 235
       this.props.decelerationRate,
228 236
     );
229 237
 
230
-    const source = this.props.source || {};
231
-    if (this.props.html) {
232
-      source.html = this.props.html;
233
-    } else if (this.props.url) {
234
-      source.uri = this.props.url;
238
+    let source: WebViewSource = this.props.source || {};
239
+    if (!this.props.source && this.props.html) {
240
+      source = { html: this.props.html };
241
+    } else if (!this.props.source && this.props.url) {
242
+      source = { uri: this.props.url };
235 243
     }
236 244
 
237 245
     const messagingEnabled = typeof this.props.onMessage === 'function';
@@ -345,7 +353,7 @@ class WebView extends React.Component {
345 353
    * document.addEventListener('message', e => { document.title = e.data; });
346 354
    * ```
347 355
    */
348
-  postMessage = data => {
356
+  postMessage = (data: string) => {
349 357
     UIManager.dispatchViewManagerCommand(
350 358
       this.getWebViewHandle(),
351 359
       this._getCommands().postMessage,
@@ -359,7 +367,7 @@ class WebView extends React.Component {
359 367
    * on pages with a Content Security Policy that disallows eval(). If you need that
360 368
    * functionality, look into postMessage/onMessage.
361 369
    */
362
-  injectJavaScript = data => {
370
+  injectJavaScript = (data: string) => {
363 371
     UIManager.dispatchViewManagerCommand(
364 372
       this.getWebViewHandle(),
365 373
       this._getCommands().injectJavaScript,
@@ -371,7 +379,7 @@ class WebView extends React.Component {
371 379
    * We return an event with a bunch of fields including:
372 380
    *  url, title, loading, canGoBack, canGoForward
373 381
    */
374
-  _updateNavigationState = (event) => {
382
+  _updateNavigationState = (event: WebViewNavigationEvent) => {
375 383
     if (this.props.onNavigationStateChange) {
376 384
       this.props.onNavigationStateChange(event.nativeEvent);
377 385
     }
@@ -384,13 +392,13 @@ class WebView extends React.Component {
384 392
     return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
385 393
   };
386 394
 
387
-  _onLoadingStart = (event) => {
395
+  _onLoadingStart = (event: WebViewNavigationEvent) => {
388 396
     const onLoadStart = this.props.onLoadStart;
389 397
     onLoadStart && onLoadStart(event);
390 398
     this._updateNavigationState(event);
391 399
   };
392 400
 
393
-  _onLoadingError = (event) => {
401
+  _onLoadingError = (event: WebViewErrorEvent) => {
394 402
     event.persist(); // persist this event because we need to store it
395 403
     const { onError, onLoadEnd } = this.props;
396 404
     onError && onError(event);
@@ -403,7 +411,7 @@ class WebView extends React.Component {
403 411
     });
404 412
   };
405 413
 
406
-  _onLoadingFinish = (event) => {
414
+  _onLoadingFinish = (event: WebViewNavigationEvent) => {
407 415
     const { onLoad, onLoadEnd } = this.props;
408 416
     onLoad && onLoad(event);
409 417
     onLoadEnd && onLoadEnd(event);
@@ -413,12 +421,12 @@ class WebView extends React.Component {
413 421
     this._updateNavigationState(event);
414 422
   };
415 423
 
416
-  _onMessage = (event) => {
424
+  _onMessage = (event: WebViewMessageEvent) => {
417 425
     const { onMessage } = this.props;
418 426
     onMessage && onMessage(event);
419 427
   };
420 428
 
421
-  componentDidUpdate(prevProps) {
429
+  componentDidUpdate(prevProps: WebViewSharedProps) {
422 430
     if (!(prevProps.useWebKit && this.props.useWebKit)) {
423 431
       return;
424 432
     }
@@ -434,7 +442,7 @@ class WebView extends React.Component {
434 442
     }
435 443
   }
436 444
 
437
-  _showRedboxOnPropChanges(prevProps, propName) {
445
+  _showRedboxOnPropChanges(prevProps, propName: string) {
438 446
     if (this.props[propName] !== prevProps[propName]) {
439 447
       console.error(
440 448
         `Changes to property ${propName} do nothing after the initial render.`,
@@ -443,16 +451,8 @@ class WebView extends React.Component {
443 451
   }
444 452
 }
445 453
 
446
-const RCTWebView = requireNativeComponent(
447
-  'RCTWebView',
448
-  WebView,
449
-  WebView.extraNativeComponentConfig,
450
-);
451
-const RCTWKWebView = requireNativeComponent(
452
-  'RCTWKWebView',
453
-  WebView,
454
-  WebView.extraNativeComponentConfig,
455
-);
454
+const RCTWebView = requireNativeComponent('RCTWebView');
455
+const RCTWKWebView = requireNativeComponent('RCTWKWebView');
456 456
 
457 457
 const styles = StyleSheet.create({
458 458
   container: {

+ 3
- 3
js/WebViewShared.js Datei anzeigen

@@ -14,11 +14,11 @@ const escapeStringRegexp = require('escape-string-regexp');
14 14
 
15 15
 const WebViewShared = {
16 16
   defaultOriginWhitelist: ['http://*', 'https://*'],
17
-  extractOrigin: (url) => {
17
+  extractOrigin: (url: string) => {
18 18
     const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url);
19
-    return result === null ? null : result[0];
19
+    return result === null ? '' : result[0];
20 20
   },
21
-  originWhitelistToRegex: (originWhitelist) => {
21
+  originWhitelistToRegex: (originWhitelist: string) => {
22 22
     return escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
23 23
   },
24 24
 };

+ 406
- 0
js/WebViewTypes.js Datei anzeigen

@@ -0,0 +1,406 @@
1
+/**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @flow
9
+ */
10
+
11
+'use strict';
12
+
13
+import type {Node, Element, ComponentType} from 'react';
14
+
15
+import type {SyntheticEvent} from 'CoreEventTypes';
16
+import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
17
+import type {ViewStyleProp} from 'StyleSheet';
18
+import type {ViewProps} from 'ViewPropTypes';
19
+
20
+export type WebViewNativeEvent = $ReadOnly<{|
21
+  url: string,
22
+  loading: boolean,
23
+  title: string,
24
+  canGoBack: boolean,
25
+  canGoForward: boolean,
26
+|}>;
27
+
28
+export type WebViewNavigation = $ReadOnly<{|
29
+  ...WebViewNativeEvent,
30
+  navigationType:
31
+    | 'click'
32
+    | 'formsubmit'
33
+    | 'backforward'
34
+    | 'reload'
35
+    | 'formresubmit'
36
+    | 'other',
37
+|}>;
38
+
39
+export type WebViewMessage = $ReadOnly<{|
40
+  ...WebViewNativeEvent,
41
+  data: string,
42
+|}>;
43
+
44
+export type WebViewError = $ReadOnly<{|
45
+  ...WebViewNativeEvent,
46
+  /**
47
+   * `domain` is only used on iOS
48
+   */
49
+  domain: ?string,
50
+  code: number,
51
+  description: string,
52
+|}>;
53
+
54
+export type WebViewEvent = SyntheticEvent<WebViewNativeEvent>;
55
+
56
+export type WebViewNavigationEvent = SyntheticEvent<WebViewNavigation>;
57
+
58
+export type WebViewMessageEvent = SyntheticEvent<WebViewMessage>;
59
+
60
+export type WebViewErrorEvent = SyntheticEvent<WebViewError>;
61
+
62
+export type DataDetectorTypes =
63
+  | 'phoneNumber'
64
+  | 'link'
65
+  | 'address'
66
+  | 'calendarEvent'
67
+  | 'trackingNumber'
68
+  | 'flightNumber'
69
+  | 'lookupSuggestion'
70
+  | 'none'
71
+  | 'all';
72
+
73
+export type WebViewSourceUri = $ReadOnly<{|
74
+  /**
75
+   * The URI to load in the `WebView`. Can be a local or remote file.
76
+   */
77
+  uri?: ?string,
78
+
79
+  /**
80
+   * The HTTP Method to use. Defaults to GET if not specified.
81
+   * NOTE: On Android, only GET and POST are supported.
82
+   */
83
+  method?: string,
84
+
85
+  /**
86
+   * Additional HTTP headers to send with the request.
87
+   * NOTE: On Android, this can only be used with GET requests.
88
+   */
89
+  headers?: Object,
90
+
91
+  /**
92
+   * The HTTP body to send with the request. This must be a valid
93
+   * UTF-8 string, and will be sent exactly as specified, with no
94
+   * additional encoding (e.g. URL-escaping or base64) applied.
95
+   * NOTE: On Android, this can only be used with POST requests.
96
+   */
97
+  body?: string,
98
+|}>;
99
+
100
+export type WebViewSourceHtml = $ReadOnly<{|
101
+  /**
102
+   * A static HTML page to display in the WebView.
103
+   */
104
+  html?: ?string,
105
+  /**
106
+   * The base URL to be used for any relative links in the HTML.
107
+   */
108
+  baseUrl?: ?string,
109
+|}>;
110
+
111
+export type WebViewSource = WebViewSourceUri | WebViewSourceHtml;
112
+
113
+export type WebViewNativeConfig = $ReadOnly<{|
114
+  /*
115
+   * The native component used to render the WebView.
116
+   */
117
+  component?: ComponentType<WebViewSharedProps>,
118
+  /*
119
+   * Set props directly on the native component WebView. Enables custom props which the
120
+   * original WebView doesn't pass through.
121
+   */
122
+  props?: ?Object,
123
+  /*
124
+   * Set the ViewManager to use for communication with the native side.
125
+   * @platform ios
126
+   */
127
+  viewManager?: ?Object,
128
+|}>;
129
+
130
+export type IOSWebViewProps = $ReadOnly<{|
131
+  /**
132
+   * If true, use WKWebView instead of UIWebView.
133
+   * @platform ios
134
+   */
135
+  useWebKit?: ?boolean,
136
+
137
+  /**
138
+   * Boolean value that determines whether the web view bounces
139
+   * when it reaches the edge of the content. The default value is `true`.
140
+   * @platform ios
141
+   */
142
+  bounces?: ?boolean,
143
+
144
+  /**
145
+   * A floating-point number that determines how quickly the scroll view
146
+   * decelerates after the user lifts their finger. You may also use the
147
+   * string shortcuts `"normal"` and `"fast"` which match the underlying iOS
148
+   * settings for `UIScrollViewDecelerationRateNormal` and
149
+   * `UIScrollViewDecelerationRateFast` respectively:
150
+   *
151
+   *   - normal: 0.998
152
+   *   - fast: 0.99 (the default for iOS web view)
153
+   * @platform ios
154
+   */
155
+  decelerationRate?: ?('fast' | 'normal' | number),
156
+
157
+  /**
158
+   * Boolean value that determines whether scrolling is enabled in the
159
+   * `WebView`. The default value is `true`.
160
+   * @platform ios
161
+   */
162
+  scrollEnabled?: ?boolean,
163
+
164
+  /**
165
+   * The amount by which the web view content is inset from the edges of
166
+   * the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}.
167
+   * @platform ios
168
+   */
169
+  contentInset?: ?EdgeInsetsProp,
170
+
171
+  /**
172
+   * Determines the types of data converted to clickable URLs in the web view's content.
173
+   * By default only phone numbers are detected.
174
+   *
175
+   * You can provide one type or an array of many types.
176
+   *
177
+   * Possible values for `dataDetectorTypes` are:
178
+   *
179
+   * - `'phoneNumber'`
180
+   * - `'link'`
181
+   * - `'address'`
182
+   * - `'calendarEvent'`
183
+   * - `'none'`
184
+   * - `'all'`
185
+   *
186
+   * With the new WebKit implementation, we have three new values:
187
+   * - `'trackingNumber'`,
188
+   * - `'flightNumber'`,
189
+   * - `'lookupSuggestion'`,
190
+   *
191
+   * @platform ios
192
+   */
193
+  dataDetectorTypes?:
194
+    | ?DataDetectorTypes
195
+    | $ReadOnlyArray<DataDetectorTypes>,
196
+
197
+  /**
198
+   * Function that allows custom handling of any web view requests. Return
199
+   * `true` from the function to continue loading the request and `false`
200
+   * to stop loading.
201
+   * @platform ios
202
+   */
203
+  onShouldStartLoadWithRequest?: (event: WebViewEvent) => mixed,
204
+
205
+  /**
206
+   * Boolean that determines whether HTML5 videos play inline or use the
207
+   * native full-screen controller. The default value is `false`.
208
+   *
209
+   * **NOTE** : In order for video to play inline, not only does this
210
+   * property need to be set to `true`, but the video element in the HTML
211
+   * document must also include the `webkit-playsinline` attribute.
212
+   * @platform ios
213
+   */
214
+  allowsInlineMediaPlayback?: ?boolean,
215
+|}>;
216
+
217
+export type AndroidWebViewProps = $ReadOnly<{|
218
+  onNavigationStateChange?: (event: WebViewNavigation) => mixed,
219
+  onContentSizeChange?: (event: WebViewEvent) => mixed,
220
+
221
+  /**
222
+   * Sets whether Geolocation is enabled. The default is false.
223
+   * @platform android
224
+   */
225
+  geolocationEnabled?: ?boolean,
226
+
227
+  /**
228
+   * Boolean that sets whether JavaScript running in the context of a file
229
+   * scheme URL should be allowed to access content from any origin.
230
+   * Including accessing content from other file scheme URLs
231
+   * @platform android
232
+   */
233
+  allowUniversalAccessFromFileURLs?: ?boolean,
234
+
235
+  /**
236
+   * Used on Android only, controls whether form autocomplete data should be saved
237
+   * @platform android
238
+   */
239
+  saveFormDataDisabled?: ?boolean,
240
+
241
+  /*
242
+   * Used on Android only, controls whether the given list of URL prefixes should
243
+   * make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a
244
+   * default activity intent for those URL instead of loading it within the webview.
245
+   * Use this to list URLs that WebView cannot handle, e.g. a PDF url.
246
+   * @platform android
247
+   */
248
+  urlPrefixesForDefaultIntent?: $ReadOnlyArray<string>,
249
+
250
+  /**
251
+   * Boolean value to enable JavaScript in the `WebView`. Used on Android only
252
+   * as JavaScript is enabled by default on iOS. The default value is `true`.
253
+   * @platform android
254
+   */
255
+  javaScriptEnabled?: ?boolean,
256
+
257
+  /**
258
+   * Boolean value to enable third party cookies in the `WebView`. Used on
259
+   * Android Lollipop and above only as third party cookies are enabled by
260
+   * default on Android Kitkat and below and on iOS. The default value is `true`.
261
+   * @platform android
262
+   */
263
+  thirdPartyCookiesEnabled?: ?boolean,
264
+
265
+  /**
266
+   * Boolean value to control whether DOM Storage is enabled. Used only in
267
+   * Android.
268
+   * @platform android
269
+   */
270
+  domStorageEnabled?: ?boolean,
271
+
272
+  /**
273
+   * Sets the user-agent for the `WebView`.
274
+   * @platform android
275
+   */
276
+  userAgent?: ?string,
277
+
278
+  /**
279
+   * Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin.
280
+   *
281
+   * Possible values for `mixedContentMode` are:
282
+   *
283
+   * - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin.
284
+   * - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure.
285
+   * - `'compatibility'` -  WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content.
286
+   * @platform android
287
+   */
288
+  mixedContentMode?: ?('never' | 'always' | 'compatibility'),
289
+|}>;
290
+
291
+export type WebViewSharedProps =  $ReadOnly<{|
292
+  ...ViewProps,
293
+  ...IOSWebViewProps,
294
+  ...AndroidWebViewProps,
295
+  /**
296
+   * Deprecated. Use `source` instead.
297
+   */
298
+  url?: ?string,
299
+  /**
300
+   * Deprecated. Use `source` instead.
301
+   */
302
+  html?: ?string,
303
+
304
+  /**
305
+   * Loads static html or a uri (with optional headers) in the WebView.
306
+   */
307
+  source?: ?WebViewSource,
308
+
309
+  /**
310
+   * Function that returns a view to show if there's an error.
311
+   */
312
+  renderError: (errorDomain: ?string, errorCode: number, errorDesc: string) => Element<any>, // view to show if there's an error
313
+
314
+  /**
315
+   * Function that returns a loading indicator.
316
+   */
317
+  renderLoading: () => Element<any>,
318
+
319
+  /**
320
+   * Function that is invoked when the `WebView` has finished loading.
321
+   */
322
+  onLoad: (event: WebViewNavigationEvent) => mixed,
323
+
324
+  /**
325
+   * Function that is invoked when the `WebView` load succeeds or fails.
326
+   */
327
+  onLoadEnd: (event: WebViewNavigationEvent | WebViewErrorEvent) => mixed,
328
+
329
+  /**
330
+   * Function that is invoked when the `WebView` starts loading.
331
+   */
332
+  onLoadStart: (event: WebViewNavigationEvent) => mixed,
333
+
334
+  /**
335
+   * Function that is invoked when the `WebView` load fails.
336
+   */
337
+  onError: (event: WebViewErrorEvent) => mixed,
338
+
339
+  /**
340
+   * Controls whether to adjust the content inset for web views that are
341
+   * placed behind a navigation bar, tab bar, or toolbar. The default value
342
+   * is `true`.
343
+   */
344
+  automaticallyAdjustContentInsets?: ?boolean,
345
+
346
+  /**
347
+   * Function that is invoked when the `WebView` loading starts or ends.
348
+   */
349
+  onNavigationStateChange?: (event: WebViewNavigation) => mixed,
350
+
351
+  /**
352
+   * A function that is invoked when the webview calls `window.postMessage`.
353
+   * Setting this property will inject a `postMessage` global into your
354
+   * webview, but will still call pre-existing values of `postMessage`.
355
+   *
356
+   * `window.postMessage` accepts one argument, `data`, which will be
357
+   * available on the event object, `event.nativeEvent.data`. `data`
358
+   * must be a string.
359
+   */
360
+  onMessage?: (event: WebViewMessageEvent) => mixed,
361
+
362
+  /**
363
+   * Boolean value that forces the `WebView` to show the loading view
364
+   * on the first load.
365
+   */
366
+  startInLoadingState?: ?boolean,
367
+
368
+  /**
369
+   * Set this to provide JavaScript that will be injected into the web page
370
+   * when the view loads.
371
+   */
372
+  injectedJavaScript?: ?string,
373
+
374
+  /**
375
+   * Boolean that controls whether the web content is scaled to fit
376
+   * the view and enables the user to change the scale. The default value
377
+   * is `true`.
378
+   *
379
+   * On iOS, when `useWebKit=true`, this prop will not work.
380
+   */
381
+  scalesPageToFit?: ?boolean,
382
+
383
+  /**
384
+   * Boolean that determines whether HTML5 audio and video requires the user
385
+   * to tap them before they start playing. The default value is `true`.
386
+   */
387
+  mediaPlaybackRequiresUserAction?: ?boolean,
388
+
389
+  /**
390
+   * List of origin strings to allow being navigated to. The strings allow
391
+   * wildcards and get matched against *just* the origin (not the full URL).
392
+   * If the user taps to navigate to a new page but the new page is not in
393
+   * this whitelist, we will open the URL in Safari.
394
+   * The default whitelisted origins are "http://*" and "https://*".
395
+   */
396
+  originWhitelist?: $ReadOnlyArray<string>,
397
+
398
+  /**
399
+   * Override the native component used to render the WebView. Enables a custom native
400
+   * WebView which uses the same JavaScript as the original WebView.
401
+   */
402
+  nativeConfig?: ?WebViewNativeConfig,
403
+
404
+  style?: ViewStyleProp,
405
+  children: Node,
406
+|}>;

+ 8
- 0
package.json Datei anzeigen

@@ -5,6 +5,10 @@
5 5
   "author": "Jamon Holmgren <jamon@infinite.red>",
6 6
   "version": "0.1.0",
7 7
   "homepage": "https://github.com/react-native-community/react-native-webview#readme",
8
+  "scripts": {
9
+    "flow": "flow check",
10
+    "flow-android": "flow check --flowconfig-name .flowconfig.android"
11
+  },
8 12
   "peerDependencies": {
9 13
     "react": "^16.0",
10 14
     "react-native": "^0.56"
@@ -12,5 +16,9 @@
12 16
   "dependencies": {
13 17
     "escape-string-regexp": "^1.0.5",
14 18
     "fbjs": "^0.8.17"
19
+  },
20
+  "devDependencies": {
21
+    "flow-bin": "^0.80.0",
22
+    "react-native": "^0.56"
15 23
   }
16 24
 }