mirror of https://github.com/vuejs/vitepress
parent
905f58b2a8
commit
d2838e3755
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: bar
|
||||||
|
---
|
||||||
|
|
||||||
|
Hello
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
world
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: foo
|
||||||
|
---
|
||||||
|
|
||||||
|
Hello
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
world
|
@ -0,0 +1,13 @@
|
|||||||
|
import { createContentLoader } from 'vitepress'
|
||||||
|
|
||||||
|
export default createContentLoader('data-loading/content/*.md', {
|
||||||
|
includeSrc: true,
|
||||||
|
excerpt: true,
|
||||||
|
render: true,
|
||||||
|
transform(data) {
|
||||||
|
return data.map((item) => ({
|
||||||
|
...item,
|
||||||
|
transformed: true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
@ -0,0 +1,10 @@
|
|||||||
|
# Static Data
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { data } from './basic.data.js'
|
||||||
|
import { data as contentData } from './contentLoader.data.js'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<pre id="basic">{{ data }}</pre>
|
||||||
|
|
||||||
|
<pre id="content">{{ contentData }}</pre>
|
@ -0,0 +1,42 @@
|
|||||||
|
describe('static data file support in vite 3', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await goto('/data-loading/data')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('render correct content', async () => {
|
||||||
|
expect(await page.textContent('pre#basic')).toMatchInlineSnapshot(`
|
||||||
|
"[
|
||||||
|
{
|
||||||
|
\\"foo\\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"bar\\": true
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
`)
|
||||||
|
expect(await page.textContent('pre#content')).toMatchInlineSnapshot(`
|
||||||
|
"[
|
||||||
|
{
|
||||||
|
\\"src\\": \\"---\\\\ntitle: bar\\\\n---\\\\n\\\\nHello\\\\n\\\\n---\\\\n\\\\nworld\\\\n\\",
|
||||||
|
\\"html\\": \\"<p>Hello</p>\\\\n<hr>\\\\n<p>world</p>\\\\n\\",
|
||||||
|
\\"frontmatter\\": {
|
||||||
|
\\"title\\": \\"bar\\"
|
||||||
|
},
|
||||||
|
\\"excerpt\\": \\"<p>Hello</p>\\\\n\\",
|
||||||
|
\\"url\\": \\"/data-loading/content/bar.html\\",
|
||||||
|
\\"transformed\\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"src\\": \\"---\\\\ntitle: foo\\\\n---\\\\n\\\\nHello\\\\n\\\\n---\\\\n\\\\nworld\\\\n\\",
|
||||||
|
\\"html\\": \\"<p>Hello</p>\\\\n<hr>\\\\n<p>world</p>\\\\n\\",
|
||||||
|
\\"frontmatter\\": {
|
||||||
|
\\"title\\": \\"foo\\"
|
||||||
|
},
|
||||||
|
\\"excerpt\\": \\"<p>Hello</p>\\\\n\\",
|
||||||
|
\\"url\\": \\"/data-loading/content/foo.html\\",
|
||||||
|
\\"transformed\\": true
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
})
|
@ -1,14 +0,0 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
||||||
|
|
||||||
exports[`static data file support in vite 3 > render correct content 1`] = `
|
|
||||||
[
|
|
||||||
"[
|
|
||||||
{
|
|
||||||
\\"foo\\": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
\\"bar\\": true
|
|
||||||
}
|
|
||||||
]",
|
|
||||||
]
|
|
||||||
`;
|
|
@ -1,7 +0,0 @@
|
|||||||
# Static Data
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { data } from './static.data.js'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{ data }}
|
|
@ -1,12 +0,0 @@
|
|||||||
describe('static data file support in vite 3', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await goto('/static-data/data')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('render correct content', async () => {
|
|
||||||
const pLocator = page.locator('.VPContent p')
|
|
||||||
|
|
||||||
const pContents = await pLocator.allTextContents()
|
|
||||||
expect(pContents).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
})
|
|
@ -0,0 +1,140 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import glob from 'fast-glob'
|
||||||
|
import type { SiteConfig } from './config'
|
||||||
|
import matter from 'gray-matter'
|
||||||
|
import { normalizePath } from 'vite'
|
||||||
|
import { createMarkdownRenderer, type MarkdownRenderer } from './markdown'
|
||||||
|
|
||||||
|
export interface ContentOptions<T = ContentData[]> {
|
||||||
|
/**
|
||||||
|
* Include src?
|
||||||
|
* default: false
|
||||||
|
*/
|
||||||
|
includeSrc?: boolean
|
||||||
|
/**
|
||||||
|
* Render src to HTML and include in data?
|
||||||
|
* default: false
|
||||||
|
*/
|
||||||
|
render?: boolean
|
||||||
|
/**
|
||||||
|
* Whether to parse and include excerpt (rendered as HTML)
|
||||||
|
* default: false
|
||||||
|
*/
|
||||||
|
excerpt?: boolean
|
||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContentData {
|
||||||
|
url: string
|
||||||
|
src: string | undefined
|
||||||
|
html: string | undefined
|
||||||
|
frontmatter: Record<string, any>
|
||||||
|
excerpt: string | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a loader object that can be directly used as the default export
|
||||||
|
* of a data loader file.
|
||||||
|
*/
|
||||||
|
export function createContentLoader<T = ContentData[]>(
|
||||||
|
/**
|
||||||
|
* files to glob / watch - relative to <project root>
|
||||||
|
*/
|
||||||
|
pattern: string | string[],
|
||||||
|
{
|
||||||
|
includeSrc,
|
||||||
|
render,
|
||||||
|
excerpt: renderExcerpt,
|
||||||
|
transform
|
||||||
|
}: ContentOptions<T> = {}
|
||||||
|
): {
|
||||||
|
watch: string | string[]
|
||||||
|
load: () => Promise<T>
|
||||||
|
} {
|
||||||
|
const config: SiteConfig = (global as any).VITEPRESS_CONFIG
|
||||||
|
if (!config) {
|
||||||
|
throw new Error(
|
||||||
|
'content loader invoked without an active vitepress process, ' +
|
||||||
|
'or before vitepress config is resolved.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof pattern === 'string') pattern = [pattern]
|
||||||
|
pattern = pattern.map((p) => normalizePath(path.join(config.root, p)))
|
||||||
|
|
||||||
|
let md: MarkdownRenderer
|
||||||
|
|
||||||
|
const cache = new Map<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
data: any
|
||||||
|
timestamp: number
|
||||||
|
}
|
||||||
|
>()
|
||||||
|
|
||||||
|
return {
|
||||||
|
watch: pattern,
|
||||||
|
async load(files?: string[]) {
|
||||||
|
if (!files) {
|
||||||
|
// the loader is being called directly, do a fresh glob
|
||||||
|
files = (
|
||||||
|
await glob(pattern, {
|
||||||
|
ignore: ['**/node_modules/**', '**/dist/**']
|
||||||
|
})
|
||||||
|
).sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
md =
|
||||||
|
md ||
|
||||||
|
(await createMarkdownRenderer(
|
||||||
|
config.srcDir,
|
||||||
|
config.markdown,
|
||||||
|
config.site.base,
|
||||||
|
config.logger
|
||||||
|
))
|
||||||
|
|
||||||
|
const raw: ContentData[] = []
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (!file.endsWith('.md')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const timestamp = fs.statSync(file).mtimeMs
|
||||||
|
const cached = cache.get(file)
|
||||||
|
if (cached && timestamp === cached.timestamp) {
|
||||||
|
raw.push(cached.data)
|
||||||
|
} else {
|
||||||
|
const src = fs.readFileSync(file, 'utf-8')
|
||||||
|
const { data: frontmatter, excerpt } = matter(src, {
|
||||||
|
excerpt: true
|
||||||
|
})
|
||||||
|
const url =
|
||||||
|
'/' +
|
||||||
|
normalizePath(path.relative(config.root, file)).replace(
|
||||||
|
/\.md$/,
|
||||||
|
config.cleanUrls ? '' : '.html'
|
||||||
|
)
|
||||||
|
const html = render ? md.render(src) : undefined
|
||||||
|
const renderedExcerpt = renderExcerpt
|
||||||
|
? excerpt && md.render(excerpt)
|
||||||
|
: undefined
|
||||||
|
const data: ContentData = {
|
||||||
|
src: includeSrc ? src : undefined,
|
||||||
|
html,
|
||||||
|
frontmatter,
|
||||||
|
excerpt: renderedExcerpt,
|
||||||
|
url
|
||||||
|
}
|
||||||
|
cache.set(file, { data, timestamp })
|
||||||
|
raw.push(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (transform ? transform(raw) : raw) as any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue