/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ /* @flow */ import React, { Component } from 'react'; import CodeBlock from './StackFrameCodeBlock'; import { getPrettyURL } from '../utils/getPrettyURL'; import { darkGray } from '../styles'; const linkStyle = { fontSize: '0.9em', marginBottom: '0.9em', }; const anchorStyle = { textDecoration: 'none', color: darkGray, cursor: 'pointer', }; const codeAnchorStyle = { cursor: 'pointer', }; const toggleStyle = { marginBottom: '1.5em', color: darkGray, cursor: 'pointer', border: 'none', display: 'block', width: '100%', textAlign: 'left', background: '#fff', fontFamily: 'Consolas, Menlo, monospace', fontSize: '1em', padding: '0px', lineHeight: '1.5', }; class StackFrame extends Component { state = { compiled: false, }; toggleCompiled = () => { this.setState(state => ({ compiled: !state.compiled, })); }; canOpenInEditor() { if (!this.props.launchEditorEndpoint) { return; } const { _originalFileName: sourceFileName } = this.props.frame; // Unknown file if (!sourceFileName) { return false; } // e.g. "/path-to-my-app/webpack/bootstrap eaddeb46b67d75e4dfc1" const isInternalWebpackBootstrapCode = sourceFileName.trim().indexOf(' ') !== -1; if (isInternalWebpackBootstrapCode) { return false; } // Code is in a real file return true; } openInEditor = () => { if (!this.canOpenInEditor()) { return; } const { _originalFileName: sourceFileName, _originalLineNumber: sourceLineNumber, } = this.props.frame; // Keep this in sync with react-error-overlay/middleware.js fetch( `${this.props.launchEditorEndpoint}?fileName=` + window.encodeURIComponent(sourceFileName) + '&lineNumber=' + window.encodeURIComponent(sourceLineNumber || 1) ).then(() => {}, () => {}); }; onKeyDown = (e: SyntheticKeyboardEvent) => { if (e.key === 'Enter') { this.openInEditor(); } }; render() { const { frame, contextSize, critical, showCode } = this.props; const { fileName, lineNumber, columnNumber, _scriptCode: scriptLines, _originalFileName: sourceFileName, _originalLineNumber: sourceLineNumber, _originalColumnNumber: sourceColumnNumber, _originalScriptCode: sourceLines, } = frame; const functionName = frame.getFunctionName(); const compiled = this.state.compiled; const url = getPrettyURL( sourceFileName, sourceLineNumber, sourceColumnNumber, fileName, lineNumber, columnNumber, compiled ); let codeBlockProps = null; if (showCode) { if ( compiled && scriptLines && scriptLines.length !== 0 && lineNumber != null ) { codeBlockProps = { lines: scriptLines, lineNum: lineNumber, columnNum: columnNumber, contextSize, main: critical, }; } else if ( !compiled && sourceLines && sourceLines.length !== 0 && sourceLineNumber != null ) { codeBlockProps = { lines: sourceLines, lineNum: sourceLineNumber, columnNum: sourceColumnNumber, contextSize, main: critical, }; } } const canOpenInEditor = this.canOpenInEditor(); return ( <div> <div> {functionName} </div> <div style={linkStyle}> <a style={canOpenInEditor ? anchorStyle : null} onClick={canOpenInEditor ? this.openInEditor : null} onKeyDown={canOpenInEditor ? this.onKeyDown : null} tabIndex={canOpenInEditor ? '0' : null} > {url} </a> </div> {codeBlockProps && <span> <a onClick={canOpenInEditor ? this.openInEditor : null} style={canOpenInEditor ? codeAnchorStyle : null} > <CodeBlock {...codeBlockProps} /> </a> <button style={toggleStyle} onClick={this.toggleCompiled}> {'View ' + (compiled ? 'source' : 'compiled')} </button> </span>} </div> ); } } export default StackFrame;