Skip to content

Commit b373971

Browse files
authored
fix: frontmatter provide strategy (#1724)
1 parent 5231979 commit b373971

File tree

4 files changed

+36
-58
lines changed

4 files changed

+36
-58
lines changed

packages/client/context.ts

+1-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { computed, ref, toRef } from 'vue'
2-
import { injectLocal, objectOmit, provideLocal } from '@vueuse/core'
2+
import { injectLocal, objectOmit } from '@vueuse/core'
33
import {
44
FRONTMATTER_FIELDS,
55
HEADMATTER_FIELDS,
@@ -44,28 +44,6 @@ export function useSlideContext() {
4444

4545
export type SlideContext = ReturnType<typeof useSlideContext>
4646

47-
/**
48-
* @internal
49-
*/
50-
export function provideFrontmatter(frontmatter: Record<string, any>) {
51-
provideLocal(injectionFrontmatter, frontmatter)
52-
53-
const {
54-
$slidev,
55-
$page,
56-
} = useSlideContext()
57-
58-
// update frontmatter in router to make HMR work better
59-
const route = $slidev.nav.slides.find(i => i.no === $page.value)
60-
if (route?.meta?.slide?.frontmatter) {
61-
for (const key of Object.keys(route.meta.slide.frontmatter)) {
62-
if (!(key in frontmatter))
63-
delete route.meta.slide.frontmatter[key]
64-
}
65-
Object.assign(route.meta.slide.frontmatter, frontmatter)
66-
}
67-
}
68-
6947
/**
7048
* Convert frontmatter options to props for v-bind
7149
* It removes known options fields, and expose an extra `frontmatter` field that contains full frontmatter

packages/client/internals/SlideWrapper.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { computed, defineAsyncComponent, defineComponent, h, ref, toRef } from '
33
import type { CSSProperties, PropType } from 'vue'
44
import { provideLocal } from '@vueuse/core'
55
import type { ClicksContext, RenderContext, SlideRoute } from '@slidev/types'
6-
import { injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionRoute, injectionSlideZoom } from '../constants'
6+
import { injectionClicksContext, injectionCurrentPage, injectionFrontmatter, injectionRenderContext, injectionRoute, injectionSlideZoom } from '../constants'
77
import { getSlideClass } from '../utils'
88
import { configs } from '../env'
99
import SlideLoading from './SlideLoading.vue'
@@ -27,6 +27,7 @@ const props = defineProps({
2727
const zoom = computed(() => props.route.meta?.slide?.frontmatter.zoom ?? 1)
2828
2929
provideLocal(injectionRoute, props.route)
30+
provideLocal(injectionFrontmatter, props.route.meta.slide.frontmatter)
3031
provideLocal(injectionCurrentPage, ref(props.route.no))
3132
provideLocal(injectionRenderContext, ref(props.renderContext))
3233
provideLocal(injectionClicksContext, toRef(props, 'clicksContext'))

packages/client/shim-vue.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ declare module 'vue-router' {
99

1010
interface RouteMeta {
1111
// inherited from frontmatter
12-
layout: string
12+
layout?: string
1313
name?: string
1414
class?: string
1515
clicks?: number
@@ -26,7 +26,7 @@ declare module 'vue-router' {
2626
}
2727

2828
// private fields
29-
__clicksContext: import('@slidev/types').ClicksContext | undefined
29+
__clicksContext: import('@slidev/types').ClicksContext | null
3030
__preloaded?: boolean
3131
}
3232
}

packages/slidev/node/vite/loaders.ts

+31-32
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,8 @@ const regexId = /^\/@slidev\/slide\/(\d+)\.(md|json)(?:\?import)?$/
2525
const regexIdQuery = /(\d+)\.(md|json|frontmatter)$/
2626

2727
const templateInjectionMarker = '/* @slidev-injection */'
28-
const templateImportContextUtils = `import {
29-
useSlideContext,
30-
provideFrontmatter as _provideFrontmatter,
31-
frontmatterToProps as _frontmatterToProps,
32-
} from "@slidev/client/context.ts"`.replace(/\n\s*/g, ' ')
33-
const templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $page, $renderContext, $frontmatter } = useSlideContext()`
28+
const templateImportContextUtils = `import { useSlideContext as _useSlideContext, frontmatterToProps as _frontmatterToProps } from "@slidev/client/context.ts"`
29+
const templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $page, $renderContext, $frontmatter } = _useSlideContext()`
3430

3531
export function getBodyJson(req: Connect.IncomingMessage) {
3632
return new Promise<any>((resolve, reject) => {
@@ -321,37 +317,42 @@ export function createSlidesLoader(
321317
return {
322318
code: [
323319
'// @unocss-include',
324-
'import { reactive, computed } from "vue"',
325-
`export const frontmatter = reactive(${JSON.stringify(fontmatter)})`,
326-
`export const meta = reactive({
327-
layout: computed(() => frontmatter.layout),
328-
transition: computed(() => frontmatter.transition),
329-
class: computed(() => frontmatter.class),
330-
clicks: computed(() => frontmatter.clicks),
331-
name: computed(() => frontmatter.name),
332-
preload: computed(() => frontmatter.preload),
333-
slide: {
334-
...(${JSON.stringify(slideBase)}),
335-
frontmatter,
336-
filepath: ${JSON.stringify(mode === 'dev' ? slide.source.filepath : '')},
337-
start: ${JSON.stringify(slide.source.start)},
338-
id: ${pageNo},
339-
no: ${no},
340-
},
341-
__clicksContext: null,
342-
__preloaded: false,
343-
})`,
344-
'export default frontmatter',
320+
'import { computed, reactive, shallowReactive } from "vue"',
321+
`export const frontmatterData = ${JSON.stringify(fontmatter)}`,
345322
// handle HMR, update frontmatter with update
346323
'if (import.meta.hot) {',
347-
' import.meta.hot.accept(({ frontmatter: update }) => {',
348-
' if(!update) return',
324+
' const firstLoad = !import.meta.hot.data.frontmatter',
325+
' import.meta.hot.data.frontmatter ??= reactive(frontmatterData)',
326+
' import.meta.hot.accept(({ frontmatterData: update }) => {',
327+
' if (firstLoad) return',
328+
' const frontmatter = import.meta.hot.data.frontmatter',
349329
' Object.keys(frontmatter).forEach(key => {',
350330
' if (!(key in update)) delete frontmatter[key]',
351331
' })',
352332
' Object.assign(frontmatter, update)',
353333
' })',
354334
'}',
335+
'export const frontmatter = import.meta.hot ? import.meta.hot.data.frontmatter : reactive(frontmatterData)',
336+
'export default frontmatter',
337+
'export const meta = shallowReactive({',
338+
' get layout(){ return frontmatter.layout },',
339+
' get transition(){ return frontmatter.transition },',
340+
' get class(){ return frontmatter.class },',
341+
' get clicks(){ return frontmatter.clicks },',
342+
' get name(){ return frontmatter.name },',
343+
' get preload(){ return frontmatter.preload },',
344+
// No need to be reactive, as it's only used once after reload
345+
' slide: {',
346+
` ...(${JSON.stringify(slideBase)}),`,
347+
` frontmatter,`,
348+
` filepath: ${JSON.stringify(mode === 'dev' ? slide.source.filepath : '')},`,
349+
` start: ${JSON.stringify(slide.source.start)},`,
350+
` id: ${pageNo},`,
351+
` no: ${no},`,
352+
' },',
353+
' __clicksContext: null,',
354+
' __preloaded: false,',
355+
'})',
355356
].join('\n'),
356357
map: { mappings: '' },
357358
}
@@ -430,9 +431,7 @@ export function createSlidesLoader(
430431
delete frontmatter.title
431432
const imports = [
432433
`import InjectedLayout from "${toAtFS(layouts[layoutName])}"`,
433-
`import frontmatter from "${toAtFS(`${VIRTUAL_SLIDE_PREFIX + (index + 1)}.frontmatter`)}"`,
434434
templateImportContextUtils,
435-
'_provideFrontmatter(frontmatter)',
436435
templateInitContext,
437436
templateInjectionMarker,
438437
]
@@ -443,7 +442,7 @@ export function createSlidesLoader(
443442
let body = code.slice(injectA, injectB).trim()
444443
if (body.startsWith('<div>') && body.endsWith('</div>'))
445444
body = body.slice(5, -6)
446-
code = `${code.slice(0, injectA)}\n<InjectedLayout v-bind="_frontmatterToProps(frontmatter,${index})">\n${body}\n</InjectedLayout>\n${code.slice(injectB)}`
445+
code = `${code.slice(0, injectA)}\n<InjectedLayout v-bind="_frontmatterToProps($frontmatter,${index})">\n${body}\n</InjectedLayout>\n${code.slice(injectB)}`
447446

448447
return code
449448
}

0 commit comments

Comments
 (0)