Skip to content

Commit 6ce4386

Browse files
authored
feat: migrate to Content v3 (#209)
1 parent f919184 commit 6ce4386

File tree

11 files changed

+3900
-4313
lines changed

11 files changed

+3900
-4313
lines changed

.github/workflows/nuxthub.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ on: push
33

44
jobs:
55
deploy:
6-
name: "Deploy to NuxtHub"
6+
name: Deploy to NuxtHub
77
runs-on: ubuntu-latest
88
environment:
99
name: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
@@ -22,7 +22,7 @@ jobs:
2222
uses: actions/setup-node@v4
2323
with:
2424
node-version: 22
25-
cache: 'pnpm'
25+
cache: pnpm
2626

2727
- name: Install dependencies
2828
run: pnpm install

components/ContentNavCard.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defineProps<{
44
icon?: string
55
title?: string
66
subheader: string
7-
description: string
7+
description?: string
88
}>()
99
</script>
1010

@@ -19,7 +19,7 @@ defineProps<{
1919
<div class="my-0 text-lg font-semibold">
2020
{{ title }}
2121
</div>
22-
<div class="line-clamp line-clamp-2 mb-0 mt-1 text-[14px] op50">
22+
<div v-if="description" class="line-clamp line-clamp-2 mb-0 mt-1 text-[14px] op50">
2323
{{ description }}
2424
<slot />
2525
</div>

components/ContentNavItem.vue

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
<script setup lang="ts">
2-
import type { NavItem } from '@nuxt/content'
2+
import type { ContentNavigationItem } from '@nuxt/content'
33
44
const props = withDefaults(
55
defineProps<{
6-
item: NavItem
6+
item: ContentNavigationItem
77
level?: number
88
}>(),
99
{
1010
level: 0,
1111
},
1212
)
1313
14+
const route = useRoute()
1415
const ui = useUiState()
1516
1617
const resolved = computed(() => {
@@ -25,7 +26,7 @@ const paddingLeft = computed(() => `${0.5 + props.level * 0.8}rem`)
2526
<template>
2627
<div class="content-nav-item">
2728
<template v-if="resolved.children?.length">
28-
<details :open="$route.path.includes(resolved._path)">
29+
<details :open="route.path.includes(resolved.path)">
2930
<summary>
3031
<div
3132
flex="~ gap-1 items-center" cursor-pointer select-none px1 py0.5
@@ -41,18 +42,20 @@ const paddingLeft = computed(() => `${0.5 + props.level * 0.8}rem`)
4142
</summary>
4243
<div v-if="resolved.children?.length">
4344
<ContentNavItem
44-
v-for="child in resolved.children"
45-
:key="child.url" :item="child" :level="props.level + 1"
45+
v-for="child of resolved.children"
46+
:key="child.path"
47+
:item="child"
48+
:level="props.level + 1"
4649
/>
4750
</div>
4851
</details>
4952
</template>
5053
<NuxtLink
5154
v-else
52-
:to="resolved._path"
55+
:to="resolved.path"
5356
px1 py0.5
5457
:style="{ paddingLeft }"
55-
:class="{ 'text-primary bg-active': resolved._path === $route.path }"
58+
:class="{ 'text-primary bg-active': resolved.path === route.path }"
5659
flex="~ gap-1 items-center"
5760
hover="text-primary bg-active "
5861
@click="ui.isContentDropdownShown = false"

components/MainPlayground.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const terminalPaneProps = computed(() => {
4343
}
4444
})
4545
46+
const route = useRoute()
47+
4648
// For panes size initialization on SSR
4749
const isMounted = useMounted()
4850
const panelInitDocs = computed(() => isMounted.value || {
@@ -73,7 +75,7 @@ const panelInitTerminal = computed(() => isMounted.value || {
7375
:size="ui.panelDocs" min-size="10"
7476
:style="panelInitDocs"
7577
>
76-
<PanelDocs />
78+
<PanelDocs :key="route.path" />
7779
</Pane>
7880
<PaneSplitter />
7981
<Pane

components/PanelDocs.vue

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,46 @@
11
<script setup lang="ts">
2-
import type { NavItem, ParsedContent } from '@nuxt/content'
2+
import type { ContentNavigationItem } from '@nuxt/content'
33
44
const runtime = useRuntimeConfig()
5-
const {
6-
navigation,
7-
page,
8-
next,
9-
prev,
10-
} = useContent() as {
11-
navigation: Ref<NavItem[]>
12-
page: Ref<ParsedContent>
13-
next: Ref<ParsedContent | undefined>
14-
prev: Ref<ParsedContent | undefined>
15-
}
5+
const route = useRoute()
6+
const { data: page } = useAsyncData(route.path, () => {
7+
return queryCollection('tutorials').path(route.path).first()
8+
})
9+
const { data: navigation } = useAsyncData(`navigation`, () => {
10+
return queryCollectionNavigation('tutorials')
11+
})
12+
const { data: surroundings } = useAsyncData(`${route.path}-surroundings`, () => {
13+
return queryCollectionItemSurroundings('tutorials', route.path, {
14+
fields: ['title', 'description'],
15+
})
16+
})
17+
18+
const prev = computed(() => surroundings.value?.[0])
19+
const next = computed(() => surroundings.value?.[1])
1620
1721
interface BreadcrumbItem {
1822
title: string
1923
path?: string
2024
}
2125
22-
function findNavItemFromPath(path: string, items = navigation.value): NavItem | undefined {
23-
const item = items.find(i => i._path === path)
26+
function findNavItemFromPath(
27+
path: string,
28+
items: ContentNavigationItem[] | null = navigation.value,
29+
): ContentNavigationItem | undefined {
30+
const item = items?.find(i => i.path === path)
2431
if (item)
2532
return item
2633
2734
const parts = path.split('/').filter(Boolean)
2835
for (let i = parts.length - 1; i > 0; i--) {
2936
const parentPath = `/${parts.slice(0, i).join('/')}`
30-
const parent = items.find(i => i._path === parentPath)
37+
const parent = items?.find(i => i.path === parentPath)
3138
if (parent)
3239
return findNavItemFromPath(path, parent.children || [])
3340
}
3441
}
3542
36-
const contentPath = computed(() => page.value?._path as string | undefined)
43+
const contentPath = computed(() => page.value?.path as string | undefined)
3744
const breadcrumbs = computed(() => {
3845
const parts = contentPath.value?.split('/').filter(Boolean) || []
3946
const breadcrumbs = parts
@@ -58,8 +65,8 @@ const breadcrumbs = computed(() => {
5865
const ui = useUiState()
5966
6067
const sourceUrl = computed(() =>
61-
page.value?._file
62-
? `${runtime.public.repoUrl}/edit/main/content/${page.value._file}`
68+
page.value?.id
69+
? `${runtime.public.repoUrl}/edit/main/content/${page.value.id}`
6370
: undefined,
6471
)
6572
@@ -94,24 +101,24 @@ router.beforeEach(() => {
94101
</div>
95102
<div relative h-full of-hidden>
96103
<article ref="docsEl" class="max-w-none prose" h-full of-auto p6>
97-
<ContentDoc />
104+
<ContentRenderer v-if="page" :key="page.id" :value="page" />
98105
<div mt8 py2 grid="~ cols-[1fr_1fr] gap-4">
99106
<div>
100107
<ContentNavCard
101108
v-if="prev"
102-
:to="prev._path"
109+
:to="prev.path"
103110
:title="prev.title"
104-
:description="prev.description"
111+
:description="prev.description as string"
105112
subheader="Previous section"
106113
icon="i-ph-arrow-left"
107114
/>
108115
</div>
109116
<div>
110117
<ContentNavCard
111118
v-if="next"
112-
:to="next._path"
119+
:to="next.path"
113120
:title="next.title"
114-
:description="next.description"
121+
:description="next.description as string"
115122
subheader="Next section"
116123
icon="i-ph-arrow-right"
117124
items-end text-right
@@ -140,7 +147,7 @@ router.beforeEach(() => {
140147
absolute left-0 right-0 top-0 max-h-60vh py2
141148
backdrop-blur-10 bg-base important-bg-opacity-80
142149
>
143-
<ContentNavItem v-for="item of navigation" :key="item.url" :item="item" />
150+
<ContentNavItem v-for="item of navigation" :key="item.path" :item="item" />
144151
</div>
145152
</Transition>
146153
</div>

content.config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineCollection, defineContentConfig } from '@nuxt/content'
2+
3+
export default defineContentConfig({
4+
collections: {
5+
tutorials: defineCollection({
6+
type: 'page',
7+
source: {
8+
include: '**',
9+
exclude: ['**/.template/**'],
10+
},
11+
}),
12+
},
13+
})

eslint.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,10 @@ export default antfu(
77
formatters: true,
88
},
99
nuxt(),
10+
{
11+
files: ['**/template/files/**'],
12+
rules: {
13+
'no-console': 'off',
14+
},
15+
},
1016
)

nuxt.config.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ export default defineNuxtConfig({
44
modules: [
55
'@vueuse/nuxt',
66
'@unocss/nuxt',
7+
'@nuxtjs/seo',
78
'@nuxt/content',
89
'@nuxt/image',
910
'@nuxtjs/color-mode',
1011
'@pinia/nuxt',
1112
'floating-vue/nuxt',
12-
'@nuxtjs/seo',
13-
// '@nuxt/icon',
1413
'@nuxt/eslint',
1514

1615
// local
@@ -32,27 +31,26 @@ export default defineNuxtConfig({
3231
},
3332
},
3433
site: {
35-
url: 'https://learn-dev.nuxt.com',
34+
url: 'https://learn.nuxt.com',
3635
},
3736
colorMode: {
3837
classSuffix: '',
3938
},
39+
4040
content: {
41-
documentDriven: true,
42-
highlight: {
43-
theme: {
44-
default: 'vitesse-light',
45-
dark: 'vitesse-dark',
41+
build: {
42+
markdown: {
43+
rehypePlugins: {
44+
'rehype-external-links': {},
45+
},
46+
highlight: {
47+
theme: {
48+
default: 'vitesse-light',
49+
dark: 'vitesse-dark',
50+
},
51+
},
4652
},
4753
},
48-
markdown: {
49-
rehypePlugins: [
50-
'rehype-external-links',
51-
],
52-
},
53-
experimental: {
54-
search: {},
55-
},
5654
},
5755

5856
runtimeConfig: {

0 commit comments

Comments
 (0)