mirror of
https://github.com/zhigang1992/create-react-app.git
synced 2026-03-25 17:55:04 +08:00
Dark scheme overlay (#7052)
This commit is contained in:
committed by
Ian Schmitz
parent
a51729cb22
commit
c24314d960
@@ -1577,20 +1577,20 @@ Unhandled Promise rejections will now crash tests. You can fix them by explicitl
|
||||
After the regular update procedure above, add these line to `<head>` in `public/index.html`:
|
||||
|
||||
```html
|
||||
<meta name="theme-color" content="#000000">
|
||||
<!--
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
```
|
||||
|
||||
Add `<noscript>` to `<body>` in `public/index.html`:
|
||||
|
||||
```html
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
```
|
||||
|
||||
Then create a file called `public/manifest.json` that looks like this:
|
||||
|
||||
@@ -23,4 +23,4 @@ You can adjust various development and production settings by setting environmen
|
||||
| INLINE_RUNTIME_CHUNK | 🚫 Ignored | ✅ Used | By default, Create React App will embed the runtime script into `index.html` during the production build. When set to `false`, the script will not be embedded and will be imported as usual. This is normally required when dealing with CSP. |
|
||||
| IMAGE_INLINE_SIZE_LIMIT | 🚫 Ignored | ✅ Used | By default, images smaller than 10,000 bytes are encoded as a data URI in base64 and inlined in the CSS or JS build artifact. Set this to control the size limit in bytes. Setting it to 0 will disable the inlining of images. |
|
||||
| EXTEND_ESLINT | ✅ Used | ✅ Used | When set to `true`, ESLint configs that extend `eslint-config-react-app` will be used by `eslint-loader`. Any rules that are set to `"error"` will stop the application from building. |
|
||||
| TSC_COMPILE_ON_ERROR | ✅ Used | ✅ Used | When set to `true`, you can run and properly build TypeScript projects even if there are TypeScript type check errors. These errors are printed as warnings in the terminal and/or browser console. |
|
||||
| TSC_COMPILE_ON_ERROR | ✅ Used | ✅ Used | When set to `true`, you can run and properly build TypeScript projects even if there are TypeScript type check errors. These errors are printed as warnings in the terminal and/or browser console. |
|
||||
|
||||
@@ -248,7 +248,9 @@ function createApp(
|
||||
if (npmInfo.npmVersion) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`You are using npm ${npmInfo.npmVersion} so the project will be bootstrapped with an old unsupported version of tools.\n\n` +
|
||||
`You are using npm ${
|
||||
npmInfo.npmVersion
|
||||
} so the project will be bootstrapped with an old unsupported version of tools.\n\n` +
|
||||
`Please update to npm 5 or higher for a better, fully supported experience.\n`
|
||||
)
|
||||
);
|
||||
@@ -262,7 +264,9 @@ function createApp(
|
||||
if (yarnInfo.yarnVersion) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`You are using Yarn ${yarnInfo.yarnVersion} together with the --use-pnp flag, but Plug'n'Play is only supported starting from the 1.12 release.\n\n` +
|
||||
`You are using Yarn ${
|
||||
yarnInfo.yarnVersion
|
||||
} together with the --use-pnp flag, but Plug'n'Play is only supported starting from the 1.12 release.\n\n` +
|
||||
`Please update to Yarn 1.12 or higher for a better, fully supported experience.\n`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"eslint-plugin-import": "2.18.2",
|
||||
"eslint-plugin-jsx-a11y": "6.2.3",
|
||||
"eslint-plugin-react": "7.14.3",
|
||||
"flow-bin": "^0.63.1",
|
||||
"flow-bin": "^0.110.0",
|
||||
"html-entities": "1.2.1",
|
||||
"jest": "24.9.0",
|
||||
"jest-fetch-mock": "2.1.2",
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React from 'react';
|
||||
import { black } from '../styles';
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
import type { Theme } from '../styles';
|
||||
|
||||
const closeButtonStyle = {
|
||||
color: black,
|
||||
const closeButtonStyle = (theme: Theme) => ({
|
||||
color: theme.closeColor,
|
||||
lineHeight: '1rem',
|
||||
fontSize: '1.5rem',
|
||||
padding: '1rem',
|
||||
@@ -18,15 +19,19 @@ const closeButtonStyle = {
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
};
|
||||
});
|
||||
|
||||
type CloseCallback = () => void;
|
||||
function CloseButton({ close }: {| close: CloseCallback |}) {
|
||||
type CloseButtonPropsType = {|
|
||||
close: () => void,
|
||||
|};
|
||||
|
||||
function CloseButton({ close }: CloseButtonPropsType) {
|
||||
const theme = useContext(ThemeContext);
|
||||
return (
|
||||
<span
|
||||
title="Click or press Escape to dismiss."
|
||||
onClick={close}
|
||||
style={closeButtonStyle}
|
||||
style={closeButtonStyle(theme)}
|
||||
>
|
||||
×
|
||||
</span>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React from 'react';
|
||||
import { redTransparent, yellowTransparent } from '../styles';
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
|
||||
const _preStyle = {
|
||||
position: 'relative',
|
||||
@@ -20,16 +20,6 @@ const _preStyle = {
|
||||
borderRadius: '0.25rem',
|
||||
};
|
||||
|
||||
const primaryPreStyle = {
|
||||
..._preStyle,
|
||||
backgroundColor: redTransparent,
|
||||
};
|
||||
|
||||
const secondaryPreStyle = {
|
||||
..._preStyle,
|
||||
backgroundColor: yellowTransparent,
|
||||
};
|
||||
|
||||
const codeStyle = {
|
||||
fontFamily: 'Consolas, Menlo, monospace',
|
||||
};
|
||||
@@ -39,9 +29,20 @@ type CodeBlockPropsType = {|
|
||||
codeHTML: string,
|
||||
|};
|
||||
|
||||
function CodeBlock(props: CodeBlockPropsType) {
|
||||
const preStyle = props.main ? primaryPreStyle : secondaryPreStyle;
|
||||
const codeBlock = { __html: props.codeHTML };
|
||||
function CodeBlock({ main, codeHTML }: CodeBlockPropsType) {
|
||||
const theme = useContext(ThemeContext);
|
||||
const primaryPreStyle = {
|
||||
..._preStyle,
|
||||
backgroundColor: theme.primaryPreBackground,
|
||||
color: theme.primaryPreColor,
|
||||
};
|
||||
const secondaryPreStyle = {
|
||||
..._preStyle,
|
||||
backgroundColor: theme.secondaryPreBackground,
|
||||
color: theme.secondaryPreColor,
|
||||
};
|
||||
const preStyle = main ? primaryPreStyle : secondaryPreStyle;
|
||||
const codeBlock = { __html: codeHTML };
|
||||
|
||||
return (
|
||||
<pre style={preStyle}>
|
||||
|
||||
@@ -6,81 +6,76 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React, { Component } from 'react';
|
||||
import { black } from '../styles';
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
|
||||
import type { Element as ReactElement } from 'react';
|
||||
import type { Theme } from '../styles';
|
||||
|
||||
const _collapsibleStyle = {
|
||||
color: black,
|
||||
cursor: 'pointer',
|
||||
border: 'none',
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
textAlign: 'left',
|
||||
background: '#fff',
|
||||
fontFamily: 'Consolas, Menlo, monospace',
|
||||
fontSize: '1em',
|
||||
padding: '0px',
|
||||
lineHeight: '1.5',
|
||||
};
|
||||
|
||||
const collapsibleCollapsedStyle = {
|
||||
const collapsibleCollapsedStyle = (theme: Theme) => ({
|
||||
..._collapsibleStyle,
|
||||
color: theme.color,
|
||||
background: theme.background,
|
||||
marginBottom: '1.5em',
|
||||
};
|
||||
});
|
||||
|
||||
const collapsibleExpandedStyle = {
|
||||
const collapsibleExpandedStyle = (theme: Theme) => ({
|
||||
..._collapsibleStyle,
|
||||
color: theme.color,
|
||||
background: theme.background,
|
||||
marginBottom: '0.6em',
|
||||
};
|
||||
});
|
||||
|
||||
type Props = {|
|
||||
type CollapsiblePropsType = {|
|
||||
children: ReactElement<any>[],
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
collapsed: boolean,
|
||||
|};
|
||||
function Collapsible(props: CollapsiblePropsType) {
|
||||
const theme = useContext(ThemeContext);
|
||||
const [collapsed, setCollapsed] = useState(true);
|
||||
|
||||
class Collapsible extends Component<Props, State> {
|
||||
state = {
|
||||
collapsed: true,
|
||||
const toggleCollapsed = () => {
|
||||
setCollapsed(!collapsed);
|
||||
};
|
||||
|
||||
toggleCollapsed = () => {
|
||||
this.setState(state => ({
|
||||
collapsed: !state.collapsed,
|
||||
}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const count = this.props.children.length;
|
||||
const collapsed = this.state.collapsed;
|
||||
return (
|
||||
<div>
|
||||
const count = props.children.length;
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={toggleCollapsed}
|
||||
style={
|
||||
collapsed
|
||||
? collapsibleCollapsedStyle(theme)
|
||||
: collapsibleExpandedStyle(theme)
|
||||
}
|
||||
>
|
||||
{(collapsed ? '▶' : '▼') +
|
||||
` ${count} stack frames were ` +
|
||||
(collapsed ? 'collapsed.' : 'expanded.')}
|
||||
</button>
|
||||
<div style={{ display: collapsed ? 'none' : 'block' }}>
|
||||
{props.children}
|
||||
<button
|
||||
onClick={this.toggleCollapsed}
|
||||
style={
|
||||
collapsed ? collapsibleCollapsedStyle : collapsibleExpandedStyle
|
||||
}
|
||||
onClick={toggleCollapsed}
|
||||
style={collapsibleExpandedStyle(theme)}
|
||||
>
|
||||
{(collapsed ? '▶' : '▼') +
|
||||
` ${count} stack frames were ` +
|
||||
(collapsed ? 'collapsed.' : 'expanded.')}
|
||||
{`▲ ${count} stack frames were expanded.`}
|
||||
</button>
|
||||
<div style={{ display: collapsed ? 'none' : 'block' }}>
|
||||
{this.props.children}
|
||||
<button
|
||||
onClick={this.toggleCollapsed}
|
||||
style={collapsibleExpandedStyle}
|
||||
>
|
||||
{`▲ ${count} stack frames were expanded.`}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Collapsible;
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React, { Component } from 'react';
|
||||
import { black } from '../styles';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
|
||||
import type { Node as ReactNode } from 'react';
|
||||
import type { Theme } from '../styles';
|
||||
|
||||
const overlayStyle = {
|
||||
const overlayStyle = (theme: Theme) => ({
|
||||
position: 'relative',
|
||||
display: 'inline-flex',
|
||||
flexDirection: 'column',
|
||||
@@ -28,56 +29,50 @@ const overlayStyle = {
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
lineHeight: 1.5,
|
||||
color: black,
|
||||
};
|
||||
color: theme.color,
|
||||
});
|
||||
|
||||
type Props = {|
|
||||
type ErrorOverlayPropsType = {|
|
||||
children: ReactNode,
|
||||
shortcutHandler?: (eventKey: string) => void,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
collapsed: boolean,
|
||||
|};
|
||||
let iframeWindow: window = null;
|
||||
|
||||
class ErrorOverlay extends Component<Props, State> {
|
||||
iframeWindow: window = null;
|
||||
function ErrorOverlay(props: ErrorOverlayPropsType) {
|
||||
const theme = useContext(ThemeContext);
|
||||
|
||||
getIframeWindow = (element: ?HTMLDivElement) => {
|
||||
const getIframeWindow = (element: ?HTMLDivElement) => {
|
||||
if (element) {
|
||||
const document = element.ownerDocument;
|
||||
this.iframeWindow = document.defaultView;
|
||||
iframeWindow = document.defaultView;
|
||||
}
|
||||
};
|
||||
const { shortcutHandler } = props;
|
||||
|
||||
onKeyDown = (e: KeyboardEvent) => {
|
||||
const { shortcutHandler } = this.props;
|
||||
if (shortcutHandler) {
|
||||
shortcutHandler(e.key);
|
||||
useEffect(() => {
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (shortcutHandler) {
|
||||
shortcutHandler(e.key);
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
if (iframeWindow) {
|
||||
iframeWindow.addEventListener('keydown', onKeyDown);
|
||||
}
|
||||
};
|
||||
return () => {
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
if (iframeWindow) {
|
||||
iframeWindow.removeEventListener('keydown', onKeyDown);
|
||||
}
|
||||
};
|
||||
}, [shortcutHandler]);
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.onKeyDown);
|
||||
if (this.iframeWindow) {
|
||||
this.iframeWindow.addEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.onKeyDown);
|
||||
if (this.iframeWindow) {
|
||||
this.iframeWindow.removeEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={overlayStyle} ref={this.getIframeWindow}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div style={overlayStyle(theme)} ref={getIframeWindow}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ErrorOverlay;
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React from 'react';
|
||||
import { darkGray } from '../styles';
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
import type { Theme } from '../styles';
|
||||
|
||||
const footerStyle = {
|
||||
const footerStyle = (theme: Theme) => ({
|
||||
fontFamily: 'sans-serif',
|
||||
color: darkGray,
|
||||
color: theme.footer,
|
||||
marginTop: '0.5rem',
|
||||
flex: '0 0 auto',
|
||||
};
|
||||
});
|
||||
|
||||
type FooterPropsType = {|
|
||||
line1: string,
|
||||
@@ -22,8 +23,9 @@ type FooterPropsType = {|
|
||||
|};
|
||||
|
||||
function Footer(props: FooterPropsType) {
|
||||
const theme = useContext(ThemeContext);
|
||||
return (
|
||||
<div style={footerStyle}>
|
||||
<div style={footerStyle(theme)}>
|
||||
{props.line1}
|
||||
<br />
|
||||
{props.line2}
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React from 'react';
|
||||
import { red } from '../styles';
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
import type { Theme } from '../styles';
|
||||
|
||||
const headerStyle = {
|
||||
const headerStyle = (theme: Theme) => ({
|
||||
fontSize: '2em',
|
||||
fontFamily: 'sans-serif',
|
||||
color: red,
|
||||
color: theme.headerColor,
|
||||
whiteSpace: 'pre-wrap',
|
||||
// Top bottom margin spaces header
|
||||
// Right margin revents overlap with close button
|
||||
@@ -20,14 +21,15 @@ const headerStyle = {
|
||||
flex: '0 0 auto',
|
||||
maxHeight: '50%',
|
||||
overflow: 'auto',
|
||||
};
|
||||
});
|
||||
|
||||
type HeaderPropType = {|
|
||||
headerText: string,
|
||||
|};
|
||||
|
||||
function Header(props: HeaderPropType) {
|
||||
return <div style={headerStyle}>{props.headerText}</div>;
|
||||
const theme = useContext(ThemeContext);
|
||||
return <div style={headerStyle(theme)}>{props.headerText}</div>;
|
||||
}
|
||||
|
||||
export default Header;
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React from 'react';
|
||||
import { red, redTransparent } from '../styles';
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
import type { Theme } from '../styles';
|
||||
|
||||
const navigationBarStyle = {
|
||||
marginBottom: '0.5rem',
|
||||
@@ -18,26 +19,28 @@ const buttonContainerStyle = {
|
||||
};
|
||||
|
||||
const _navButtonStyle = {
|
||||
backgroundColor: redTransparent,
|
||||
color: red,
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
padding: '3px 6px',
|
||||
cursor: 'pointer',
|
||||
};
|
||||
|
||||
const leftButtonStyle = {
|
||||
const leftButtonStyle = (theme: Theme) => ({
|
||||
..._navButtonStyle,
|
||||
backgroundColor: theme.navBackground,
|
||||
color: theme.navArrow,
|
||||
borderTopRightRadius: '0px',
|
||||
borderBottomRightRadius: '0px',
|
||||
marginRight: '1px',
|
||||
};
|
||||
});
|
||||
|
||||
const rightButtonStyle = {
|
||||
const rightButtonStyle = (theme: Theme) => ({
|
||||
..._navButtonStyle,
|
||||
backgroundColor: theme.navBackground,
|
||||
color: theme.navArrow,
|
||||
borderTopLeftRadius: '0px',
|
||||
borderBottomLeftRadius: '0px',
|
||||
};
|
||||
});
|
||||
|
||||
type Callback = () => void;
|
||||
|
||||
@@ -49,14 +52,15 @@ type NavigationBarPropsType = {|
|
||||
|};
|
||||
|
||||
function NavigationBar(props: NavigationBarPropsType) {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { currentError, totalErrors, previous, next } = props;
|
||||
return (
|
||||
<div style={navigationBarStyle}>
|
||||
<span style={buttonContainerStyle}>
|
||||
<button onClick={previous} style={leftButtonStyle}>
|
||||
<button onClick={previous} style={leftButtonStyle(theme)}>
|
||||
←
|
||||
</button>
|
||||
<button onClick={next} style={rightButtonStyle}>
|
||||
<button onClick={next} style={rightButtonStyle(theme)}>
|
||||
→
|
||||
</button>
|
||||
</span>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
import ErrorOverlay from '../components/ErrorOverlay';
|
||||
import Footer from '../components/Footer';
|
||||
import Header from '../components/Header';
|
||||
@@ -19,31 +20,28 @@ const codeAnchorStyle = {
|
||||
cursor: 'pointer',
|
||||
};
|
||||
|
||||
type Props = {|
|
||||
type CompileErrorContainerPropsType = {|
|
||||
error: string,
|
||||
editorHandler: (errorLoc: ErrorLocation) => void,
|
||||
|};
|
||||
|
||||
class CompileErrorContainer extends PureComponent<Props, void> {
|
||||
render() {
|
||||
const { error, editorHandler } = this.props;
|
||||
const errLoc: ?ErrorLocation = parseCompileError(error);
|
||||
const canOpenInEditor = errLoc !== null && editorHandler !== null;
|
||||
return (
|
||||
<ErrorOverlay>
|
||||
<Header headerText="Failed to compile" />
|
||||
<div
|
||||
onClick={
|
||||
canOpenInEditor && errLoc ? () => editorHandler(errLoc) : null
|
||||
}
|
||||
style={canOpenInEditor ? codeAnchorStyle : null}
|
||||
>
|
||||
<CodeBlock main={true} codeHTML={generateAnsiHTML(error)} />
|
||||
</div>
|
||||
<Footer line1="This error occurred during the build time and cannot be dismissed." />
|
||||
</ErrorOverlay>
|
||||
);
|
||||
}
|
||||
function CompileErrorContainer(props: CompileErrorContainerPropsType) {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { error, editorHandler } = props;
|
||||
const errLoc: ?ErrorLocation = parseCompileError(error);
|
||||
const canOpenInEditor = errLoc !== null && editorHandler !== null;
|
||||
return (
|
||||
<ErrorOverlay>
|
||||
<Header headerText="Failed to compile" />
|
||||
<div
|
||||
onClick={canOpenInEditor && errLoc ? () => editorHandler(errLoc) : null}
|
||||
style={canOpenInEditor ? codeAnchorStyle : null}
|
||||
>
|
||||
<CodeBlock main={true} codeHTML={generateAnsiHTML(error, theme)} />
|
||||
</div>
|
||||
<Footer line1="This error occurred during the build time and cannot be dismissed." />
|
||||
</ErrorOverlay>
|
||||
);
|
||||
}
|
||||
|
||||
export default CompileErrorContainer;
|
||||
|
||||
@@ -6,45 +6,46 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React, { Component } from 'react';
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
import CodeBlock from './StackFrameCodeBlock';
|
||||
import { getPrettyURL } from '../utils/getPrettyURL';
|
||||
import { darkGray } from '../styles';
|
||||
|
||||
import type { StackFrame as StackFrameType } from '../utils/stack-frame';
|
||||
import type { ErrorLocation } from '../utils/parseCompileError';
|
||||
import type { Theme } from '../styles';
|
||||
|
||||
const linkStyle = {
|
||||
const linkStyle = (theme: Theme) => ({
|
||||
fontSize: '0.9em',
|
||||
marginBottom: '0.9em',
|
||||
};
|
||||
});
|
||||
|
||||
const anchorStyle = {
|
||||
const anchorStyle = (theme: Theme) => ({
|
||||
textDecoration: 'none',
|
||||
color: darkGray,
|
||||
color: theme.anchorColor,
|
||||
cursor: 'pointer',
|
||||
};
|
||||
});
|
||||
|
||||
const codeAnchorStyle = {
|
||||
const codeAnchorStyle = (theme: Theme) => ({
|
||||
cursor: 'pointer',
|
||||
};
|
||||
});
|
||||
|
||||
const toggleStyle = {
|
||||
const toggleStyle = (theme: Theme) => ({
|
||||
marginBottom: '1.5em',
|
||||
color: darkGray,
|
||||
color: theme.toggleColor,
|
||||
cursor: 'pointer',
|
||||
border: 'none',
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
textAlign: 'left',
|
||||
background: '#fff',
|
||||
background: theme.toggleBackground,
|
||||
fontFamily: 'Consolas, Menlo, monospace',
|
||||
fontSize: '1em',
|
||||
padding: '0px',
|
||||
lineHeight: '1.5',
|
||||
};
|
||||
});
|
||||
|
||||
type Props = {|
|
||||
type StackFramePropsType = {|
|
||||
frame: StackFrameType,
|
||||
contextSize: number,
|
||||
critical: boolean,
|
||||
@@ -52,26 +53,19 @@ type Props = {|
|
||||
editorHandler: (errorLoc: ErrorLocation) => void,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
compiled: boolean,
|
||||
|};
|
||||
function StackFrame(props: StackFramePropsType) {
|
||||
const theme = useContext(ThemeContext);
|
||||
const [compiled, setCompiled] = useState(false);
|
||||
|
||||
class StackFrame extends Component<Props, State> {
|
||||
state = {
|
||||
compiled: false,
|
||||
const toggleCompiled = () => {
|
||||
setCompiled(!compiled);
|
||||
};
|
||||
|
||||
toggleCompiled = () => {
|
||||
this.setState(state => ({
|
||||
compiled: !state.compiled,
|
||||
}));
|
||||
};
|
||||
|
||||
getErrorLocation(): ErrorLocation | null {
|
||||
const getErrorLocation = (): ErrorLocation | null => {
|
||||
const {
|
||||
_originalFileName: fileName,
|
||||
_originalLineNumber: lineNumber,
|
||||
} = this.props.frame;
|
||||
} = props.frame;
|
||||
// Unknown file
|
||||
if (!fileName) {
|
||||
return null;
|
||||
@@ -83,109 +77,106 @@ class StackFrame extends Component<Props, State> {
|
||||
}
|
||||
// Code is in a real file
|
||||
return { fileName, lineNumber: lineNumber || 1 };
|
||||
}
|
||||
};
|
||||
|
||||
editorHandler = () => {
|
||||
const errorLoc = this.getErrorLocation();
|
||||
const editorHandler = () => {
|
||||
const errorLoc = getErrorLocation();
|
||||
if (!errorLoc) {
|
||||
return;
|
||||
}
|
||||
this.props.editorHandler(errorLoc);
|
||||
props.editorHandler(errorLoc);
|
||||
};
|
||||
|
||||
onKeyDown = (e: SyntheticKeyboardEvent<>) => {
|
||||
const onKeyDown = (e: SyntheticKeyboardEvent<any>) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.editorHandler();
|
||||
editorHandler();
|
||||
}
|
||||
};
|
||||
|
||||
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 { frame, contextSize, critical, showCode } = 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
|
||||
);
|
||||
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,
|
||||
};
|
||||
}
|
||||
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.getErrorLocation() !== null && this.props.editorHandler !== null;
|
||||
return (
|
||||
<div>
|
||||
<div>{functionName}</div>
|
||||
<div style={linkStyle}>
|
||||
<span
|
||||
style={canOpenInEditor ? anchorStyle : null}
|
||||
onClick={canOpenInEditor ? this.editorHandler : null}
|
||||
onKeyDown={canOpenInEditor ? this.onKeyDown : null}
|
||||
tabIndex={canOpenInEditor ? '0' : null}
|
||||
>
|
||||
{url}
|
||||
</span>
|
||||
</div>
|
||||
{codeBlockProps && (
|
||||
<span>
|
||||
<span
|
||||
onClick={canOpenInEditor ? this.editorHandler : null}
|
||||
style={canOpenInEditor ? codeAnchorStyle : null}
|
||||
>
|
||||
<CodeBlock {...codeBlockProps} />
|
||||
</span>
|
||||
<button style={toggleStyle} onClick={this.toggleCompiled}>
|
||||
{'View ' + (compiled ? 'source' : 'compiled')}
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const canOpenInEditor =
|
||||
getErrorLocation() !== null && props.editorHandler !== null;
|
||||
return (
|
||||
<div>
|
||||
<div>{functionName}</div>
|
||||
<div style={linkStyle(theme)}>
|
||||
<span
|
||||
style={canOpenInEditor ? anchorStyle(theme) : null}
|
||||
onClick={canOpenInEditor ? editorHandler : null}
|
||||
onKeyDown={canOpenInEditor ? onKeyDown : null}
|
||||
tabIndex={canOpenInEditor ? '0' : null}
|
||||
>
|
||||
{url}
|
||||
</span>
|
||||
</div>
|
||||
{codeBlockProps && (
|
||||
<span>
|
||||
<span
|
||||
onClick={canOpenInEditor ? editorHandler : null}
|
||||
style={canOpenInEditor ? codeAnchorStyle(theme) : null}
|
||||
>
|
||||
<CodeBlock {...codeBlockProps} />
|
||||
</span>
|
||||
<button style={toggleStyle(theme)} onClick={toggleCompiled}>
|
||||
{'View ' + (compiled ? 'source' : 'compiled')}
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default StackFrame;
|
||||
|
||||
@@ -6,12 +6,11 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeContext } from '../iframeScript';
|
||||
import CodeBlock from '../components/CodeBlock';
|
||||
import { applyStyles } from '../utils/dom/css';
|
||||
import { absolutifyCaret } from '../utils/dom/absolutifyCaret';
|
||||
import type { ScriptLine } from '../utils/stack-frame';
|
||||
import { primaryErrorStyle, secondaryErrorStyle } from '../styles';
|
||||
import generateAnsiHTML from '../utils/generateAnsiHTML';
|
||||
|
||||
import { codeFrameColumns } from '@babel/code-frame';
|
||||
@@ -29,6 +28,7 @@ type StackFrameCodeBlockPropsType = {|
|
||||
type Exact<T> = $Shape<T>;
|
||||
|
||||
function StackFrameCodeBlock(props: Exact<StackFrameCodeBlockPropsType>) {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { lines, lineNum, columnNum, contextSize, main } = props;
|
||||
const sourceCode = [];
|
||||
let whiteSpace = Infinity;
|
||||
@@ -70,7 +70,7 @@ function StackFrameCodeBlock(props: Exact<StackFrameCodeBlockPropsType>) {
|
||||
linesBelow: contextSize,
|
||||
}
|
||||
);
|
||||
const htmlHighlight = generateAnsiHTML(ansiHighlight);
|
||||
const htmlHighlight = generateAnsiHTML(ansiHighlight, theme);
|
||||
const code = document.createElement('code');
|
||||
code.innerHTML = htmlHighlight;
|
||||
absolutifyCaret(code);
|
||||
@@ -89,8 +89,6 @@ function StackFrameCodeBlock(props: Exact<StackFrameCodeBlockPropsType>) {
|
||||
if (text.indexOf(' ' + lineNum + ' |') === -1) {
|
||||
continue;
|
||||
}
|
||||
// $FlowFixMe
|
||||
applyStyles(node, main ? primaryErrorStyle : secondaryErrorStyle);
|
||||
// eslint-disable-next-line
|
||||
break oLoop;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ let boundErrorHandler = null;
|
||||
type ErrorCallback = (error: Error) => void;
|
||||
|
||||
function errorHandler(callback: ErrorCallback, e: Event): void {
|
||||
// $FlowFixMe
|
||||
if (!e.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
30
packages/react-error-overlay/src/iframeScript.js
vendored
30
packages/react-error-overlay/src/iframeScript.js
vendored
@@ -6,14 +6,16 @@
|
||||
*/
|
||||
|
||||
import 'react-app-polyfill/ie9';
|
||||
import React from 'react';
|
||||
import React, { createContext } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import CompileErrorContainer from './containers/CompileErrorContainer';
|
||||
import RuntimeErrorContainer from './containers/RuntimeErrorContainer';
|
||||
import { overlayStyle } from './styles';
|
||||
import { applyStyles } from './utils/dom/css';
|
||||
import { applyStyles, getTheme } from './utils/dom/css';
|
||||
|
||||
let iframeRoot = null;
|
||||
const theme = getTheme();
|
||||
export const ThemeContext = createContext();
|
||||
|
||||
function render({
|
||||
currentBuildError,
|
||||
@@ -23,19 +25,23 @@ function render({
|
||||
}) {
|
||||
if (currentBuildError) {
|
||||
return (
|
||||
<CompileErrorContainer
|
||||
error={currentBuildError}
|
||||
editorHandler={editorHandler}
|
||||
/>
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<CompileErrorContainer
|
||||
error={currentBuildError}
|
||||
editorHandler={editorHandler}
|
||||
/>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
if (currentRuntimeErrorRecords.length > 0) {
|
||||
return (
|
||||
<RuntimeErrorContainer
|
||||
errorRecords={currentRuntimeErrorRecords}
|
||||
close={dismissRuntimeErrors}
|
||||
editorHandler={editorHandler}
|
||||
/>
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<RuntimeErrorContainer
|
||||
errorRecords={currentRuntimeErrorRecords}
|
||||
close={dismissRuntimeErrors}
|
||||
editorHandler={editorHandler}
|
||||
/>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -57,6 +63,6 @@ document.body.style.margin = '0';
|
||||
// Keep popup within body boundaries for iOS Safari
|
||||
document.body.style['max-width'] = '100vw';
|
||||
iframeRoot = document.createElement('div');
|
||||
applyStyles(iframeRoot, overlayStyle);
|
||||
applyStyles(iframeRoot, overlayStyle(theme));
|
||||
document.body.appendChild(iframeRoot);
|
||||
window.parent.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__.iframeReady();
|
||||
|
||||
137
packages/react-error-overlay/src/styles.js
vendored
137
packages/react-error-overlay/src/styles.js
vendored
@@ -6,14 +6,109 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
const black = '#293238',
|
||||
darkGray = '#878e91',
|
||||
red = '#ce1126',
|
||||
redTransparent = 'rgba(206, 17, 38, 0.05)',
|
||||
lightRed = '#fccfcf',
|
||||
yellow = '#fbf5b4',
|
||||
yellowTransparent = 'rgba(251, 245, 180, 0.3)',
|
||||
white = '#ffffff';
|
||||
export type Theme = {|
|
||||
// Colors for components styles
|
||||
background: string, // Page background
|
||||
color: string, // Base text
|
||||
headerColor: string, // Header text
|
||||
primaryPreBackground: string, // <pre/> Error background
|
||||
primaryPreColor: string, // <pre/> Error text
|
||||
secondaryPreBackground: string, // <pre/> Warning background
|
||||
secondaryPreColor: string, // <pre/> Warning text
|
||||
footer: string, // Footer text
|
||||
anchorColor: string, // Link color
|
||||
toggleBackground: string, // Toggle stack background
|
||||
toggleColor: string, // Toggle stack text
|
||||
closeColor: string, // Close button color
|
||||
navBackground: string, // Navigation arrow background
|
||||
navArrow: string, // Navigation arrow color
|
||||
// ANSI colors
|
||||
// base00: string; // Default Background
|
||||
base01: string, // Lighter Background (Used for status bars)
|
||||
// base02: string, // Selection Background
|
||||
base03: string, // Comments, Invisibles, Line Highlighting
|
||||
// base04: string, // Dark Foreground (Used for status bars)
|
||||
base05: string, // Default Foreground, Caret, Delimiters, Operators
|
||||
// base06: string, // Light Foreground (Not often used)
|
||||
// base07: string, // Light Background (Not often used)
|
||||
base08: string, // Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted
|
||||
// base09: string, // Integers, Boolean, Constants, XML Attributes, Markup Link Url
|
||||
// base0A: string, // Classes, Markup Bold, Search Text Background
|
||||
base0B: string, // Strings, Inherited Class, Markup Code, Diff Inserted
|
||||
base0C: string, // Support, Regular Expressions, Escape Characters, Markup Quotes
|
||||
// base0D: string, // Functions, Methods, Attribute IDs, Headings
|
||||
base0E: string, // Keywords, Storage, Selector, Markup Italic, Diff Changed
|
||||
// base0F: string, // Deprecated, Opening/Closing Embedded Language Tags e.g. <?php ?>
|
||||
|};
|
||||
const lightTheme: Theme = {
|
||||
// Colors for components styles
|
||||
background: 'white',
|
||||
color: 'black',
|
||||
headerColor: '#ce1126',
|
||||
primaryPreBackground: 'rgba(206, 17, 38, 0.05)',
|
||||
primaryPreColor: 'inherit',
|
||||
secondaryPreBackground: 'rgba(251, 245, 180, 0.3)',
|
||||
secondaryPreColor: 'inherit',
|
||||
footer: '#878e91',
|
||||
anchorColor: '#878e91',
|
||||
toggleBackground: 'transparent',
|
||||
toggleColor: '#878e91',
|
||||
closeColor: '#293238',
|
||||
navBackground: 'rgba(206, 17, 38, 0.05)',
|
||||
navArrow: '#ce1126',
|
||||
// Light color scheme inspired by https://chriskempson.github.io/base16/css/base16-github.css
|
||||
// base00: '#ffffff',
|
||||
base01: '#f5f5f5',
|
||||
// base02: '#c8c8fa',
|
||||
base03: '#6e6e6e',
|
||||
// base04: '#e8e8e8',
|
||||
base05: '#333333',
|
||||
// base06: '#ffffff',
|
||||
// base07: '#ffffff',
|
||||
base08: '#881280',
|
||||
// base09: '#0086b3',
|
||||
// base0A: '#795da3',
|
||||
base0B: '#1155cc',
|
||||
base0C: '#994500',
|
||||
// base0D: '#795da3',
|
||||
base0E: '#c80000',
|
||||
// base0F: '#333333',
|
||||
};
|
||||
|
||||
const darkTheme: Theme = {
|
||||
// Colors for components styles
|
||||
background: '#353535',
|
||||
color: 'white',
|
||||
headerColor: '#e83b46',
|
||||
primaryPreBackground: 'rgba(206, 17, 38, 0.1)',
|
||||
primaryPreColor: '#fccfcf',
|
||||
secondaryPreBackground: 'rgba(251, 245, 180, 0.1)',
|
||||
secondaryPreColor: '#fbf5b4',
|
||||
footer: '#878e91',
|
||||
anchorColor: '#878e91',
|
||||
toggleBackground: 'transparent',
|
||||
toggleColor: '#878e91',
|
||||
closeColor: '#ffffff',
|
||||
navBackground: 'rgba(206, 17, 38, 0.2)',
|
||||
navArrow: '#ce1126',
|
||||
// Dark color scheme inspired by https://github.com/atom/base16-tomorrow-dark-theme/blob/master/styles/colors.less
|
||||
// base00: '#1d1f21',
|
||||
base01: '#282a2e',
|
||||
// base02: '#373b41',
|
||||
base03: '#969896',
|
||||
// base04: '#b4b7b4',
|
||||
base05: '#c5c8c6',
|
||||
// base06: '#e0e0e0',
|
||||
// base07: '#ffffff',
|
||||
base08: '#cc6666',
|
||||
// base09: '#de935f',
|
||||
// base0A: '#f0c674',
|
||||
base0B: '#b5bd68',
|
||||
base0C: '#8abeb7',
|
||||
// base0D: '#81a2be',
|
||||
base0E: '#b294bb',
|
||||
// base0F: '#a3685a',
|
||||
};
|
||||
|
||||
const iframeStyle = {
|
||||
position: 'fixed',
|
||||
@@ -25,30 +120,12 @@ const iframeStyle = {
|
||||
'z-index': 2147483647,
|
||||
};
|
||||
|
||||
const overlayStyle = {
|
||||
const overlayStyle = (theme: Theme) => ({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
'box-sizing': 'border-box',
|
||||
'text-align': 'center',
|
||||
'background-color': white,
|
||||
};
|
||||
'background-color': theme.background,
|
||||
});
|
||||
|
||||
const primaryErrorStyle = {
|
||||
'background-color': lightRed,
|
||||
};
|
||||
|
||||
const secondaryErrorStyle = {
|
||||
'background-color': yellow,
|
||||
};
|
||||
|
||||
export {
|
||||
iframeStyle,
|
||||
overlayStyle,
|
||||
primaryErrorStyle,
|
||||
secondaryErrorStyle,
|
||||
black,
|
||||
darkGray,
|
||||
red,
|
||||
redTransparent,
|
||||
yellowTransparent,
|
||||
};
|
||||
export { iframeStyle, overlayStyle, lightTheme, darkTheme };
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import { lightTheme, darkTheme } from '../../styles';
|
||||
|
||||
let injectedCount = 0;
|
||||
const injectedCache = {};
|
||||
|
||||
@@ -44,4 +46,11 @@ function applyStyles(element: HTMLElement, styles: Object) {
|
||||
}
|
||||
}
|
||||
|
||||
export { getHead, injectCss, removeCss, applyStyles };
|
||||
function getTheme() {
|
||||
return window.matchMedia &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? darkTheme
|
||||
: lightTheme;
|
||||
}
|
||||
|
||||
export { getHead, injectCss, removeCss, applyStyles, getTheme };
|
||||
|
||||
@@ -9,44 +9,27 @@
|
||||
|
||||
import Anser from 'anser';
|
||||
import { AllHtmlEntities as Entities } from 'html-entities';
|
||||
import type { Theme } from '../styles';
|
||||
|
||||
var entities = new Entities();
|
||||
|
||||
// Color scheme inspired by https://chriskempson.github.io/base16/css/base16-github.css
|
||||
// var base00 = 'ffffff'; // Default Background
|
||||
var base01 = 'f5f5f5'; // Lighter Background (Used for status bars)
|
||||
// var base02 = 'c8c8fa'; // Selection Background
|
||||
var base03 = '6e6e6e'; // Comments, Invisibles, Line Highlighting
|
||||
// var base04 = 'e8e8e8'; // Dark Foreground (Used for status bars)
|
||||
var base05 = '333333'; // Default Foreground, Caret, Delimiters, Operators
|
||||
// var base06 = 'ffffff'; // Light Foreground (Not often used)
|
||||
// var base07 = 'ffffff'; // Light Background (Not often used)
|
||||
var base08 = '881280'; // Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted
|
||||
// var base09 = '0086b3'; // Integers, Boolean, Constants, XML Attributes, Markup Link Url
|
||||
// var base0A = '795da3'; // Classes, Markup Bold, Search Text Background
|
||||
var base0B = '1155cc'; // Strings, Inherited Class, Markup Code, Diff Inserted
|
||||
var base0C = '994500'; // Support, Regular Expressions, Escape Characters, Markup Quotes
|
||||
// var base0D = '795da3'; // Functions, Methods, Attribute IDs, Headings
|
||||
var base0E = 'c80000'; // Keywords, Storage, Selector, Markup Italic, Diff Changed
|
||||
// var base0F = '333333'; // Deprecated, Opening/Closing Embedded Language Tags e.g. <?php ?>
|
||||
const entities = new Entities();
|
||||
|
||||
// Map ANSI colors from what babel-code-frame uses to base16-github
|
||||
// See: https://github.com/babel/babel/blob/e86f62b304d280d0bab52c38d61842b853848ba6/packages/babel-code-frame/src/index.js#L9-L22
|
||||
var colors = {
|
||||
reset: [base05, 'transparent'],
|
||||
black: base05,
|
||||
red: base08 /* marker, bg-invalid */,
|
||||
green: base0B /* string */,
|
||||
yellow: base08 /* capitalized, jsx_tag, punctuator */,
|
||||
blue: base0C,
|
||||
magenta: base0C /* regex */,
|
||||
cyan: base0E /* keyword */,
|
||||
gray: base03 /* comment, gutter */,
|
||||
lightgrey: base01,
|
||||
darkgrey: base03,
|
||||
};
|
||||
const colors = (theme: Theme) => ({
|
||||
reset: [theme.base05, 'transparent'],
|
||||
black: theme.base05,
|
||||
red: theme.base08 /* marker, bg-invalid */,
|
||||
green: theme.base0B /* string */,
|
||||
yellow: theme.base08 /* capitalized, jsx_tag, punctuator */,
|
||||
blue: theme.base0C,
|
||||
magenta: theme.base0C /* regex */,
|
||||
cyan: theme.base0E /* keyword */,
|
||||
gray: theme.base03 /* comment, gutter */,
|
||||
lightgrey: theme.base01,
|
||||
darkgrey: theme.base03,
|
||||
});
|
||||
|
||||
var anserMap = {
|
||||
const anserMap = {
|
||||
'ansi-bright-black': 'black',
|
||||
'ansi-bright-yellow': 'yellow',
|
||||
'ansi-yellow': 'yellow',
|
||||
@@ -61,28 +44,28 @@ var anserMap = {
|
||||
'ansi-white': 'darkgrey',
|
||||
};
|
||||
|
||||
function generateAnsiHTML(txt: string): string {
|
||||
var arr = new Anser().ansiToJson(entities.encode(txt), {
|
||||
function generateAnsiHTML(txt: string, theme: Theme): string {
|
||||
const arr = new Anser().ansiToJson(entities.encode(txt), {
|
||||
use_classes: true,
|
||||
});
|
||||
|
||||
var result = '';
|
||||
var open = false;
|
||||
for (var index = 0; index < arr.length; ++index) {
|
||||
var c = arr[index];
|
||||
var content = c.content,
|
||||
let result = '';
|
||||
let open = false;
|
||||
for (let index = 0; index < arr.length; ++index) {
|
||||
const c = arr[index];
|
||||
const content = c.content,
|
||||
fg = c.fg;
|
||||
|
||||
var contentParts = content.split('\n');
|
||||
for (var _index = 0; _index < contentParts.length; ++_index) {
|
||||
const contentParts = content.split('\n');
|
||||
for (let _index = 0; _index < contentParts.length; ++_index) {
|
||||
if (!open) {
|
||||
result += '<span data-ansi-line="true">';
|
||||
open = true;
|
||||
}
|
||||
var part = contentParts[_index].replace('\r', '');
|
||||
var color = colors[anserMap[fg]];
|
||||
const part = contentParts[_index].replace('\r', '');
|
||||
const color = colors(theme)[anserMap[fg]];
|
||||
if (color != null) {
|
||||
result += '<span style="color: #' + color + ';">' + part + '</span>';
|
||||
result += '<span style="color: ' + color + ';">' + part + '</span>';
|
||||
} else {
|
||||
if (fg != null) {
|
||||
console.log('Missing color mapping: ', fg);
|
||||
|
||||
@@ -16,6 +16,7 @@ import { SourceMapConsumer } from 'source-map';
|
||||
class SourceMap {
|
||||
__source_map: SourceMapConsumer;
|
||||
|
||||
// $FlowFixMe
|
||||
constructor(sourceMap) {
|
||||
this.__source_map = sourceMap;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ function getStackFrames(
|
||||
): Promise<StackFrame[] | null> {
|
||||
const parsedFrames = parse(error);
|
||||
let enhancedFramesPromise;
|
||||
// $FlowFixMe
|
||||
if (error.__unmap_source) {
|
||||
enhancedFramesPromise = unmap(
|
||||
// $FlowFixMe
|
||||
|
||||
24
packages/react-error-overlay/src/utils/parser.js
vendored
24
packages/react-error-overlay/src/utils/parser.js
vendored
@@ -10,17 +10,21 @@ import StackFrame from './stack-frame';
|
||||
|
||||
const regexExtractLocation = /\(?(.+?)(?::(\d+))?(?::(\d+))?\)?$/;
|
||||
|
||||
// $FlowFixMe
|
||||
function extractLocation(token: string): [string, number, number] {
|
||||
return regexExtractLocation
|
||||
.exec(token)
|
||||
.slice(1)
|
||||
.map(v => {
|
||||
const p = Number(v);
|
||||
if (!isNaN(p)) {
|
||||
return p;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
return (
|
||||
regexExtractLocation
|
||||
.exec(token)
|
||||
// $FlowFixMe
|
||||
.slice(1)
|
||||
.map(v => {
|
||||
const p = Number(v);
|
||||
if (!isNaN(p)) {
|
||||
return p;
|
||||
}
|
||||
return v;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const regexValidFrame_Chrome = /^\s*(at|in)\s.+(:\d+)/;
|
||||
|
||||
Reference in New Issue
Block a user