Skip to content

Commit 7949a69

Browse files
authored
Merge branch 'vuejs:main' into main
2 parents 6aba818 + 6dfcdd3 commit 7949a69

File tree

4 files changed

+116
-38
lines changed

4 files changed

+116
-38
lines changed

src/node/build/generateSitemap.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
type NewsItem
1010
} from 'sitemap'
1111
import type { SiteConfig } from '../config'
12+
import { slash } from '../shared'
1213
import { getGitTimestamp } from '../utils/getGitTimestamp'
1314
import { task } from '../utils/task'
1415

@@ -29,7 +30,7 @@ export async function generateSitemap(siteConfig: SiteConfig) {
2930
if (data.lastUpdated === false) return undefined
3031
if (data.lastUpdated instanceof Date) return +data.lastUpdated
3132

32-
return (await getGitTimestamp(file)) || undefined
33+
return (await getGitTimestamp(slash(file))) || undefined
3334
}
3435

3536
await task('generating sitemap', async () => {

src/node/markdownToVue.ts

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ function getResolutionCache(siteConfig: SiteConfig) {
8383
export async function createMarkdownToVueRenderFn(
8484
srcDir: string,
8585
options: MarkdownOptions = {},
86-
isBuild = false,
8786
base = '/',
8887
includeLastUpdatedData = false,
8988
cleanUrls = false,
@@ -115,7 +114,7 @@ export async function createMarkdownToVueRenderFn(
115114
const relativePath = slash(path.relative(srcDir, file))
116115

117116
const cacheKey = JSON.stringify({ src, ts, relativePath })
118-
if (isBuild || options.cache !== false) {
117+
if (options.cache !== false) {
119118
const cached = cache.get(cacheKey)
120119
if (cached) {
121120
debug(`[cache hit] ${relativePath}`)
@@ -177,15 +176,9 @@ export async function createMarkdownToVueRenderFn(
177176
}
178177

179178
return siteConfig.ignoreDeadLinks.some((ignore) => {
180-
if (typeof ignore === 'string') {
181-
return url === ignore
182-
}
183-
if (ignore instanceof RegExp) {
184-
return ignore.test(url)
185-
}
186-
if (typeof ignore === 'function') {
187-
return ignore(url, fileOrig)
188-
}
179+
if (typeof ignore === 'string') return url === ignore
180+
if (ignore instanceof RegExp) return ignore.test(url)
181+
if (typeof ignore === 'function') return ignore(url, fileOrig)
189182
return false
190183
})
191184
}
@@ -198,6 +191,7 @@ export async function createMarkdownToVueRenderFn(
198191

199192
url = url.replace(/[?#].*$/, '').replace(/\.(html|md)$/, '')
200193
if (url.endsWith('/')) url += `index`
194+
201195
let resolved = decodeURIComponent(
202196
slash(
203197
url.startsWith('/')
@@ -207,6 +201,7 @@ export async function createMarkdownToVueRenderFn(
207201
)
208202
resolved =
209203
siteConfig?.rewrites.inv[resolved + '.md']?.slice(0, -3) || resolved
204+
210205
if (
211206
!pages.includes(resolved) &&
212207
!fs.existsSync(path.resolve(dir, publicDir, `${resolved}.html`)) &&
@@ -239,12 +234,7 @@ export async function createMarkdownToVueRenderFn(
239234
for (const fn of transformPageData) {
240235
if (fn) {
241236
const dataToMerge = await fn(pageData, { siteConfig })
242-
if (dataToMerge) {
243-
pageData = {
244-
...pageData,
245-
...dataToMerge
246-
}
247-
}
237+
if (dataToMerge) pageData = { ...pageData, ...dataToMerge }
248238
}
249239
}
250240

@@ -260,15 +250,8 @@ export async function createMarkdownToVueRenderFn(
260250

261251
debug(`[render] ${file} in ${Date.now() - start}ms.`)
262252

263-
const result = {
264-
vueSrc,
265-
pageData,
266-
deadLinks,
267-
includes
268-
}
269-
if (isBuild || options.cache !== false) {
270-
cache.set(cacheKey, result)
271-
}
253+
const result = { vueSrc, pageData, deadLinks, includes }
254+
if (options.cache !== false) cache.set(cacheKey, result)
272255
return result
273256
}
274257
}

src/node/plugin.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { staticDataPlugin } from './plugins/staticDataPlugin'
3131
import { webFontsPlugin } from './plugins/webFontsPlugin'
3232
import { slash, type PageDataPayload } from './shared'
3333
import { deserializeFunctions, serializeFunctions } from './utils/fnSerialize'
34+
import { cacheAllGitTimestamps } from './utils/getGitTimestamp'
3435

3536
declare module 'vite' {
3637
interface UserConfig {
@@ -113,10 +114,11 @@ export async function createVitePressPlugin(
113114

114115
async configResolved(resolvedConfig) {
115116
config = resolvedConfig
117+
// pre-resolve git timestamps
118+
if (lastUpdated) await cacheAllGitTimestamps(srcDir)
116119
markdownToVue = await createMarkdownToVueRenderFn(
117120
srcDir,
118121
markdown,
119-
config.command === 'build',
120122
config.base,
121123
lastUpdated,
122124
cleanUrls,

src/node/utils/getGitTimestamp.ts

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,121 @@
1-
import { spawn } from 'cross-spawn'
2-
import fs from 'fs-extra'
3-
import { basename, dirname } from 'node:path'
1+
import { spawn, sync } from 'cross-spawn'
2+
import _debug from 'debug'
3+
import fs from 'node:fs'
4+
import path from 'node:path'
5+
import { slash } from '../shared'
46

7+
const debug = _debug('vitepress:git')
58
const cache = new Map<string, number>()
69

7-
export function getGitTimestamp(file: string) {
10+
const RS = 0x1e
11+
const NUL = 0x00
12+
13+
export async function cacheAllGitTimestamps(
14+
root: string,
15+
pathspec: string[] = ['*.md']
16+
): Promise<void> {
17+
const cp = sync('git', ['rev-parse', '--show-toplevel'], { cwd: root })
18+
if (cp.error) throw cp.error
19+
const gitRoot = cp.stdout.toString('utf8').trim()
20+
21+
const args = [
22+
'log',
23+
'--pretty=format:%x1e%at%x00', // RS + epoch + NUL
24+
'--name-only',
25+
'-z',
26+
'--',
27+
...pathspec
28+
]
29+
30+
return new Promise((resolve, reject) => {
31+
const out = new Map<string, number>()
32+
const child = spawn('git', args, { cwd: root })
33+
34+
let buf = Buffer.alloc(0)
35+
child.stdout.on('data', (chunk: Buffer<ArrayBuffer>) => {
36+
buf = buf.length ? Buffer.concat([buf, chunk]) : chunk
37+
38+
let scanFrom = 0
39+
let ts = 0
40+
41+
while (true) {
42+
if (ts === 0) {
43+
const rs = buf.indexOf(RS, scanFrom)
44+
if (rs === -1) break
45+
scanFrom = rs + 1
46+
47+
const nul = buf.indexOf(NUL, scanFrom)
48+
if (nul === -1) break
49+
scanFrom = nul + 2 // skip LF after NUL
50+
51+
const tsSec = buf.toString('utf8', rs + 1, nul)
52+
ts = Number.parseInt(tsSec, 10) * 1000
53+
}
54+
55+
let nextNul
56+
while (true) {
57+
nextNul = buf.indexOf(NUL, scanFrom)
58+
if (nextNul === -1) break
59+
60+
// double NUL, move to next record
61+
if (nextNul === scanFrom) {
62+
scanFrom += 1
63+
ts = 0
64+
break
65+
}
66+
67+
const file = buf.toString('utf8', scanFrom, nextNul)
68+
if (file && !out.has(file)) out.set(file, ts)
69+
scanFrom = nextNul + 1
70+
}
71+
72+
if (nextNul === -1) break
73+
}
74+
75+
if (scanFrom > 0) buf = buf.subarray(scanFrom)
76+
})
77+
78+
child.on('close', async () => {
79+
cache.clear()
80+
81+
for (const [file, ts] of out) {
82+
const abs = path.resolve(gitRoot, file)
83+
if (fs.existsSync(abs)) cache.set(slash(abs), ts)
84+
}
85+
86+
out.clear()
87+
resolve()
88+
})
89+
90+
child.on('error', reject)
91+
})
92+
}
93+
94+
export async function getGitTimestamp(file: string): Promise<number> {
895
const cached = cache.get(file)
996
if (cached) return cached
1097

98+
// most likely will never happen except for recently added files in dev
99+
debug(`[cache miss] ${file}`)
100+
11101
if (!fs.existsSync(file)) return 0
12102

13-
return new Promise<number>((resolve, reject) => {
103+
return new Promise((resolve, reject) => {
14104
const child = spawn(
15105
'git',
16-
['log', '-1', '--pretty="%ai"', basename(file)],
17-
{ cwd: dirname(file) }
106+
['log', '-1', '--pretty=%at', path.basename(file)],
107+
{ cwd: path.dirname(file) }
18108
)
19109

20110
let output = ''
21111
child.stdout.on('data', (d) => (output += String(d)))
22112

23113
child.on('close', () => {
24-
const timestamp = +new Date(output)
25-
cache.set(file, timestamp)
26-
resolve(timestamp)
114+
const ts = Number.parseInt(output.trim(), 10) * 1000
115+
if (!(ts > 0)) return resolve(0)
116+
117+
cache.set(file, ts)
118+
resolve(ts)
27119
})
28120

29121
child.on('error', reject)

0 commit comments

Comments
 (0)