Skip to content

Commit 4d7377b

Browse files
authored
feat: make features configurable (#246)
1 parent 1202515 commit 4d7377b

39 files changed

+476
-162
lines changed

.nuxtrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
imports.autoImport=false
22
typescript.includeWorkspace=true
3+
setups.@nuxt/test-utils="4.0.0"

client/.nuxtrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
imports.autoImport=true
2+
setups.@nuxt/test-utils="4.0.0"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script setup lang="ts">
2+
const hydration = useNuxtApp().$hydrationMismatches
3+
const hydrationCount = computed(() => hydration.value.length)
4+
</script>
5+
6+
<template>
7+
<NuxtLink
8+
to="/hydration"
9+
class="block"
10+
>
11+
<n-card class="flex items-center justify-between p-4 hover:border-neutral-400 dark:hover:border-neutral-500">
12+
<div class="flex items-center gap-3 min-w-0">
13+
<Icon
14+
name="material-symbols:water-full"
15+
class="text-xl text-cyan-500"
16+
/>
17+
<div class="min-w-0">
18+
<div class="text-sm font-medium truncate">
19+
Hydration
20+
</div>
21+
<div class="text-xs text-neutral-500">
22+
SSR vs client diffs
23+
</div>
24+
</div>
25+
</div>
26+
<n-tip
27+
v-if="hydrationCount"
28+
size="small"
29+
type="error"
30+
:bordered="false"
31+
>
32+
{{ hydrationCount }} issues
33+
</n-tip>
34+
</n-card>
35+
</NuxtLink>
36+
</template>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<script setup lang="ts">
2+
const lazyLoad = useNuxtApp().$lazyLoadHints
3+
const lazyLoadCount = computed(() =>
4+
lazyLoad.value.reduce(
5+
(sum: number, entry: { state: { directImports: { rendered: boolean }[] } }) => sum + entry.state.directImports.filter((i: { rendered: boolean }) => !i.rendered).length,
6+
0,
7+
),
8+
)
9+
</script>
10+
11+
<template>
12+
<NuxtLink
13+
to="/component-lazy-load"
14+
class="block"
15+
>
16+
<n-card class="flex items-center justify-between p-4 hover:border-neutral-400 dark:hover:border-neutral-500">
17+
<div class="flex items-center gap-3 min-w-0">
18+
<Icon
19+
name="material-symbols:speed"
20+
class="text-xl text-amber-500"
21+
/>
22+
<div class="min-w-0">
23+
<div class="text-sm font-medium truncate">
24+
Lazy Load
25+
</div>
26+
<div class="text-xs text-neutral-500">
27+
Components to lazy-load
28+
</div>
29+
</div>
30+
</div>
31+
<n-tip
32+
v-if="lazyLoadCount"
33+
size="small"
34+
type="warning"
35+
:bordered="false"
36+
>
37+
{{ lazyLoadCount }} suggestions
38+
</n-tip>
39+
</n-card>
40+
</NuxtLink>
41+
</template>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<template>
2+
<NuxtLink
3+
to="/third-party-scripts"
4+
class="block"
5+
>
6+
<n-card class="p-4 flex items-center gap-3 hover:border-neutral-400 dark:hover:border-neutral-500">
7+
<Icon
8+
name="material-symbols:extension"
9+
class="text-xl text-violet-500"
10+
/>
11+
<div class="min-w-0">
12+
<div class="text-sm font-medium truncate">
13+
Third party scripts
14+
</div>
15+
<div class="text-xs text-neutral-500">
16+
Analyze third-party scripts speed.
17+
</div>
18+
</div>
19+
</n-card>
20+
</NuxtLink>
21+
</template>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<script setup lang="ts">
2+
const { allMetrics } = useHostWebVitals()
3+
</script>
4+
5+
<template>
6+
<NuxtLink
7+
to="/web-vitals"
8+
class="block"
9+
>
10+
<n-card
11+
class="flex items-center justify-between p-4 hover:border-neutral-400 dark:hover:border-neutral-500"
12+
>
13+
<div class="flex items-center gap-3 min-w-0">
14+
<Icon
15+
name="material-symbols:monitoring"
16+
class="text-xl text-blue-500"
17+
/>
18+
<div class="min-w-0">
19+
<div class="text-sm font-medium truncate">
20+
Web Vitals
21+
</div>
22+
<div class="text-xs text-neutral-500">
23+
LCP / INP / CLS
24+
</div>
25+
</div>
26+
</div>
27+
<n-badge
28+
v-if="allMetrics.length"
29+
>
30+
{{ allMetrics.length }} issues
31+
</n-badge>
32+
</n-card>
33+
</NuxtLink>
34+
</template>

client/app/components/HydrationIssue.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { codeToHtml } from 'shiki/bundle/web'
33
import { diffLines, type ChangeObject } from 'diff'
44
import { transformerNotationDiff } from '@shikijs/transformers'
55
import type { HydrationMismatchPayload, LocalHydrationMismatch } from '../../../src/runtime/hydration/types'
6-
import { HYDRATION_ROUTE } from '../../../src/runtime/hydration/utils'
6+
import { HYDRATION_ROUTE } from '../utils/routes'
77
import { transformerRenderHtmlFold, attachFoldToggleListener } from 'shiki-transformer-fold'
88
99
const props = defineProps<{

client/app/composables/features.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { FeaturesName } from '../../../src/runtime/core/types'
2+
3+
export function useHintsConfig() {
4+
const hostNuxt = useHostNuxt()
5+
6+
return hostNuxt.hints.config
7+
}
8+
9+
export function useEnabledHintsFeatures(): Record<FeaturesName, boolean> {
10+
const config = useHintsConfig()
11+
return Object.fromEntries<boolean>(
12+
Object.entries(config.features).map(([feature, flags]) => [
13+
feature,
14+
typeof flags === 'object' ? flags.devtools : Boolean(flags),
15+
] as [FeaturesName, boolean]),
16+
) as Record<FeaturesName, boolean>
17+
}
18+
19+
export function useHintsFeature(feature: FeaturesName): boolean {
20+
const config = useHintsConfig()
21+
return typeof config.features[feature] === 'object' ? config.features[feature].devtools : Boolean(config.features[feature])
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { FeaturesName } from '../../../src/runtime/core/types'
2+
3+
declare module 'nuxt/app' {
4+
interface PageMeta {
5+
feature?: FeaturesName
6+
}
7+
}
8+
9+
export default defineNuxtRouteMiddleware((to) => {
10+
const feature = to.meta.feature
11+
12+
if (!feature) return
13+
14+
const isFeatureEnabled = useHintsFeature(feature)
15+
16+
if (!isFeatureEnabled) {
17+
return navigateTo('/', { replace: true })
18+
}
19+
})

client/app/pages/component-lazy-load.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<script setup lang="ts">
22
import type { ComponentLazyLoadData, DirectImportInfo } from '../../../src/runtime/lazy-load/schema'
3-
import { LAZY_LOAD_ROUTE } from '../../../src/runtime/lazy-load/utils'
3+
import { LAZY_LOAD_ROUTE } from '../utils/routes'
44
55
definePageMeta({
66
title: 'Component Lazy Load',
7+
middleware: 'feature-gate',
8+
feature: 'lazyLoad',
79
})
810
911
const nuxtApp = useNuxtApp()

0 commit comments

Comments
 (0)