-
-
Notifications
You must be signed in to change notification settings - Fork 424
Expand file tree
/
Copy pathoutdated-dependencies.ts
More file actions
125 lines (116 loc) · 4.11 KB
/
outdated-dependencies.ts
File metadata and controls
125 lines (116 loc) · 4.11 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
import type { ReleaseType } from 'semver'
/** Information about an outdated dependency */
export interface OutdatedDependencyInfo {
/** The resolved version that satisfies the constraint */
resolved: string
/** The latest available version */
latest: string
/** How many major versions behind */
majorsBehind: number
/** How many minor versions behind (when same major) */
minorsBehind: number
/** The type of version difference */
diffType: ReleaseType | null
}
/**
* Check if a version constraint explicitly includes a prerelease tag.
* e.g., "^1.0.0-alpha" or ">=2.0.0-beta.1" include prereleases
*/
export function constraintIncludesPrerelease(constraint: string): boolean {
return (
/-(?:alpha|beta|rc|next|canary|dev|preview|pre|experimental)/i.test(constraint) ||
/-\d/.test(constraint)
)
}
/** Parsed result of an npm dependency value */
export interface ParsedDepValue {
/** The real package name (different from key only for aliases) */
name: string | null
/** The semver range or version, null for non-resolvable values (file:, git, etc.) */
range: string | null
}
/**
* Parse a dependency value which may be a semver range, an npm alias, or a non-semver reference.
*
* Examples:
* "^4.2.0" { name: null, range: "^4.2.0" }
* "npm:string-width@^4.2.0" { name: "string-width", range: "^4.2.0" }
* "npm:@scope/pkg@^1.0.0" { name: "@scope/pkg", range: "^1.0.0" }
* "file:../foo" { name: null, range: null }
*/
export function parseDepValue(value: string): ParsedDepValue {
if (value.startsWith('npm:')) {
const aliasBody = value.slice(4) // strip "npm:"
// Scoped: @scope/name@range
if (aliasBody.startsWith('@')) {
const secondAt = aliasBody.indexOf('@', 1)
if (secondAt !== -1) {
return { name: aliasBody.slice(0, secondAt), range: aliasBody.slice(secondAt + 1) }
}
return { name: aliasBody, range: null }
}
// Unscoped: name@range
const atIndex = aliasBody.indexOf('@')
if (atIndex !== -1) {
return { name: aliasBody.slice(0, atIndex), range: aliasBody.slice(atIndex + 1) }
}
return { name: aliasBody, range: null }
}
if (isNonSemverConstraint(value)) {
return { name: null, range: null }
}
return { name: null, range: value }
}
/**
* Check if a constraint is a non-semver value (git URL, file path, etc.)
*/
function isNonSemverConstraint(constraint: string): boolean {
return (
constraint.startsWith('git') ||
constraint.startsWith('http') ||
constraint.startsWith('file:') ||
constraint.startsWith('link:') ||
constraint.startsWith('workspace:') ||
constraint.includes('/')
)
}
/**
* Get tooltip text for an outdated dependency
*/
export function getOutdatedTooltip(
info: OutdatedDependencyInfo,
t: (key: string, params?: Record<string, unknown>, plural?: number) => string,
): string {
if (info.majorsBehind > 0) {
return t(
'package.dependencies.outdated_major',
{ count: info.majorsBehind, latest: info.latest },
info.majorsBehind,
)
}
if (info.minorsBehind > 0) {
return t(
'package.dependencies.outdated_minor',
{ count: info.minorsBehind, latest: info.latest },
info.minorsBehind,
)
}
return t('package.dependencies.outdated_patch', { latest: info.latest })
}
/**
* Get CSS class for a dependency version based on outdated status
*/
export function getVersionClass(info: OutdatedDependencyInfo | undefined): string {
if (!info) return 'text-fg-subtle'
// Green for up-to-date (e.g. "latest" constraint)
if (info.majorsBehind === 0 && info.minorsBehind === 0 && info.resolved === info.latest) {
return 'text-green-700 dark:text-green-500 cursor-help'
}
// Red for major versions behind
if (info.majorsBehind > 0) return 'text-red-700 dark:text-red-500 cursor-help'
// if (info.majorsBehind > 0) return 'text-#db0000 dark:text-red-500 cursor-help'
// Orange for minor versions behind
if (info.minorsBehind > 0) return 'text-orange-700 dark:text-orange-500 cursor-help'
// Yellow for patch versions behind
return 'text-yellow-700 dark:text-yellow-500 cursor-help'
}