-
-
Notifications
You must be signed in to change notification settings - Fork 424
Expand file tree
/
Copy pathuseOutdatedDependencies.ts
More file actions
129 lines (108 loc) · 3.54 KB
/
useOutdatedDependencies.ts
File metadata and controls
129 lines (108 loc) · 3.54 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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import type { PackageVersionsInfo } from 'fast-npm-meta'
import { getVersionsBatch } from 'fast-npm-meta'
import { maxSatisfying, prerelease, major, minor, diff, gt } from 'semver'
import {
type OutdatedDependencyInfo,
constraintIncludesPrerelease,
parseDepValue,
} from '~/utils/npm/outdated-dependencies'
const BATCH_SIZE = 50
function resolveOutdated(
versions: string[],
latestTag: string,
constraint: string,
): OutdatedDependencyInfo | null {
if (constraint === 'latest') {
return {
resolved: latestTag,
latest: latestTag,
majorsBehind: 0,
minorsBehind: 0,
diffType: null,
}
}
let filteredVersions = versions
if (!constraintIncludesPrerelease(constraint)) {
filteredVersions = versions.filter(v => !prerelease(v))
}
const resolved = maxSatisfying(filteredVersions, constraint)
if (!resolved) return null
if (resolved === latestTag) return null
// Resolved is newer than latest (e.g. ^2.0.0-rc when latest is 1.x)
if (gt(resolved, latestTag)) {
return null
}
const diffType = diff(resolved, latestTag)
const majorsBehind = major(latestTag) - major(resolved)
const minorsBehind = majorsBehind === 0 ? minor(latestTag) - minor(resolved) : 0
return {
resolved,
latest: latestTag,
majorsBehind,
minorsBehind,
diffType,
}
}
/**
* Check for outdated dependencies via fast-npm-meta batch version lookups.
* Returns a reactive map of dependency name to outdated info.
*/
export function useOutdatedDependencies(
dependencies: MaybeRefOrGetter<Record<string, string> | undefined>,
) {
const outdated = shallowRef<Record<string, OutdatedDependencyInfo>>({})
async function fetchOutdatedInfo(deps: Record<string, string> | undefined) {
if (!deps || Object.keys(deps).length === 0) {
outdated.value = {}
return
}
// Resolve npm: aliases and filter out non-semver constraints
const resolvedEntries = Object.entries(deps)
.map(([key, value]) => {
const parsed = parseDepValue(value)
return { key, realName: parsed.name ?? key, range: parsed.range }
})
.filter((e): e is typeof e & { range: string } => e.range !== null)
if (resolvedEntries.length === 0) {
outdated.value = {}
return
}
const packageNames = [...new Set(resolvedEntries.map(e => e.realName))]
const chunks: string[][] = []
for (let i = 0; i < packageNames.length; i += BATCH_SIZE) {
chunks.push(packageNames.slice(i, i + BATCH_SIZE))
}
const batchResults = await Promise.all(
chunks.map(chunk => getVersionsBatch(chunk, { throw: false })),
)
const allVersionData = batchResults.flat()
// Build a lookup map from package name to version data
const versionMap = new Map<string, PackageVersionsInfo>()
for (const data of allVersionData) {
if ('error' in data) continue
versionMap.set(data.name, data)
}
const results: Record<string, OutdatedDependencyInfo> = {}
for (const { key, realName, range } of resolvedEntries) {
const data = versionMap.get(realName)
if (!data) continue
const latestTag = data.distTags.latest
if (!latestTag) continue
const info = resolveOutdated(data.versions, latestTag, range)
if (info) {
results[key] = info
}
}
outdated.value = results
}
watch(
() => toValue(dependencies),
deps => {
fetchOutdatedInfo(deps).catch(() => {
// Network failure or fast-npm-meta outage — leave stale results in place
})
},
{ immediate: true },
)
return outdated
}