feat(build): custom excerpt for `createContentLoader` (#2698)

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>
pull/2705/head
烽宁 1 year ago committed by GitHub
parent 68f25f5a9c
commit 13f94a6663
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -23,7 +23,7 @@ The loader module is evaluated only in Node.js, so you can import Node APIs and
You can then import data from this file in `.md` pages and `.vue` components using the `data` named export:
```html
```vue
<script setup>
import { data } from './example.data.js'
</script>
@ -70,7 +70,7 @@ export default {
// watchedFiles will be an array of absolute paths of the matched files.
// generate an array of blog post metadata that can be used to render
// a list in the theme layout
return watchedFiles.map(file => {
return watchedFiles.map((file) => {
return parse(fs.readFileSync(file, 'utf-8'), {
columns: true,
skip_empty_lines: true
@ -147,7 +147,7 @@ export default createContentLoader('posts/*.md', {
// the final result is what will be shipped to the client.
return rawData.sort((a, b) => {
return +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)
}).map(page => {
}).map((page) => {
page.src // raw markdown source
page.html // rendered full page HTML
page.excerpt // rendered excerpt HTML (content above first `---`)
@ -159,7 +159,7 @@ export default createContentLoader('posts/*.md', {
Check out how it is used in the [Vue.js blog](https://github.com/vuejs/blog/blob/main/.vitepress/theme/posts.data.ts).
The `createContentLoader` API can also be used inside [build hooks](/reference/site-config#build-hooks):
The `createContentLoader` API can also be used inside [build hooks](../reference/site-config#build-hooks):
```js
// .vitepress/config.js
@ -171,6 +171,48 @@ export default {
}
```
**Types**
```ts
interface ContentOptions<T = ContentData[]> {
/**
* Include src?
* @default false
*/
includeSrc?: boolean
/**
* Render src to HTML and include in data?
* @default false
*/
render?: boolean
/**
* If `boolean`, whether to parse and include excerpt? (rendered as HTML)
*
* If `function`, control how the excerpt is extracted from the content.
*
* If `string`, define a custom separator to be used for extracting the
* excerpt. Default separator is `---` if `excerpt` is `true`.
*
* @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt
* @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt_separator
*
* @default false
*/
excerpt?:
| boolean
| ((file: { data: { [key: string]: any }; content: string; excerpt?: string }, options?: any) => void)
| string
/**
* Transform the data. Note the data will be inlined as JSON in the client
* bundle if imported from components or markdown files.
*/
transform?: (data: ContentData[]) => T | Promise<T>
}
```
## Typed Data Loaders
When using TypeScript, you can type your loader and `data` export like so:

@ -73,7 +73,7 @@ Cache-Control: max-age=31536000,immutable
cache-control: immutable
```
Note: the `_headers` file should be placed in the [public directory](/guide/asset-handling#the-public-directory) - in our case, `docs/public/_headers` - so that it is copied verbatim to the output directory.
Note: the `_headers` file should be placed in the [public directory](./asset-handling#the-public-directory) - in our case, `docs/public/_headers` - so that it is copied verbatim to the output directory.
[Netlify custom headers documentation](https://docs.netlify.com/routing/headers/)

@ -59,10 +59,10 @@ export default DefaultTheme
```
::: warning
If you are using optional components like the [Team Page](/reference/default-theme-team-page) components, make sure to also import them from `vitepress/theme-without-fonts`!
If you are using optional components like the [Team Page](../reference/default-theme-team-page) components, make sure to also import them from `vitepress/theme-without-fonts`!
:::
If your font is a local file referenced via `@font-face`, it will be processed as an asset and included under `.vitepress/dist/assets` with hashed filename. To preload this file, use the [transformHead](/reference/site-config#transformhead) build hook:
If your font is a local file referenced via `@font-face`, it will be processed as an asset and included under `.vitepress/dist/assets` with hashed filename. To preload this file, use the [transformHead](../reference/site-config#transformhead) build hook:
```js
// .vitepress/config.js

@ -48,7 +48,7 @@ if (!import.meta.env.SSR) {
}
```
Since [`Theme.enhanceApp`](/guide/custom-theme#theme-interface) can be async, you can conditionally import and register Vue plugins that access browser APIs on import:
Since [`Theme.enhanceApp`](./custom-theme#theme-interface) can be async, you can conditionally import and register Vue plugins that access browser APIs on import:
```js
// .vitepress/theme/index.js

@ -141,7 +141,7 @@ If you are using or demoing components that are not SSR-friendly (for example, c
</ClientOnly>
```
- Related: [SSR Compatibility](/guide/ssr-compat)
- Related: [SSR Compatibility](../guide/ssr-compat)
## `$frontmatter` <Badge type="info" text="template global" />

@ -9,19 +9,41 @@ import { createMarkdownRenderer, type MarkdownRenderer } from './markdown'
export interface ContentOptions<T = ContentData[]> {
/**
* Include src?
* default: false
* @default false
*/
includeSrc?: boolean
/**
* Render src to HTML and include in data?
* default: false
* @default false
*/
render?: boolean
/**
* Whether to parse and include excerpt (rendered as HTML)
* default: false
* If `boolean`, whether to parse and include excerpt? (rendered as HTML)
*
* If `function`, control how the excerpt is extracted from the content.
*
* If `string`, define a custom separator to be used for extracting the
* excerpt. Default separator is `---` if `excerpt` is `true`.
*
* @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt
* @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt_separator
*
* @default false
*/
excerpt?: boolean
excerpt?:
| boolean
| ((
file: {
data: { [key: string]: any }
content: string
excerpt?: string
},
options?: any
) => void)
| string
/**
* Transform the data. Note the data will be inlined as JSON in the client
* bundle if imported from components or markdown files.
@ -110,9 +132,13 @@ export function createContentLoader<T = ContentData[]>(
raw.push(cached.data)
} else {
const src = fs.readFileSync(file, 'utf-8')
const { data: frontmatter, excerpt } = matter(src, {
excerpt: true
})
const { data: frontmatter, excerpt } = matter(
src,
// @ts-expect-error gray-matter types are wrong
typeof renderExcerpt === 'string'
? { excerpt_separator: renderExcerpt }
: { excerpt: renderExcerpt }
)
const url =
'/' +
normalizePath(path.relative(config.srcDir, file)).replace(

Loading…
Cancel
Save