/
getChangedPackages.ts
90 lines (79 loc) · 3.07 KB
/
getChangedPackages.ts
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
80
81
82
83
84
85
86
87
88
89
90
import path from 'path'
import PnpmError from '@pnpm/error'
import * as micromatch from 'micromatch'
import execa from 'execa'
import findUp from 'find-up'
type ChangeType = 'source' | 'test'
interface ChangedDir { dir: string, changeType: ChangeType }
export default async function changedSince (packageDirs: string[], commit: string, opts: { workspaceDir: string, testPattern?: string[], changedFilesIgnorePattern?: string[] }): Promise<[string[], string[]]> {
const repoRoot = path.resolve(await findUp('.git', { cwd: opts.workspaceDir, type: 'directory' }) ?? opts.workspaceDir, '..')
const changedDirs = (await getChangedDirsSinceCommit(commit, opts.workspaceDir, opts.testPattern ?? [], opts.changedFilesIgnorePattern ?? []))
.map(changedDir => ({ ...changedDir, dir: path.join(repoRoot, changedDir.dir) }))
const pkgChangeTypes = new Map<string, ChangeType | undefined>()
for (const pkgDir of packageDirs) {
pkgChangeTypes.set(pkgDir, undefined)
}
for (const changedDir of changedDirs) {
let currentDir = changedDir.dir
while (!pkgChangeTypes.has(currentDir)) {
const nextDir = path.dirname(currentDir)
if (nextDir === currentDir) break
currentDir = nextDir
}
if (pkgChangeTypes.get(currentDir) === 'source') continue
pkgChangeTypes.set(currentDir, changedDir.changeType)
}
const changedPkgs = [] as string[]
const ignoreDependentForPkgs = [] as string[]
for (const [changedDir, changeType] of pkgChangeTypes.entries()) {
switch (changeType) {
case 'source':
changedPkgs.push(changedDir)
break
case 'test':
ignoreDependentForPkgs.push(changedDir)
break
}
}
return [changedPkgs, ignoreDependentForPkgs]
}
async function getChangedDirsSinceCommit (commit: string, workingDir: string, testPattern: string[], changedFilesIgnorePattern: string[]): Promise<ChangedDir[]> {
let diff!: string
try {
diff = (
await execa('git', [
'diff',
'--name-only',
commit,
'--',
workingDir,
], { cwd: workingDir })
).stdout
} catch (err: any) { // eslint-disable-line
throw new PnpmError('FILTER_CHANGED', `Filtering by changed packages failed. ${err.stderr as string}`)
}
const changedDirs = new Map<string, ChangeType>()
if (!diff) {
return []
}
const allChangedFiles = diff.split('\n')
// The prefix and suffix '"' are appended to the Korean path
.map(line => line.replace(/^"/, '').replace(/"$/, ''))
const patterns = changedFilesIgnorePattern.filter(
(pattern) => pattern.length
)
const changedFiles = (patterns.length > 0)
? micromatch.not(allChangedFiles, patterns, {
dot: true,
})
: allChangedFiles
for (const changedFile of changedFiles) {
const dir = path.dirname(changedFile)
if (changedDirs.get(dir) === 'source') continue
const changeType: ChangeType = testPattern.some(pattern => micromatch.isMatch(changedFile, pattern))
? 'test'
: 'source'
changedDirs.set(dir, changeType)
}
return Array.from(changedDirs.entries()).map(([dir, changeType]) => ({ dir, changeType }))
}