๐Ÿ“ฆ J-Sek / vuetify-nuxt-example

๐Ÿ“„ error.vue ยท 80 lines
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>