LogBoxInspectorReactFrames.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. * @flow strict-local
  8. * @format
  9. */
  10. 'use strict';
  11. import * as React from 'react';
  12. import StyleSheet from '../../StyleSheet/StyleSheet';
  13. import Platform from '../../Utilities/Platform';
  14. import Text from '../../Text/Text';
  15. import View from '../../Components/View/View';
  16. import LogBoxButton from './LogBoxButton';
  17. import * as LogBoxStyle from './LogBoxStyle';
  18. import LogBoxInspectorSection from './LogBoxInspectorSection';
  19. import openFileInEditor from '../../Core/Devtools/openFileInEditor';
  20. import type LogBoxLog from '../Data/LogBoxLog';
  21. type Props = $ReadOnly<{|
  22. log: LogBoxLog,
  23. |}>;
  24. const BEFORE_SLASH_RE = /^(.*)[\\/]/;
  25. // Taken from React https://github.com/facebook/react/blob/206d61f72214e8ae5b935f0bf8628491cb7f0797/packages/react-devtools-shared/src/backend/describeComponentFrame.js#L27-L41
  26. function getPrettyFileName(path) {
  27. let fileName = path.replace(BEFORE_SLASH_RE, '');
  28. // In DEV, include code for a common special case:
  29. // prefer "folder/index.js" instead of just "index.js".
  30. if (/^index\./.test(fileName)) {
  31. const match = path.match(BEFORE_SLASH_RE);
  32. if (match) {
  33. const pathBeforeSlash = match[1];
  34. if (pathBeforeSlash) {
  35. const folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, '');
  36. // Note the below string contains a zero width space after the "/" character.
  37. // This is to prevent browsers like Chrome from formatting the file name as a link.
  38. // (Since this is a source link, it would not work to open the source file anyway.)
  39. fileName = folderName + '/​' + fileName;
  40. }
  41. }
  42. }
  43. return fileName;
  44. }
  45. function LogBoxInspectorReactFrames(props: Props): React.Node {
  46. const [collapsed, setCollapsed] = React.useState(true);
  47. if (props.log.componentStack == null || props.log.componentStack.length < 1) {
  48. return null;
  49. }
  50. function getStackList() {
  51. if (collapsed) {
  52. return props.log.componentStack.slice(0, 3);
  53. } else {
  54. return props.log.componentStack;
  55. }
  56. }
  57. function getCollapseMessage() {
  58. if (props.log.componentStack.length <= 3) {
  59. return;
  60. }
  61. const count = props.log.componentStack.length - 3;
  62. if (collapsed) {
  63. return `See ${count} more components`;
  64. } else {
  65. return `Collapse ${count} components`;
  66. }
  67. }
  68. return (
  69. <LogBoxInspectorSection heading="Component Stack">
  70. {getStackList().map((frame, index) => (
  71. <View
  72. // Unfortunately we don't have a unique identifier for stack traces.
  73. key={index}
  74. style={componentStyles.frameContainer}>
  75. <LogBoxButton
  76. backgroundColor={{
  77. default: 'transparent',
  78. pressed: LogBoxStyle.getBackgroundColor(1),
  79. }}
  80. onPress={
  81. // Older versions of DevTools do not provide full path.
  82. // This will not work on Windows, remove check once the
  83. // DevTools return the full file path.
  84. frame.fileName.startsWith('/')
  85. ? () =>
  86. openFileInEditor(frame.fileName, frame.location?.row ?? 1)
  87. : null
  88. }
  89. style={componentStyles.frame}>
  90. <View style={componentStyles.component}>
  91. <Text style={componentStyles.frameName}>
  92. <Text style={componentStyles.bracket}>{'<'}</Text>
  93. {frame.content}
  94. <Text style={componentStyles.bracket}>{' />'}</Text>
  95. </Text>
  96. </View>
  97. <Text style={componentStyles.frameLocation}>
  98. {getPrettyFileName(frame.fileName)}
  99. {frame.location ? `:${frame.location.row}` : ''}
  100. </Text>
  101. </LogBoxButton>
  102. </View>
  103. ))}
  104. <View style={componentStyles.collapseContainer}>
  105. <LogBoxButton
  106. backgroundColor={{
  107. default: 'transparent',
  108. pressed: LogBoxStyle.getBackgroundColor(1),
  109. }}
  110. onPress={() => setCollapsed(!collapsed)}
  111. style={componentStyles.collapseButton}>
  112. <Text style={componentStyles.collapse}>{getCollapseMessage()}</Text>
  113. </LogBoxButton>
  114. </View>
  115. </LogBoxInspectorSection>
  116. );
  117. }
  118. const componentStyles = StyleSheet.create({
  119. collapseContainer: {
  120. marginLeft: 15,
  121. flexDirection: 'row',
  122. },
  123. collapseButton: {
  124. borderRadius: 5,
  125. },
  126. collapse: {
  127. color: LogBoxStyle.getTextColor(0.7),
  128. fontSize: 12,
  129. fontWeight: '300',
  130. lineHeight: 20,
  131. marginTop: 0,
  132. paddingVertical: 5,
  133. paddingHorizontal: 10,
  134. },
  135. frameContainer: {
  136. flexDirection: 'row',
  137. paddingHorizontal: 15,
  138. },
  139. frame: {
  140. flex: 1,
  141. paddingVertical: 4,
  142. paddingHorizontal: 10,
  143. borderRadius: 5,
  144. },
  145. component: {
  146. flexDirection: 'row',
  147. paddingRight: 10,
  148. },
  149. frameName: {
  150. fontFamily: Platform.select({android: 'monospace', ios: 'Menlo'}),
  151. color: LogBoxStyle.getTextColor(1),
  152. fontSize: 14,
  153. includeFontPadding: false,
  154. lineHeight: 18,
  155. },
  156. bracket: {
  157. fontFamily: Platform.select({android: 'monospace', ios: 'Menlo'}),
  158. color: LogBoxStyle.getTextColor(0.4),
  159. fontSize: 14,
  160. fontWeight: '500',
  161. includeFontPadding: false,
  162. lineHeight: 18,
  163. },
  164. frameLocation: {
  165. color: LogBoxStyle.getTextColor(0.7),
  166. fontSize: 12,
  167. fontWeight: '300',
  168. includeFontPadding: false,
  169. lineHeight: 16,
  170. paddingLeft: 10,
  171. },
  172. });
  173. export default LogBoxInspectorReactFrames;