1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80<template>
<v-app>
<v-main>
<v-container class="flex h-screen">
<v-progress-circular v-if="reloading" indeterminate size="200" width="3" />
<v-card v-else class="ma" variant="tonal" width="900">
<template #title>
<span class="text-h1">Oops!</span>
</template>
<template #text>
<strong>{{ error?.statusCode ?? '?' }} | {{ error?.statusMessage || 'Unknown Error' }}</strong>
<p class="text-sm">It looks like something went wrong.</p>
<pre v-show="showMore" class="mt-6 raw-error">{{ rawError }}</pre>
</template>
<template #actions>
<v-btn color="primary" prepend-icon="$arrowleft" text="Back" to="/" />
<v-btn
:text="`${showMore ? 'Hide' : 'Show'} Details`"
class="ml-auto"
@click="showMore = !showMore"
/>
</template>
</v-card>
</v-container>
</v-main>
</v-app>
</template>
<script setup lang="ts">
import type { NuxtError } from '#app'
const props = defineProps<{ error: NuxtError }>()
const showMore = shallowRef(false)
const rawError = computed(() => cleanErrorStack(props.error?.stack))
function cleanErrorStack(stack?: string) {
return stack?.split('\n')
.filter((line) => !/(@vue)|(\$fetch)/.test(line))
.map((line) => line.replace(/\([^_ ]+_nuxt/gi, '(~'))
.map((line) => line.replace(/[^_ ]+_nuxt/gi, '~'))
.map((line) => line.replace(/\(.+node_modules/gi, '(node_modules'))
.map((line) => line.replace(/\?t=\d+/gi, ''))
.map((line) => line.trimStart())
.join('\n')
}
const reloading = shallowRef(false)
const reloadCount = computed({
get: () => Number(localStorage['dev:reload-count'] || 0),
set: (v) => localStorage['dev:reload-count'] = v
})
watch(rawError, (v) => {
if ((v ?? '').includes('Failed to fetch dynamically imported module')) {
if (reloadCount.value > 2) {
return /* stop auto-reload */
}
reloading.value = true
reloadCount.value++
setTimeout(() => location.reload(), 1000)
} else {
reloadCount.value = 0
}
}, { immediate: true })
</script>
<style scoped>
.raw-error {
color: rgb(var(--v-theme-error));
font-size: .8rem;
scrollbar-width: none;
overflow-x: scroll;
display: -webkit-box;
-webkit-box-orient: vertical;
box-orient: vertical;
-webkit-line-clamp: 12;
line-clamp: 12;
}
</style>