reslop 0.2.4 → 0.2.6
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 +25 -1
- package/LICENSE +1 -1
- package/README.md +12 -13
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +32 -19
- package/lib/capabilities.js +8 -12
- package/lib/cli.js +40 -74
- package/lib/clipboard.js +48 -18
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +332 -0
- package/lib/dashboard/npm.js +193 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +196 -404
- package/lib/deps.js +240 -399
- 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 +138 -141
- 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 +90 -206
- package/lib/editor.js +98 -55
- package/lib/files.js +86 -103
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +182 -344
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +141 -212
- 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 +157 -132
- 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 +16 -26
- 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 +22 -35
- package/lib/highlight/txt.js +9 -22
- package/lib/highlight.js +17 -46
- package/lib/keys.js +62 -85
- package/lib/manifest.js +67 -130
- package/lib/npm-commands.js +119 -189
- package/lib/remote.js +141 -197
- package/lib/render/branches.js +66 -73
- package/lib/render/chrome.js +264 -323
- package/lib/render/commits.js +187 -500
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +245 -0
- package/lib/render/dash-table.js +290 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -228
- package/lib/render/files.js +45 -99
- package/lib/render/frame.js +129 -183
- package/lib/render/notes.js +144 -160
- package/lib/render/npm.js +234 -112
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +131 -170
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/tiles.js +260 -0
- package/lib/render/unit.js +15 -28
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -38
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -78
- package/lib/report-test.js +256 -141
- package/lib/report-tools.js +96 -161
- package/lib/review-merge.js +114 -152
- package/lib/review.js +115 -189
- package/lib/runs.js +225 -0
- package/lib/select.js +89 -75
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +82 -63
- package/lib/session/branches.js +193 -247
- package/lib/session/change-actions.js +171 -214
- package/lib/session/commit.js +280 -342
- package/lib/session/compose.js +559 -674
- package/lib/session/dashboard.js +385 -0
- package/lib/session/dispatch.js +164 -157
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +85 -53
- package/lib/session/items.js +73 -104
- package/lib/session/lifecycle.js +403 -541
- package/lib/session/load.js +48 -68
- package/lib/session/navigation.js +96 -87
- package/lib/session/npm.js +409 -345
- package/lib/session/ops.js +196 -63
- package/lib/session/pointer.js +23 -15
- package/lib/session/progress.js +33 -21
- package/lib/session/review.js +33 -54
- package/lib/session/tasks.js +333 -0
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -136
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -97
- package/lib/session/view.js +52 -83
- package/lib/session/watch.js +208 -204
- package/lib/session.js +112 -237
- package/lib/update.js +45 -116
- package/lib/utilities.js +67 -89
- package/lib/wrap.js +33 -44
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
package/lib/git-deps.js
CHANGED
|
@@ -4,376 +4,232 @@ const fs = require('node:fs');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
6
|
const deps = require('./deps.js');
|
|
7
|
-
const { foldDepItems,
|
|
7
|
+
const { foldDepItems, mergeProposedItems } = deps;
|
|
8
8
|
const { parseAuditReport, parseOutdatedReport } = deps;
|
|
9
|
-
const { mergeProposedItems, MANIFEST, LOCKFILE } = deps;
|
|
10
9
|
const { collectUsedNames, collectExportMeta } = deps;
|
|
10
|
+
const manifest = require('./manifest.js');
|
|
11
|
+
const { MANIFEST, LOCKFILE, depFileMeta, mergeDepFile, relOf } = manifest;
|
|
11
12
|
const files = require('./files.js');
|
|
12
13
|
const { itemPath, isPathInScope } = files;
|
|
13
|
-
const
|
|
14
|
-
const { abortError, npmBin,
|
|
15
|
-
const
|
|
16
|
-
const {
|
|
17
|
-
const {
|
|
18
|
-
const { writeFile, writeIndex } = git;
|
|
14
|
+
const utilities = require('./utilities.js');
|
|
15
|
+
const { IS_WIN, abortError, npmBin, oneLine, runProc, runProcSync } = utilities;
|
|
16
|
+
const worktree = require('./git-worktree.js');
|
|
17
|
+
const { requireOk, runGit, gitText, worktreeText, readDepSides } = worktree;
|
|
18
|
+
const { revertOnePath, groupByPath, writeFile, writeIndex } = worktree;
|
|
19
19
|
|
|
20
20
|
const AUDIT_MS = 15000;
|
|
21
21
|
const INSTALL_MS = 120000;
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const NPM_REPORT = {
|
|
24
|
+
audit: {
|
|
25
|
+
file: LOCKFILE,
|
|
26
|
+
args: ['audit', '--json', '--package-lock-only'],
|
|
27
|
+
parse: parseAuditReport,
|
|
28
|
+
},
|
|
29
|
+
outdated: {
|
|
30
|
+
file: MANIFEST,
|
|
31
|
+
args: ['outdated', '--json', '--long'],
|
|
32
|
+
parse: parseOutdatedReport,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const npmOptions = (cwd, timeout, signal) => ({
|
|
24
37
|
cwd,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
signal: extra.signal,
|
|
29
|
-
shell: npmOpts().shell,
|
|
38
|
+
timeout,
|
|
39
|
+
signal,
|
|
40
|
+
shell: IS_WIN,
|
|
30
41
|
});
|
|
31
42
|
|
|
32
43
|
const runNpm = (cwd, args) =>
|
|
33
|
-
runProcSync(npmBin(), args,
|
|
44
|
+
runProcSync(npmBin(), args, npmOptions(cwd, INSTALL_MS));
|
|
34
45
|
|
|
35
|
-
const runNpmAsync = (cwd, args
|
|
36
|
-
|
|
37
|
-
const signal = extra.signal;
|
|
38
|
-
const options = npmProcOptions(cwd, { timeout: INSTALL_MS, signal });
|
|
39
|
-
return runProc(bin, args, options);
|
|
40
|
-
};
|
|
46
|
+
const runNpmAsync = (cwd, args) =>
|
|
47
|
+
runProc(npmBin(), args, npmOptions(cwd, INSTALL_MS));
|
|
41
48
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
const npmOutput = (result) => (result.error ? '' : result.stdout);
|
|
50
|
+
|
|
51
|
+
const collectReport = (top, kind) => {
|
|
52
|
+
const report = NPM_REPORT[kind];
|
|
53
|
+
if (!fs.existsSync(path.join(top, report.file))) return null;
|
|
54
|
+
const options = npmOptions(top, AUDIT_MS);
|
|
55
|
+
return report.parse(npmOutput(runProcSync(npmBin(), report.args, options)));
|
|
46
56
|
};
|
|
47
57
|
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
);
|
|
54
|
-
if (result.error) return '';
|
|
55
|
-
return result.stdout || '';
|
|
58
|
+
const collectReportAsync = async (top, kind, signal) => {
|
|
59
|
+
const report = NPM_REPORT[kind];
|
|
60
|
+
if (!fs.existsSync(path.join(top, report.file))) return null;
|
|
61
|
+
const options = npmOptions(top, AUDIT_MS, signal);
|
|
62
|
+
const result = await runProc(npmBin(), report.args, options);
|
|
63
|
+
return report.parse(npmOutput(result));
|
|
56
64
|
};
|
|
57
65
|
|
|
58
66
|
const depPaths = (item) => item.dep?.files ?? [];
|
|
59
67
|
|
|
60
|
-
const origDepItems = (item) => item.dep?.items ?? [];
|
|
61
|
-
|
|
62
68
|
const depChange = (item) => item.dep?.change ?? null;
|
|
63
69
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
const hasProposed = (item) => !!depChange(item)?.propose;
|
|
71
|
+
|
|
72
|
+
const isStagedOrCommit = (item) =>
|
|
73
|
+
item.origin === 'staged' || item.origin === 'commit';
|
|
74
|
+
|
|
75
|
+
const firstDepRel = (rels, kind) =>
|
|
76
|
+
rels.find((rel) => depFileMeta(rel)?.kind === kind) ?? '';
|
|
71
77
|
|
|
72
78
|
const proposedNpmPlan = (top, item) => {
|
|
73
79
|
const change = depChange(item);
|
|
74
80
|
if (!change) return null;
|
|
75
|
-
const
|
|
81
|
+
const rels = depPaths(item);
|
|
76
82
|
const lockOnly = change.section === 'resolved';
|
|
77
83
|
const removing = !!change.unused;
|
|
78
|
-
const
|
|
79
|
-
const lockRel = firstDepRel(
|
|
80
|
-
const
|
|
81
|
-
const target = picked || files[0] || '';
|
|
82
|
-
const meta = depFileMeta(target);
|
|
84
|
+
const manifestRel = firstDepRel(rels, 'manifest');
|
|
85
|
+
const lockRel = firstDepRel(rels, 'lockfile');
|
|
86
|
+
const meta = depFileMeta((lockOnly ? lockRel : manifestRel) || rels[0]);
|
|
83
87
|
if (!meta) return null;
|
|
84
|
-
const cwd = meta.dir === '.' ? top : path.join(top, meta.dir);
|
|
85
88
|
let npmArgs = ['i'];
|
|
86
89
|
if (removing) npmArgs = ['uninstall', change.name];
|
|
87
90
|
else if (lockOnly) npmArgs = ['audit', 'fix'];
|
|
88
|
-
const fail = removing ? 'npm uninstall failed' : 'npm i failed';
|
|
89
|
-
const dirLock = meta.dir === '.' ? LOCKFILE : `${meta.dir}/${LOCKFILE}`;
|
|
90
|
-
const lockPath = lockRel || dirLock;
|
|
91
|
-
const prepareManifest = !lockOnly && !removing && !!manifest;
|
|
92
91
|
return {
|
|
93
|
-
cwd,
|
|
92
|
+
cwd: meta.dir === '.' ? top : path.join(top, meta.dir),
|
|
94
93
|
npmArgs,
|
|
95
|
-
fail,
|
|
94
|
+
fail: removing ? 'npm uninstall failed' : 'npm i failed',
|
|
96
95
|
lockOnly,
|
|
97
|
-
lockPath,
|
|
98
|
-
manifest,
|
|
99
|
-
prepareManifest,
|
|
96
|
+
lockPath: lockRel || relOf(meta.dir, LOCKFILE),
|
|
97
|
+
manifest: manifestRel,
|
|
98
|
+
prepareManifest: !lockOnly && !removing && !!manifestRel,
|
|
100
99
|
runner: item.dep.install,
|
|
101
100
|
};
|
|
102
101
|
};
|
|
103
102
|
|
|
104
|
-
const hasProposed = (item) => {
|
|
105
|
-
const change = depChange(item);
|
|
106
|
-
return !!(change && change.propose);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
103
|
const itemsForPath = (rel, items) => {
|
|
110
104
|
const group = [];
|
|
111
105
|
for (const item of items) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
continue;
|
|
106
|
+
const listed = item.dep ? item.dep.items : [item];
|
|
107
|
+
for (const entry of listed ?? []) {
|
|
108
|
+
if (itemPath(entry) === rel) group.push(entry);
|
|
117
109
|
}
|
|
118
|
-
if (itemPath(item) === rel) group.push(item);
|
|
119
110
|
}
|
|
120
111
|
return group;
|
|
121
112
|
};
|
|
122
113
|
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const collectAudit = (top) => {
|
|
131
|
-
const lock = path.join(top, LOCKFILE);
|
|
132
|
-
if (!fs.existsSync(lock)) return null;
|
|
133
|
-
const args = ['audit', '--json', '--package-lock-only'];
|
|
134
|
-
const text = runNpmJson(top, args, AUDIT_MS);
|
|
135
|
-
return parseAuditReport(text);
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const collectOutdated = (top) => {
|
|
139
|
-
const manifest = path.join(top, MANIFEST);
|
|
140
|
-
if (!fs.existsSync(manifest)) return null;
|
|
141
|
-
const args = ['outdated', '--json', '--long'];
|
|
142
|
-
const text = runNpmJson(top, args, AUDIT_MS);
|
|
143
|
-
return parseOutdatedReport(text);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const collectAuditAsync = async (top, extra, signal) => {
|
|
147
|
-
if (extra.auditMap !== undefined) return extra.auditMap;
|
|
148
|
-
const lock = path.join(top, LOCKFILE);
|
|
149
|
-
if (!fs.existsSync(lock)) return null;
|
|
150
|
-
const args = ['audit', '--json', '--package-lock-only'];
|
|
151
|
-
const text = await runNpmJsonAsync(top, args, AUDIT_MS, signal);
|
|
152
|
-
return parseAuditReport(text);
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const collectOutdatedAsync = async (top, extra, signal) => {
|
|
156
|
-
if (extra.outdatedMap !== undefined) return extra.outdatedMap;
|
|
157
|
-
const manifest = path.join(top, MANIFEST);
|
|
158
|
-
if (!fs.existsSync(manifest)) return null;
|
|
159
|
-
const args = ['outdated', '--json', '--long'];
|
|
160
|
-
const text = await runNpmJsonAsync(top, args, AUDIT_MS, signal);
|
|
161
|
-
return parseOutdatedReport(text);
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
const pickNpmMap = (override, collect, top) => {
|
|
165
|
-
if (override !== undefined) return override;
|
|
166
|
-
return collect(top);
|
|
114
|
+
const extrasNeeds = (items, rev, extra) => {
|
|
115
|
+
const live = extra.audit === true && !rev;
|
|
116
|
+
const hasDepDiff = items.some((item) => depFileMeta(itemPath(item)));
|
|
117
|
+
const audit = live || (Boolean(extra.audit) && hasDepDiff);
|
|
118
|
+
return { live, scan: live || hasDepDiff, audit };
|
|
167
119
|
};
|
|
168
120
|
|
|
169
|
-
const foldLoaded = (items, top, rev, extra
|
|
121
|
+
const foldLoaded = (items, top, rev, extra) => {
|
|
170
122
|
const readSides = (origin, rel) => readDepSides(top, rev, origin, rel);
|
|
171
|
-
const
|
|
172
|
-
const hasDepDiff = hasDepFile(items);
|
|
123
|
+
const needs = extrasNeeds(items, rev, extra);
|
|
173
124
|
const collect = extra.collect !== false;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
let outdated = extra
|
|
177
|
-
|
|
178
|
-
if (collect && usedNames === null && (hasDepDiff || live)) {
|
|
179
|
-
usedNames = collectUsedNames(top);
|
|
180
|
-
}
|
|
181
|
-
if (collect && exportEntries === undefined && (hasDepDiff || live)) {
|
|
125
|
+
const scan = collect && needs.scan;
|
|
126
|
+
const usedNames = extra.usedNames ?? (scan ? collectUsedNames(top) : null);
|
|
127
|
+
let { exportEntries, auditMap: audit, outdatedMap: outdated } = extra;
|
|
128
|
+
if (scan && exportEntries === undefined) {
|
|
182
129
|
exportEntries = collectExportMeta(top);
|
|
183
130
|
}
|
|
184
|
-
if (collect &&
|
|
185
|
-
audit =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
131
|
+
if (collect && needs.audit && audit === undefined) {
|
|
132
|
+
audit = collectReport(top, 'audit');
|
|
133
|
+
}
|
|
134
|
+
if (collect && needs.live && outdated === undefined) {
|
|
135
|
+
outdated = collectReport(top, 'outdated');
|
|
189
136
|
}
|
|
190
|
-
|
|
191
|
-
|
|
137
|
+
audit ??= null;
|
|
138
|
+
outdated ??= null;
|
|
192
139
|
const folded = foldDepItems(items, readSides, usedNames, audit, outdated, {
|
|
193
140
|
root: top,
|
|
194
141
|
exportEntries,
|
|
195
142
|
});
|
|
196
|
-
if (!live) return folded;
|
|
197
|
-
if (!isPathInScope(MANIFEST, extra.paths ?? [])) return folded;
|
|
143
|
+
if (!needs.live || !isPathInScope(MANIFEST, extra.paths)) return folded;
|
|
198
144
|
const pkgText = worktreeText(top, MANIFEST);
|
|
199
145
|
const lockText = worktreeText(top, LOCKFILE);
|
|
200
146
|
const options = { lockText, usedNames, root: top, exportEntries };
|
|
201
147
|
return mergeProposedItems(folded, pkgText, outdated, audit, options);
|
|
202
148
|
};
|
|
203
149
|
|
|
204
|
-
const needsExtras = (parsed, rev, extra) => {
|
|
205
|
-
const live = extra.audit === true && !rev;
|
|
206
|
-
if (live) return true;
|
|
207
|
-
if (!extra.audit) return false;
|
|
208
|
-
return hasDepFile(parsed);
|
|
209
|
-
};
|
|
210
|
-
|
|
211
150
|
const finishLoad = (snapshot, extra) => {
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
const parsed = snapshot.parsed;
|
|
215
|
-
const top = snapshot.top;
|
|
216
|
-
const rev = snapshot.rev;
|
|
151
|
+
const { top, rev, parsed } = snapshot;
|
|
152
|
+
const options = { ...extra, collect: !extra.deferExtras };
|
|
217
153
|
const items = foldLoaded(parsed, top, rev, options);
|
|
218
|
-
const pending = extra.deferExtras &&
|
|
154
|
+
const pending = extra.deferExtras && extrasNeeds(parsed, rev, extra).audit;
|
|
219
155
|
return { ...snapshot, items, pending };
|
|
220
156
|
};
|
|
221
157
|
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (needScan) {
|
|
228
|
-
usedNames = Promise.resolve().then(() => collectUsedNames(top));
|
|
229
|
-
}
|
|
230
|
-
let exportEntries = Promise.resolve(undefined);
|
|
231
|
-
if (extra.exportEntries !== undefined) {
|
|
232
|
-
exportEntries = Promise.resolve(extra.exportEntries);
|
|
233
|
-
} else if (needScan) {
|
|
234
|
-
exportEntries = Promise.resolve().then(() => collectExportMeta(top));
|
|
235
|
-
}
|
|
236
|
-
let audit = Promise.resolve(null);
|
|
237
|
-
if (live || (extra.audit && hasDepDiff)) {
|
|
238
|
-
audit = collectAuditAsync(top, extra, signal);
|
|
239
|
-
}
|
|
240
|
-
let outdated = Promise.resolve(null);
|
|
241
|
-
if (live) {
|
|
242
|
-
outdated = collectOutdatedAsync(top, extra, signal);
|
|
243
|
-
}
|
|
244
|
-
return { usedNames, audit, outdated, exportEntries };
|
|
158
|
+
const later = (fn) => Promise.resolve().then(fn);
|
|
159
|
+
|
|
160
|
+
const reportTask = (wanted, known, collect) => {
|
|
161
|
+
if (!wanted) return null;
|
|
162
|
+
return known === undefined ? collect() : known;
|
|
245
163
|
};
|
|
246
164
|
|
|
247
165
|
const loadExtras = async (snapshot, extra = {}) => {
|
|
248
|
-
const top = snapshot
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
const tasks = extrasMetaOf(parsed, rev, extra, top, signal);
|
|
166
|
+
const { top, rev, parsed } = snapshot;
|
|
167
|
+
const { signal } = extra;
|
|
168
|
+
const needs = extrasNeeds(parsed, rev, extra);
|
|
169
|
+
const known = extra.exportEntries;
|
|
253
170
|
const [usedNames, audit, outdated, exportEntries] = await Promise.all([
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
171
|
+
needs.scan ? later(() => collectUsedNames(top)) : null,
|
|
172
|
+
reportTask(needs.audit, extra.auditMap, () =>
|
|
173
|
+
collectReportAsync(top, 'audit', signal),
|
|
174
|
+
),
|
|
175
|
+
reportTask(needs.live, extra.outdatedMap, () =>
|
|
176
|
+
collectReportAsync(top, 'outdated', signal),
|
|
177
|
+
),
|
|
178
|
+
known === undefined && needs.scan
|
|
179
|
+
? later(() => collectExportMeta(top))
|
|
180
|
+
: known,
|
|
258
181
|
]);
|
|
259
182
|
if (signal && signal.aborted) throw abortError();
|
|
260
|
-
const
|
|
183
|
+
const found = { usedNames, auditMap: audit, outdatedMap: outdated };
|
|
184
|
+
const items = foldLoaded(parsed, top, rev, {
|
|
261
185
|
...extra,
|
|
186
|
+
...found,
|
|
262
187
|
paths: extra.paths ?? [],
|
|
263
|
-
usedNames,
|
|
264
|
-
auditMap: audit,
|
|
265
|
-
outdatedMap: outdated,
|
|
266
188
|
exportEntries,
|
|
267
189
|
collect: false,
|
|
268
190
|
});
|
|
269
|
-
return {
|
|
270
|
-
...snapshot,
|
|
271
|
-
items: folded,
|
|
272
|
-
pending: false,
|
|
273
|
-
usedNames,
|
|
274
|
-
auditMap: audit,
|
|
275
|
-
outdatedMap: outdated,
|
|
276
|
-
exportEntries,
|
|
277
|
-
};
|
|
191
|
+
return { ...snapshot, items, pending: false, ...found, exportEntries };
|
|
278
192
|
};
|
|
279
193
|
|
|
280
194
|
const captureFile = (top, rel) => {
|
|
281
|
-
const abs = path.join(top, rel);
|
|
282
195
|
try {
|
|
283
|
-
const text = fs.readFileSync(
|
|
196
|
+
const text = fs.readFileSync(path.join(top, rel), 'utf8');
|
|
284
197
|
return { rel, existed: true, text };
|
|
285
198
|
} catch (error) {
|
|
286
|
-
if (error.code
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
throw error;
|
|
199
|
+
if (error.code !== 'ENOENT') throw error;
|
|
200
|
+
return { rel, existed: false, text: '' };
|
|
290
201
|
}
|
|
291
202
|
};
|
|
292
203
|
|
|
293
204
|
const restoreFile = (top, saved) => {
|
|
294
205
|
const abs = path.join(top, saved.rel);
|
|
295
|
-
if (saved.existed)
|
|
296
|
-
|
|
297
|
-
fs.unlinkSync(abs);
|
|
298
|
-
} catch {
|
|
299
|
-
// missing
|
|
300
|
-
}
|
|
206
|
+
if (saved.existed) fs.writeFileSync(abs, saved.text);
|
|
207
|
+
else fs.rmSync(abs, { force: true });
|
|
301
208
|
};
|
|
302
209
|
|
|
303
|
-
const
|
|
304
|
-
const
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
return owned;
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const restoreFiles = (top, owned) => {
|
|
311
|
-
for (const saved of owned) restoreFile(top, saved);
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const mergeDepLive = (top, rel, change, origin, base, side, kind) => {
|
|
315
|
-
const sides = readDepSides(top, null, origin, rel);
|
|
210
|
+
const mergeLive = (top, rel, item, base, side) => {
|
|
211
|
+
const sides = readDepSides(top, null, item.origin, rel);
|
|
212
|
+
const { kind } = depFileMeta(rel);
|
|
213
|
+
const change = depChange(item);
|
|
316
214
|
return mergeDepFile(base, sides.oldText, sides.newText, change, side, kind);
|
|
317
215
|
};
|
|
318
216
|
|
|
319
|
-
const applyDepIndex = (top, rel,
|
|
320
|
-
|
|
321
|
-
if (!meta) return;
|
|
217
|
+
const applyDepIndex = (top, rel, item, side) => {
|
|
218
|
+
if (!depFileMeta(rel)) return;
|
|
322
219
|
const indexed = gitText(top, `:${rel}`) || gitText(top, `HEAD:${rel}`);
|
|
323
|
-
|
|
324
|
-
writeIndex(top, rel, text);
|
|
220
|
+
writeIndex(top, rel, mergeLive(top, rel, item, indexed, side));
|
|
325
221
|
};
|
|
326
222
|
|
|
327
|
-
const applyDepWorktree = (top, rel,
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const worktree = worktreeText(top, rel);
|
|
331
|
-
const text = mergeDepLive(
|
|
332
|
-
top,
|
|
333
|
-
rel,
|
|
334
|
-
change,
|
|
335
|
-
origin,
|
|
336
|
-
worktree,
|
|
337
|
-
'old',
|
|
338
|
-
meta.kind,
|
|
339
|
-
);
|
|
223
|
+
const applyDepWorktree = (top, rel, item, side) => {
|
|
224
|
+
if (!depFileMeta(rel)) return;
|
|
225
|
+
const text = mergeLive(top, rel, item, worktreeText(top, rel), side);
|
|
340
226
|
writeFile(top, rel, text);
|
|
341
227
|
};
|
|
342
228
|
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
rels.push(plan.lockPath);
|
|
348
|
-
}
|
|
349
|
-
return rels;
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
const errorFromResult = (result, fail) => {
|
|
353
|
-
if (!result) return new Error(fail);
|
|
354
|
-
if (result.error) {
|
|
355
|
-
const msg = result.error.message || fail;
|
|
356
|
-
return new Error(oneLine(msg, fail));
|
|
357
|
-
}
|
|
358
|
-
const msg = result.stderr || result.stdout || fail;
|
|
359
|
-
return new Error(oneLine(msg, fail));
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
const applyPreparation = (top, item, plan) => {
|
|
363
|
-
if (!plan.prepareManifest || !plan.manifest) return;
|
|
364
|
-
const change = depChange(item);
|
|
365
|
-
const origin = item.origin;
|
|
366
|
-
const worktree = worktreeText(top, plan.manifest);
|
|
367
|
-
const text = mergeDepLive(
|
|
368
|
-
top,
|
|
369
|
-
plan.manifest,
|
|
370
|
-
change,
|
|
371
|
-
origin,
|
|
372
|
-
worktree,
|
|
373
|
-
'new',
|
|
374
|
-
'manifest',
|
|
375
|
-
);
|
|
376
|
-
writeFile(top, plan.manifest, text);
|
|
229
|
+
const npmError = (result, fail) => {
|
|
230
|
+
const { error } = result;
|
|
231
|
+
const msg = error ? error.message : result.stderr || result.stdout;
|
|
232
|
+
return new Error(oneLine(msg || fail, fail));
|
|
377
233
|
};
|
|
378
234
|
|
|
379
235
|
const stagePlan = (top, plan) => {
|
|
@@ -384,139 +240,121 @@ const stagePlan = (top, plan) => {
|
|
|
384
240
|
requireOk(runGit(['add', '--', ...toAdd], top));
|
|
385
241
|
};
|
|
386
242
|
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const
|
|
392
|
-
|
|
243
|
+
const startPlan = (top, item) => {
|
|
244
|
+
const plan = proposedNpmPlan(top, item);
|
|
245
|
+
if (!plan) return null;
|
|
246
|
+
const rels = [...new Set([plan.manifest, plan.lockPath].filter(Boolean))];
|
|
247
|
+
const owned = rels.map((rel) => captureFile(top, rel));
|
|
248
|
+
return { plan, owned };
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const finishPlan = (top, plan, result, stage = stagePlan) => {
|
|
252
|
+
const failed = result && (result.error || result.status !== 0);
|
|
253
|
+
if (failed) throw npmError(result, plan.fail);
|
|
254
|
+
stage(top, plan);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const restoreOwned = (top, owned) => {
|
|
258
|
+
for (const saved of owned) restoreFile(top, saved);
|
|
393
259
|
};
|
|
394
260
|
|
|
395
261
|
const applyProposedUpdate = (top, item, extra = {}) => {
|
|
396
|
-
const
|
|
397
|
-
if (!
|
|
398
|
-
const owned =
|
|
262
|
+
const started = startPlan(top, item);
|
|
263
|
+
if (!started) return;
|
|
264
|
+
const { plan, owned } = started;
|
|
399
265
|
try {
|
|
400
|
-
|
|
266
|
+
if (plan.prepareManifest) applyDepWorktree(top, plan.manifest, item, 'new');
|
|
401
267
|
const install = extra.run ?? plan.runner ?? runNpm;
|
|
402
|
-
|
|
403
|
-
finishPlan(top, plan, result, extra.stage);
|
|
268
|
+
finishPlan(top, plan, install(plan.cwd, plan.npmArgs), extra.stage);
|
|
404
269
|
} catch (error) {
|
|
405
|
-
|
|
270
|
+
restoreOwned(top, owned);
|
|
406
271
|
throw error;
|
|
407
272
|
}
|
|
408
273
|
};
|
|
409
274
|
|
|
410
|
-
const applyProposedUpdateAsync = async (top, item
|
|
411
|
-
const
|
|
412
|
-
if (!
|
|
413
|
-
const owned =
|
|
275
|
+
const applyProposedUpdateAsync = async (top, item) => {
|
|
276
|
+
const started = startPlan(top, item);
|
|
277
|
+
if (!started) return;
|
|
278
|
+
const { plan, owned } = started;
|
|
414
279
|
try {
|
|
415
|
-
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
finishPlan(top, plan, result, extra.stage);
|
|
280
|
+
if (plan.prepareManifest) applyDepWorktree(top, plan.manifest, item, 'new');
|
|
281
|
+
const install = plan.runner ?? runNpmAsync;
|
|
282
|
+
finishPlan(top, plan, await install(plan.cwd, plan.npmArgs));
|
|
419
283
|
} catch (error) {
|
|
420
|
-
|
|
284
|
+
restoreOwned(top, owned);
|
|
421
285
|
throw error;
|
|
422
286
|
}
|
|
423
287
|
};
|
|
424
288
|
|
|
425
289
|
const applyDepChange = (top, item, mode) => {
|
|
426
290
|
const change = depChange(item);
|
|
427
|
-
if (change
|
|
291
|
+
if (change.propose) {
|
|
428
292
|
if (mode === 'add') return void applyProposedUpdate(top, item);
|
|
429
293
|
if (!change.unused) return;
|
|
430
294
|
}
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (mode === 'add') {
|
|
435
|
-
applyDepIndex(top, rel, change, origin, 'new');
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
if (mode === 'unstage') {
|
|
439
|
-
applyDepIndex(top, rel, change, origin, 'old');
|
|
295
|
+
for (const rel of depPaths(item)) {
|
|
296
|
+
if (mode !== 'revert') {
|
|
297
|
+
applyDepIndex(top, rel, item, mode === 'add' ? 'new' : 'old');
|
|
440
298
|
continue;
|
|
441
299
|
}
|
|
442
|
-
if (origin === 'staged') applyDepIndex(top, rel,
|
|
443
|
-
applyDepWorktree(top, rel,
|
|
300
|
+
if (item.origin === 'staged') applyDepIndex(top, rel, item, 'old');
|
|
301
|
+
applyDepWorktree(top, rel, item, 'old');
|
|
444
302
|
}
|
|
445
303
|
};
|
|
446
304
|
|
|
447
305
|
const addItem = (top, item) => {
|
|
448
|
-
if (item.
|
|
306
|
+
if (!item.dep) return void worktree.addItem(top, item);
|
|
307
|
+
if (isStagedOrCommit(item)) return;
|
|
449
308
|
if (depChange(item)) return void applyDepChange(top, item, 'add');
|
|
450
|
-
const
|
|
451
|
-
if (
|
|
452
|
-
requireOk(runGit(['add', '--', ...files], top));
|
|
453
|
-
}
|
|
309
|
+
const rels = depPaths(item);
|
|
310
|
+
if (rels.length) requireOk(runGit(['add', '--', ...rels], top));
|
|
454
311
|
};
|
|
455
312
|
|
|
456
313
|
const addItemAsync = async (top, item) => {
|
|
457
|
-
if (item
|
|
458
|
-
|
|
459
|
-
await applyProposedUpdateAsync(top, item);
|
|
460
|
-
return;
|
|
314
|
+
if (isStagedOrCommit(item) || !hasProposed(item)) {
|
|
315
|
+
return void addItem(top, item);
|
|
461
316
|
}
|
|
462
|
-
|
|
317
|
+
await applyProposedUpdateAsync(top, item);
|
|
463
318
|
};
|
|
464
319
|
|
|
465
320
|
const unstageItem = (top, item) => {
|
|
321
|
+
if (!item.dep) return void worktree.unstageItem(top, item);
|
|
466
322
|
if (item.origin !== 'staged') return;
|
|
467
323
|
if (depChange(item)) return void applyDepChange(top, item, 'unstage');
|
|
468
|
-
const
|
|
469
|
-
if (
|
|
470
|
-
|
|
471
|
-
}
|
|
324
|
+
const rels = depPaths(item);
|
|
325
|
+
if (!rels.length) return;
|
|
326
|
+
requireOk(runGit(['restore', '--staged', '--', ...rels], top));
|
|
472
327
|
};
|
|
473
328
|
|
|
474
|
-
const
|
|
329
|
+
const revertItem = (top, item) => {
|
|
330
|
+
if (item.origin === 'commit') return;
|
|
331
|
+
if (!item.dep) return void worktree.revertItem(top, item);
|
|
475
332
|
if (depChange(item)) return void applyDepChange(top, item, 'revert');
|
|
476
|
-
const byPath =
|
|
477
|
-
for (const
|
|
478
|
-
const rel = itemPath(orig);
|
|
479
|
-
const list = byPath.get(rel);
|
|
480
|
-
if (list) list.push(orig);
|
|
481
|
-
else byPath.set(rel, [orig]);
|
|
482
|
-
}
|
|
483
|
-
for (const rel of byPath.keys()) {
|
|
484
|
-
revertOnePath(top, rel, byPath.get(rel));
|
|
485
|
-
}
|
|
333
|
+
const byPath = groupByPath(item.dep.items ?? []);
|
|
334
|
+
for (const rel of byPath.keys()) revertOnePath(top, rel, byPath.get(rel));
|
|
486
335
|
};
|
|
487
336
|
|
|
488
337
|
const revertFile = (top, rel, items) => {
|
|
489
|
-
const
|
|
338
|
+
const rels = new Set([rel]);
|
|
490
339
|
for (const item of items) {
|
|
491
|
-
for (const file of depPaths(item))
|
|
340
|
+
for (const file of depPaths(item)) rels.add(file);
|
|
492
341
|
}
|
|
493
|
-
for (const file of
|
|
342
|
+
for (const file of rels) {
|
|
494
343
|
const group = itemsForPath(file, items);
|
|
495
|
-
|
|
496
|
-
revertOnePath(top, file, targets);
|
|
344
|
+
revertOnePath(top, file, group.length ? group : items);
|
|
497
345
|
}
|
|
498
346
|
};
|
|
499
347
|
|
|
500
348
|
module.exports = {
|
|
501
|
-
hasProposed,
|
|
502
|
-
itemsForPath,
|
|
503
349
|
finishLoad,
|
|
504
350
|
loadExtras,
|
|
505
351
|
captureFile,
|
|
506
352
|
restoreFile,
|
|
353
|
+
proposedNpmPlan,
|
|
507
354
|
applyProposedUpdate,
|
|
508
|
-
applyProposedUpdateAsync,
|
|
509
|
-
applyDepChange,
|
|
510
355
|
addItem,
|
|
511
356
|
addItemAsync,
|
|
512
357
|
unstageItem,
|
|
513
|
-
|
|
358
|
+
revertItem,
|
|
514
359
|
revertFile,
|
|
515
|
-
AUDIT_MS,
|
|
516
|
-
INSTALL_MS,
|
|
517
|
-
runNpm,
|
|
518
|
-
runNpmAsync,
|
|
519
|
-
runNpmJson,
|
|
520
|
-
runNpmJsonAsync,
|
|
521
|
-
proposedNpmPlan,
|
|
522
360
|
};
|