-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(gatsby): Set up Fast Refresh #29588
Changes from all commits
e50f900
05cf60b
bb034fd
ff6b3e3
e321d95
59b7187
a9b700e
975ccf6
4c53a42
481b09a
dbcb80a
6a8e091
1bcd4be
b8bc828
c6dec14
eb924fd
6bb67fc
66cbadd
3c23349
4673f29
ed5a4b5
987ad0d
b8c469b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import * as React from "react" | ||
import { useId } from "./use-id" | ||
import * as keys from "../helpers/keys" | ||
import { match } from "../helpers/match" | ||
|
||
function ChevronIcon() { | ||
return ( | ||
<svg | ||
focusable="false" | ||
preserveAspectRatio="xMidYMid meet" | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="currentColor" | ||
width="16" | ||
height="16" | ||
viewBox="0 0 16 16" | ||
aria-hidden="true" | ||
data-gatsby-overlay="chevron-icon" | ||
> | ||
<path d="M11 8L6 13 5.3 12.3 9.6 8 5.3 3.7 6 3z" /> | ||
</svg> | ||
) | ||
} | ||
|
||
export function Accordion({ children, ...rest }) { | ||
return ( | ||
<ul data-gatsby-overlay="accordion" {...rest}> | ||
{children} | ||
</ul> | ||
) | ||
} | ||
|
||
export function AccordionItem({ | ||
children, | ||
disabled = false, | ||
open = false, | ||
title = `title`, | ||
...rest | ||
}) { | ||
const [isOpen, setIsOpen] = React.useState(open) | ||
const [prevIsOpen, setPrevIsOpen] = React.useState(open) | ||
const id = useId(`accordion-item`) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need a stable id here (for both SSR and client for re-hydration) so that's why this helper exists |
||
|
||
if (open !== prevIsOpen) { | ||
setIsOpen(open) | ||
setPrevIsOpen(open) | ||
} | ||
|
||
const toggleOpen = () => { | ||
const nextValue = !isOpen | ||
setIsOpen(nextValue) | ||
} | ||
|
||
// If the AccordionItem is open, and the user hits the ESC key, then close it | ||
const onKeyDown = event => { | ||
if (isOpen && match(event, keys.Escape)) { | ||
setIsOpen(false) | ||
} | ||
} | ||
|
||
return ( | ||
<li | ||
data-gatsby-overlay="accordion__item" | ||
data-accordion-active={isOpen ? `true` : `false`} | ||
{...rest} | ||
> | ||
<button | ||
data-gatsby-overlay="accordion__item__heading" | ||
type="button" | ||
disabled={disabled} | ||
aria-controls={id} | ||
aria-expanded={isOpen} | ||
onClick={toggleOpen} | ||
onKeyDown={onKeyDown} | ||
> | ||
<ChevronIcon /> | ||
<div data-gatsby-overlay="accordion__item__title">{title}</div> | ||
</button> | ||
<div id={id} data-gatsby-overlay="accordion__item__content"> | ||
{children} | ||
</div> | ||
</li> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,35 @@ | ||
import React from "react" | ||
import Overlay from "./overlay" | ||
import Anser from "anser" | ||
import CodeFrame from "./code-frame" | ||
import { prettifyStack } from "../utils" | ||
import * as React from "react" | ||
import { Overlay, Header, Body, Footer, HeaderOpenClose } from "./overlay" | ||
import { CodeFrame } from "./code-frame" | ||
import { prettifyStack, openInEditor } from "../utils" | ||
|
||
const BuildError = ({ error, open, dismiss }) => { | ||
const [file, cause, _emptyLine, ...rest] = error.split(`\n`) | ||
const [_fullPath, _detailedError] = rest | ||
const detailedError = Anser.ansiToJson(_detailedError, { | ||
remove_empty: true, | ||
json: true, | ||
}) | ||
const lineNumberRegex = /^[0-9]*:[0-9]*$/g | ||
const lineNumberFiltered = detailedError.filter( | ||
d => d.content !== ` ` && d.content.match(lineNumberRegex) | ||
)[0]?.content | ||
const lineNumber = lineNumberFiltered.substr( | ||
0, | ||
lineNumberFiltered.indexOf(`:`) | ||
) | ||
// Error that is thrown on e.g. webpack errors and thus can't be dismissed and must be fixed | ||
export function BuildError({ error }) { | ||
// Incoming build error shape is like this: | ||
// ./relative-path-to-file | ||
// Additional information (sometimes empty line => handled in "prettifyStack" function) | ||
// /absolute-path-to-file | ||
// Errors/Warnings | ||
const [file, ...rest] = error.split(`\n`) | ||
const decoded = prettifyStack(rest) | ||
|
||
const header = ( | ||
<> | ||
<div data-gatsby-overlay="header__cause-file"> | ||
<p>{cause}</p> | ||
<span>{file}</span> | ||
</div> | ||
<button | ||
onClick={() => open(file, lineNumber)} | ||
data-gatsby-overlay="header__open-in-editor" | ||
> | ||
Open in editor | ||
</button> | ||
</> | ||
return ( | ||
<Overlay> | ||
<Header data-gatsby-error-type="build-error"> | ||
<div data-gatsby-overlay="header__cause-file"> | ||
<h1 id="gatsby-overlay-labelledby">Failed to compile</h1> | ||
<span>{file}</span> | ||
</div> | ||
<HeaderOpenClose open={() => openInEditor(file, 1)} dismiss={false} /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a follow up: we should put this openInEditor on the same place as runtime errors so people know where to look. |
||
</Header> | ||
<Body> | ||
<h2>Source</h2> | ||
<CodeFrame decoded={decoded} /> | ||
</Body> | ||
<Footer id="gatsby-overlay-describedby"> | ||
This error occurred during the build process and can only be dismissed | ||
by fixing the error. | ||
</Footer> | ||
</Overlay> | ||
) | ||
|
||
const body = <CodeFrame decoded={decoded} /> | ||
|
||
return <Overlay header={header} body={body} dismiss={dismiss} /> | ||
} | ||
|
||
export default BuildError |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,54 @@ | ||
import React from "react" | ||
import * as React from "react" | ||
|
||
const CodeFrame = ({ decoded }) => ( | ||
<pre data-gatsby-overlay="pre"> | ||
<code data-gatsby-overlay="pre__code"> | ||
{decoded | ||
? decoded.map((entry, index) => ( | ||
export function CodeFrame({ decoded }) { | ||
if (!decoded) { | ||
return ( | ||
<pre data-gatsby-overlay="pre"> | ||
<code data-gatsby-overlay="pre__code" /> | ||
</pre> | ||
) | ||
} | ||
|
||
return ( | ||
<pre data-gatsby-overlay="pre"> | ||
<code data-gatsby-overlay="pre__code"> | ||
{decoded.map((entry, index) => { | ||
// Check if content is "Enter" and render other element that collapses | ||
// Otherwise an empty line would be printed | ||
if ( | ||
index === 0 && | ||
entry.content === | ||
` | ||
` | ||
) { | ||
return ( | ||
<span | ||
key={`frame-${index}`} | ||
data-gatsby-overlay="pre__code__span__empty" | ||
/> | ||
) | ||
} | ||
|
||
const style = { | ||
color: entry.fg ? `var(--color-${entry.fg})` : undefined, | ||
...(entry.decoration === `bold` | ||
? { fontWeight: 800 } | ||
: entry.decoration === `italic` | ||
? { fontStyle: `italic` } | ||
: undefined), | ||
} | ||
|
||
return ( | ||
<span | ||
key={`frame-${index}`} | ||
data-gatsby-overlay="pre__code__span" | ||
style={{ | ||
color: entry.fg ? `var(--color-${entry.fg})` : undefined, | ||
...(entry.decoration === `bold` | ||
? { fontWeight: 800 } | ||
: entry.decoration === `italic` | ||
? { fontStyle: `italic` } | ||
: undefined), | ||
}} | ||
style={style} | ||
> | ||
{entry.content} | ||
</span> | ||
)) | ||
: null} | ||
</code> | ||
</pre> | ||
) | ||
|
||
export default CodeFrame | ||
) | ||
})} | ||
</code> | ||
</pre> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,14 @@ | ||
import React from "react" | ||
import * as React from "react" | ||
|
||
class ErrorBoundary extends React.Component { | ||
state = { hasError: false } | ||
export class ErrorBoundary extends React.Component { | ||
state = { error: null } | ||
|
||
componentDidCatch(error) { | ||
this.props.onError(error) | ||
} | ||
|
||
componentDidMount() { | ||
this.props.clearErrors() | ||
} | ||
|
||
static getDerivedStateFromError() { | ||
return { hasError: true } | ||
this.setState({ error }) | ||
} | ||
|
||
render() { | ||
return this.state.hasError ? null : this.props.children | ||
// Without this check => possible infinite loop | ||
return this.state.error && this.props.hasErrors ? null : this.props.children | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Necessary to also have |
||
} | ||
} | ||
|
||
export default ErrorBoundary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessary to do this handling here, I grab all info in the graphql-error component itself.
Also, we can use
flatMap
because we're now at Node 12.13 👍 and that's a Node 11 feature.