forked from facebook/react
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInspectedElementSourcePanel.js
More file actions
143 lines (122 loc) · 4.15 KB
/
InspectedElementSourcePanel.js
File metadata and controls
143 lines (122 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import {copy} from 'clipboard-js';
import {toNormalUrl} from 'jsc-safe-url';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
import Skeleton from './Skeleton';
import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck';
import type {Source as InspectedElementSource} from 'react-devtools-shared/src/shared/types';
import styles from './InspectedElementSourcePanel.css';
type Props = {
source: InspectedElementSource,
symbolicatedSourcePromise: Promise<InspectedElementSource | null>,
};
function InspectedElementSourcePanel({
source,
symbolicatedSourcePromise,
}: Props): React.Node {
return (
<div>
<div className={styles.SourceHeaderRow}>
<div className={styles.SourceHeader}>source</div>
<React.Suspense fallback={<Skeleton height={16} width={16} />}>
<CopySourceButton
source={source}
symbolicatedSourcePromise={symbolicatedSourcePromise}
/>
</React.Suspense>
</div>
<React.Suspense
fallback={
<div className={styles.SourceOneLiner}>
<Skeleton height={16} width="40%" />
</div>
}>
<FormattedSourceString
source={source}
symbolicatedSourcePromise={symbolicatedSourcePromise}
/>
</React.Suspense>
</div>
);
}
function CopySourceButton({source, symbolicatedSourcePromise}: Props) {
const symbolicatedSource = React.use(symbolicatedSourcePromise);
if (symbolicatedSource == null) {
const {sourceURL, line, column} = source;
const handleCopy = withPermissionsCheck(
{permissions: ['clipboardWrite']},
() => copy(`${sourceURL}:${line}:${column}`),
);
return (
<Button onClick={handleCopy} title="Copy to clipboard">
<ButtonIcon type="copy" />
</Button>
);
}
const {sourceURL, line, column} = symbolicatedSource;
const handleCopy = withPermissionsCheck(
{permissions: ['clipboardWrite']},
() => copy(`${sourceURL}:${line}:${column}`),
);
return (
<Button onClick={handleCopy} title="Copy to clipboard">
<ButtonIcon type="copy" />
</Button>
);
}
function FormattedSourceString({source, symbolicatedSourcePromise}: Props) {
const symbolicatedSource = React.use(symbolicatedSourcePromise);
if (symbolicatedSource == null) {
const {sourceURL, line} = source;
return (
<div
className={styles.SourceOneLiner}
data-testname="InspectedElementView-FormattedSourceString">
{formatSourceForDisplay(sourceURL, line)}
</div>
);
}
const {sourceURL, line} = symbolicatedSource;
return (
<div
className={styles.SourceOneLiner}
data-testname="InspectedElementView-FormattedSourceString">
{formatSourceForDisplay(sourceURL, line)}
</div>
);
}
// This function is based on describeComponentFrame() in packages/shared/ReactComponentStackFrame
function formatSourceForDisplay(sourceURL: string, line: number) {
// Metro can return JSC-safe URLs, which have `//&` as a delimiter
// https://www.npmjs.com/package/jsc-safe-url
const sanitizedSourceURL = sourceURL.includes('//&')
? toNormalUrl(sourceURL)
: sourceURL;
// Note: this RegExp doesn't work well with URLs from Metro,
// which provides bundle URL with query parameters prefixed with /&
const BEFORE_SLASH_RE = /^(.*)[\\\/]/;
let nameOnly = sanitizedSourceURL.replace(BEFORE_SLASH_RE, '');
// In DEV, include code for a common special case:
// prefer "folder/index.js" instead of just "index.js".
if (/^index\./.test(nameOnly)) {
const match = sanitizedSourceURL.match(BEFORE_SLASH_RE);
if (match) {
const pathBeforeSlash = match[1];
if (pathBeforeSlash) {
const folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, '');
nameOnly = folderName + '/' + nameOnly;
}
}
}
return `${nameOnly}:${line}`;
}
export default InspectedElementSourcePanel;