-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
156 lines (126 loc) · 4.18 KB
/
index.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
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/**
* Rotten Deps API
* @module
*/
import { createFileReader, createConfig } from './config';
import { createOutdatedRequest, createDetailsRequest } from './npm-interactions';
import type { Config } from './config';
import type { OutdatedPackage } from './npm-interactions';
interface ReportData {
name: string,
current: string,
latest: string,
daysOutdated: number,
isOutdated: boolean,
isIgnored: boolean,
isStale: boolean,
}
interface Report {
kind: 'report',
data: ReportData[],
}
interface ReportWithWarning {
kind: 'warning',
data: ReportData[],
hasPreinstallWarning: boolean,
}
export type ReportResponse = Report | ReportWithWarning;
interface Reporter {
setTotal(total: number): any,
report(data: ReportData): any,
done(): void,
}
/**
* Compares the details on each dependency flagged as outdated in order to
* determine how stale a verison actually is.
*
* @param r optional reporter object which has a function for setting the
* total number of outdated dependencies and another for reporting a
* single dependency's data. A usecase for this would be to hook into a
* progress bar or other progress related monitoring.
*/
export const generateReport = async (c: Config, r?: Reporter): Promise<ReportResponse | Error> => {
const config = createConfig(c);
const { rules } = config;
const getOutdated = createOutdatedRequest();
const outdated = await getOutdated();
r?.setTotal(Object.keys(outdated).length);
if (outdated instanceof Error) return outdated;
try {
const reportData: ReportData[] = [];
let hasPreinstallWarning = false;
for (const x of Object.entries(outdated)) {
const [name, desiredDetails]: [string, OutdatedPackage] = x;
const getDetails = createDetailsRequest(name);
const details = await getDetails();
if (details instanceof Error) return details;
if (!desiredDetails.current) hasPreinstallWarning = true;
// it's the time prop in the npm response but it's a collection of versions and dates
const { time: versions } = details;
/* When running `npm outdated` without first installing the current version will be
missing causing a breakdown in determination of days outdated. This will use the
wanted version instead. */
const currentVersion = !desiredDetails.current
? versions[desiredDetails.wanted]
: versions[desiredDetails.current];
const latestVersion = versions[desiredDetails.latest];
const currentDate = new Date(currentVersion);
const latestDate = new Date(latestVersion);
const currentTime = currentDate.getTime();
const latestTime = latestDate.getTime();
// 86400000 represents the amount of milliseconds in a day
const daysOutdated = Math.floor((latestTime - currentTime) / 86400000)
let isOutdated = false;
let isIgnored = false;
let isStale = false;
const rule = rules.filter(x => x.dependencyName === name).shift();
if (!rule) isOutdated = true;
if (!rule && config.defaultExpiration && config.defaultExpiration > daysOutdated) isOutdated = false;
if (rule && rule.daysUntilExpiration && rule.daysUntilExpiration <= daysOutdated) isOutdated = true;
if (rule && rule.daysUntilExpiration && rule.daysUntilExpiration > daysOutdated) isStale = true;
if (rule && rule.ignore) {
isIgnored = true;
isOutdated = false;
}
const data = {
name,
current: !desiredDetails.current ? desiredDetails.wanted : desiredDetails.current,
latest: desiredDetails.latest,
daysOutdated,
isOutdated,
isIgnored,
isStale,
};
r?.report(data);
reportData.push(data);
}
r?.done();
if (hasPreinstallWarning) {
return {
kind: 'warning',
data: reportData,
hasPreinstallWarning: true,
};
} else {
return {
kind: 'report',
data: reportData,
};
}
} catch (err) {
return err;
}
};
export const configuration = {
createFileReader,
createConfig,
};
export const npm = {
createOutdatedRequest,
createDetailsRequest,
};
export default {
configuration,
npm,
generateReport,
};