1 | /**
|
2 | * Copyright (c) Facebook, Inc. and its affiliates.
|
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 | ;
|
12 |
|
13 | const getDevServer = require('getDevServer');
|
14 |
|
15 | const {SourceCode} = require('NativeModules');
|
16 |
|
17 | // Avoid requiring fetch on load of this module; see symbolicateStackTrace
|
18 | let fetch;
|
19 |
|
20 | import type {StackFrame} from 'parseErrorStack';
|
21 |
|
22 | function isSourcedFromDisk(sourcePath: string): boolean {
|
23 | return !/^http/.test(sourcePath) && /[\\/]/.test(sourcePath);
|
24 | }
|
25 |
|
26 | async function symbolicateStackTrace(
|
27 | stack: Array<StackFrame>,
|
28 | ): Promise<Array<StackFrame>> {
|
29 | // RN currently lazy loads whatwg-fetch using a custom fetch module, which,
|
30 | // when called for the first time, requires and re-exports 'whatwg-fetch'.
|
31 | // However, when a dependency of the project tries to require whatwg-fetch
|
32 | // either directly or indirectly, whatwg-fetch is required before
|
33 | // RN can lazy load whatwg-fetch. As whatwg-fetch checks
|
34 | // for a fetch polyfill before loading, it will in turn try to load
|
35 | // RN's fetch module, which immediately tries to import whatwg-fetch AGAIN.
|
36 | // This causes a circular require which results in RN's fetch module
|
37 | // exporting fetch as 'undefined'.
|
38 | // The fix below postpones trying to load fetch until the first call to symbolicateStackTrace.
|
39 | // At that time, we will have either global.fetch (whatwg-fetch) or RN's fetch.
|
40 | if (!fetch) {
|
41 | fetch = global.fetch || require('fetch').fetch;
|
42 | }
|
43 |
|
44 | const devServer = getDevServer();
|
45 | if (!devServer.bundleLoadedFromServer) {
|
46 | throw new Error('Bundle was not loaded from the packager');
|
47 | }
|
48 |
|
49 | let stackCopy = stack;
|
50 |
|
51 | if (SourceCode.scriptURL) {
|
52 | let foundInternalSource: boolean = false;
|
53 | stackCopy = stack.map((frame: StackFrame) => {
|
54 | // If the sources exist on disk rather than appearing to come from the packager,
|
55 | // replace the location with the packager URL until we reach an internal source
|
56 | // which does not have a path (no slashes), indicating a switch from within
|
57 | // the application to a surrounding debugging environment.
|
58 | if (!foundInternalSource && isSourcedFromDisk(frame.file)) {
|
59 | // Copy frame into new object and replace 'file' property
|
60 | return {...frame, file: SourceCode.scriptURL};
|
61 | }
|
62 |
|
63 | foundInternalSource = true;
|
64 | return frame;
|
65 | });
|
66 | }
|
67 |
|
68 | const response = await fetch(devServer.url + 'symbolicate', {
|
69 | method: 'POST',
|
70 | body: JSON.stringify({stack: stackCopy}),
|
71 | });
|
72 | const json = await response.json();
|
73 | return json.stack;
|
74 | }
|
75 |
|
76 | module.exports = symbolicateStackTrace;
|