reslop 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -1
- package/LICENSE +1 -1
- package/README.md +11 -11
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +72 -19
- package/lib/api.js +1 -1
- package/lib/capabilities.js +8 -8
- package/lib/cli.js +61 -75
- package/lib/clipboard.js +39 -47
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +357 -0
- package/lib/dashboard/npm.js +290 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +197 -402
- package/lib/deps.js +113 -240
- package/lib/detect.js +8 -18
- package/lib/diff/align.js +41 -113
- package/lib/diff/diff.js +12 -43
- package/lib/diff/display.js +25 -36
- package/lib/diff/edit.js +6 -20
- package/lib/diff/inline.js +76 -144
- package/lib/diff/items.js +33 -47
- package/lib/diff/parse.js +28 -56
- package/lib/diff/patch.js +12 -33
- package/lib/diff/unit.js +79 -178
- package/lib/editor.js +52 -69
- package/lib/files.js +79 -91
- package/lib/find.js +61 -0
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +148 -272
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +161 -214
- package/lib/git.js +21 -49
- package/lib/github.js +67 -181
- package/lib/gitlab.js +40 -90
- package/lib/highlight/bash.js +55 -177
- package/lib/highlight/core.js +155 -141
- package/lib/highlight/css.js +25 -58
- package/lib/highlight/csv.js +19 -37
- package/lib/highlight/dart.js +72 -213
- package/lib/highlight/dot.js +14 -14
- package/lib/highlight/html.js +42 -63
- package/lib/highlight/js.js +177 -506
- package/lib/highlight/json.js +17 -37
- package/lib/highlight/log.js +19 -25
- package/lib/highlight/txt.js +7 -10
- package/lib/highlight.js +17 -46
- package/lib/keys.js +75 -83
- package/lib/manifest.js +67 -126
- package/lib/npm-commands.js +120 -169
- package/lib/remote.js +140 -190
- package/lib/render/branches.js +63 -61
- package/lib/render/chrome.js +412 -347
- package/lib/render/commits.js +160 -361
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +496 -0
- package/lib/render/dash-table.js +295 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -258
- package/lib/render/files.js +60 -86
- package/lib/render/frame.js +82 -117
- package/lib/render/notes.js +150 -210
- package/lib/render/npm.js +213 -177
- package/lib/render/packages.js +93 -0
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +77 -172
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/repos.js +226 -0
- package/lib/render/search.js +25 -0
- package/lib/render/tiles.js +355 -0
- package/lib/render/unit.js +15 -31
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -37
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -77
- package/lib/report-test.js +221 -99
- package/lib/report-tools.js +96 -154
- package/lib/review-merge.js +33 -41
- package/lib/review.js +69 -129
- package/lib/runs.js +225 -0
- package/lib/select.js +53 -91
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +189 -65
- package/lib/session/branches.js +164 -222
- package/lib/session/change-actions.js +49 -119
- package/lib/session/commit.js +298 -352
- package/lib/session/compose.js +572 -715
- package/lib/session/dashboard.js +530 -0
- package/lib/session/dispatch.js +197 -171
- package/lib/session/files-pane.js +92 -45
- package/lib/session/find.js +94 -0
- package/lib/session/items.js +6 -17
- package/lib/session/lifecycle.js +420 -466
- package/lib/session/load.js +1 -14
- package/lib/session/navigation.js +4 -2
- package/lib/session/npm.js +403 -420
- package/lib/session/ops.js +5 -13
- package/lib/session/packages.js +143 -0
- package/lib/session/pointer.js +30 -14
- package/lib/session/progress.js +39 -30
- package/lib/session/review.js +1 -3
- package/lib/session/tasks.js +277 -236
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -132
- package/lib/session/view.js +59 -80
- package/lib/session/watch.js +201 -212
- package/lib/session/workspace.js +455 -0
- package/lib/session.js +95 -161
- package/lib/update.js +44 -111
- package/lib/utilities.js +46 -88
- package/lib/workspace.js +166 -0
- package/lib/wrap.js +29 -55
- package/package.json +1 -1
package/lib/deps.js
CHANGED
|
@@ -9,30 +9,40 @@ const { itemPath } = files;
|
|
|
9
9
|
const utilities = require('./utilities.js');
|
|
10
10
|
const { asText } = utilities;
|
|
11
11
|
const manifest = require('./manifest.js');
|
|
12
|
-
const { MANIFEST, LOCKFILE, SECTION_KEYS,
|
|
13
|
-
const {
|
|
14
|
-
const {
|
|
15
|
-
const {
|
|
12
|
+
const { MANIFEST, LOCKFILE, SECTION_KEYS, isObject } = manifest;
|
|
13
|
+
const { depFileMeta, relOf, listFilePath, readSections } = manifest;
|
|
14
|
+
const { lockEntries, findDeclared, applyWantedRange } = manifest;
|
|
15
|
+
const { proposedLockVersion, proposedWanted } = manifest;
|
|
16
16
|
const depDiff = require('./deps-diff.js');
|
|
17
|
-
const {
|
|
18
|
-
const {
|
|
17
|
+
const { summarizeGroup, collectGroups, takeManifestItems } = depDiff;
|
|
18
|
+
const { foldedDepNames, stripFoldedDepItem } = depDiff;
|
|
19
19
|
const { makeDepItems, stubFileItem } = depDiff;
|
|
20
20
|
|
|
21
21
|
const SOURCE_EXT = ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'];
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const SPEC_PATTERNS = [
|
|
23
|
+
/require\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
24
|
+
/\bfrom\s+['"]([^'"]+)['"]/g,
|
|
25
|
+
/import\s+['"]([^'"]+)['"]/g,
|
|
26
|
+
/import\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
27
|
+
];
|
|
28
|
+
const SCRIPT_RUNNERS = ['npx', 'npm', 'node'];
|
|
29
|
+
const SKIP_WALK = ['node_modules', '.git', '.review', 'coverage', 'dist'];
|
|
30
|
+
const SEVERITY_RANK = { critical: 4, high: 3, moderate: 2, low: 1, info: 0 };
|
|
31
|
+
|
|
32
|
+
const readText = (file) => {
|
|
33
|
+
try {
|
|
34
|
+
return fs.readFileSync(file, 'utf8');
|
|
35
|
+
} catch {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
};
|
|
26
39
|
|
|
27
40
|
const toPackageName = (spec) => {
|
|
28
41
|
if (!spec || spec.startsWith('.') || spec.startsWith('/')) return '';
|
|
29
42
|
if (spec.startsWith('node:')) return '';
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return `${parts[0]}/${parts[1]}`;
|
|
34
|
-
}
|
|
35
|
-
return spec.split('/')[0];
|
|
43
|
+
const parts = spec.split('/');
|
|
44
|
+
if (!spec.startsWith('@')) return parts[0];
|
|
45
|
+
return parts.length < 2 ? '' : `${parts[0]}/${parts[1]}`;
|
|
36
46
|
};
|
|
37
47
|
|
|
38
48
|
const addSpec = (used, spec) => {
|
|
@@ -40,54 +50,39 @@ const addSpec = (used, spec) => {
|
|
|
40
50
|
if (name) used.add(name);
|
|
41
51
|
};
|
|
42
52
|
|
|
43
|
-
const addMatches = (used, text, pattern) => {
|
|
44
|
-
const matches = text.matchAll(pattern);
|
|
45
|
-
for (const match of matches) addSpec(used, match[1]);
|
|
46
|
-
};
|
|
47
|
-
|
|
48
53
|
const collectUsedFromText = (used, text) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
addMatches(used, text, IMPORT_CALL);
|
|
54
|
+
for (const pattern of SPEC_PATTERNS) {
|
|
55
|
+
for (const match of text.matchAll(pattern)) addSpec(used, match[1]);
|
|
56
|
+
}
|
|
53
57
|
};
|
|
54
58
|
|
|
55
59
|
const collectUsedFromScripts = (used, scripts) => {
|
|
56
|
-
if (!scripts
|
|
60
|
+
if (!isObject(scripts)) return;
|
|
57
61
|
for (const script of Object.values(scripts)) {
|
|
58
|
-
const
|
|
59
|
-
for (const token of tokens) {
|
|
62
|
+
for (const token of `${script}`.split(/[\s;&|]+/)) {
|
|
60
63
|
if (!token || token.startsWith('-')) continue;
|
|
61
|
-
if (token
|
|
62
|
-
addSpec(used, token);
|
|
64
|
+
if (!SCRIPT_RUNNERS.includes(token)) addSpec(used, token);
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
};
|
|
66
68
|
|
|
69
|
+
const hasText = (value) => typeof value === 'string' && value !== '';
|
|
70
|
+
|
|
67
71
|
const pkgHasExportEntry = (pkg) => {
|
|
68
|
-
if (!pkg
|
|
69
|
-
|
|
70
|
-
if (typeof main === 'string' && main) return true;
|
|
71
|
-
const esm = pkg.module;
|
|
72
|
-
if (typeof esm === 'string' && esm) return true;
|
|
72
|
+
if (!isObject(pkg)) return false;
|
|
73
|
+
if (hasText(pkg.main) || hasText(pkg.module)) return true;
|
|
73
74
|
const exported = pkg.exports;
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
if (Array.isArray(exported)) return exported.length > 0;
|
|
77
|
-
return Object.keys(exported).length > 0;
|
|
75
|
+
if (hasText(exported)) return true;
|
|
76
|
+
return isObject(exported) && Object.keys(exported).length > 0;
|
|
78
77
|
};
|
|
79
78
|
|
|
80
|
-
const isRuntimeFile = (value) =>
|
|
81
|
-
if (typeof value !== 'string' || !value) return false;
|
|
82
|
-
return !value.endsWith('.d.ts');
|
|
83
|
-
};
|
|
79
|
+
const isRuntimeFile = (value) => hasText(value) && !value.endsWith('.d.ts');
|
|
84
80
|
|
|
85
81
|
const hasRuntimeExport = (value) => {
|
|
86
82
|
if (isRuntimeFile(value)) return true;
|
|
87
83
|
if (Array.isArray(value)) return value.some(hasRuntimeExport);
|
|
88
|
-
if (!value
|
|
89
|
-
const
|
|
90
|
-
for (const key of keys) {
|
|
84
|
+
if (!isObject(value)) return false;
|
|
85
|
+
for (const key of Object.keys(value)) {
|
|
91
86
|
if (key === 'types' || key === 'typings') continue;
|
|
92
87
|
if (hasRuntimeExport(value[key])) return true;
|
|
93
88
|
}
|
|
@@ -95,35 +90,17 @@ const hasRuntimeExport = (value) => {
|
|
|
95
90
|
};
|
|
96
91
|
|
|
97
92
|
const pkgHasBin = (pkg) => {
|
|
98
|
-
if (!pkg || typeof pkg !== 'object') return false;
|
|
99
93
|
const bin = pkg.bin;
|
|
100
94
|
if (typeof bin === 'string') return !!bin;
|
|
101
|
-
|
|
102
|
-
return Object.keys(bin).length > 0;
|
|
95
|
+
return isHashObject(bin) && Object.keys(bin).length > 0;
|
|
103
96
|
};
|
|
104
97
|
|
|
105
98
|
const pkgExportsLibrary = (pkg) => {
|
|
106
|
-
if (!pkg ||
|
|
107
|
-
if (
|
|
108
|
-
if (isRuntimeFile(pkg.main)) return true;
|
|
109
|
-
if (isRuntimeFile(pkg.module)) return true;
|
|
99
|
+
if (!isObject(pkg) || pkgHasBin(pkg)) return false;
|
|
100
|
+
if (isRuntimeFile(pkg.main) || isRuntimeFile(pkg.module)) return true;
|
|
110
101
|
return hasRuntimeExport(pkg.exports);
|
|
111
102
|
};
|
|
112
103
|
|
|
113
|
-
const SKIP_WALK = ['node_modules', '.git', '.review', 'coverage', 'dist'];
|
|
114
|
-
|
|
115
|
-
const collectUsedFromManifest = (used, rel) => {
|
|
116
|
-
let text;
|
|
117
|
-
try {
|
|
118
|
-
text = fs.readFileSync(rel, 'utf8');
|
|
119
|
-
} catch {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const pkg = jsonParse(text);
|
|
123
|
-
if (!pkg) return;
|
|
124
|
-
collectUsedFromScripts(used, pkg.scripts);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
104
|
const walkUsedNames = (used, dir) => {
|
|
128
105
|
let entries;
|
|
129
106
|
try {
|
|
@@ -135,55 +112,36 @@ const walkUsedNames = (used, dir) => {
|
|
|
135
112
|
if (entry.isSymbolicLink()) continue;
|
|
136
113
|
const full = path.join(dir, entry.name);
|
|
137
114
|
if (entry.isDirectory()) {
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
collectUsedFromManifest(used, full);
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
const ext = path.extname(entry.name);
|
|
148
|
-
if (!SOURCE_EXT.includes(ext)) continue;
|
|
149
|
-
let text;
|
|
150
|
-
try {
|
|
151
|
-
text = fs.readFileSync(full, 'utf8');
|
|
152
|
-
} catch {
|
|
153
|
-
continue;
|
|
115
|
+
const skip = SKIP_WALK.includes(entry.name) || entry.name.startsWith('.');
|
|
116
|
+
if (!skip) walkUsedNames(used, full);
|
|
117
|
+
} else if (entry.name === MANIFEST) {
|
|
118
|
+
collectUsedFromScripts(used, jsonParse(readText(full))?.scripts);
|
|
119
|
+
} else if (SOURCE_EXT.includes(path.extname(entry.name))) {
|
|
120
|
+
collectUsedFromText(used, readText(full));
|
|
154
121
|
}
|
|
155
|
-
collectUsedFromText(used, text);
|
|
156
122
|
}
|
|
157
123
|
};
|
|
158
124
|
|
|
159
125
|
const collectUsedNames = (root) => {
|
|
160
126
|
const used = new Set();
|
|
161
|
-
if (
|
|
162
|
-
walkUsedNames(used, root);
|
|
127
|
+
if (root) walkUsedNames(used, root);
|
|
163
128
|
return used;
|
|
164
129
|
};
|
|
165
130
|
|
|
166
131
|
const depModuleParts = (name) => {
|
|
167
132
|
if (!name.startsWith('@')) return [name];
|
|
168
133
|
const parts = name.split('/');
|
|
169
|
-
|
|
170
|
-
return parts.slice(0, 2);
|
|
134
|
+
return parts.length < 2 ? [] : parts.slice(0, 2);
|
|
171
135
|
};
|
|
172
136
|
|
|
173
137
|
const readDepPkg = (root, pkgDir, name) => {
|
|
174
|
-
if (!
|
|
138
|
+
if (!name) return null;
|
|
175
139
|
const parts = depModuleParts(name);
|
|
176
140
|
if (!parts.length) return null;
|
|
177
|
-
|
|
178
|
-
let dir = nested ? path.join(root, nested) : root;
|
|
141
|
+
let dir = path.join(root, pkgDir || '.');
|
|
179
142
|
while (true) {
|
|
180
143
|
const file = path.join(dir, 'node_modules', ...parts, MANIFEST);
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
text = fs.readFileSync(file, 'utf8');
|
|
184
|
-
} catch {
|
|
185
|
-
// Missing node_modules copy; try a parent directory.
|
|
186
|
-
}
|
|
144
|
+
const text = readText(file);
|
|
187
145
|
if (text) return jsonParse(text);
|
|
188
146
|
const parent = path.dirname(dir);
|
|
189
147
|
if (parent === dir) return null;
|
|
@@ -191,61 +149,37 @@ const readDepPkg = (root, pkgDir, name) => {
|
|
|
191
149
|
}
|
|
192
150
|
};
|
|
193
151
|
|
|
194
|
-
const depHasExportEntry = (root, pkgDir, name) => {
|
|
195
|
-
const pkg = readDepPkg(root, pkgDir, name);
|
|
196
|
-
if (!pkg) return false;
|
|
197
|
-
return pkgExportsLibrary(pkg);
|
|
198
|
-
};
|
|
199
|
-
|
|
200
152
|
const collectExportEntries = (root, pkgDir, names) => {
|
|
201
153
|
const entries = new Map();
|
|
202
154
|
if (!root) return entries;
|
|
203
155
|
for (const name of names) {
|
|
204
|
-
const
|
|
205
|
-
entries.set(name,
|
|
156
|
+
const pkg = readDepPkg(root, pkgDir, name);
|
|
157
|
+
entries.set(name, !!pkg && pkgExportsLibrary(pkg));
|
|
206
158
|
}
|
|
207
159
|
return entries;
|
|
208
160
|
};
|
|
209
161
|
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
text = fs.readFileSync(file, 'utf8');
|
|
216
|
-
} catch {
|
|
217
|
-
return [];
|
|
218
|
-
}
|
|
219
|
-
const maps = readSections(jsonParse(text));
|
|
220
|
-
return SECTION_KEYS.flatMap((key) => [...maps[key].keys()]);
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
const collectExportMeta = (root, extra = {}) => {
|
|
224
|
-
if (extra.exportEntries !== undefined) return extra.exportEntries;
|
|
225
|
-
const pkgDir = extra.dir ?? '.';
|
|
226
|
-
const names = extra.names ?? declaredDepNames(root, pkgDir);
|
|
227
|
-
return collectExportEntries(root, pkgDir, names);
|
|
162
|
+
const collectExportMeta = (root) => {
|
|
163
|
+
const pkg = jsonParse(readText(path.join(root, MANIFEST)));
|
|
164
|
+
const maps = readSections(pkg);
|
|
165
|
+
const names = SECTION_KEYS.flatMap((key) => [...maps[key].keys()]);
|
|
166
|
+
return collectExportEntries(root, '.', names);
|
|
228
167
|
};
|
|
229
168
|
|
|
230
|
-
const SEVERITY_RANK = { critical: 4, high: 3, moderate: 2, low: 1, info: 0 };
|
|
231
|
-
|
|
232
169
|
const namedTitle = (name, title) => {
|
|
233
170
|
if (!title) return '';
|
|
234
171
|
if (!name) return title;
|
|
235
172
|
const prefix = `${name}: `;
|
|
236
|
-
|
|
237
|
-
return `${prefix}${title}`;
|
|
173
|
+
return title.startsWith(prefix) ? title : `${prefix}${title}`;
|
|
238
174
|
};
|
|
239
175
|
|
|
240
176
|
const viaNotes = (via, pkgName) => {
|
|
241
177
|
if (!Array.isArray(via)) return [];
|
|
242
178
|
const notes = [];
|
|
243
179
|
for (const item of via) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const hasName = typeof item.name === 'string' && item.name;
|
|
248
|
-
const named = hasName ? item.name : pkgName;
|
|
180
|
+
const title = asText(item?.title);
|
|
181
|
+
if (!isObject(item) || !title) continue;
|
|
182
|
+
const named = hasText(item.name) ? item.name : pkgName;
|
|
249
183
|
const note = namedTitle(named, title);
|
|
250
184
|
if (!notes.includes(note)) notes.push(note);
|
|
251
185
|
}
|
|
@@ -254,23 +188,13 @@ const viaNotes = (via, pkgName) => {
|
|
|
254
188
|
|
|
255
189
|
const severityRank = (severity) => SEVERITY_RANK[severity] ?? 0;
|
|
256
190
|
|
|
257
|
-
const auditFix = (entry) =>
|
|
258
|
-
const available = entry.fixAvailable;
|
|
259
|
-
if (!available || typeof available !== 'object') return '';
|
|
260
|
-
if (typeof available.version !== 'string') return '';
|
|
261
|
-
return available.version;
|
|
262
|
-
};
|
|
191
|
+
const auditFix = (entry) => asText(entry.fixAvailable?.version);
|
|
263
192
|
|
|
264
193
|
const auditEntryNames = (key, entry) => {
|
|
265
|
-
const
|
|
194
|
+
const effects = Array.isArray(entry.effects) ? entry.effects : [];
|
|
195
|
+
const names = effects.filter(hasText);
|
|
266
196
|
const primary = entry.name || key;
|
|
267
|
-
|
|
268
|
-
const effects = entry.effects;
|
|
269
|
-
if (!Array.isArray(effects)) return names;
|
|
270
|
-
for (const effect of effects) {
|
|
271
|
-
if (typeof effect === 'string' && effect) names.push(effect);
|
|
272
|
-
}
|
|
273
|
-
return names;
|
|
197
|
+
return primary ? [primary, ...names] : names;
|
|
274
198
|
};
|
|
275
199
|
|
|
276
200
|
const uniqueTexts = (list) => [...new Set(list.filter(Boolean))];
|
|
@@ -295,12 +219,11 @@ const keepHighestAudit = (audit, found) => {
|
|
|
295
219
|
audit.set(name, { name, severity, title, titles, fix, range });
|
|
296
220
|
};
|
|
297
221
|
|
|
298
|
-
const isEntry = (value) => Boolean(value) && typeof value === 'object';
|
|
299
|
-
|
|
300
222
|
const vulnFindings = (vulns) => {
|
|
301
223
|
const found = [];
|
|
302
|
-
for (const
|
|
303
|
-
|
|
224
|
+
for (const key of Object.keys(vulns)) {
|
|
225
|
+
const entry = vulns[key];
|
|
226
|
+
if (!isObject(entry)) continue;
|
|
304
227
|
const { severity } = entry;
|
|
305
228
|
const titles = viaNotes(entry.via, entry.name || key);
|
|
306
229
|
const fix = auditFix(entry);
|
|
@@ -323,13 +246,12 @@ const advisoryFinding = (entry) => {
|
|
|
323
246
|
|
|
324
247
|
const parseAuditReport = (text) => {
|
|
325
248
|
const audit = new Map();
|
|
326
|
-
|
|
327
|
-
const data = jsonParse(text);
|
|
249
|
+
const data = text ? jsonParse(text) : null;
|
|
328
250
|
if (!isHashObject(data)) return audit;
|
|
329
251
|
const { vulnerabilities, advisories } = data;
|
|
330
|
-
const found =
|
|
331
|
-
if (
|
|
332
|
-
const listed = Object.values(advisories).filter(
|
|
252
|
+
const found = isObject(vulnerabilities) ? vulnFindings(vulnerabilities) : [];
|
|
253
|
+
if (isObject(advisories)) {
|
|
254
|
+
const listed = Object.values(advisories).filter(isObject);
|
|
333
255
|
found.push(...listed.map(advisoryFinding));
|
|
334
256
|
}
|
|
335
257
|
for (const entry of found) keepHighestAudit(audit, entry);
|
|
@@ -338,14 +260,12 @@ const parseAuditReport = (text) => {
|
|
|
338
260
|
|
|
339
261
|
const parseOutdatedReport = (text) => {
|
|
340
262
|
const outdated = new Map();
|
|
341
|
-
|
|
342
|
-
const data = jsonParse(text);
|
|
263
|
+
const data = text ? jsonParse(text) : null;
|
|
343
264
|
if (!isHashObject(data)) return outdated;
|
|
344
265
|
for (const name of Object.keys(data)) {
|
|
345
266
|
const entry = data[name];
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if (!wanted) continue;
|
|
267
|
+
const wanted = asText(entry?.wanted);
|
|
268
|
+
if (!isObject(entry) || !wanted) continue;
|
|
349
269
|
const current = asText(entry.current);
|
|
350
270
|
const latest = asText(entry.latest);
|
|
351
271
|
const type = asText(entry.type);
|
|
@@ -357,36 +277,26 @@ const parseOutdatedReport = (text) => {
|
|
|
357
277
|
const markChanges = (changes, report, kind) => {
|
|
358
278
|
if (!report) return changes;
|
|
359
279
|
return changes.map((change) => {
|
|
360
|
-
if (change.action === 'removed'
|
|
361
|
-
return change;
|
|
362
|
-
}
|
|
280
|
+
if (change.action === 'removed') return change;
|
|
363
281
|
const found = report.get(change.name);
|
|
364
282
|
if (!found) return change;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
283
|
+
const isCurrent = found.wanted && found.wanted === found.current;
|
|
284
|
+
if (kind === 'outdated' && isCurrent) return change;
|
|
368
285
|
return { ...change, [kind]: found };
|
|
369
286
|
});
|
|
370
287
|
};
|
|
371
288
|
|
|
372
289
|
const depExports = (exportEntries, name) => exportEntries?.get(name) === true;
|
|
373
290
|
|
|
374
|
-
const isAddedDep = (change) =>
|
|
375
|
-
|
|
376
|
-
if (change.section === 'resolved') return false;
|
|
377
|
-
if (change.section === 'field') return false;
|
|
378
|
-
return true;
|
|
379
|
-
};
|
|
291
|
+
const isAddedDep = (change) =>
|
|
292
|
+
change.action === 'added' && change.section !== 'resolved';
|
|
380
293
|
|
|
381
294
|
const unusedChangeNames = (changes, used) => {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
names.push(change.name);
|
|
388
|
-
}
|
|
389
|
-
return names;
|
|
295
|
+
if (!used || !used.size) return [];
|
|
296
|
+
const unused = changes.filter(
|
|
297
|
+
(change) => isAddedDep(change) && !used.has(change.name),
|
|
298
|
+
);
|
|
299
|
+
return unused.map((change) => change.name);
|
|
390
300
|
};
|
|
391
301
|
|
|
392
302
|
const unusedDeclared = (pkg, used) => {
|
|
@@ -458,15 +368,12 @@ const declaredChange = (name, declared, out, vul) => {
|
|
|
458
368
|
|
|
459
369
|
const mapKeys = (map) => (map ? [...map.keys()] : []);
|
|
460
370
|
|
|
461
|
-
const buildProposedChanges = (pkg, outdated, audit, lock, options
|
|
371
|
+
const buildProposedChanges = (pkg, outdated, audit, lock, options) => {
|
|
462
372
|
const listed = [...new Set([...mapKeys(outdated), ...mapKeys(audit)])];
|
|
463
373
|
listed.sort();
|
|
464
374
|
const resolved = lockEntries(lock);
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
options.used,
|
|
468
|
-
options.exportEntries,
|
|
469
|
-
);
|
|
375
|
+
const { used, exportEntries } = options;
|
|
376
|
+
const changes = unusedDeclaredChanges(pkg, used, exportEntries);
|
|
470
377
|
const taken = new Set(changes.map((change) => change.name));
|
|
471
378
|
for (const name of listed) {
|
|
472
379
|
if (taken.has(name)) continue;
|
|
@@ -481,59 +388,32 @@ const buildProposedChanges = (pkg, outdated, audit, lock, options = {}) => {
|
|
|
481
388
|
return changes;
|
|
482
389
|
};
|
|
483
390
|
|
|
484
|
-
const loadExportEntries = (names, extra
|
|
391
|
+
const loadExportEntries = (names, extra, dir) => {
|
|
485
392
|
const supplied = extra.exportEntries;
|
|
486
393
|
if (supplied === null) return new Map();
|
|
487
|
-
const
|
|
488
|
-
const missing = [];
|
|
489
|
-
for (const name of names) {
|
|
490
|
-
if (!supplied || !supplied.has(name)) missing.push(name);
|
|
491
|
-
}
|
|
394
|
+
const missing = names.filter((name) => !supplied || !supplied.has(name));
|
|
492
395
|
if (!missing.length) return supplied ?? new Map();
|
|
493
396
|
const found = collectExportEntries(extra.root, dir, missing);
|
|
494
|
-
|
|
495
|
-
return new Map([...supplied, ...found]);
|
|
397
|
+
return supplied ? new Map([...supplied, ...found]) : found;
|
|
496
398
|
};
|
|
497
399
|
|
|
498
|
-
const
|
|
400
|
+
const spliceAtSlots = (out, synthetic, slot) => {
|
|
499
401
|
const byPath = new Map();
|
|
500
|
-
for (const
|
|
501
|
-
const
|
|
502
|
-
const rel = relOf(dir, MANIFEST);
|
|
402
|
+
for (const key of synthetic.keys()) {
|
|
403
|
+
const rel = relOf(key.slice(key.indexOf('\0') + 1), MANIFEST);
|
|
503
404
|
const list = byPath.get(rel) ?? [];
|
|
504
|
-
|
|
405
|
+
list.push(...synthetic.get(key));
|
|
505
406
|
byPath.set(rel, list);
|
|
506
407
|
}
|
|
507
|
-
|
|
508
|
-
};
|
|
509
|
-
|
|
510
|
-
const spliceAtSlots = (out, synthetic, slot) => {
|
|
511
|
-
const byPath = groupedSynthetics(synthetic);
|
|
512
|
-
const inserts = [...byPath].map(([rel, items]) => ({
|
|
408
|
+
const inserts = [...byPath.keys()].map((rel) => ({
|
|
513
409
|
at: slot.get(rel) ?? out.length,
|
|
514
|
-
items,
|
|
410
|
+
items: byPath.get(rel),
|
|
515
411
|
}));
|
|
516
412
|
inserts.sort((left, right) => right.at - left.at);
|
|
517
|
-
for (const insert of inserts)
|
|
518
|
-
out.splice(insert.at, 0, ...insert.items);
|
|
519
|
-
}
|
|
413
|
+
for (const insert of inserts) out.splice(insert.at, 0, ...insert.items);
|
|
520
414
|
return out;
|
|
521
415
|
};
|
|
522
416
|
|
|
523
|
-
const lastPathIndex = (items, rel) => {
|
|
524
|
-
let at = -1;
|
|
525
|
-
for (let i = 0; i < items.length; i++) {
|
|
526
|
-
if (listFilePath(itemPath(items[i])) === rel) at = i;
|
|
527
|
-
}
|
|
528
|
-
return at;
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
const insertAfterPath = (items, added, rel) => {
|
|
532
|
-
const at = lastPathIndex(items, rel);
|
|
533
|
-
if (at < 0) return [...items, ...added];
|
|
534
|
-
return [...items.slice(0, at + 1), ...added, ...items.slice(at + 1)];
|
|
535
|
-
};
|
|
536
|
-
|
|
537
417
|
const placeFolded = (items, { folded, synthetic, leftoverNames }) => {
|
|
538
418
|
const out = [];
|
|
539
419
|
const slot = new Map();
|
|
@@ -613,39 +493,32 @@ const proposeDepItems = (pkgText, outdated, audit, extra = {}) => {
|
|
|
613
493
|
|
|
614
494
|
const mergeProposedItems = (items, pkgText, outdated, audit, extra = {}) => {
|
|
615
495
|
const proposed = proposeDepItems(pkgText, outdated, audit, extra);
|
|
616
|
-
if (!proposed.length) return items;
|
|
617
496
|
const taken = new Set();
|
|
618
497
|
for (const item of items) {
|
|
619
|
-
const change = item.dep
|
|
498
|
+
const change = item.dep?.change;
|
|
620
499
|
if (change) taken.add(change.name);
|
|
621
500
|
}
|
|
622
|
-
const added =
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
added.push(item);
|
|
627
|
-
}
|
|
501
|
+
const added = proposed.filter((item) => {
|
|
502
|
+
const change = item.dep?.change;
|
|
503
|
+
return change && !taken.has(change.name);
|
|
504
|
+
});
|
|
628
505
|
if (!added.length) return items;
|
|
629
506
|
const rel = relOf(extra.dir ?? '.', MANIFEST);
|
|
630
|
-
|
|
507
|
+
const at = items.findLastIndex(
|
|
508
|
+
(item) => listFilePath(itemPath(item)) === rel,
|
|
509
|
+
);
|
|
510
|
+
if (at < 0) return [...items, ...added];
|
|
511
|
+
return [...items.slice(0, at + 1), ...added, ...items.slice(at + 1)];
|
|
631
512
|
};
|
|
632
513
|
|
|
633
514
|
module.exports = {
|
|
634
515
|
MANIFEST,
|
|
635
516
|
LOCKFILE,
|
|
636
517
|
depFileMeta,
|
|
637
|
-
readSections,
|
|
638
|
-
lockEntries,
|
|
639
|
-
lockPackageCount,
|
|
640
|
-
diffSections,
|
|
641
|
-
mergeDepFile,
|
|
642
|
-
mergeResolved,
|
|
643
518
|
collectUsedNames,
|
|
644
519
|
collectExportMeta,
|
|
645
520
|
parseAuditReport,
|
|
646
521
|
parseOutdatedReport,
|
|
647
|
-
applyWantedRange,
|
|
648
|
-
patchedFromRange,
|
|
649
522
|
proposeDepItems,
|
|
650
523
|
foldDepItems,
|
|
651
524
|
mergeProposedItems,
|
package/lib/detect.js
CHANGED
|
@@ -16,29 +16,19 @@ const PROSE_BASENAMES = [
|
|
|
16
16
|
'copyright',
|
|
17
17
|
];
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
if (PROSE_BASENAMES.includes(name)) return true;
|
|
22
|
-
const stem = name.replace(/\.(txt|text|md|markdown)$/i, '');
|
|
19
|
+
const isProse = (base) => {
|
|
20
|
+
const stem = base.replace(/\.(txt|text|md|markdown)$/, '');
|
|
23
21
|
return PROSE_BASENAMES.includes(stem);
|
|
24
22
|
};
|
|
25
23
|
|
|
26
|
-
const LANG_RULES = [
|
|
27
|
-
{ test: (base) => base.endsWith('.d.ts'), lang: 'dts' },
|
|
28
|
-
{ test: isProseBasename, lang: 'md' },
|
|
29
|
-
{ test: (base) => base.endsWith('.log'), lang: 'log' },
|
|
30
|
-
{ test: (base) => base.startsWith('.'), lang: 'dot' },
|
|
31
|
-
{ test: (base) => !base.includes('.'), lang: 'dot' },
|
|
32
|
-
];
|
|
33
|
-
|
|
34
24
|
const detectLang = (filePath) => {
|
|
35
25
|
const base = path.basename(filePath || '').toLowerCase();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
if (base.endsWith('.d.ts')) return 'dts';
|
|
27
|
+
if (isProse(base)) return 'md';
|
|
28
|
+
if (base.endsWith('.log')) return 'log';
|
|
29
|
+
if (base.startsWith('.') || !base.includes('.')) return 'dot';
|
|
39
30
|
const ext = fileExt(base);
|
|
40
|
-
|
|
41
|
-
return 'dot';
|
|
31
|
+
return ext ? mapLang(ext) : 'dot';
|
|
42
32
|
};
|
|
43
33
|
|
|
44
|
-
module.exports = {
|
|
34
|
+
module.exports = { detectLang };
|