mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
e5287287aa
Stacked on https://github.com/facebook/react/pull/28351, please review only the last commit. Top-level description of the approach: 1. Once user selects an element from the tree, frontend asks backend to return the inspected element, this is where we simulate an error happening in `render` function of the component and then we parse the error stack. As an improvement, we should probably migrate from custom implementation of error stack parser to `error-stack-parser` from npm. 2. When frontend receives the inspected element and this object is being propagated, we create a Promise for symbolicated source, which is then passed down to all components, which are using `source`. 3. These components use `use` hook for this promise and are wrapped in Suspense. Caching: 1. For browser extension, we cache Promises based on requested resource + key + column, also added use of `chrome.devtools.inspectedWindow.getResource` API. 2. For standalone case (RN), we cache based on requested resource url, we cache the content of it.
91 lines
2.4 KiB
JavaScript
91 lines
2.4 KiB
JavaScript
/**
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
import * as React from 'react';
|
|
|
|
import Button from 'react-devtools-shared/src/devtools/views/Button';
|
|
import ButtonIcon from 'react-devtools-shared/src/devtools/views/ButtonIcon';
|
|
|
|
import type {Source} from 'react-devtools-shared/src/shared/types';
|
|
|
|
type Props = {
|
|
editorURL: string,
|
|
source: Source,
|
|
symbolicatedSourcePromise: Promise<Source | null>,
|
|
};
|
|
|
|
function checkConditions(
|
|
editorURL: string,
|
|
source: Source,
|
|
): {url: URL | null, shouldDisableButton: boolean} {
|
|
try {
|
|
const url = new URL(editorURL);
|
|
|
|
let sourceURL = source.sourceURL;
|
|
|
|
// Check if sourceURL is a correct URL, which has a protocol specified
|
|
if (sourceURL.includes('://')) {
|
|
if (!__IS_INTERNAL_VERSION__) {
|
|
// In this case, we can't really determine the path to a file, disable a button
|
|
return {url: null, shouldDisableButton: true};
|
|
} else {
|
|
const endOfSourceMapURLPattern = '.js/';
|
|
const endOfSourceMapURLIndex = sourceURL.lastIndexOf(
|
|
endOfSourceMapURLPattern,
|
|
);
|
|
|
|
if (endOfSourceMapURLIndex === -1) {
|
|
return {url: null, shouldDisableButton: true};
|
|
} else {
|
|
sourceURL = sourceURL.slice(
|
|
endOfSourceMapURLIndex + endOfSourceMapURLPattern.length,
|
|
sourceURL.length,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
const lineNumberAsString = String(source.line);
|
|
|
|
url.href = url.href
|
|
.replace('{path}', sourceURL)
|
|
.replace('{line}', lineNumberAsString)
|
|
.replace('%7Bpath%7D', sourceURL)
|
|
.replace('%7Bline%7D', lineNumberAsString);
|
|
|
|
return {url, shouldDisableButton: false};
|
|
} catch (e) {
|
|
// User has provided incorrect editor url
|
|
return {url: null, shouldDisableButton: true};
|
|
}
|
|
}
|
|
|
|
function OpenInEditorButton({
|
|
editorURL,
|
|
source,
|
|
symbolicatedSourcePromise,
|
|
}: Props): React.Node {
|
|
const symbolicatedSource = React.use(symbolicatedSourcePromise);
|
|
|
|
const {url, shouldDisableButton} = checkConditions(
|
|
editorURL,
|
|
symbolicatedSource ? symbolicatedSource : source,
|
|
);
|
|
|
|
return (
|
|
<Button
|
|
disabled={shouldDisableButton}
|
|
onClick={() => window.open(url)}
|
|
title="Open in editor">
|
|
<ButtonIcon type="editor" />
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
export default OpenInEditorButton;
|