Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(document-driven): rendering flash #1336

Merged
merged 14 commits into from
Jul 7, 2022
6 changes: 6 additions & 0 deletions playground/document-driven/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<div>
<NuxtLoadingBar />
<NuxtPage />
</div>
</template>
17 changes: 17 additions & 0 deletions playground/document-driven/components/content/Alert.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<template>
<div>
<div style="display: flex; align-items: center">
<div style="font-size: 64px;">
❕
</div>
<div>
<h3 style="margin: 0">
<Markdown :use="$slots.title" unwrap="p" />
</h3>
<div>
<Markdown :use="$slots.default" unwrap="p" />
</div>
</div>
</div>
</div>
</template>
14 changes: 14 additions & 0 deletions playground/document-driven/components/content/List.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
// Utils from Nuxt Content
const { flatUnwrap } = useUnwrap()
</script>

<template>
<ul>
<li v-for="(item, index) of flatUnwrap($slots.default(), ['ul'])" :key="index">
β˜‘οΈŽ
<span><Markdown :use="() => item" unwrap="li" /></span>
</li>
</ul>
</template>
10 changes: 10 additions & 0 deletions playground/document-driven/content/10.alert.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
layout: blog
---

::alert{icon="ph:circle-wavy-warning-duotone"}
#title
This is an alert
#default
This is the default content of my alert!
::
8 changes: 8 additions & 0 deletions playground/document-driven/content/9.list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
::list
- Item 1
::list{icon="ph:check-circle-light"}
- Item 1.1
- Item 1.2
::
- Item 2
::
5 changes: 1 addition & 4 deletions playground/document-driven/layouts/reversed.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<template>
<div>
<NuxtLoadingBar />

<NuxtPage />

<slot />
<PageNav />
</div>
</template>
Expand Down
8 changes: 6 additions & 2 deletions playground/document-driven/pages/vue-file.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<script setup>
definePageMeta({
layout: 'reversed'
documentDriven: {
surround: false
}
})
const { page } = useContent()
useContentHead(page)
</script>

<template>
<ContentRenderer :value="page" />
<NuxtLayout name="reversed">
<ContentRenderer :value="page" />
</NuxtLayout>
</template>
18 changes: 18 additions & 0 deletions playground/shared/layouts/blog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<template>
<div style="max-width: 680px; margin: 0 auto">
<NuxtLoadingBar />

<PageNav />

<slot />
</div>
</template>

<style>
body, html {
margin: 0;
padding: 0;
min-height: 100vh;
min-width: 100vw;
}
</style>
5 changes: 1 addition & 4 deletions playground/shared/layouts/default.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
<template>
<div>
<NuxtLoadingBar />

<PageNav />

<NuxtPage />
<slot />
</div>
</template>

