-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathchangeLog.ts
More file actions
79 lines (64 loc) · 2.13 KB
/
Copy pathchangeLog.ts
File metadata and controls
79 lines (64 loc) · 2.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import type { Changelog, GitLogOptions } from '../types'
import process from 'node:process'
import { git } from '.'
import { shouldIncludeCommit } from '../types'
/**
* ASCII Unit Separator — used as field delimiter in git pretty formats.
*/
const FIELD_SEP = '\x1F'
let cache: Changelog[] | undefined
const RE_BACKSLASH = /\\/g
const RE_PACKAGE_PATH = /^packages\/\w+\/(\w+)\/\w+\.ts$/
/**
* Separator used to split combined git log output into per-commit blocks.
*/
const COMMIT_SEP = '---COMMIT_SEP---'
export async function getChangelog(maxCount = 200, path?: string, options?: GitLogOptions) {
// Only use cache when CI env var is set (CI builds call this multiple times for the virtual module)
if (cache && process.env.CI)
return cache
// Fetch commits together with their changed files in a single git call.
const raw = await git.raw([
'log',
`--max-count=${maxCount}`,
'--name-only',
`--pretty=format:${COMMIT_SEP}%n%H${FIELD_SEP}%an${FIELD_SEP}%ae${FIELD_SEP}%aI${FIELD_SEP}%s`,
...(path ? ['--', path] : []),
])
const blocks = raw.split(COMMIT_SEP).filter(Boolean)
const logs: Changelog[] = []
for (const block of blocks) {
const lines = block.trim().split('\n')
if (!lines.length)
continue
const headerLine = lines[0]
const [hash, authorName, authorEmail, date, ...rest] = headerLine.split(FIELD_SEP)
const message = rest.join(FIELD_SEP) || ''
// Apply configurable filter
if (!shouldIncludeCommit(message, options?.changelog))
continue
const log: Changelog = {
hash,
date,
message,
refs: '',
author_name: authorName,
author_email: authorEmail,
}
if (message.includes('chore: release')) {
log.version = message.split(' ')[2]?.trim()
}
else {
// Changed files are on the remaining non-empty lines
const files = lines.slice(1).filter(Boolean).map(f => f.replace(RE_BACKSLASH, '/'))
log.functions = [...new Set(
files
.map(i => i.match(RE_PACKAGE_PATH)?.[1])
.filter((v): v is string => Boolean(v)),
)]
}
logs.push(log)
}
cache = logs
return logs
}