' +
- 'This error occurred during the build time and cannot be dismissed.
';
+if (module.hot && typeof module.hot.dispose === 'function') {
+ module.hot.dispose(function() {
+ // TODO: why do we need this?
+ ErrorOverlay.stopReportingRuntimeErrors();
});
}
-function destroyErrorOverlay() {
- if (!overlayDiv) {
- // It is not there in the first place.
- return;
- }
-
- // Clean up and reset internal state.
- document.body.removeChild(overlayIframe);
- overlayDiv = null;
- overlayIframe = null;
- lastOnOverlayDivReady = null;
-}
-
// Connect to WebpackDevServer via a socket.
var connection = new SockJS(
url.format({
@@ -207,9 +89,9 @@ function handleSuccess() {
// Attempt to apply hot updates or reload.
if (isHotUpdate) {
tryApplyUpdates(function onHotUpdateSuccess() {
- // Only destroy it when we're sure it's a hot update.
+ // Only dismiss it when we're sure it's a hot update.
// Otherwise it would flicker right before the reload.
- destroyErrorOverlay();
+ ErrorOverlay.dismissBuildError();
});
}
}
@@ -249,9 +131,9 @@ function handleWarnings(warnings) {
// Only print warnings if we aren't refreshing the page.
// Otherwise they'll disappear right away anyway.
printWarnings();
- // Only destroy it when we're sure it's a hot update.
+ // Only dismiss it when we're sure it's a hot update.
// Otherwise it would flicker right before the reload.
- destroyErrorOverlay();
+ ErrorOverlay.dismissBuildError();
});
} else {
// Print initial warnings immediately.
@@ -273,7 +155,7 @@ function handleErrors(errors) {
});
// Only show the first error.
- showErrorOverlay(formatted.errors[0]);
+ ErrorOverlay.reportBuildError(formatted.errors[0]);
// Also log them to the console.
if (typeof console !== 'undefined' && typeof console.error === 'function') {
diff --git a/packages/react-error-overlay/.flowconfig b/packages/react-error-overlay/.flowconfig
index 261b8646fc3..8d7de784e29 100644
--- a/packages/react-error-overlay/.flowconfig
+++ b/packages/react-error-overlay/.flowconfig
@@ -1,4 +1,5 @@
[ignore]
+.*/node_modules/eslint-plugin-jsx-a11y/.*
[include]
src/**/*.js
diff --git a/packages/react-error-overlay/fixtures/bundle.json b/packages/react-error-overlay/fixtures/bundle.json
index 7dfd31f5863..16670f6231f 100644
--- a/packages/react-error-overlay/fixtures/bundle.json
+++ b/packages/react-error-overlay/fixtures/bundle.json
@@ -240,11 +240,11 @@
]
},
{
- "functionName": "Object.batchedUpdates",
+ "functionName": "batchedUpdates",
"fileName": "http://localhost:3000/static/js/bundle.js",
"lineNumber": 33900,
"columnNumber": 26,
- "_originalFunctionName": "Object.batchedUpdates",
+ "_originalFunctionName": "batchedUpdates",
"_originalFileName": "webpack:///packages/react-scripts/~/react-dom/lib/ReactDefaultBatchingStrategy.js",
"_originalLineNumber": 62,
"_originalColumnNumber": 0,
@@ -264,11 +264,11 @@
]
},
{
- "functionName": "Object.batchedUpdates",
+ "functionName": "batchedUpdates",
"fileName": "http://localhost:3000/static/js/bundle.js",
"lineNumber": 2181,
"columnNumber": 27,
- "_originalFunctionName": "Object.batchedUpdates",
+ "_originalFunctionName": "batchedUpdates",
"_originalFileName": "webpack:///packages/react-scripts/~/react-dom/lib/ReactUpdates.js",
"_originalLineNumber": 97,
"_originalColumnNumber": 0,
@@ -288,11 +288,11 @@
]
},
{
- "functionName": "Object._renderNewRootComponent",
+ "functionName": "_renderNewRootComponent",
"fileName": "http://localhost:3000/static/js/bundle.js",
"lineNumber": 14398,
"columnNumber": 18,
- "_originalFunctionName": "Object._renderNewRootComponent",
+ "_originalFunctionName": "_renderNewRootComponent",
"_originalFileName": "webpack:///packages/react-scripts/~/react-dom/lib/ReactMount.js",
"_originalLineNumber": 320,
"_originalColumnNumber": 0,
@@ -312,11 +312,11 @@
]
},
{
- "functionName": "Object._renderSubtreeIntoContainer",
+ "functionName": "_renderSubtreeIntoContainer",
"fileName": "http://localhost:3000/static/js/bundle.js",
"lineNumber": 14479,
"columnNumber": 32,
- "_originalFunctionName": "Object._renderSubtreeIntoContainer",
+ "_originalFunctionName": "_renderSubtreeIntoContainer",
"_originalFileName": "webpack:///packages/react-scripts/~/react-dom/lib/ReactMount.js",
"_originalLineNumber": 401,
"_originalColumnNumber": 0,
@@ -336,11 +336,11 @@
]
},
{
- "functionName": "Object.render",
+ "functionName": "render",
"fileName": "http://localhost:3000/static/js/bundle.js",
"lineNumber": 14500,
"columnNumber": 23,
- "_originalFunctionName": "Object.render",
+ "_originalFunctionName": "render",
"_originalFileName": "webpack:///packages/react-scripts/~/react-dom/lib/ReactMount.js",
"_originalLineNumber": 422,
"_originalColumnNumber": 0,
@@ -360,11 +360,11 @@
]
},
{
- "functionName": "Object.friendlySyntaxErrorLabel",
+ "functionName": null,
"fileName": "http://localhost:3000/static/js/bundle.js",
"lineNumber": 17287,
"columnNumber": 20,
- "_originalFunctionName": "Object.friendlySyntaxErrorLabel",
+ "_originalFunctionName": null,
"_originalFileName": "webpack:///packages/react-scripts/template/src/index.js",
"_originalLineNumber": 6,
"_originalColumnNumber": 0,
@@ -432,11 +432,11 @@
]
},
{
- "functionName": "Object.",
+ "functionName": null,
"fileName": "http://localhost:3000/static/js/bundle.js",
"lineNumber": 41219,
"columnNumber": 18,
- "_originalFunctionName": "Object.",
+ "_originalFunctionName": null,
"_originalFileName": null,
"_originalLineNumber": null,
"_originalColumnNumber": null,
diff --git a/packages/react-error-overlay/package.json b/packages/react-error-overlay/package.json
index 15bf9e484b4..ba4676c4a77 100644
--- a/packages/react-error-overlay/package.json
+++ b/packages/react-error-overlay/package.json
@@ -1,6 +1,6 @@
{
"name": "react-error-overlay",
- "version": "1.0.9",
+ "version": "2.0.0",
"description": "An overlay for displaying stack frames.",
"main": "lib/index.js",
"scripts": {
@@ -31,27 +31,29 @@
"middleware.js"
],
"dependencies": {
- "anser": "1.2.5",
+ "anser": "1.4.1",
"babel-code-frame": "6.22.0",
- "babel-runtime": "6.23.0",
- "react-dev-utils": "^3.0.2",
+ "babel-runtime": "6.26.0",
+ "html-entities": "1.2.1",
+ "react": "^15 || ^16",
+ "react-dom": "^15 || ^16",
"settle-promise": "1.0.0",
"source-map": "0.5.6"
},
"devDependencies": {
"babel-cli": "6.24.1",
"babel-eslint": "7.2.3",
- "babel-preset-react-app": "^3.0.1",
- "cross-env": "5.0.0",
- "eslint": "3.19.0",
- "eslint-config-react-app": "^1.0.5",
- "eslint-plugin-flowtype": "2.33.0",
- "eslint-plugin-import": "2.2.0",
- "eslint-plugin-jsx-a11y": "5.0.3",
- "eslint-plugin-react": "7.0.1",
- "flow-bin": "0.49.1",
- "jest": "20.0.1",
- "jest-fetch-mock": "1.1.1"
+ "babel-preset-react-app": "^3.0.2",
+ "cross-env": "5.0.5",
+ "eslint": "4.4.1",
+ "eslint-config-react-app": "^2.0.0",
+ "eslint-plugin-flowtype": "2.35.0",
+ "eslint-plugin-import": "2.7.0",
+ "eslint-plugin-jsx-a11y": "5.1.1",
+ "eslint-plugin-react": "7.1.0",
+ "flow-bin": "0.52.0",
+ "jest": "20.0.4",
+ "jest-fetch-mock": "1.2.1"
},
"jest": {
"setupFiles": [
diff --git a/packages/react-error-overlay/src/__tests__/stack-frame.js b/packages/react-error-overlay/src/__tests__/stack-frame.js
index dc6a01b4a06..5a015260ab2 100644
--- a/packages/react-error-overlay/src/__tests__/stack-frame.js
+++ b/packages/react-error-overlay/src/__tests__/stack-frame.js
@@ -13,9 +13,9 @@ test('proper empty shape', () => {
const empty = new StackFrame();
expect(empty).toMatchSnapshot();
- expect(empty.getFunctionName()).toBe(null);
+ expect(empty.getFunctionName()).toBe('(anonymous function)');
expect(empty.getSource()).toBe('');
- expect(empty.toString()).toBe('');
+ expect(empty.toString()).toBe('(anonymous function)');
});
test('proper full shape', () => {
diff --git a/packages/react-error-overlay/src/components/CloseButton.js b/packages/react-error-overlay/src/components/CloseButton.js
new file mode 100644
index 00000000000..503b1198c3f
--- /dev/null
+++ b/packages/react-error-overlay/src/components/CloseButton.js
@@ -0,0 +1,38 @@
+/**
+ * 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 from 'react';
+import { black } from '../styles';
+
+const closeButtonStyle = {
+ color: black,
+ lineHeight: '1rem',
+ fontSize: '1.5rem',
+ padding: '1rem',
+ cursor: 'pointer',
+ position: 'absolute',
+ right: 0,
+ top: 0,
+};
+
+type CloseCallback = () => void;
+function CloseButton({ close }: {| close: CloseCallback |}) {
+ return (
+
+ ×
+
+ );
+}
+
+export default CloseButton;
diff --git a/packages/react-error-overlay/src/components/CodeBlock.js b/packages/react-error-overlay/src/components/CodeBlock.js
new file mode 100644
index 00000000000..478f0111b9b
--- /dev/null
+++ b/packages/react-error-overlay/src/components/CodeBlock.js
@@ -0,0 +1,54 @@
+/**
+ * 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 from 'react';
+import { redTransparent, yellowTransparent } from '../styles';
+
+const _preStyle = {
+ display: 'block',
+ padding: '0.5em',
+ marginTop: '0.5em',
+ marginBottom: '0.5em',
+ overflowX: 'auto',
+ whiteSpace: 'pre-wrap',
+ borderRadius: '0.25rem',
+};
+
+const primaryPreStyle = {
+ ..._preStyle,
+ backgroundColor: redTransparent,
+};
+
+const secondaryPreStyle = {
+ ..._preStyle,
+ backgroundColor: yellowTransparent,
+};
+
+const codeStyle = {
+ fontFamily: 'Consolas, Menlo, monospace',
+};
+
+type CodeBlockPropsType = {|
+ main: boolean,
+ codeHTML: string,
+|};
+
+function CodeBlock(props: CodeBlockPropsType) {
+ const preStyle = props.main ? primaryPreStyle : secondaryPreStyle;
+ const codeBlock = { __html: props.codeHTML };
+
+ return (
+
+
+
+ );
+}
+
+export default CodeBlock;
diff --git a/packages/react-error-overlay/src/components/Collapsible.js b/packages/react-error-overlay/src/components/Collapsible.js
new file mode 100644
index 00000000000..92f1de4295c
--- /dev/null
+++ b/packages/react-error-overlay/src/components/Collapsible.js
@@ -0,0 +1,78 @@
+/**
+ * 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 { black } 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 = {
+ ..._collapsibleStyle,
+ marginBottom: '1.5em',
+};
+
+const collapsibleExpandedStyle = {
+ ..._collapsibleStyle,
+ marginBottom: '0.6em',
+};
+
+class Collapsible extends Component {
+ state = {
+ collapsed: true,
+ };
+
+ toggleCollaped = () => {
+ this.setState(state => ({
+ collapsed: !state.collapsed,
+ }));
+ };
+
+ render() {
+ const count = this.props.children.length;
+ const collapsed = this.state.collapsed;
+ return (
+
+
+
+ {this.props.children}
+
+
+
+ );
+ }
+}
+
+export default Collapsible;
diff --git a/packages/react-error-overlay/src/components/Footer.js b/packages/react-error-overlay/src/components/Footer.js
new file mode 100644
index 00000000000..68eb8465674
--- /dev/null
+++ b/packages/react-error-overlay/src/components/Footer.js
@@ -0,0 +1,36 @@
+/**
+ * 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 from 'react';
+import { darkGray } from '../styles';
+
+const footerStyle = {
+ fontFamily: 'sans-serif',
+ color: darkGray,
+ marginTop: '0.5rem',
+ flex: '0 0 auto',
+};
+
+type FooterPropsType = {|
+ line1: string,
+ line2?: string,
+|};
+
+function Footer(props: FooterPropsType) {
+ return (
+
+ {props.line1}
+
+ {props.line2}
+
+ );
+}
+
+export default Footer;
diff --git a/packages/react-error-overlay/src/components/Header.js b/packages/react-error-overlay/src/components/Header.js
new file mode 100644
index 00000000000..a2f40973d00
--- /dev/null
+++ b/packages/react-error-overlay/src/components/Header.js
@@ -0,0 +1,39 @@
+/**
+ * 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 from 'react';
+import { red } from '../styles';
+
+const headerStyle = {
+ fontSize: '2em',
+ fontFamily: 'sans-serif',
+ color: red,
+ whiteSpace: 'pre-wrap',
+ // Top bottom margin spaces header
+ // Right margin revents overlap with close button
+ margin: '0 2rem 0.75rem 0',
+ flex: '0 0 auto',
+ maxHeight: '50%',
+ overflow: 'auto',
+};
+
+type HeaderPropType = {|
+ headerText: string,
+|};
+
+function Header(props: HeaderPropType) {
+ return (
+
+ {props.headerText}
+
+ );
+}
+
+export default Header;
diff --git a/packages/react-error-overlay/src/components/NavigationBar.js b/packages/react-error-overlay/src/components/NavigationBar.js
new file mode 100644
index 00000000000..4eba743cef7
--- /dev/null
+++ b/packages/react-error-overlay/src/components/NavigationBar.js
@@ -0,0 +1,70 @@
+/**
+ * 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 from 'react';
+import { red, redTransparent } from '../styles';
+
+const navigationBarStyle = {
+ marginBottom: '0.5rem',
+};
+
+const buttonContainerStyle = {
+ marginRight: '1em',
+};
+
+const _navButtonStyle = {
+ backgroundColor: redTransparent,
+ color: red,
+ border: 'none',
+ borderRadius: '4px',
+ padding: '3px 6px',
+ cursor: 'pointer',
+};
+
+const leftButtonStyle = {
+ ..._navButtonStyle,
+ borderTopRightRadius: '0px',
+ borderBottomRightRadius: '0px',
+ marginRight: '1px',
+};
+
+const rightButtonStyle = {
+ ..._navButtonStyle,
+ borderTopLeftRadius: '0px',
+ borderBottomLeftRadius: '0px',
+};
+
+type Callback = () => void;
+
+type NavigationBarPropsType = {|
+ currentError: number,
+ totalErrors: number,
+ previous: Callback,
+ next: Callback,
+|};
+
+function NavigationBar(props: NavigationBarPropsType) {
+ const { currentError, totalErrors, previous, next } = props;
+ return (
+
+
+
+
+
+ {`${currentError} of ${totalErrors} errors on the page`}
+
+ );
+}
+
+export default NavigationBar;
diff --git a/packages/react-error-overlay/src/components/Overlay.js b/packages/react-error-overlay/src/components/Overlay.js
new file mode 100644
index 00000000000..4fe530b6fee
--- /dev/null
+++ b/packages/react-error-overlay/src/components/Overlay.js
@@ -0,0 +1,74 @@
+/**
+ * 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 { black } from '../styles';
+
+const overlayStyle = {
+ position: 'relative',
+ display: 'inline-flex',
+ flexDirection: 'column',
+ height: '100%',
+ width: '1024px',
+ maxWidth: '100%',
+ overflowX: 'hidden',
+ overflowY: 'auto',
+ padding: '0.5rem',
+ boxSizing: 'border-box',
+ textAlign: 'left',
+ fontFamily: 'Consolas, Menlo, monospace',
+ fontSize: '11px',
+ whiteSpace: 'pre-wrap',
+ wordBreak: 'break-word',
+ lineHeight: 1.5,
+ color: black,
+};
+
+class Overlay extends Component {
+ iframeWindow: window = null;
+
+ getIframeWindow = (element: HTMLDivElement) => {
+ if (element) {
+ const document = element.ownerDocument;
+ this.iframeWindow = document.defaultView;
+ }
+ };
+
+ onKeyDown = (e: KeyboardEvent) => {
+ const { shortcutHandler } = this.props;
+ if (shortcutHandler) {
+ shortcutHandler(e.key);
+ }
+ };
+
+ 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 (
+
+ {this.props.children}
+
+ );
+ }
+}
+
+export default Overlay;
diff --git a/packages/react-error-overlay/src/components/additional.js b/packages/react-error-overlay/src/components/additional.js
deleted file mode 100644
index b573c740634..00000000000
--- a/packages/react-error-overlay/src/components/additional.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * 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 { applyStyles } from '../utils/dom/css';
-import {
- additionalChildStyle,
- groupStyle,
- groupElemLeft,
- groupElemRight,
-} from '../styles';
-import { consumeEvent } from '../utils/dom/consumeEvent';
-import { enableTabClick } from '../utils/dom/enableTabClick';
-
-type SwitchCallback = (offset: number) => void;
-function updateAdditional(
- document: Document,
- additionalReference: HTMLDivElement,
- currentError: number,
- totalErrors: number,
- switchCallback: SwitchCallback
-) {
- if (additionalReference.lastChild) {
- additionalReference.removeChild(additionalReference.lastChild);
- }
-
- if (totalErrors <= 1) {
- return;
- }
-
- const div = document.createElement('div');
- applyStyles(div, additionalChildStyle);
-
- const group = document.createElement('span');
- applyStyles(group, groupStyle);
-
- const left = document.createElement('button');
- applyStyles(left, groupElemLeft);
- left.addEventListener('click', function(e: MouseEvent) {
- consumeEvent(e);
- switchCallback(-1);
- });
- left.appendChild(document.createTextNode('←'));
- enableTabClick(left);
-
- const right = document.createElement('button');
- applyStyles(right, groupElemRight);
- right.addEventListener('click', function(e: MouseEvent) {
- consumeEvent(e);
- switchCallback(1);
- });
- right.appendChild(document.createTextNode('→'));
- enableTabClick(right);
-
- group.appendChild(left);
- group.appendChild(right);
- div.appendChild(group);
-
- const text = `${currentError} of ${totalErrors} errors on the page`;
- div.appendChild(document.createTextNode(text));
-
- additionalReference.appendChild(div);
-}
-
-export type { SwitchCallback };
-export { updateAdditional };
diff --git a/packages/react-error-overlay/src/components/close.js b/packages/react-error-overlay/src/components/close.js
deleted file mode 100644
index 2ced8d0ce92..00000000000
--- a/packages/react-error-overlay/src/components/close.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * 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 { applyStyles } from '../utils/dom/css';
-import { hintsStyle, hintStyle, closeButtonStyle } from '../styles';
-
-function createHint(document: Document, hint: string, title: string) {
- const span = document.createElement('span');
- span.appendChild(document.createTextNode(hint));
- span.setAttribute('title', title);
- applyStyles(span, hintStyle);
- return span;
-}
-
-type CloseCallback = () => void;
-function createClose(document: Document, callback: CloseCallback) {
- const hints = document.createElement('div');
- applyStyles(hints, hintsStyle);
-
- const close = createHint(document, '×', 'Click or press Escape to dismiss.');
- close.addEventListener('click', () => callback());
- applyStyles(close, closeButtonStyle);
- hints.appendChild(close);
- return hints;
-}
-
-export type { CloseCallback };
-export { createClose };
diff --git a/packages/react-error-overlay/src/components/footer.js b/packages/react-error-overlay/src/components/footer.js
deleted file mode 100644
index 4586a04ff2b..00000000000
--- a/packages/react-error-overlay/src/components/footer.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * 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 { applyStyles } from '../utils/dom/css';
-import { footerStyle } from '../styles';
-
-function createFooter(document: Document) {
- const div = document.createElement('div');
- applyStyles(div, footerStyle);
- div.appendChild(
- document.createTextNode(
- 'This screen is visible only in development. It will not appear if the app crashes in production.'
- )
- );
- div.appendChild(document.createElement('br'));
- div.appendChild(
- document.createTextNode(
- 'Open your browser’s developer console to further inspect this error.'
- )
- );
- return div;
-}
-
-export { createFooter };
diff --git a/packages/react-error-overlay/src/components/frame.js b/packages/react-error-overlay/src/components/frame.js
deleted file mode 100644
index 43d0d4043a3..00000000000
--- a/packages/react-error-overlay/src/components/frame.js
+++ /dev/null
@@ -1,355 +0,0 @@
-/**
- * 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 { enableTabClick } from '../utils/dom/enableTabClick';
-import { createCode } from './code';
-import { isInternalFile } from '../utils/isInternalFile';
-import type { StackFrame } from '../utils/stack-frame';
-import type { FrameSetting, OmitsObject } from './frames';
-import { applyStyles } from '../utils/dom/css';
-import {
- omittedFramesExpandedStyle,
- omittedFramesCollapsedStyle,
- functionNameStyle,
- depStyle,
- linkStyle,
- anchorStyle,
- hiddenStyle,
-} from '../styles';
-
-function getGroupToggle(
- document: Document,
- omitsCount: number,
- omitBundle: number
-) {
- const omittedFrames = document.createElement('div');
- enableTabClick(omittedFrames);
- const text1 = document.createTextNode(
- '\u25B6 ' + omitsCount + ' stack frames were collapsed.'
- );
- omittedFrames.appendChild(text1);
- omittedFrames.addEventListener('click', function() {
- const hide = text1.textContent.match(/▲/);
- const list = document.getElementsByName('bundle-' + omitBundle);
- for (let index = 0; index < list.length; ++index) {
- const n = list[index];
- if (hide) {
- n.style.display = 'none';
- } else {
- n.style.display = '';
- }
- }
- if (hide) {
- text1.textContent = text1.textContent.replace(/▲/, '▶');
- text1.textContent = text1.textContent.replace(/expanded/, 'collapsed');
- applyStyles(omittedFrames, omittedFramesCollapsedStyle);
- } else {
- text1.textContent = text1.textContent.replace(/▶/, '▲');
- text1.textContent = text1.textContent.replace(/collapsed/, 'expanded');
- applyStyles(omittedFrames, omittedFramesExpandedStyle);
- }
- });
- applyStyles(omittedFrames, omittedFramesCollapsedStyle);
- return omittedFrames;
-}
-
-function insertBeforeBundle(
- document: Document,
- parent: Node,
- omitsCount: number,
- omitBundle: number,
- actionElement
-) {
- const children = document.getElementsByName('bundle-' + omitBundle);
- if (children.length < 1) {
- return;
- }
- let first: ?Node = children[0];
- while (first != null && first.parentNode !== parent) {
- first = first.parentNode;
- }
- const div = document.createElement('div');
- enableTabClick(div);
- div.setAttribute('name', 'bundle-' + omitBundle);
- const text = document.createTextNode(
- '\u25BC ' + omitsCount + ' stack frames were expanded.'
- );
- div.appendChild(text);
- div.addEventListener('click', function() {
- return actionElement.click();
- });
- applyStyles(div, omittedFramesExpandedStyle);
- div.style.display = 'none';
-
- parent.insertBefore(div, first);
-}
-
-function frameDiv(
- document: Document,
- functionName,
- url,
- internalUrl,
- onSourceClick: ?Function
-) {
- const frame = document.createElement('div');
- const frameFunctionName = document.createElement('div');
-
- let cleanedFunctionName;
- if (!functionName || functionName === 'Object.') {
- cleanedFunctionName = '(anonymous function)';
- } else {
- cleanedFunctionName = functionName;
- }
-
- const cleanedUrl = url.replace('webpack://', '.');
-
- if (internalUrl) {
- applyStyles(
- frameFunctionName,
- Object.assign({}, functionNameStyle, depStyle)
- );
- } else {
- applyStyles(frameFunctionName, functionNameStyle);
- }
-
- frameFunctionName.appendChild(document.createTextNode(cleanedFunctionName));
- frame.appendChild(frameFunctionName);
-
- const frameLink = document.createElement('div');
- applyStyles(frameLink, linkStyle);
- const frameAnchor = document.createElement('a');
- applyStyles(frameAnchor, anchorStyle);
- frameAnchor.appendChild(document.createTextNode(cleanedUrl));
- frameLink.appendChild(frameAnchor);
- frame.appendChild(frameLink);
-
- if (typeof onSourceClick === 'function') {
- let handler = onSourceClick;
- enableTabClick(frameAnchor);
- frameAnchor.style.cursor = 'pointer';
- frameAnchor.addEventListener('click', function() {
- handler();
- });
- }
-
- return frame;
-}
-
-function isBultinErrorName(errorName: ?string) {
- switch (errorName) {
- case 'EvalError':
- case 'InternalError':
- case 'RangeError':
- case 'ReferenceError':
- case 'SyntaxError':
- case 'TypeError':
- case 'URIError':
- return true;
- default:
- return false;
- }
-}
-
-function getPrettyURL(
- sourceFileName: ?string,
- sourceLineNumber: ?number,
- sourceColumnNumber: ?number,
- fileName: ?string,
- lineNumber: ?number,
- columnNumber: ?number,
- compiled: boolean
-): string {
- let prettyURL;
- if (!compiled && sourceFileName && typeof sourceLineNumber === 'number') {
- // Remove everything up to the first /src/ or /node_modules/
- const trimMatch = /^[/|\\].*?[/|\\]((src|node_modules)[/|\\].*)/.exec(
- sourceFileName
- );
- if (trimMatch && trimMatch[1]) {
- prettyURL = trimMatch[1];
- } else {
- prettyURL = sourceFileName;
- }
- prettyURL += ':' + sourceLineNumber;
- // Note: we intentionally skip 0's because they're produced by cheap Webpack maps
- if (sourceColumnNumber) {
- prettyURL += ':' + sourceColumnNumber;
- }
- } else if (fileName && typeof lineNumber === 'number') {
- prettyURL = fileName + ':' + lineNumber;
- // Note: we intentionally skip 0's because they're produced by cheap Webpack maps
- if (columnNumber) {
- prettyURL += ':' + columnNumber;
- }
- } else {
- prettyURL = 'unknown';
- }
- return prettyURL;
-}
-
-function createFrame(
- document: Document,
- frameSetting: FrameSetting,
- frame: StackFrame,
- contextSize: number,
- critical: boolean,
- omits: OmitsObject,
- omitBundle: number,
- parentContainer: HTMLDivElement,
- lastElement: boolean,
- errorName: ?string
-) {
- const { compiled } = frameSetting;
- let { functionName, _originalFileName: sourceFileName } = frame;
- const {
- fileName,
- lineNumber,
- columnNumber,
- _scriptCode: scriptLines,
- _originalLineNumber: sourceLineNumber,
- _originalColumnNumber: sourceColumnNumber,
- _originalScriptCode: sourceLines,
- } = frame;
-
- // TODO: find a better place for this.
- // Chrome has a bug with inferring function.name:
- // https://github.com/facebookincubator/create-react-app/issues/2097
- // Let's ignore a meaningless name we get for top-level modules.
- if (
- functionName === 'Object.friendlySyntaxErrorLabel' ||
- functionName === 'Object.exports.__esModule'
- ) {
- functionName = '(anonymous function)';
- }
-
- const prettyURL = getPrettyURL(
- sourceFileName,
- sourceLineNumber,
- sourceColumnNumber,
- fileName,
- lineNumber,
- columnNumber,
- compiled
- );
-
- let needsHidden = false;
- const isInternalUrl = isInternalFile(sourceFileName, fileName);
- const isThrownIntentionally = !isBultinErrorName(errorName);
- const shouldCollapse =
- isInternalUrl && (isThrownIntentionally || omits.hasReachedAppCode);
-
- if (!isInternalUrl) {
- omits.hasReachedAppCode = true;
- }
-
- if (shouldCollapse) {
- ++omits.value;
- needsHidden = true;
- }
-
- let collapseElement = null;
- if (!shouldCollapse || lastElement) {
- if (omits.value > 0) {
- const capV = omits.value;
- const omittedFrames = getGroupToggle(document, capV, omitBundle);
- window.requestAnimationFrame(() => {
- insertBeforeBundle(
- document,
- parentContainer,
- capV,
- omitBundle,
- omittedFrames
- );
- });
- if (lastElement && shouldCollapse) {
- collapseElement = omittedFrames;
- } else {
- parentContainer.appendChild(omittedFrames);
- }
- ++omits.bundle;
- }
- omits.value = 0;
- }
-
- let onSourceClick = null;
- if (sourceFileName) {
- // e.g. "/path-to-my-app/webpack/bootstrap eaddeb46b67d75e4dfc1"
- const isInternalWebpackBootstrapCode =
- sourceFileName.trim().indexOf(' ') !== -1;
- if (!isInternalWebpackBootstrapCode) {
- onSourceClick = () => {
- // Keep this in sync with react-error-overlay/middleware.js
- fetch(
- '/__open-stack-frame-in-editor?fileName=' +
- window.encodeURIComponent(sourceFileName) +
- '&lineNumber=' +
- window.encodeURIComponent(sourceLineNumber || 1)
- ).then(() => {}, () => {});
- };
- }
- }
-
- const elem = frameDiv(
- document,
- functionName,
- prettyURL,
- shouldCollapse,
- onSourceClick
- );
- if (needsHidden) {
- applyStyles(elem, hiddenStyle);
- elem.setAttribute('name', 'bundle-' + omitBundle);
- }
-
- let hasSource = false;
- if (!shouldCollapse) {
- if (
- compiled &&
- scriptLines &&
- scriptLines.length !== 0 &&
- lineNumber != null
- ) {
- elem.appendChild(
- createCode(
- document,
- scriptLines,
- lineNumber,
- columnNumber,
- contextSize,
- critical,
- onSourceClick
- )
- );
- hasSource = true;
- } else if (
- !compiled &&
- sourceLines &&
- sourceLines.length !== 0 &&
- sourceLineNumber != null
- ) {
- elem.appendChild(
- createCode(
- document,
- sourceLines,
- sourceLineNumber,
- sourceColumnNumber,
- contextSize,
- critical,
- onSourceClick
- )
- );
- hasSource = true;
- }
- }
-
- return { elem: elem, hasSource: hasSource, collapseElement: collapseElement };
-}
-
-export { createFrame };
diff --git a/packages/react-error-overlay/src/components/frames.js b/packages/react-error-overlay/src/components/frames.js
deleted file mode 100644
index 8bd50509295..00000000000
--- a/packages/react-error-overlay/src/components/frames.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
- * 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 type { StackFrame } from '../utils/stack-frame';
-import { applyStyles } from '../utils/dom/css';
-import { traceStyle, toggleStyle } from '../styles';
-import { enableTabClick } from '../utils/dom/enableTabClick';
-import { createFrame } from './frame';
-
-type OmitsObject = {
- value: number,
- bundle: number,
- hasReachedAppCode: boolean,
-};
-type FrameSetting = { compiled: boolean };
-export type { OmitsObject, FrameSetting };
-
-function createFrameWrapper(
- document: Document,
- parent: HTMLDivElement,
- factory,
- lIndex: number,
- frameSettings: FrameSetting[],
- contextSize: number
-) {
- const fac = factory();
- if (fac == null) {
- return;
- }
- const { hasSource, elem, collapseElement } = fac;
-
- const elemWrapper = document.createElement('div');
- elemWrapper.appendChild(elem);
-
- if (hasSource) {
- const compiledDiv = document.createElement('div');
- enableTabClick(compiledDiv);
- applyStyles(compiledDiv, toggleStyle);
-
- const o = frameSettings[lIndex];
- const compiledText = document.createTextNode(
- 'View ' + (o && o.compiled ? 'source' : 'compiled')
- );
- compiledDiv.addEventListener('click', function() {
- if (o) {
- o.compiled = !o.compiled;
- }
-
- const next = createFrameWrapper(
- document,
- parent,
- factory,
- lIndex,
- frameSettings,
- contextSize
- );
- if (next != null) {
- parent.insertBefore(next, elemWrapper);
- parent.removeChild(elemWrapper);
- }
- });
- compiledDiv.appendChild(compiledText);
- elemWrapper.appendChild(compiledDiv);
- }
-
- if (collapseElement != null) {
- elemWrapper.appendChild(collapseElement);
- }
-
- return elemWrapper;
-}
-
-function createFrames(
- document: Document,
- resolvedFrames: StackFrame[],
- frameSettings: FrameSetting[],
- contextSize: number,
- errorName: ?string
-) {
- if (resolvedFrames.length !== frameSettings.length) {
- throw new Error(
- 'You must give a frame settings array of identical length to resolved frames.'
- );
- }
- const trace = document.createElement('div');
- applyStyles(trace, traceStyle);
-
- let index = 0;
- let critical = true;
- const omits: OmitsObject = { value: 0, bundle: 1, hasReachedAppCode: false };
- resolvedFrames.forEach(function(frame) {
- const lIndex = index++;
- const elem = createFrameWrapper(
- document,
- trace,
- createFrame.bind(
- undefined,
- document,
- frameSettings[lIndex],
- frame,
- contextSize,
- critical,
- omits,
- omits.bundle,
- trace,
- index === resolvedFrames.length,
- errorName
- ),
- lIndex,
- frameSettings,
- contextSize
- );
- if (elem == null) {
- return;
- }
- critical = false;
- trace.appendChild(elem);
- });
- //TODO: fix this
- omits.value = 0;
-
- return trace;
-}
-
-export { createFrames };
diff --git a/packages/react-error-overlay/src/components/overlay.js b/packages/react-error-overlay/src/components/overlay.js
deleted file mode 100644
index 69acf9ad43f..00000000000
--- a/packages/react-error-overlay/src/components/overlay.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * 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 { applyStyles } from '../utils/dom/css';
-import { containerStyle, overlayStyle, headerStyle } from '../styles';
-import { createClose } from './close';
-import { createFrames } from './frames';
-import { createFooter } from './footer';
-import type { CloseCallback } from './close';
-import type { StackFrame } from '../utils/stack-frame';
-import { updateAdditional } from './additional';
-import type { FrameSetting } from './frames';
-import type { SwitchCallback } from './additional';
-
-function createOverlay(
- document: Document,
- name: ?string,
- message: string,
- frames: StackFrame[],
- contextSize: number,
- currentError: number,
- totalErrors: number,
- switchCallback: SwitchCallback,
- closeCallback: CloseCallback
-): {
- overlay: HTMLDivElement,
- additional: HTMLDivElement,
-} {
- const frameSettings: FrameSetting[] = frames.map(() => ({ compiled: false }));
- // Create overlay
- const overlay = document.createElement('div');
- applyStyles(overlay, overlayStyle);
-
- // Create container
- const container = document.createElement('div');
- applyStyles(container, containerStyle);
- overlay.appendChild(container);
- container.appendChild(createClose(document, closeCallback));
-
- // Create "Errors X of Y" in case of multiple errors
- const additional = document.createElement('div');
- updateAdditional(
- document,
- additional,
- currentError,
- totalErrors,
- switchCallback
- );
- container.appendChild(additional);
-
- // Create header
- const header = document.createElement('div');
- applyStyles(header, headerStyle);
-
- // Make message prettier
- let finalMessage =
- message.match(/^\w*:/) || !name ? message : name + ': ' + message;
-
- finalMessage = finalMessage
- // TODO: maybe remove this prefix from fbjs?
- // It's just scaring people
- .replace(/^Invariant Violation:\s*/, '')
- // This is not helpful either:
- .replace(/^Warning:\s*/, '')
- // Break the actionable part to the next line.
- // AFAIK React 16+ should already do this.
- .replace(' Check the render method', '\n\nCheck the render method')
- .replace(' Check your code at', '\n\nCheck your code at');
-
- // Put it in the DOM
- header.appendChild(document.createTextNode(finalMessage));
- container.appendChild(header);
-
- // Create trace
- container.appendChild(
- createFrames(document, frames, frameSettings, contextSize, name)
- );
-
- // Show message
- container.appendChild(createFooter(document));
-
- return {
- overlay,
- additional,
- };
-}
-
-export { createOverlay };
diff --git a/packages/react-error-overlay/src/containers/CompileErrorContainer.js b/packages/react-error-overlay/src/containers/CompileErrorContainer.js
new file mode 100644
index 00000000000..bd193eb50b4
--- /dev/null
+++ b/packages/react-error-overlay/src/containers/CompileErrorContainer.js
@@ -0,0 +1,31 @@
+/**
+ * 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, { PureComponent } from 'react';
+import Overlay from '../components/Overlay';
+import Footer from '../components/Footer';
+import Header from '../components/Header';
+import CodeBlock from '../components/CodeBlock';
+import generateAnsiHTML from '../utils/generateAnsiHTML';
+
+class CompileErrorContainer extends PureComponent {
+ render() {
+ const { error } = this.props;
+ return (
+
+
+
+
+
+ );
+ }
+}
+
+export default CompileErrorContainer;
diff --git a/packages/react-error-overlay/src/containers/RuntimeError.js b/packages/react-error-overlay/src/containers/RuntimeError.js
new file mode 100644
index 00000000000..c64824137d2
--- /dev/null
+++ b/packages/react-error-overlay/src/containers/RuntimeError.js
@@ -0,0 +1,68 @@
+/**
+ * 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 from 'react';
+import Header from '../components/Header';
+import StackTrace from './StackTrace';
+import type { StackFrame } from '../utils/stack-frame';
+
+const wrapperStyle = {
+ display: 'flex',
+ flexDirection: 'column',
+};
+
+type ErrorRecord = {|
+ error: Error,
+ unhandledRejection: boolean,
+ contextSize: number,
+ stackFrames: StackFrame[],
+|};
+
+type Props = {|
+ errorRecord: ErrorRecord,
+ launchEditorEndpoint: ?string,
+|};
+
+function RuntimeError({ errorRecord, launchEditorEndpoint }: Props) {
+ const { error, unhandledRejection, contextSize, stackFrames } = errorRecord;
+ const errorName = unhandledRejection
+ ? 'Unhandled Rejection (' + error.name + ')'
+ : error.name;
+
+ // Make header prettier
+ const message = error.message;
+ let headerText =
+ message.match(/^\w*:/) || !errorName ? message : errorName + ': ' + message;
+
+ headerText = headerText
+ // TODO: maybe remove this prefix from fbjs?
+ // It's just scaring people
+ .replace(/^Invariant Violation:\s*/, '')
+ // This is not helpful either:
+ .replace(/^Warning:\s*/, '')
+ // Break the actionable part to the next line.
+ // AFAIK React 16+ should already do this.
+ .replace(' Check the render method', '\n\nCheck the render method')
+ .replace(' Check your code at', '\n\nCheck your code at');
+
+ return (
+
+
+
+
+ );
+}
+
+export default RuntimeError;
diff --git a/packages/react-error-overlay/src/containers/RuntimeErrorContainer.js b/packages/react-error-overlay/src/containers/RuntimeErrorContainer.js
new file mode 100644
index 00000000000..c84adb19492
--- /dev/null
+++ b/packages/react-error-overlay/src/containers/RuntimeErrorContainer.js
@@ -0,0 +1,77 @@
+/**
+ * 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, { PureComponent } from 'react';
+import Overlay from '../components/Overlay';
+import CloseButton from '../components/CloseButton';
+import NavigationBar from '../components/NavigationBar';
+import RuntimeError from './RuntimeError';
+import Footer from '../components/Footer';
+
+class RuntimeErrorContainer extends PureComponent {
+ state = {
+ currentIndex: 0,
+ };
+
+ previous = () => {
+ this.setState((state, props) => ({
+ currentIndex:
+ state.currentIndex > 0
+ ? state.currentIndex - 1
+ : props.errorRecords.length - 1,
+ }));
+ };
+
+ next = () => {
+ this.setState((state, props) => ({
+ currentIndex:
+ state.currentIndex < props.errorRecords.length - 1
+ ? state.currentIndex + 1
+ : 0,
+ }));
+ };
+
+ shortcutHandler = (key: string) => {
+ if (key === 'Escape') {
+ this.props.close();
+ } else if (key === 'ArrowLeft') {
+ this.previous();
+ } else if (key === 'ArrowRight') {
+ this.next();
+ }
+ };
+
+ render() {
+ const { errorRecords, close } = this.props;
+ const totalErrors = errorRecords.length;
+ return (
+
+
+ {totalErrors > 1 &&
+ }
+
+
+
+ );
+ }
+}
+
+export default RuntimeErrorContainer;
diff --git a/packages/react-error-overlay/src/containers/StackFrame.js b/packages/react-error-overlay/src/containers/StackFrame.js
new file mode 100644
index 00000000000..c95ce003f49
--- /dev/null
+++ b/packages/react-error-overlay/src/containers/StackFrame.js
@@ -0,0 +1,188 @@
+/**
+ * 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 (
+
+ );
+ }
+}
+
+export default StackFrame;
diff --git a/packages/react-error-overlay/src/components/code.js b/packages/react-error-overlay/src/containers/StackFrameCodeBlock.js
similarity index 71%
rename from packages/react-error-overlay/src/components/code.js
rename to packages/react-error-overlay/src/containers/StackFrameCodeBlock.js
index 580fe3b1be5..2ed685cff49 100644
--- a/packages/react-error-overlay/src/components/code.js
+++ b/packages/react-error-overlay/src/containers/StackFrameCodeBlock.js
@@ -8,33 +8,29 @@
*/
/* @flow */
-import type { ScriptLine } from '../utils/stack-frame';
+import React from 'react';
+import CodeBlock from '../components/CodeBlock';
import { applyStyles } from '../utils/dom/css';
import { absolutifyCaret } from '../utils/dom/absolutifyCaret';
-import {
- codeStyle,
- primaryErrorStyle,
- primaryPreStyle,
- secondaryErrorStyle,
- secondaryPreStyle,
-} from '../styles';
-
-import generateAnsiHtml from 'react-dev-utils/ansiHTML';
+import type { ScriptLine } from '../utils/stack-frame';
+import { primaryErrorStyle, secondaryErrorStyle } from '../styles';
+import generateAnsiHTML from '../utils/generateAnsiHTML';
import codeFrame from 'babel-code-frame';
-function createCode(
- document: Document,
- sourceLines: ScriptLine[],
+type StackFrameCodeBlockPropsType = {|
+ lines: ScriptLine[],
lineNum: number,
- columnNum: number | null,
+ columnNum: number,
contextSize: number,
main: boolean,
- onSourceClick: ?Function
-) {
+|};
+
+function StackFrameCodeBlock(props: StackFrameCodeBlockPropsType) {
+ const { lines, lineNum, columnNum, contextSize, main } = props;
const sourceCode = [];
let whiteSpace = Infinity;
- sourceLines.forEach(function(e) {
+ lines.forEach(function(e) {
const { content: text } = e;
const m = text.match(/^\s*/);
if (text === '') {
@@ -46,7 +42,7 @@ function createCode(
whiteSpace = 0;
}
});
- sourceLines.forEach(function(e) {
+ lines.forEach(function(e) {
let { content: text } = e;
const { lineNumber: line } = e;
@@ -65,11 +61,10 @@ function createCode(
linesBelow: contextSize,
}
);
- const htmlHighlight = generateAnsiHtml(ansiHighlight);
+ const htmlHighlight = generateAnsiHTML(ansiHighlight);
const code = document.createElement('code');
code.innerHTML = htmlHighlight;
absolutifyCaret(code);
- applyStyles(code, codeStyle);
const ccn = code.childNodes;
// eslint-disable-next-line
@@ -91,19 +86,8 @@ function createCode(
break oLoop;
}
}
- const pre = document.createElement('pre');
- applyStyles(pre, main ? primaryPreStyle : secondaryPreStyle);
- pre.appendChild(code);
-
- if (typeof onSourceClick === 'function') {
- let handler = onSourceClick;
- pre.style.cursor = 'pointer';
- pre.addEventListener('click', function() {
- handler();
- });
- }
- return pre;
+ return ;
}
-export { createCode };
+export default StackFrameCodeBlock;
diff --git a/packages/react-error-overlay/src/containers/StackTrace.js b/packages/react-error-overlay/src/containers/StackTrace.js
new file mode 100644
index 00000000000..4cb20bce128
--- /dev/null
+++ b/packages/react-error-overlay/src/containers/StackTrace.js
@@ -0,0 +1,95 @@
+/**
+ * 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 StackFrame from './StackFrame';
+import Collapsible from '../components/Collapsible';
+import { isInternalFile } from '../utils/isInternalFile';
+import { isBultinErrorName } from '../utils/isBultinErrorName';
+
+const traceStyle = {
+ fontSize: '1em',
+ flex: '0 1 auto',
+ minHeight: '0px',
+ overflow: 'auto',
+};
+
+class StackTrace extends Component {
+ renderFrames() {
+ const {
+ stackFrames,
+ errorName,
+ contextSize,
+ launchEditorEndpoint,
+ } = this.props;
+ const renderedFrames = [];
+ let hasReachedAppCode = false,
+ currentBundle = [],
+ bundleCount = 0;
+
+ stackFrames.forEach((frame, index) => {
+ const { fileName, _originalFileName: sourceFileName } = frame;
+ const isInternalUrl = isInternalFile(sourceFileName, fileName);
+ const isThrownIntentionally = !isBultinErrorName(errorName);
+ const shouldCollapse =
+ isInternalUrl && (isThrownIntentionally || hasReachedAppCode);
+
+ if (!isInternalUrl) {
+ hasReachedAppCode = true;
+ }
+
+ const frameEle = (
+
+ );
+ const lastElement = index === stackFrames.length - 1;
+
+ if (shouldCollapse) {
+ currentBundle.push(frameEle);
+ }
+
+ if (!shouldCollapse || lastElement) {
+ if (currentBundle.length === 1) {
+ renderedFrames.push(currentBundle[0]);
+ } else if (currentBundle.length > 1) {
+ bundleCount++;
+ renderedFrames.push(
+
+ {currentBundle}
+
+ );
+ }
+ currentBundle = [];
+ }
+
+ if (!shouldCollapse) {
+ renderedFrames.push(frameEle);
+ }
+ });
+
+ return renderedFrames;
+ }
+
+ render() {
+ return (
+
+ {this.renderFrames()}
+
+ );
+ }
+}
+
+export default StackTrace;
diff --git a/packages/react-error-overlay/src/effects/shortcuts.js b/packages/react-error-overlay/src/effects/shortcuts.js
deleted file mode 100644
index bf8fd6d5eb7..00000000000
--- a/packages/react-error-overlay/src/effects/shortcuts.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * 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 */
-const SHORTCUT_ESCAPE = 'SHORTCUT_ESCAPE',
- SHORTCUT_LEFT = 'SHORTCUT_LEFT',
- SHORTCUT_RIGHT = 'SHORTCUT_RIGHT';
-
-let boundKeyHandler = null;
-
-type ShortcutCallback = (type: string) => void;
-
-function keyHandler(callback: ShortcutCallback, e: KeyboardEvent) {
- const { key, keyCode, which } = e;
- if (key === 'Escape' || keyCode === 27 || which === 27) {
- callback(SHORTCUT_ESCAPE);
- } else if (key === 'ArrowLeft' || keyCode === 37 || which === 37) {
- callback(SHORTCUT_LEFT);
- } else if (key === 'ArrowRight' || keyCode === 39 || which === 39) {
- callback(SHORTCUT_RIGHT);
- }
-}
-
-function registerShortcuts(target: EventTarget, callback: ShortcutCallback) {
- if (boundKeyHandler !== null) {
- return;
- }
- boundKeyHandler = keyHandler.bind(undefined, callback);
- target.addEventListener('keydown', boundKeyHandler);
-}
-
-function unregisterShortcuts(target: EventTarget) {
- if (boundKeyHandler === null) {
- return;
- }
- target.removeEventListener('keydown', boundKeyHandler);
- boundKeyHandler = null;
-}
-
-export {
- SHORTCUT_ESCAPE,
- SHORTCUT_LEFT,
- SHORTCUT_RIGHT,
- registerShortcuts as register,
- unregisterShortcuts as unregister,
- keyHandler as handler,
-};
diff --git a/packages/react-error-overlay/src/index.js b/packages/react-error-overlay/src/index.js
index 4f3b2316727..ff4f1c71404 100644
--- a/packages/react-error-overlay/src/index.js
+++ b/packages/react-error-overlay/src/index.js
@@ -8,11 +8,158 @@
*/
/* @flow */
-import { inject, uninject } from './overlay';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import CompileErrorContainer from './containers/CompileErrorContainer';
+import RuntimeErrorContainer from './containers/RuntimeErrorContainer';
+import { listenToRuntimeErrors } from './listenToRuntimeErrors';
+import { iframeStyle, overlayStyle } from './styles';
+import { applyStyles } from './utils/dom/css';
-inject();
-if (module.hot && typeof module.hot.dispose === 'function') {
- module.hot.dispose(function() {
- uninject();
+import type { ErrorRecord } from './listenToRuntimeErrors';
+
+type RuntimeReportingOptions = {|
+ onError: () => void,
+ launchEditorEndpoint: string,
+|};
+
+let iframe: null | HTMLIFrameElement = null;
+let isLoadingIframe: boolean = false;
+
+let renderedElement: null | React.Element = null;
+let currentBuildError: null | string = null;
+let currentRuntimeErrorRecords: Array = [];
+let currentRuntimeErrorOptions: null | RuntimeReportingOptions = null;
+let stopListeningToRuntimeErrors: null | (() => void) = null;
+
+export function reportBuildError(error: string) {
+ currentBuildError = error;
+ update();
+}
+
+export function dismissBuildError() {
+ currentBuildError = null;
+ update();
+}
+
+export function startReportingRuntimeErrors(options: RuntimeReportingOptions) {
+ if (stopListeningToRuntimeErrors !== null) {
+ throw new Error('Already listening');
+ }
+ currentRuntimeErrorOptions = options;
+ listenToRuntimeErrors(errorRecord => {
+ try {
+ if (typeof options.onError === 'function') {
+ options.onError.call(null);
+ }
+ } finally {
+ handleRuntimeError(errorRecord);
+ }
});
}
+
+function handleRuntimeError(errorRecord) {
+ if (
+ currentRuntimeErrorRecords.some(({ error }) => error === errorRecord.error)
+ ) {
+ // Deduplicate identical errors.
+ // This fixes https://github.com/facebookincubator/create-react-app/issues/3011.
+ return;
+ }
+ currentRuntimeErrorRecords = currentRuntimeErrorRecords.concat([errorRecord]);
+ update();
+}
+
+function dismissRuntimeErrors() {
+ currentRuntimeErrorRecords = [];
+ update();
+}
+
+export function stopReportingRuntimeErrors() {
+ if (stopListeningToRuntimeErrors === null) {
+ throw new Error('Not currently listening');
+ }
+ currentRuntimeErrorOptions = null;
+ try {
+ stopListeningToRuntimeErrors();
+ } finally {
+ stopListeningToRuntimeErrors = null;
+ }
+}
+
+function update() {
+ renderedElement = render();
+ // Loading iframe can be either sync or async depending on the browser.
+ if (isLoadingIframe) {
+ // Iframe is loading.
+ // First render will happen soon--don't need to do anything.
+ return;
+ }
+ if (iframe) {
+ // Iframe has already loaded.
+ // Just update it.
+ updateIframeContent();
+ return;
+ }
+ // We need to schedule the first render.
+ isLoadingIframe = true;
+ const loadingIframe = window.document.createElement('iframe');
+ applyStyles(loadingIframe, iframeStyle);
+ loadingIframe.onload = function() {
+ const iframeDocument = loadingIframe.contentDocument;
+ if (iframeDocument != null && iframeDocument.body != null) {
+ iframeDocument.body.style.margin = '0';
+ // Keep popup within body boundaries for iOS Safari
+ iframeDocument.body.style['max-width'] = '100vw';
+ const iframeRoot = iframeDocument.createElement('div');
+ applyStyles(iframeRoot, overlayStyle);
+ iframeDocument.body.appendChild(iframeRoot);
+
+ // Ready! Now we can update the UI.
+ iframe = loadingIframe;
+ isLoadingIframe = false;
+ updateIframeContent();
+ }
+ };
+ const appDocument = window.document;
+ appDocument.body.appendChild(loadingIframe);
+}
+
+function render() {
+ if (currentBuildError) {
+ return ;
+ }
+ if (currentRuntimeErrorRecords.length > 0) {
+ if (!currentRuntimeErrorOptions) {
+ throw new Error('Expected options to be injected.');
+ }
+ return (
+
+ );
+ }
+ return null;
+}
+
+function updateIframeContent() {
+ if (iframe === null) {
+ throw new Error('Iframe has not been created yet.');
+ }
+ const iframeBody = iframe.contentDocument.body;
+ if (!iframeBody) {
+ throw new Error('Expected iframe to have a body.');
+ }
+ const iframeRoot = iframeBody.firstChild;
+ if (renderedElement === null) {
+ // Destroy iframe and force it to be recreated on next error
+ window.document.body.removeChild(iframe);
+ ReactDOM.unmountComponentAtNode(iframeRoot);
+ iframe = null;
+ return;
+ }
+ // Update the overlay
+ ReactDOM.render(renderedElement, iframeRoot);
+}
diff --git a/packages/react-error-overlay/src/listenToRuntimeErrors.js b/packages/react-error-overlay/src/listenToRuntimeErrors.js
new file mode 100644
index 00000000000..45c43fa5d40
--- /dev/null
+++ b/packages/react-error-overlay/src/listenToRuntimeErrors.js
@@ -0,0 +1,83 @@
+/**
+ * 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 {
+ register as registerError,
+ unregister as unregisterError,
+} from './effects/unhandledError';
+import {
+ register as registerPromise,
+ unregister as unregisterPromise,
+} from './effects/unhandledRejection';
+import {
+ register as registerStackTraceLimit,
+ unregister as unregisterStackTraceLimit,
+} from './effects/stackTraceLimit';
+import {
+ permanentRegister as permanentRegisterConsole,
+ registerReactStack,
+ unregisterReactStack,
+} from './effects/proxyConsole';
+import { massage as massageWarning } from './utils/warnings';
+import getStackFrames from './utils/getStackFrames';
+
+import type { StackFrame } from './utils/stack-frame';
+
+const CONTEXT_SIZE: number = 3;
+
+export type ErrorRecord = {|
+ error: Error,
+ unhandledRejection: boolean,
+ contextSize: number,
+ stackFrames: StackFrame[],
+|};
+
+export function listenToRuntimeErrors(crash: ErrorRecord => void) {
+ function crashWithFrames(error: Error, unhandledRejection = false) {
+ getStackFrames(error, unhandledRejection, CONTEXT_SIZE)
+ .then(stackFrames => {
+ if (stackFrames == null) {
+ return;
+ }
+ crash({
+ error,
+ unhandledRejection,
+ contextSize: CONTEXT_SIZE,
+ stackFrames,
+ });
+ })
+ .catch(e => {
+ console.log('Could not get the stack frames of error:', e);
+ });
+ }
+ registerError(window, error => crashWithFrames(error, false));
+ registerPromise(window, error => crashWithFrames(error, true));
+ registerStackTraceLimit();
+ registerReactStack();
+ permanentRegisterConsole('error', (warning, stack) => {
+ const data = massageWarning(warning, stack);
+ crashWithFrames(
+ // $FlowFixMe
+ {
+ message: data.message,
+ stack: data.stack,
+ __unmap_source: '/static/js/bundle.js',
+ },
+ false
+ );
+ });
+
+ return function stopListening() {
+ unregisterStackTraceLimit();
+ unregisterPromise(window);
+ unregisterError(window);
+ unregisterReactStack();
+ };
+}
diff --git a/packages/react-error-overlay/src/overlay.js b/packages/react-error-overlay/src/overlay.js
deleted file mode 100644
index 181cb02716d..00000000000
--- a/packages/react-error-overlay/src/overlay.js
+++ /dev/null
@@ -1,235 +0,0 @@
-/**
- * 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 {
- register as registerError,
- unregister as unregisterError,
-} from './effects/unhandledError';
-import {
- register as registerPromise,
- unregister as unregisterPromise,
-} from './effects/unhandledRejection';
-import {
- register as registerShortcuts,
- unregister as unregisterShortcuts,
- handler as keyEventHandler,
- SHORTCUT_ESCAPE,
- SHORTCUT_LEFT,
- SHORTCUT_RIGHT,
-} from './effects/shortcuts';
-import {
- register as registerStackTraceLimit,
- unregister as unregisterStackTraceLimit,
-} from './effects/stackTraceLimit';
-import {
- permanentRegister as permanentRegisterConsole,
- registerReactStack,
- unregisterReactStack,
-} from './effects/proxyConsole';
-import { massage as massageWarning } from './utils/warnings';
-
-import {
- consume as consumeError,
- getErrorRecord,
- drain as drainErrors,
-} from './utils/errorRegister';
-import type { ErrorRecordReference } from './utils/errorRegister';
-
-import type { StackFrame } from './utils/stack-frame';
-import { iframeStyle } from './styles';
-import { applyStyles } from './utils/dom/css';
-import { createOverlay } from './components/overlay';
-import { updateAdditional } from './components/additional';
-
-const CONTEXT_SIZE: number = 3;
-let iframeReference: HTMLIFrameElement | null = null;
-let additionalReference = null;
-let errorReferences: ErrorRecordReference[] = [];
-let currReferenceIndex: number = -1;
-
-function render(name: ?string, message: string, resolvedFrames: StackFrame[]) {
- disposeCurrentView();
-
- const iframe = window.document.createElement('iframe');
- applyStyles(iframe, iframeStyle);
- iframeReference = iframe;
- iframe.onload = () => {
- if (iframeReference == null) {
- return;
- }
- const w = iframeReference.contentWindow;
- const document = iframeReference.contentDocument;
-
- const { overlay, additional } = createOverlay(
- document,
- name,
- message,
- resolvedFrames,
- CONTEXT_SIZE,
- currReferenceIndex + 1,
- errorReferences.length,
- offset => {
- switchError(offset);
- },
- () => {
- unmount();
- }
- );
- if (w != null) {
- w.onkeydown = event => {
- keyEventHandler(type => shortcutHandler(type), event);
- };
- }
- if (document.body != null) {
- document.body.style.margin = '0';
- // Keep popup within body boundaries for iOS Safari
- // $FlowFixMe
- document.body.style['max-width'] = '100vw';
-
- (document.body: any).appendChild(overlay);
- }
- additionalReference = additional;
- };
- window.document.body.appendChild(iframe);
-}
-
-function renderErrorByIndex(index: number) {
- currReferenceIndex = index;
-
- const { error, unhandledRejection, enhancedFrames } = getErrorRecord(
- errorReferences[index]
- );
-
- if (unhandledRejection) {
- render(
- 'Unhandled Rejection (' + error.name + ')',
- error.message,
- enhancedFrames
- );
- } else {
- render(error.name, error.message, enhancedFrames);
- }
-}
-
-function switchError(offset) {
- if (errorReferences.length === 0) {
- return;
- }
-
- let nextView = currReferenceIndex + offset;
-
- if (nextView < 0) {
- nextView = errorReferences.length - 1;
- } else if (nextView >= errorReferences.length) {
- nextView = 0;
- }
-
- renderErrorByIndex(nextView);
-}
-
-function disposeCurrentView() {
- if (iframeReference === null) {
- return;
- }
- window.document.body.removeChild(iframeReference);
- iframeReference = null;
- additionalReference = null;
-}
-
-function unmount() {
- disposeCurrentView();
- drainErrors();
- errorReferences = [];
- currReferenceIndex = -1;
-}
-
-function crash(error: Error, unhandledRejection = false) {
- if (module.hot && typeof module.hot.decline === 'function') {
- module.hot.decline();
- }
- consumeError(error, unhandledRejection, CONTEXT_SIZE)
- .then(ref => {
- if (ref == null) {
- return;
- }
- errorReferences.push(ref);
- if (iframeReference !== null && additionalReference !== null) {
- updateAdditional(
- iframeReference.contentDocument,
- additionalReference,
- currReferenceIndex + 1,
- errorReferences.length,
- offset => {
- switchError(offset);
- }
- );
- } else {
- if (errorReferences.length !== 1) {
- throw new Error('Something is *really* wrong.');
- }
- renderErrorByIndex((currReferenceIndex = 0));
- }
- })
- .catch(e => {
- console.log('Could not consume error:', e);
- });
-}
-
-function shortcutHandler(type: string) {
- switch (type) {
- case SHORTCUT_ESCAPE: {
- unmount();
- break;
- }
- case SHORTCUT_LEFT: {
- switchError(-1);
- break;
- }
- case SHORTCUT_RIGHT: {
- switchError(1);
- break;
- }
- default: {
- //TODO: this
- break;
- }
- }
-}
-
-function inject() {
- registerError(window, error => crash(error));
- registerPromise(window, error => crash(error, true));
- registerShortcuts(window, shortcutHandler);
- registerStackTraceLimit();
-
- registerReactStack();
- permanentRegisterConsole('error', (warning, stack) => {
- const data = massageWarning(warning, stack);
- crash(
- // $FlowFixMe
- {
- message: data.message,
- stack: data.stack,
- __unmap_source: '/static/js/bundle.js',
- },
- false
- );
- });
-}
-
-function uninject() {
- unregisterStackTraceLimit();
- unregisterShortcuts(window);
- unregisterPromise(window);
- unregisterError(window);
- unregisterReactStack();
-}
-
-export { inject, uninject };
diff --git a/packages/react-error-overlay/src/styles.js b/packages/react-error-overlay/src/styles.js
index bf17561d721..d6557c5d95f 100644
--- a/packages/react-error-overlay/src/styles.js
+++ b/packages/react-error-overlay/src/styles.js
@@ -24,7 +24,7 @@ const iframeStyle = {
width: '100%',
height: '100%',
border: 'none',
- 'z-index': 2147483647 - 1, // below the compile error overlay
+ 'z-index': 2147483647,
};
const overlayStyle = {
@@ -35,84 +35,6 @@ const overlayStyle = {
'background-color': white,
};
-const containerStyle = {
- position: 'relative',
- display: 'inline-flex',
- 'flex-direction': 'column',
- height: '100%',
- width: '1024px',
- 'max-width': '100%',
- 'overflow-x': 'hidden',
- 'overflow-y': 'auto',
- padding: '0.5rem',
- 'box-sizing': 'border-box',
- 'text-align': 'left',
- 'font-family': 'Consolas, Menlo, monospace',
- 'font-size': '11px',
- 'white-space': 'pre-wrap',
- 'word-break': 'break-word',
- 'line-height': 1.5,
- color: black,
-};
-
-const hintsStyle = {
- color: darkGray,
-};
-
-const hintStyle = {
- padding: '0.5em 1em',
- cursor: 'pointer',
-};
-
-const closeButtonStyle = {
- color: black,
- 'line-height': '1rem',
- 'font-size': '1.5rem',
- padding: '1rem',
- cursor: 'pointer',
- position: 'absolute',
- right: 0,
- top: 0,
-};
-
-const additionalChildStyle = {
- 'margin-bottom': '0.5rem',
-};
-
-const headerStyle = {
- 'font-size': '2em',
- 'font-family': 'sans-serif',
- color: red,
- 'white-space': 'pre-wrap',
- // Top bottom margin spaces header
- // Right margin revents overlap with close button
- margin: '0 2rem 0.75rem 0',
- flex: '0 0 auto',
- 'max-height': '50%',
- overflow: 'auto',
-};
-
-const functionNameStyle = {};
-
-const linkStyle = {
- 'font-size': '0.9em',
- 'margin-bottom': '0.9em',
-};
-
-const anchorStyle = {
- 'text-decoration': 'none',
- color: darkGray,
-};
-
-const traceStyle = {
- 'font-size': '1em',
- flex: '0 1 auto',
- 'min-height': '0px',
- overflow: 'auto',
-};
-
-const depStyle = {};
-
const primaryErrorStyle = {
'background-color': lightRed,
};
@@ -121,104 +43,14 @@ const secondaryErrorStyle = {
'background-color': yellow,
};
-const omittedFramesCollapsedStyle = {
- color: black,
- cursor: 'pointer',
- 'margin-bottom': '1.5em',
-};
-
-const omittedFramesExpandedStyle = {
- color: black,
- cursor: 'pointer',
- 'margin-bottom': '0.6em',
-};
-
-const _preStyle = {
- display: 'block',
- padding: '0.5em',
- 'margin-top': '0.5em',
- 'margin-bottom': '0.5em',
- 'overflow-x': 'auto',
- 'white-space': 'pre-wrap',
- 'border-radius': '0.25rem',
-};
-const primaryPreStyle = Object.assign({}, _preStyle, {
- 'background-color': redTransparent,
-});
-const secondaryPreStyle = Object.assign({}, _preStyle, {
- 'background-color': yellowTransparent,
-});
-
-const toggleStyle = {
- 'margin-bottom': '1.5em',
- color: darkGray,
- cursor: 'pointer',
-};
-
-const codeStyle = {
- 'font-family': 'Consolas, Menlo, monospace',
-};
-
-const hiddenStyle = {
- display: 'none',
-};
-
-const groupStyle = {
- 'margin-right': '1em',
-};
-
-const _groupElemStyle = {
- 'background-color': redTransparent,
- color: red,
- border: 'none',
- 'border-radius': '4px',
- padding: '3px 6px',
- cursor: 'pointer',
-};
-
-const groupElemLeft = Object.assign({}, _groupElemStyle, {
- 'border-top-right-radius': '0px',
- 'border-bottom-right-radius': '0px',
- 'margin-right': '1px',
-});
-
-const groupElemRight = Object.assign({}, _groupElemStyle, {
- 'border-top-left-radius': '0px',
- 'border-bottom-left-radius': '0px',
-});
-
-const footerStyle = {
- 'font-family': 'sans-serif',
- color: darkGray,
- 'margin-top': '0.5rem',
- flex: '0 0 auto',
-};
-
export {
- containerStyle,
iframeStyle,
overlayStyle,
- hintsStyle,
- hintStyle,
- closeButtonStyle,
- additionalChildStyle,
- headerStyle,
- functionNameStyle,
- linkStyle,
- anchorStyle,
- traceStyle,
- depStyle,
primaryErrorStyle,
- primaryPreStyle,
secondaryErrorStyle,
- secondaryPreStyle,
- omittedFramesCollapsedStyle,
- omittedFramesExpandedStyle,
- toggleStyle,
- codeStyle,
- hiddenStyle,
- groupStyle,
- groupElemLeft,
- groupElemRight,
- footerStyle,
+ black,
+ darkGray,
+ red,
+ redTransparent,
+ yellowTransparent,
};
diff --git a/packages/react-error-overlay/src/utils/dom/enableTabClick.js b/packages/react-error-overlay/src/utils/dom/enableTabClick.js
deleted file mode 100644
index b663c055f8c..00000000000
--- a/packages/react-error-overlay/src/utils/dom/enableTabClick.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * 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 */
-function enableTabClick(node: Element) {
- node.setAttribute('tabindex', '0');
- node.addEventListener('keydown', function(e: KeyboardEvent) {
- const { key, which, keyCode } = e;
- if (key === 'Enter' || which === 13 || keyCode === 13) {
- e.preventDefault();
- if (typeof e.target.click === 'function') {
- e.target.click();
- }
- }
- });
-}
-
-export { enableTabClick };
diff --git a/packages/react-dev-utils/ansiHTML.js b/packages/react-error-overlay/src/utils/generateAnsiHTML.js
similarity index 92%
rename from packages/react-dev-utils/ansiHTML.js
rename to packages/react-error-overlay/src/utils/generateAnsiHTML.js
index 9f53cea67a3..509daddc23c 100644
--- a/packages/react-dev-utils/ansiHTML.js
+++ b/packages/react-error-overlay/src/utils/generateAnsiHTML.js
@@ -7,9 +7,12 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-'use strict';
+/* @flow */
-var Anser = require('anser');
+import Anser from 'anser';
+import { AllHtmlEntities as Entities } from 'html-entities';
+
+var entities = new Entities();
// Color scheme inspired by https://chriskempson.github.io/base16/css/base16-github.css
// var base00 = 'ffffff'; // Default Background
@@ -60,8 +63,8 @@ var anserMap = {
'ansi-white': 'darkgrey',
};
-function ansiHTML(txt) {
- var arr = new Anser().ansiToJson(txt, {
+function generateAnsiHTML(txt: string): string {
+ var arr = new Anser().ansiToJson(entities.encode(txt), {
use_classes: true,
});
@@ -102,4 +105,4 @@ function ansiHTML(txt) {
return result;
}
-module.exports = ansiHTML;
+export default generateAnsiHTML;
diff --git a/packages/react-error-overlay/src/utils/getLinesAround.js b/packages/react-error-overlay/src/utils/getLinesAround.js
index a03e09a5817..7cb2ea5c54d 100644
--- a/packages/react-error-overlay/src/utils/getLinesAround.js
+++ b/packages/react-error-overlay/src/utils/getLinesAround.js
@@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-//@flow
+/* @flow */
import { ScriptLine } from './stack-frame';
/**
diff --git a/packages/react-error-overlay/src/utils/getPrettyURL.js b/packages/react-error-overlay/src/utils/getPrettyURL.js
new file mode 100644
index 00000000000..47b834d08d0
--- /dev/null
+++ b/packages/react-error-overlay/src/utils/getPrettyURL.js
@@ -0,0 +1,49 @@
+/**
+ * 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 */
+function getPrettyURL(
+ sourceFileName: ?string,
+ sourceLineNumber: ?number,
+ sourceColumnNumber: ?number,
+ fileName: ?string,
+ lineNumber: ?number,
+ columnNumber: ?number,
+ compiled: boolean
+): string {
+ let prettyURL;
+ if (!compiled && sourceFileName && typeof sourceLineNumber === 'number') {
+ // Remove everything up to the first /src/ or /node_modules/
+ const trimMatch = /^[/|\\].*?[/|\\]((src|node_modules)[/|\\].*)/.exec(
+ sourceFileName
+ );
+ if (trimMatch && trimMatch[1]) {
+ prettyURL = trimMatch[1];
+ } else {
+ prettyURL = sourceFileName;
+ }
+ prettyURL += ':' + sourceLineNumber;
+ // Note: we intentionally skip 0's because they're produced by cheap Webpack maps
+ if (sourceColumnNumber) {
+ prettyURL += ':' + sourceColumnNumber;
+ }
+ } else if (fileName && typeof lineNumber === 'number') {
+ prettyURL = fileName + ':' + lineNumber;
+ // Note: we intentionally skip 0's because they're produced by cheap Webpack maps
+ if (columnNumber) {
+ prettyURL += ':' + columnNumber;
+ }
+ } else {
+ prettyURL = 'unknown';
+ }
+ return prettyURL.replace('webpack://', '.');
+}
+
+export { getPrettyURL };
+export default getPrettyURL;
diff --git a/packages/react-error-overlay/src/utils/getSourceMap.js b/packages/react-error-overlay/src/utils/getSourceMap.js
index 80dd6002e0e..1d8405519bd 100644
--- a/packages/react-error-overlay/src/utils/getSourceMap.js
+++ b/packages/react-error-overlay/src/utils/getSourceMap.js
@@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-//@flow
+/* @flow */
import { SourceMapConsumer } from 'source-map';
/**
diff --git a/packages/react-error-overlay/src/utils/errorRegister.js b/packages/react-error-overlay/src/utils/getStackFrames.js
similarity index 59%
rename from packages/react-error-overlay/src/utils/errorRegister.js
rename to packages/react-error-overlay/src/utils/getStackFrames.js
index 0bd3379cef1..e5a4073106b 100644
--- a/packages/react-error-overlay/src/utils/errorRegister.js
+++ b/packages/react-error-overlay/src/utils/getStackFrames.js
@@ -13,22 +13,11 @@ import { parse } from './parser';
import { map } from './mapper';
import { unmap } from './unmapper';
-type ErrorRecord = {
- error: Error,
- unhandledRejection: boolean,
- contextSize: number,
- enhancedFrames: StackFrame[],
-};
-type ErrorRecordReference = number;
-const recorded: ErrorRecord[] = [];
-
-let errorsConsumed: ErrorRecordReference = 0;
-
-function consume(
+function getStackFrames(
error: Error,
unhandledRejection: boolean = false,
contextSize: number = 3
-): Promise {
+): Promise {
const parsedFrames = parse(error);
let enhancedFramesPromise;
if (error.__unmap_source) {
@@ -49,32 +38,13 @@ function consume(
) {
return null;
}
- enhancedFrames = enhancedFrames.filter(
+ return enhancedFrames.filter(
({ functionName }) =>
functionName == null ||
functionName.indexOf('__stack_frame_overlay_proxy_console__') === -1
);
- recorded[++errorsConsumed] = {
- error,
- unhandledRejection,
- contextSize,
- enhancedFrames,
- };
- return errorsConsumed;
});
}
-function getErrorRecord(ref: ErrorRecordReference): ErrorRecord {
- return recorded[ref];
-}
-
-function drain() {
- // $FlowFixMe
- const keys = Object.keys(recorded);
- for (let index = 0; index < keys.length; ++index) {
- delete recorded[keys[index]];
- }
-}
-
-export { consume, getErrorRecord, drain };
-export type { ErrorRecordReference };
+export default getStackFrames;
+export { getStackFrames };
diff --git a/packages/react-error-overlay/src/utils/isBultinErrorName.js b/packages/react-error-overlay/src/utils/isBultinErrorName.js
new file mode 100644
index 00000000000..cf732b838f4
--- /dev/null
+++ b/packages/react-error-overlay/src/utils/isBultinErrorName.js
@@ -0,0 +1,27 @@
+/**
+ * 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 */
+function isBultinErrorName(errorName: ?string) {
+ switch (errorName) {
+ case 'EvalError':
+ case 'InternalError':
+ case 'RangeError':
+ case 'ReferenceError':
+ case 'SyntaxError':
+ case 'TypeError':
+ case 'URIError':
+ return true;
+ default:
+ return false;
+ }
+}
+
+export { isBultinErrorName };
+export default isBultinErrorName;
diff --git a/packages/react-error-overlay/src/utils/mapper.js b/packages/react-error-overlay/src/utils/mapper.js
index fc3eb2266e6..656c216c144 100644
--- a/packages/react-error-overlay/src/utils/mapper.js
+++ b/packages/react-error-overlay/src/utils/mapper.js
@@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-// @flow
+/* @flow */
import StackFrame from './stack-frame';
import { getSourceMap } from './getSourceMap';
import { getLinesAround } from './getLinesAround';
diff --git a/packages/react-error-overlay/src/utils/parser.js b/packages/react-error-overlay/src/utils/parser.js
index bfbb85f7969..1cbee1d1ccc 100644
--- a/packages/react-error-overlay/src/utils/parser.js
+++ b/packages/react-error-overlay/src/utils/parser.js
@@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-// @flow
+/* @flow */
import StackFrame from './stack-frame';
const regexExtractLocation = /\(?(.+?)(?::(\d+))?(?::(\d+))?\)?$/;
diff --git a/packages/react-error-overlay/src/utils/stack-frame.js b/packages/react-error-overlay/src/utils/stack-frame.js
index ae28cb53585..49f9da7fa11 100644
--- a/packages/react-error-overlay/src/utils/stack-frame.js
+++ b/packages/react-error-overlay/src/utils/stack-frame.js
@@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-//@flow
+/* @flow */
/** A container holding a script line. */
class ScriptLine {
@@ -54,6 +54,20 @@ class StackFrame {
sourceColumnNumber: number | null = null,
sourceScriptCode: ScriptLine[] | null = null
) {
+ if (functionName && functionName.indexOf('Object.') === 0) {
+ functionName = functionName.slice('Object.'.length);
+ }
+ if (
+ // Chrome has a bug with inferring function.name:
+ // https://github.com/facebookincubator/create-react-app/issues/2097
+ // Let's ignore a meaningless name we get for top-level modules.
+ functionName === 'friendlySyntaxErrorLabel' ||
+ functionName === 'exports.__esModule' ||
+ functionName === '' ||
+ !functionName
+ ) {
+ functionName = null;
+ }
this.functionName = functionName;
this.fileName = fileName;
@@ -72,8 +86,8 @@ class StackFrame {
/**
* Returns the name of this function.
*/
- getFunctionName(): string | null {
- return this.functionName;
+ getFunctionName(): string {
+ return this.functionName || '(anonymous function)';
}
/**
@@ -98,11 +112,9 @@ class StackFrame {
* Returns a pretty version of this stack frame.
*/
toString(): string {
- const f = this.getFunctionName();
- if (f == null) {
- return this.getSource();
- }
- return `${f} (${this.getSource()})`;
+ const functionName = this.getFunctionName();
+ const source = this.getSource();
+ return `${functionName}${source ? ` (${source})` : ``}`;
}
}
diff --git a/packages/react-error-overlay/src/utils/unmapper.js b/packages/react-error-overlay/src/utils/unmapper.js
index b01736d74aa..60b2bee432b 100644
--- a/packages/react-error-overlay/src/utils/unmapper.js
+++ b/packages/react-error-overlay/src/utils/unmapper.js
@@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-// @flow
+/* @flow */
import StackFrame from './stack-frame';
import { getSourceMap } from './getSourceMap';
import { getLinesAround } from './getLinesAround';
diff --git a/packages/react-error-overlay/src/utils/warnings.js b/packages/react-error-overlay/src/utils/warnings.js
index 8dcd7e1ee30..bcc54ceb733 100644
--- a/packages/react-error-overlay/src/utils/warnings.js
+++ b/packages/react-error-overlay/src/utils/warnings.js
@@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-// @flow
+/* @flow */
import type { ReactFrame } from '../effects/proxyConsole';
function stripInlineStacktrace(message: string): string {
diff --git a/packages/react-scripts/bin/react-scripts.js b/packages/react-scripts/bin/react-scripts.js
index cb52fe71b09..a8f37e813ed 100755
--- a/packages/react-scripts/bin/react-scripts.js
+++ b/packages/react-scripts/bin/react-scripts.js
@@ -11,8 +11,13 @@
'use strict';
const spawn = require('react-dev-utils/crossSpawn');
-const script = process.argv[2];
-const args = process.argv.slice(3);
+const args = process.argv.slice(2);
+
+const scriptIndex = args.findIndex(
+ x => x === 'build' || x === 'eject' || x === 'start' || x === 'test'
+);
+const script = scriptIndex === -1 ? args[0] : args[scriptIndex];
+const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];
switch (script) {
case 'build':
@@ -21,7 +26,9 @@ switch (script) {
case 'test': {
const result = spawn.sync(
'node',
- [require.resolve('../scripts/' + script)].concat(args),
+ nodeArgs
+ .concat(require.resolve('../scripts/' + script))
+ .concat(args.slice(scriptIndex + 1)),
{ stdio: 'inherit' }
);
if (result.signal) {
diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js
index 658f0b0ef80..b2f40dc9bd8 100644
--- a/packages/react-scripts/config/webpack.config.dev.js
+++ b/packages/react-scripts/config/webpack.config.dev.js
@@ -56,8 +56,6 @@ module.exports = {
require.resolve('react-dev-utils/webpackHotDevClient'),
// We ship a few polyfills by default:
require.resolve('./polyfills'),
- // Errors should be considered fatal in development
- require.resolve('react-error-overlay'),
// Finally, this is your app's code:
paths.appIndexJs,
// We include the app code last so that if there is a runtime error during
@@ -117,7 +115,7 @@ module.exports = {
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
- new ModuleScopePlugin(paths.appSrc),
+ new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
module: {
diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js
index 256cc49b828..7e21cab0479 100644
--- a/packages/react-scripts/config/webpack.config.prod.js
+++ b/packages/react-scripts/config/webpack.config.prod.js
@@ -29,6 +29,8 @@ const publicPath = paths.servedPath;
// Some apps do not use client-side routing with pushState.
// For these, "homepage" can be set to "." to enable relative asset paths.
const shouldUseRelativeAssetPaths = publicPath === './';
+// Source maps are resource heavy and can cause out of memory issue for large source files.
+const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
@@ -62,7 +64,7 @@ module.exports = {
bail: true,
// We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
- devtool: 'source-map',
+ devtool: shouldUseSourceMap ? 'source-map' : false,
// In production, we only want to load the polyfills and the app code.
entry: [require.resolve('./polyfills'), paths.appIndexJs],
output: {
@@ -117,7 +119,7 @@ module.exports = {
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
- new ModuleScopePlugin(paths.appSrc),
+ new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
module: {
@@ -205,7 +207,7 @@ module.exports = {
options: {
importLoaders: 1,
minimize: true,
- sourceMap: true,
+ sourceMap: shouldUseSourceMap,
},
},
{
@@ -308,7 +310,7 @@ module.exports = {
// https://github.com/facebookincubator/create-react-app/issues/2488
ascii_only: true,
},
- sourceMap: true,
+ sourceMap: shouldUseSourceMap,
}),
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({
diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js
index 2a351e668bb..9c3889abae8 100644
--- a/packages/react-scripts/config/webpackDevServer.config.js
+++ b/packages/react-scripts/config/webpackDevServer.config.js
@@ -10,7 +10,7 @@
// @remove-on-eject-end
'use strict';
-const errorOverlayMiddleware = require('react-error-overlay/middleware');
+const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const config = require('./webpack.config.dev');
const paths = require('./paths');
diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json
index f32df2be282..564f533a6fe 100644
--- a/packages/react-scripts/package.json
+++ b/packages/react-scripts/package.json
@@ -1,6 +1,6 @@
{
"name": "react-scripts",
- "version": "1.0.10",
+ "version": "1.0.12",
"description": "Configuration and scripts for Create React App.",
"repository": "facebookincubator/create-react-app",
"license": "BSD-3-Clause",
@@ -21,43 +21,43 @@
"react-scripts": "./bin/react-scripts.js"
},
"dependencies": {
- "autoprefixer": "7.1.1",
+ "autoprefixer": "7.1.2",
"babel-core": "6.25.0",
"babel-eslint": "7.2.3",
"babel-jest": "20.0.3",
- "babel-loader": "7.0.0",
- "babel-preset-react-app": "^3.0.1",
- "babel-runtime": "6.23.0",
+ "babel-loader": "7.1.1",
+ "babel-preset-react-app": "^3.0.2",
+ "babel-runtime": "6.26.0",
"case-sensitive-paths-webpack-plugin": "2.1.1",
"chalk": "1.1.3",
"css-loader": "0.28.4",
"dotenv": "4.0.0",
- "eslint": "4.1.1",
- "eslint-config-react-app": "^1.0.5",
+ "eslint": "4.4.1",
+ "eslint-config-react-app": "^2.0.0",
"eslint-loader": "1.9.0",
- "eslint-plugin-flowtype": "2.34.0",
+ "eslint-plugin-flowtype": "2.35.0",
"eslint-plugin-import": "2.7.0",
"eslint-plugin-jsx-a11y": "5.1.1",
"eslint-plugin-react": "7.1.0",
- "extract-text-webpack-plugin": "2.1.2",
+ "extract-text-webpack-plugin": "3.0.0",
"file-loader": "0.11.2",
"fs-extra": "3.0.1",
"html-webpack-plugin": "2.29.0",
"jest": "20.0.4",
"node-sass": "^4.5.3",
"object-assign": "4.1.1",
- "postcss-flexbugs-fixes": "3.0.0",
+ "postcss-flexbugs-fixes": "3.2.0",
"postcss-loader": "2.0.6",
- "promise": "7.1.1",
- "react-dev-utils": "^3.0.2",
"react-error-overlay": "^1.0.9",
"sass-loader": "^6.0.6",
+ "promise": "8.0.1",
+ "react-dev-utils": "^4.0.0",
"style-loader": "0.18.2",
- "sw-precache-webpack-plugin": "0.11.3",
+ "sw-precache-webpack-plugin": "0.11.4",
"url-loader": "0.5.9",
- "webpack": "2.6.1",
- "webpack-dev-server": "2.5.1",
- "webpack-manifest-plugin": "1.1.0",
+ "webpack": "3.5.1",
+ "webpack-dev-server": "2.7.1",
+ "webpack-manifest-plugin": "1.2.1",
"whatwg-fetch": "2.0.3"
},
"devDependencies": {
diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js
index b9b65f5313d..7691bfd5282 100644
--- a/packages/react-scripts/scripts/build.js
+++ b/packages/react-scripts/scripts/build.js
@@ -34,6 +34,7 @@ const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
+const printBuildError = require('react-dev-utils/printBuildError');
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild;
@@ -104,7 +105,7 @@ measureFileSizesBeforeBuild(paths.appBuild)
},
err => {
console.log(chalk.red('Failed to compile.\n'));
- console.log((err.message || err) + '\n');
+ printBuildError(err);
process.exit(1);
}
);
@@ -121,6 +122,11 @@ function build(previousFileSizes) {
}
const messages = formatWebpackMessages(stats.toJson({}, true));
if (messages.errors.length) {
+ // Only keep the first error. Others are often indicative
+ // of the same problem, but confuse the reader with noise.
+ if (messages.errors.length > 1) {
+ messages.errors.length = 1;
+ }
return reject(new Error(messages.errors.join('\n\n')));
}
if (
diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md
index 82642ca3a8e..c55ccdf949f 100644
--- a/packages/react-scripts/template/README.md
+++ b/packages/react-scripts/template/README.md
@@ -68,6 +68,7 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Getting Started with Storybook](#getting-started-with-storybook)
- [Getting Started with Styleguidist](#getting-started-with-styleguidist)
- [Making a Progressive Web App](#making-a-progressive-web-app)
+ - [Opting Out of Caching](#opting-out-of-caching)
- [Offline-First Considerations](#offline-first-considerations)
- [Progressive Web App Metadata](#progressive-web-app-metadata)
- [Analyzing the Bundle Size](#analyzing-the-bundle-size)
@@ -80,7 +81,6 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Firebase](#firebase)
- [GitHub Pages](#github-pages)
- [Heroku](#heroku)
- - [Modulus](#modulus)
- [Netlify](#netlify)
- [Now](#now)
- [S3 and CloudFront](#s3-and-cloudfront)
@@ -91,6 +91,7 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [`npm test` hangs on macOS Sierra](#npm-test-hangs-on-macos-sierra)
- [`npm run build` exits too early](#npm-run-build-exits-too-early)
- [`npm run build` fails on Heroku](#npm-run-build-fails-on-heroku)
+ - [`npm run build` fails to minify](#npm-run-build-fails-to-minify)
- [Moment.js locales are missing](#momentjs-locales-are-missing)
- [Something Missing?](#something-missing)
@@ -242,9 +243,11 @@ If you want to enforce a coding style for your project, consider using [Prettier
## Debugging in the Editor
-**This feature is currently only supported by [Visual Studio Code](https://code.visualstudio.com) editor.**
+**This feature is currently only supported by [Visual Studio Code](https://code.visualstudio.com) and [WebStorm](https://www.jetbrains.com/webstorm/).**
-Visual Studio Code supports debugging out of the box with Create React App. This enables you as a developer to write and debug your React code without leaving the editor, and most importantly it enables you to have a continuous development workflow, where context switching is minimal, as you don’t have to switch between tools.
+Visual Studio Code and WebStorm support debugging out of the box with Create React App. This enables you as a developer to write and debug your React code without leaving the editor, and most importantly it enables you to have a continuous development workflow, where context switching is minimal, as you don’t have to switch between tools.
+
+### Visual Studio Code
You would need to have the latest version of [VS Code](https://code.visualstudio.com) and VS Code [Chrome Debugger Extension](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) installed.
@@ -266,9 +269,22 @@ Then add the block below to your `launch.json` file and put it inside the `.vsco
}]
}
```
+>Note: the URL may be different if you've made adjustments via the [HOST or PORT environment variables](#advanced-configuration).
Start your app by running `npm start`, and start debugging in VS Code by pressing `F5` or by clicking the green debug icon. You can now write code, set breakpoints, make changes to the code, and debug your newly modified code—all from your editor.
+### WebStorm
+
+You would need to have [WebStorm](https://www.jetbrains.com/webstorm/) and [JetBrains IDE Support](https://chrome.google.com/webstore/detail/jetbrains-ide-support/hmhgeddbohgjknpmjagkdomcpobmllji) Chrome extension installed.
+
+In the WebStorm menu `Run` select `Edit Configurations...`. Then click `+` and select `JavaScript Debug`. Paste `http://localhost:3000` into the URL field and save the configuration.
+
+>Note: the URL may be different if you've made adjustments via the [HOST or PORT environment variables](#advanced-configuration).
+
+Start your app by running `npm start`, then press `^D` on macOS or `F9` on Windows and Linux or click the green debug icon to start debugging in WebStorm.
+
+The same way you can debug your application in IntelliJ IDEA Ultimate, PhpStorm, PyCharm Pro, and RubyMine.
+
## Formatting Code Automatically
Prettier is an opinionated code formatter with support for JavaScript, CSS and JSON. With Prettier you can format the code you write automatically to ensure a code style within your project. See the [Prettier's GitHub page](https://github.com/prettier/prettier) for more information, and look at this [page to see it in action](https://prettier.github.io/prettier/).
@@ -1562,6 +1578,8 @@ The service worker will use a [cache-first strategy](https://developers.google.c
for handling all requests for local assets, including the initial HTML, ensuring
that your web app is reliably fast, even on a slow or unreliable network.
+### Opting Out of Caching
+
If you would prefer not to enable service workers prior to your initial
production deployment, then remove the call to `serviceWorkerRegistration.register()`
from [`src/index.js`](src/index.js).
@@ -1571,7 +1589,8 @@ have decided that you would like to disable them for all your existing users,
you can swap out the call to `serviceWorkerRegistration.register()` in
[`src/index.js`](src/index.js) with a call to `serviceWorkerRegistration.unregister()`.
After the user visits a page that has `serviceWorkerRegistration.unregister()`,
-the service worker will be uninstalled.
+the service worker will be uninstalled. Note that depending on how `/service-worker.js` is served,
+it may take up to 24 hours for the cache to be invalidated.
### Offline-First Considerations
@@ -1970,10 +1989,6 @@ remote: npm ERR! argv "/tmp/build_a2875fc163b209225122d68916f1d4df/.heroku/node/
In this case, ensure that the file is there with the proper lettercase and that’s not ignored on your local `.gitignore` or `~/.gitignore_global`.
-### Modulus
-
-See the [Modulus blog post](http://blog.modulus.io/deploying-react-apps-on-modulus) on how to deploy your react app to Modulus.
-
### Netlify
**To do a manual deploy to Netlify’s CDN:**
@@ -2052,6 +2067,8 @@ HTTPS | :white_check_mark: | :x: | When set to `true`, Create React App will run
PUBLIC_URL | :x: | :white_check_mark: | Create React App assumes your application is hosted at the serving web server's root or a subpath as specified in [`package.json` (`homepage`)](#building-for-relative-paths). Normally, Create React App ignores the hostname. You may use this variable to force assets to be referenced verbatim to the url you provide (hostname included). This may be particularly useful when using a CDN to host your application.
CI | :large_orange_diamond: | :white_check_mark: | When set to `true`, Create React App treats warnings as failures in the build. It also makes the test runner non-watching. Most CIs set this flag by default.
REACT_EDITOR | :white_check_mark: | :x: | When an app crashes in development, you will see an error overlay with clickable stack trace. When you click on it, Create React App will try to determine the editor you are using based on currently running processes, and open the relevant source file. You can [send a pull request to detect your editor of choice](https://github.com/facebookincubator/create-react-app/issues/2636). Setting this environment variable overrides the automatic detection. If you do it, make sure your systems [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) environment variable points to your editor’s bin folder.
+CHOKIDAR_USEPOLLING | :white_check_mark: | :x: | When set to `true`, the watcher runs in polling mode, as necessary inside a VM. Use this option if `npm start` isn't detecting changes.
+GENERATE_SOURCEMAP | :x: | :white_check_mark: | When set to `false`, source maps are not generated for a production build. This solves OOM issues on some smaller machines.
## Troubleshooting
@@ -2132,6 +2149,16 @@ moment.locale('fr');
This will only work for locales that have been explicitly imported before.
+### `npm run build` fails to minify
+
+You may occasionally find a package you depend on needs compiled or ships code for a non-browser environment.
+This is considered poor practice in the ecosystem and does not have an escape hatch in Create React App.
+
+To resolve this:
+1. Open an issue on the dependency's issue tracker and ask that the package be published pre-compiled (retaining ES6 Modules).
+2. Fork the package and publish a corrected version yourself.
+3. If the dependency is small enough, copy it to your `src/` folder and treat it as application code.
+
## Something Missing?
If you have ideas for more “How To” recipes that should be on this page, [let us know](https://github.com/facebookincubator/create-react-app/issues) or [contribute some!](https://github.com/facebookincubator/create-react-app/edit/master/packages/react-scripts/template/README.md)
diff --git a/packages/react-scripts/template/public/favicon.ico b/packages/react-scripts/template/public/favicon.ico
index 5c125de5d89..a11777cc471 100644
Binary files a/packages/react-scripts/template/public/favicon.ico and b/packages/react-scripts/template/public/favicon.ico differ