Expand Down
28 changes: 22 additions & 6 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,12 +448,28 @@ export default defineNuxtModule<ModuleOptions>({
nuxt.options.pages = true

nuxt.hook('pages:extend', (pages) => {
pages.unshift({
name: 'slug',
path: '/:slug(.*)*',
file: resolveRuntimeModule('./pages/document-driven.vue'),
children: []
})
// Respect user's custom catch-all page
if (!pages.find(page => page.path === '/:slug(.*)*')) {
pages.unshift({
name: 'slug',
path: '/:slug(.*)*',
file: resolveRuntimeModule('./pages/document-driven.vue'),
children: []
})
}
})
nuxt.hook('app:resolve', async (app) => {
if (app.mainComponent?.includes('@nuxt/ui-templates')) {
app.mainComponent = resolveRuntimeModule('./app.vue')
} else {
const appContent = await fs.promises.readFile(app.mainComponent!, { encoding: 'utf-8' })
if (appContent.includes('<NuxtLayout') || appContent.includes('<nuxt-layout')) {
logger.warn([
'Using `<NuxtLayout>` inside `app.vue` will cause unwanted layout shifting in your application.',
'Consider removing `<NuxtLayout>` from `app.vue` and using it in your pages.'
].join(''))
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of reading the mainComponent when we cannot overwrite it, check if it includes (<NuxtLayout) and warn the user to use <NuxtLayout> in the page to avoid layout shift with the document driven mode?

})
}
} else {
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<NuxtPage />
</template>
43 changes: 32 additions & 11 deletions src/runtime/composables/content.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import { withoutTrailingSlash } from 'ufo'
import type { NavItem, ParsedContent } from '../types'
import { computed, useState } from '#imports'
import { computed, useState, useRoute } from '#imports'

export const useContentState = () => {
/**
* Current page complete data.
* Map of loaded pages.
*/
const page = useState<ParsedContent>('dd-page')
const pages = useState<Record<string, ParsedContent>>('dd-pages', () => ({}))

/**
* Navigation tree from root of app.
* Previous and next page data.
* Format: [prev, next]
*/
const navigation = useState<NavItem[]>('dd-navigation')
const surrounds = useState<Record<string, Omit<ParsedContent, 'body'>>>('dd-surrounds', () => ({}))

/**
* Previous and next page data.
* Format: [prev, next]
* Navigation tree from root of app.
*/
const surround = useState<Omit<ParsedContent, 'body'>[]>('dd-surround')
const navigation = useState<NavItem[]>('dd-navigation')

/**
* Globally loaded content files.
Expand All @@ -25,15 +26,35 @@ export const useContentState = () => {
const globals = useState<Record<string, ParsedContent>>('dd-globals', () => ({}))

return {
page,
pages,
surrounds,
navigation,
surround,
globals
}
}

export const useContent = () => {
const { navigation, page, surround, globals } = useContentState()
const { navigation, pages, surrounds, globals } = useContentState()

const _path = computed(() => withoutTrailingSlash(useRoute().path))

/**
* Current `page` key, computed from path and content state.
*/
const page = computed(
() => {
return pages.value[_path.value]
}
)

/**
* Current `surround` key, computed from path and content state.
*/
const surround = computed(
() => {
return surrounds.value[_path.value]
}
)

/**
* Table of contents from `page`.
Expand Down
12 changes: 7 additions & 5 deletions src/runtime/pages/document-driven.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ useContentHead(page)

<template>
<div class="document-driven-page">
<ContentRenderer v-if="page" :key="page._id" :value="page">
<template #empty="{ value }">
<DocumentDrivenEmpty :value="value" />
</template>
</ContentRenderer>
<NuxtLayout v-if="page" :name="page.layout || 'default'">
<ContentRenderer :key="page._id" :value="page">
<template #empty="{ value }">
<DocumentDrivenEmpty :value="value" />
</template>
</ContentRenderer>
</NuxtLayout>
<DocumentDrivenNotFound v-else />
</div>
</template>
29 changes: 11 additions & 18 deletions src/runtime/plugins/documentDriven.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import layouts from '#build/layouts'

export default defineNuxtPlugin((nuxt) => {
const { documentDriven: moduleOptions } = useRuntimeConfig()?.public?.content
const pagesCache = new Map<string, ParsedContent>()
const surroundCache = new Map<string, ParsedContent>()

/**
* Finds a layout value from a cascade of objects.
Expand Down Expand Up @@ -52,11 +50,12 @@ export default defineNuxtPlugin((nuxt) => {
return
}

const { navigation, page, globals, surround } = useContentState()
const { navigation, pages, globals, surrounds } = useContentState()

// Normalize route path
const _path = withoutTrailingSlash(to.path)

// Promises array to be executed all at once
const promises: (() => Promise<any> | any)[] = []

/**
Expand Down Expand Up @@ -136,14 +135,11 @@ export default defineNuxtPlugin((nuxt) => {
*/
if (moduleOptions.page && routeConfig.page !== false) {
const pageQuery = () => {
const { page } = useContentState()
const { pages } = useContentState()

// Return same page as page is already loaded
if (!force && page.value && page.value._path === _path) {
return page.value
}
if (!force && process.client && pagesCache.has(_path)) {
return pagesCache.get(_path)
if (!force && pages.value[_path] && pages.value[_path]._path === _path) {
return pages.value[_path]
}

return queryContent()
Expand All @@ -163,12 +159,11 @@ export default defineNuxtPlugin((nuxt) => {
*/
if (moduleOptions.surround && routeConfig.surround !== false) {
const surroundQuery = () => {
const { surrounds } = useContentState()

// Return same surround as page is already loaded
if (!force && page.value && page.value._path === _path) {
return surround.value
}
if (!force && process.client && surroundCache.has(_path)) {
return surroundCache.get(_path)
if (!force && surrounds.value[_path]) {
return surrounds.value[_path]
}

return queryContent()
Expand Down Expand Up @@ -219,13 +214,11 @@ export default defineNuxtPlugin((nuxt) => {
to.meta.layout = layoutName

// Update values
page.value = _page
process.client && pagesCache.set(_path, _page)
pages.value[_path] = _page
}

if (_surround) {
surround.value = _surround
process.client && surroundCache.set(_path, _surround)
surrounds.value[_path] = _surround
}
})
}
Expand Down