Expose error stack traces (#211)

This commit is contained in:
Misode
2022-03-11 00:05:24 +01:00
committed by GitHub
parent 4122991b3b
commit 7ab0ec2e7e
14 changed files with 131 additions and 31 deletions

View File

@@ -1,13 +1,52 @@
import { useEffect, useMemo, useState } from 'preact/hooks'
import { mapStackTrace } from 'sourcemapped-stacktrace'
import { Octicon } from './Octicon'
type ErrorPanelProps = {
error: string,
error: string | Error,
onDismiss?: () => unknown,
}
export function ErrorPanel({ error, onDismiss }: ErrorPanelProps) {
const [stackVisible, setStackVisible] = useState(false)
const [stack, setStack] = useState<string | undefined>(undefined)
useEffect(() => {
if (error instanceof Error) {
const stack = error.stack!.split('\n').map(line => {
return line.replace(/^(\s+)at (?:async )?(https?:.*)/, '$1at ($2)')
})
setStack(stack.join('\n'))
mapStackTrace(stack.join('\n'), (mapped) => {
const mappedStack = mapped.map(line => {
return line.replace(/..\/..\/src\//, 'src/')
}).join('\n')
setStack(mappedStack)
})
}
}, [error])
const url = useMemo(() => {
let url ='https://github.com/misode/misode.github.io/issues/new'
if (error instanceof Error) {
url += `?title=${encodeURIComponent(`${error.name}: ${error.message}`)}`
if (stack) {
url += `&body=${encodeURIComponent(`\`\`\`\n${error.name}: ${error.message}\n${stack}\n\`\`\`\n`)}`
}
} else {
url += `?title=${encodeURIComponent(error.toString())}`
}
return url
}, [error, stack])
return <div class="error">
{onDismiss && <div class="error-dismiss" onClick={onDismiss}>{Octicon.x}</div>}
<h3>{error}</h3>
<p>If you think this is a bug, you can report it <a href="https://github.com/misode/misode.github.io/issues/new" target="_blank">on GitHub</a></p>
<h3>
{error instanceof Error ? error.message : error}
{stack && <span onClick={() => setStackVisible(!stackVisible)}>
{Octicon.info}
</span>}
</h3>
{stack && stackVisible && <pre>{stack}</pre>}
<p>If you think this is a bug, you can report it <a href={url} target="_blank">on GitHub</a></p>
</div>
}

View File

@@ -52,7 +52,7 @@ type SourcePanelProps = {
doDownload?: number,
doImport?: number,
copySuccess: () => unknown,
onError: (message: string) => unknown,
onError: (message: string | Error) => unknown,
}
export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doImport, copySuccess, onError }: SourcePanelProps) {
const { locale } = useLocale()
@@ -78,7 +78,12 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
const output = getSerializedOutput(model, blockStates)
editor.current.setValue(output)
} catch (e) {
onError(`Error getting JSON output: ${message(e)}`)
if (e instanceof Error) {
e.message = `Error getting JSON output: ${e.message}`
onError(e)
} else {
onError(`Error getting JSON output: ${message(e)}`)
}
console.error(e)
editor.current.setValue('')
}
@@ -91,7 +96,12 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
const data = FORMATS[format].parse(value)
model?.reset(DataModel.wrapLists(data), false)
} catch (e) {
onError(`Error importing: ${message(e)}`)
if (e instanceof Error) {
e.message = `Error importing: ${e.message}`
onError(e)
} else {
onError(`Error importing: ${message(e)}`)
}
console.error(e)
}
}

View File

@@ -48,13 +48,19 @@ export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) =>
clearInterval(scrollInterval.current)
}
if (shown) {
redraw()
if (autoScroll) {
scrollInterval.current = setInterval(() => {
offset.current -= 8
redraw()
}, 100) as any
}
(async () => {
try {
await redraw()
if (autoScroll) {
scrollInterval.current = setInterval(() => {
offset.current -= 8
redraw()
}, 100) as any
}
} catch (e) {
throw e
}
})()
}
}, [state, seed, shown, biome, biomeScale, biomeDepth, autoScroll])

View File

@@ -76,7 +76,11 @@ export function useCanvas({ size, draw, onDrag, onHover, onLeave }: {
canvas.current.height = s[1]
const img = ctx.getImageData(0, 0, s[0], s[1])
const ownCount = redrawCount.current += 1
await draw(img)
try {
await draw(img)
} catch (e) {
throw e
}
if (ownCount === redrawCount.current) {
ctx.putImageData(img, 0, 0)
}

View File

@@ -9,7 +9,7 @@ interface Props {
}
export function Changelog({}: Props) {
const { locale } = useLocale()
const [error, setError] = useState<string | null>(null)
const [error, setError] = useState<Error | null>(null)
useTitle(locale('title.changelog'))
const [changelogs, setChangelogs] = useState<Change[]>([])

View File

@@ -18,10 +18,11 @@ export function Generator({}: Props) {
const { locale } = useLocale()
const { version, changeVersion } = useVersion()
const { project, file, updateFile, openFile, closeFile } = useProject()
const [error, setError] = useState<string | null>(null)
const [error, setError] = useState<Error | string | null>(null)
const [errorBoundary, errorRetry] = useErrorBoundary()
if (errorBoundary) {
return <main><ErrorPanel error={`Something went wrong rendering the generator: ${errorBoundary.message}`} onDismiss={errorRetry} /></main>
errorBoundary.message = `Something went wrong rendering the generator: ${errorBoundary.message}`
return <main><ErrorPanel error={errorBoundary} onDismiss={errorRetry} /></main>
}
const gen = getGenerator(getCurrentUrl())
@@ -69,7 +70,7 @@ export function Generator({}: Props) {
}
setModel(m)
})
.catch(e => { console.error(e); setError(message(e)) })
.catch(e => { console.error(e); setError(e) })
}, [version, gen.id])
const [dirty, setDirty] = useState(false)
@@ -172,7 +173,7 @@ export function Generator({}: Props) {
getCollections(version).then(collections => {
setPresets(collections.get(gen.id).map(p => p.slice(10)))
})
.catch(e => { console.error(e); setError(e.message) })
.catch(e => { console.error(e); setError(e) })
}, [version, gen.id])
const selectPreset = (id: string) => {
@@ -192,7 +193,7 @@ export function Generator({}: Props) {
}
return preset
} catch (e) {
setError(message(e))
setError(e instanceof Error ? e : message(e))
}
}

View File

@@ -4,7 +4,7 @@ import { Btn, BtnMenu, ErrorPanel, SoundConfig, TextInput } from '../components'
import { useLocale, useTitle, useVersion } from '../contexts'
import type { SoundEvents, VersionId } from '../services'
import { fetchSounds } from '../services'
import { hexId, message } from '../Utils'
import { hexId } from '../Utils'
interface Props {
path?: string,
@@ -12,7 +12,7 @@ interface Props {
export function Sounds({}: Props) {
const { locale } = useLocale()
const { version, changeVersion } = useVersion()
const [error, setError] = useState<string | null>(null)
const [error, setError] = useState<Error | null>(null)
useTitle(locale('title.sounds'))
const [sounds, setSounds] = useState<SoundEvents>({})
@@ -20,7 +20,7 @@ export function Sounds({}: Props) {
useEffect(() => {
fetchSounds(version)
.then(setSounds)
.catch(e => { console.error(e); setError(message(e)) })
.catch(e => { console.error(e); setError(e) })
}, [version])
const [search, setSearch] = useState('')

View File

@@ -11,7 +11,7 @@ interface Props {
}
export function Versions({}: Props) {
const { locale } = useLocale()
const [error, setError] = useState<string | null>(null)
const [error, setError] = useState<Error | null>(null)
useTitle(locale('title.versions'))
const [versions, setVersions] = useState<VersionMeta[]>([])

View File

@@ -224,6 +224,7 @@ class LevelSlice {
public generate(generator: NoiseChunkGenerator, forcedBiome?: string) {
this.chunks.forEach((chunk, i) => {
if (!this.done[i]) {
throw new Error('Test')
generator.fill(chunk, true)
generator.buildSurface(chunk, forcedBiome)
this.done[i] = true

View File

@@ -20,6 +20,7 @@ let Changelogs: Change[] | Promise<Change[]> | null = null
export async function getChangelogs() {
if (!Changelogs) {
const index = await (await fetch(`${repo}/index.json`)).json() as string[]
throw new Error('Test')
Changelogs = (await Promise.all(
index.map((group, i) => fetchGroup(parseVersion(group), i))
)).flat().map<Change>(change => ({

View File

@@ -1,7 +1,7 @@
import type { CollectionRegistry } from '@mcschema/core'
import config from '../../config.json'
import { message } from '../Utils'
import type { BlockStateRegistry, VersionId } from './Schemas'
import type { CollectionRegistry } from '@mcschema/core';
import config from '../../config.json';
import { message } from '../Utils';
import type { BlockStateRegistry, VersionId } from './Schemas';
// Cleanup old caches
['1.15', '1.16', '1.17'].forEach(v => localStorage.removeItem(`cache_${v}`));

View File

@@ -749,6 +749,11 @@ main.has-preview {
margin: 10px 0;
}
.error h3 span {
margin-left: 8px;
cursor: pointer;
}
.error .error-dismiss {
float: right;
cursor: pointer;