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

feat: added frontmatter props #57

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ For example:
```md
---
name: My Cool App
description: This is My Cool App
---

# Hello World
Expand All @@ -170,9 +171,49 @@ Will be rendered as

```html
<h1>Hello World</h1>
<p>This is My Cool App</p>
<p>{{frontmatter.description}}</p>
```

You can override existing frontmatter values by passing the :frontmatter-merge prop to the component.

```html
<template>
<HelloWorld :frontmatter-merge="{ name: 'My Awesome App' }" />
</template>

<script>
import HelloWorld from './README.md'

export default {
components: {
HelloWorld,
},
}
</script>
```

Alternatively, you can replace all frontmatter values using the `:frontmatter-replace` prop. Any remaining frontmatter properties will not render and will be `undefined`.

```html
<template>
<HelloWorld :frontmatter-replace="{ name: 'My Super App' }" />
</template>

<script>
import HelloWorld from './README.md'

export default {
components: {
HelloWorld,
},
}
</script>
```

For example, if you use `:frontmatter-replace`, the `description` property will not be shown on the screen.

> Use either `:frontmatter-merge` or `:frontmatter-replace` for a component. If both props are provided, only `:frontmatter-replace` will take effect.

It will also be passed to the wrapper component's props if you have set `wrapperComponent` option.

## Document head and meta
Expand Down
16 changes: 14 additions & 2 deletions src/core/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,19 @@ export function createMarkdown(options: ResolvedOptions) {
if (options.excerpt && !excerptKeyOverlapping && frontmatter.excerpt !== undefined)
delete frontmatter.excerpt

scriptLines.push(`const frontmatter = ${JSON.stringify(frontmatter)}`)
scriptLines.push(
`import { computed } from 'vue'`,
'const props = defineProps({ frontmatterMerge: { type: Object } })',
`const _frontmatter = ${JSON.stringify(frontmatter)}`,
`const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})`,
)

if (options.exportFrontmatter) {
frontmatterExportsLines = Object.entries(frontmatter)
Expand All @@ -183,7 +195,7 @@ export function createMarkdown(options: ResolvedOptions) {
}

if (!isVue2 && options.exposeFrontmatter && !hasExplicitExports())
scriptLines.push('defineExpose({ frontmatter })')
scriptLines.push('defineExpose({ frontmatter: frontmatter.value })')

if (!isVue2 && headEnabled && head) {
// @ts-expect-error legacy option
Expand Down
28 changes: 24 additions & 4 deletions test/__snapshots__/excerpt.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,18 @@ exports[`excerpt > raw excerpt 1`] = `
</ul>
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {"title":"Hey"}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
const excerpt = "\\nThis is an excerpt which is kept as **raw Markdown**.\\n\\n"
</script>
<script>
Expand All @@ -32,8 +42,18 @@ exports[`excerpt > rendered excerpt 1`] = `
</ul>
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {"title":"Hey"}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
const excerpt = "<p>This is an excerpt which has been rendered to <strong>HTML</strong>.</p>\\n"
</script>
<script>
Expand Down
138 changes: 119 additions & 19 deletions test/__snapshots__/transform.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,18 @@ exports[`transform > basic 1`] = `
</ul>
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {"title":"Hey"}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
</script>
<script>
export const title = "Hey"
Expand All @@ -23,16 +33,36 @@ exports[`transform > code escape 1`] = `
</code></pre>
</div></template>
<script setup>
const frontmatter = {}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
</script>"
`;

exports[`transform > couldn't expose frontmatter 1`] = `
"<template><div class="markdown-body">
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {"title":"Hey"}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ test: 'test'})
</script>
<script>
Expand All @@ -46,17 +76,37 @@ exports[`transform > escapeCodeTagInterpolation 1`] = `
</code></pre>
</div></template>
<script setup>
const frontmatter = {}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
</script>"
`;

exports[`transform > export keyword frontmatters 1`] = `
"<template><div class="markdown-body"><p>Hello</p>
</div></template>
<script setup>
const frontmatter = {"class":"text","default":"foo"}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {"class":"text","default":"foo"}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
</script>
<script>
export const _class = "text"
Expand All @@ -68,8 +118,18 @@ exports[`transform > exposes frontmatter 1`] = `
"<template><div class="markdown-body"><h1>Hello</h1>
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {"title":"Hey"}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
</script>
<script>
export const title = "Hey"
Expand All @@ -81,8 +141,18 @@ exports[`transform > frontmatter interpolation 1`] = `
<p>This is {{frontmatter.name}}</p>
</div></template>
<script setup>
const frontmatter = {"name":"My Cool App"}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {"name":"My Cool App"}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
</script>
<script>
export const name = "My Cool App"
Expand All @@ -94,8 +164,18 @@ exports[`transform > script setup 1`] = `

</div></template>
<script setup lang="ts">
const frontmatter = {}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
import Foo from './Foo.vue'
</script>"
`;
Expand All @@ -105,8 +185,18 @@ exports[`transform > style 1`] = `

</div></template>
<script setup>
const frontmatter = {}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
</script>
<style>h1 { color: red }</style>"
`;
Expand All @@ -116,8 +206,18 @@ exports[`transform > vue directives 1`] = `
<p><button @click="onClick"></button></p>
</div></template>
<script setup lang="ts">
const frontmatter = {"name":"My Cool App"}
defineExpose({ frontmatter })
import { computed } from 'vue'
const props = defineProps({ frontmatterMerge: { type: Object } })
const _frontmatter = {"name":"My Cool App"}
const frontmatter = computed(() => {
if (props.frontmatterReplace && typeof props.frontmatterReplace === 'object') {
const replaceKeys = Object.keys(props.frontmatterReplace)
return Object.entries(_frontmatter).reduce((acc, [key, value]) => ({ ...acc, [key]: replaceKeys.includes(key) ? value : undefined }), {})
}

return { ..._frontmatter, ...props.frontmatterMerge }
})
defineExpose({ frontmatter: frontmatter.value })
function onClick() {
// ...
}
Expand Down