reslop 0.2.6 → 0.3.0
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 +27 -1
- package/README.md +16 -14
- package/bin/reslop.js +8 -5
- package/lib/agents/agents.js +504 -0
- package/lib/agents/allow.js +71 -0
- package/lib/agents/allow.json +16 -0
- package/lib/agents/models.js +460 -0
- package/lib/api.js +15 -12
- package/lib/cli.js +61 -66
- package/lib/{files.js → common/files.js} +51 -52
- package/lib/common/find.js +61 -0
- package/lib/common/format.js +101 -0
- package/lib/{utilities.js → common/process.js} +7 -95
- package/lib/common/tasks.json +27 -0
- package/lib/common/utilities.js +112 -0
- package/lib/dashboard/git.js +9 -16
- package/lib/dashboard/model.js +117 -51
- package/lib/dashboard/npm.js +133 -32
- package/lib/dashboard/tree.js +27 -30
- package/lib/{session → dashboard}/watch.js +41 -31
- package/lib/dashboard/workspace.js +151 -0
- package/lib/{deps.js → deps/deps.js} +31 -45
- package/lib/{deps-diff.js → deps/diff.js} +35 -25
- package/lib/{manifest.js → deps/manifest.js} +47 -30
- package/lib/diff/align.js +1 -2
- package/lib/diff/diff.js +2 -2
- package/lib/diff/display.js +29 -5
- package/lib/diff/inline.js +10 -14
- package/lib/diff/items.js +6 -4
- package/lib/diff/patch.js +39 -3
- package/lib/diff/unit.js +45 -16
- package/lib/{git-branches.js → git/branches.js} +53 -48
- package/lib/{git-deps.js → git/deps.js} +123 -95
- package/lib/git/git.js +71 -0
- package/lib/git/worktree.js +475 -0
- package/lib/highlight/bash.js +1 -2
- package/lib/highlight/csv.js +1 -2
- package/lib/highlight/dot.js +1 -2
- package/lib/{highlight.js → highlight/highlight.js} +42 -11
- package/lib/highlight/html.js +20 -18
- package/lib/highlight/json.js +1 -2
- package/lib/highlight/log.js +1 -2
- package/lib/highlight/txt.js +1 -2
- package/lib/{session → input}/actions.js +240 -71
- package/lib/{editor.js → input/editor.js} +7 -8
- package/lib/{keys.js → input/keys.js} +36 -39
- package/lib/input/line-edit.js +77 -0
- package/lib/input/menu-pick.js +70 -0
- package/lib/render/agents.js +640 -0
- package/lib/render/branches.js +4 -5
- package/lib/render/commits.js +54 -64
- package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
- package/lib/render/dashboard/blocks.js +490 -0
- package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
- package/lib/render/diff.js +166 -45
- package/lib/render/field.js +131 -0
- package/lib/render/files.js +22 -8
- package/lib/render/footer.js +383 -0
- package/lib/render/frame.js +127 -25
- package/lib/render/header.js +176 -0
- package/lib/render/log.js +319 -0
- package/lib/render/menu.js +60 -0
- package/lib/render/notes.js +29 -13
- package/lib/render/npm.js +322 -196
- package/lib/render/packages.js +89 -0
- package/lib/render/primitives.js +109 -16
- package/lib/render/render.js +7 -11
- package/lib/render/repos.js +214 -0
- package/lib/render/status.js +370 -0
- package/lib/render/status.json +85 -0
- package/lib/render/{dash-table.js → table.js} +19 -61
- package/lib/render/tiles.js +203 -47
- package/lib/render/unit.js +32 -13
- package/lib/{report-model.js → report/model.js} +4 -6
- package/lib/{report-parse.js → report/parse.js} +9 -14
- package/lib/{report-render.js → report/render.js} +46 -14
- package/lib/{report-run.js → report/run.js} +42 -30
- package/lib/{report-test.js → report/test.js} +56 -55
- package/lib/{report-tools.js → report/tools.js} +24 -33
- package/lib/review/agent-instructions.md +9 -0
- package/lib/review/format.js +426 -0
- package/lib/{review-merge.js → review/merge.js} +9 -8
- package/lib/review/review.js +190 -0
- package/lib/review/store.js +281 -0
- package/lib/review/templates.js +83 -0
- package/lib/runs/commands.js +14 -0
- package/lib/runs/logs.js +299 -0
- package/lib/runs/output.js +174 -0
- package/lib/runs/process.js +113 -0
- package/lib/{runs.js → runs/runs.js} +84 -36
- package/lib/runs/scripts.js +150 -0
- package/lib/session/agents/agents.js +958 -0
- package/lib/session/agents/job.js +320 -0
- package/lib/session/agents/jobs.js +355 -0
- package/lib/session/agents/options.js +153 -0
- package/lib/session/agents/plans.js +83 -0
- package/lib/session/branches.js +36 -44
- package/lib/session/change-actions.js +34 -100
- package/lib/session/commit.js +122 -56
- package/lib/session/compose.js +197 -146
- package/lib/session/dashboard.js +183 -37
- package/lib/session/dispatch.js +120 -22
- package/lib/session/files-pane.js +60 -24
- package/lib/session/find.js +81 -0
- package/lib/session/import.js +82 -0
- package/lib/session/items.js +8 -17
- package/lib/session/lifecycle.js +96 -66
- package/lib/session/load.js +2 -4
- package/lib/session/navigation.js +8 -3
- package/lib/session/npm/history.js +249 -0
- package/lib/session/npm/npm.js +815 -0
- package/lib/session/npm/packages.js +150 -0
- package/lib/session/ops.js +125 -34
- package/lib/session/pointer.js +216 -10
- package/lib/session/prefs.js +112 -0
- package/lib/session/reads.js +109 -0
- package/lib/session/review.js +28 -10
- package/lib/session/run-panel.js +40 -0
- package/lib/{session.js → session/session.js} +166 -59
- package/lib/session/tasks.js +321 -66
- package/lib/session/templates.js +1 -1
- package/lib/session/terminal.js +94 -12
- package/lib/session/unit.js +35 -30
- package/lib/session/update.js +194 -6
- package/lib/session/view.js +72 -59
- package/lib/session/workspace.js +416 -0
- package/lib/{capabilities.js → source/capabilities.js} +3 -12
- package/lib/{github.js → source/github.js} +102 -32
- package/lib/{gitlab.js → source/gitlab.js} +87 -10
- package/lib/{remote.js → source/remote.js} +144 -40
- package/lib/source/review-threads.graphql +20 -0
- package/lib/source/source.js +74 -0
- package/lib/term/ansi.js +396 -0
- package/lib/{clipboard.js → term/clipboard.js} +3 -6
- package/lib/{select.js → term/select.js} +2 -2
- package/lib/term/themes/dark.json +122 -0
- package/lib/term/themes/light.json +122 -0
- package/lib/term/wide.txt +21 -0
- package/lib/{wrap.js → term/wrap.js} +4 -4
- package/package.json +2 -2
- package/lib/ansi.js +0 -505
- package/lib/detect.js +0 -34
- package/lib/diff/edit.js +0 -33
- package/lib/git-worktree.js +0 -437
- package/lib/git.js +0 -79
- package/lib/npm-commands.js +0 -522
- package/lib/render/chrome.js +0 -628
- package/lib/render/dash-blocks.js +0 -245
- package/lib/render/present.js +0 -19
- package/lib/render/report.js +0 -43
- package/lib/review.js +0 -744
- package/lib/session/accessors.js +0 -26
- package/lib/session/edit-click.js +0 -57
- package/lib/session/npm.js +0 -452
- package/lib/session/progress.js +0 -41
- package/lib/source.js +0 -36
- package/lib/update.js +0 -192
- /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
|
@@ -4,12 +4,9 @@ const fs = require('node:fs');
|
|
|
4
4
|
const os = require('node:os');
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const {
|
|
9
|
-
const
|
|
10
|
-
const { shortAge } = files;
|
|
11
|
-
const worktree = require('./git-worktree.js');
|
|
12
|
-
const { requireOk, runGit, command, gitOp } = worktree;
|
|
7
|
+
const { oneLine, trimText } = require('../common/utilities.js');
|
|
8
|
+
const { shortAge } = require('../common/format.js');
|
|
9
|
+
const { requireOk, runGit, command, gitOp } = require('./worktree.js');
|
|
13
10
|
|
|
14
11
|
const BRANCH_FORMAT = [
|
|
15
12
|
'%(HEAD)',
|
|
@@ -43,9 +40,7 @@ const AUTOSQUASH_ARGS = [
|
|
|
43
40
|
'--interactive',
|
|
44
41
|
'--autosquash',
|
|
45
42
|
];
|
|
46
|
-
const REWORD_TODO = path.join(__dirname, '
|
|
47
|
-
|
|
48
|
-
const trimText = (value) => `${value ?? ''}`.trim();
|
|
43
|
+
const REWORD_TODO = path.join(__dirname, 'reword-todo.js');
|
|
49
44
|
|
|
50
45
|
const required = (value, message) => {
|
|
51
46
|
const text = trimText(value);
|
|
@@ -60,9 +55,9 @@ const messageBody = (message) => {
|
|
|
60
55
|
return text.endsWith('\n') ? text : `${text}\n`;
|
|
61
56
|
};
|
|
62
57
|
|
|
63
|
-
const commitMessage = (top, sha) => {
|
|
58
|
+
const commitMessage = async (top, sha) => {
|
|
64
59
|
const spec = trimText(sha) || 'HEAD';
|
|
65
|
-
const result = runGit(['log', '-1', '--format=%B', spec], top);
|
|
60
|
+
const result = await runGit(['log', '-1', '--format=%B', spec], top);
|
|
66
61
|
return result.status === 0 ? result.stdout.trimEnd() : '';
|
|
67
62
|
};
|
|
68
63
|
|
|
@@ -112,7 +107,7 @@ const statCount = (stat, word) => {
|
|
|
112
107
|
return match ? parseInt(match[1], 10) : 0;
|
|
113
108
|
};
|
|
114
109
|
|
|
115
|
-
const listCommits = (top) => {
|
|
110
|
+
const listCommits = async (top) => {
|
|
116
111
|
const args = [
|
|
117
112
|
'log',
|
|
118
113
|
'-z',
|
|
@@ -120,7 +115,7 @@ const listCommits = (top) => {
|
|
|
120
115
|
`--format=${COMMIT_FORMAT}`,
|
|
121
116
|
`--max-count=${COMMIT_LIMIT}`,
|
|
122
117
|
];
|
|
123
|
-
const result = requireOk(runGit(args, top), 'log failed');
|
|
118
|
+
const result = requireOk(await runGit(args, top), 'log failed');
|
|
124
119
|
const commits = [];
|
|
125
120
|
for (const chunk of result.stdout.split('\0')) {
|
|
126
121
|
const stat = STAT_LEAD.exec(chunk);
|
|
@@ -141,11 +136,10 @@ const parseTrack = (text) => {
|
|
|
141
136
|
const raw = trimText(text).replace(/^\[|\]$/g, '');
|
|
142
137
|
const ahead = /ahead (\d+)/.exec(raw);
|
|
143
138
|
const behind = /behind (\d+)/.exec(raw);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
};
|
|
139
|
+
const aheadCount = ahead ? Number(ahead[1]) : 0;
|
|
140
|
+
const behindCount = behind ? Number(behind[1]) : 0;
|
|
141
|
+
const gone = raw === 'gone';
|
|
142
|
+
return { ahead: aheadCount, behind: behindCount, gone };
|
|
149
143
|
};
|
|
150
144
|
|
|
151
145
|
const parseBranchLine = (line) => {
|
|
@@ -163,31 +157,33 @@ const parseBranchLine = (line) => {
|
|
|
163
157
|
};
|
|
164
158
|
};
|
|
165
159
|
|
|
166
|
-
const defaultBranch = (top, names) => {
|
|
167
|
-
const
|
|
160
|
+
const defaultBranch = async (top, names) => {
|
|
161
|
+
const args = ['symbolic-ref', '--short', ORIGIN_HEAD];
|
|
162
|
+
const result = await runGit(args, top);
|
|
168
163
|
const ref = result.status === 0 ? result.stdout.trim() : '';
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
164
|
+
let remote = ref;
|
|
165
|
+
if (ref.startsWith(ORIGIN_PREFIX)) {
|
|
166
|
+
remote = ref.slice(ORIGIN_PREFIX.length);
|
|
167
|
+
}
|
|
172
168
|
if (names.has(remote)) return remote;
|
|
173
169
|
return DEFAULT_BRANCHES.find((name) => names.has(name)) ?? '';
|
|
174
170
|
};
|
|
175
171
|
|
|
176
|
-
const listBranches = (top) => {
|
|
172
|
+
const listBranches = async (top) => {
|
|
177
173
|
const args = ['for-each-ref', `--format=${BRANCH_FORMAT}`, 'refs/heads'];
|
|
178
|
-
const result = requireOk(runGit(args, top), 'branch failed');
|
|
174
|
+
const result = requireOk(await runGit(args, top), 'branch failed');
|
|
179
175
|
const lines = result.stdout.split('\n').filter(Boolean);
|
|
180
176
|
const branches = lines.map((line) => parseBranchLine(line)).filter(Boolean);
|
|
181
177
|
const names = new Set(branches.map((entry) => entry.name));
|
|
182
|
-
const primary = defaultBranch(top, names);
|
|
178
|
+
const primary = await defaultBranch(top, names);
|
|
183
179
|
return branches.map((entry) => ({
|
|
184
180
|
...entry,
|
|
185
181
|
isDefault: entry.name === primary,
|
|
186
182
|
}));
|
|
187
183
|
};
|
|
188
184
|
|
|
189
|
-
const hasStaged = (top) => {
|
|
190
|
-
const result = runGit(['diff', '--cached', '--quiet'], top);
|
|
185
|
+
const hasStaged = async (top) => {
|
|
186
|
+
const result = await runGit(['diff', '--cached', '--quiet'], top);
|
|
191
187
|
return result.status === 1;
|
|
192
188
|
};
|
|
193
189
|
|
|
@@ -243,6 +239,24 @@ const commitOp = gitOp(function* (kind, message) {
|
|
|
243
239
|
requireOk(result);
|
|
244
240
|
});
|
|
245
241
|
|
|
242
|
+
const updateOp = gitOp(function* (sha) {
|
|
243
|
+
const commit = yield* resolveCommit(required(sha, 'empty commit'));
|
|
244
|
+
const head = yield* resolveCommit('HEAD');
|
|
245
|
+
if (commit === head) {
|
|
246
|
+
const result = yield command(['commit', '--amend', '--no-edit']);
|
|
247
|
+
return void requireOk(result);
|
|
248
|
+
}
|
|
249
|
+
const fixed = yield command(['commit', '--fixup', commit]);
|
|
250
|
+
requireOk(fixed);
|
|
251
|
+
const range = yield* rebaseRange(commit);
|
|
252
|
+
try {
|
|
253
|
+
yield* rebase([...AUTOSQUASH_ARGS, ...range]);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
yield command(['reset', '--soft', 'HEAD~1']);
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
246
260
|
const dropCommitOp = gitOp(function* (sha) {
|
|
247
261
|
const target = yield* resolveCommit(required(sha, 'empty commit'));
|
|
248
262
|
const parent = yield command(['rev-parse', '--verify', `${target}^`]);
|
|
@@ -363,24 +377,15 @@ module.exports = {
|
|
|
363
377
|
listCommits,
|
|
364
378
|
listBranches,
|
|
365
379
|
hasStaged,
|
|
366
|
-
checkoutBranch: checkoutOp
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
applyFixupAsync: fixupOp.async,
|
|
378
|
-
rewordCommit: rewordOp.sync,
|
|
379
|
-
rewordCommitAsync: rewordOp.async,
|
|
380
|
-
pullChanges: pullOp.sync,
|
|
381
|
-
pullChangesAsync: pullOp.async,
|
|
382
|
-
pushChanges: pushOp.sync,
|
|
383
|
-
pushChangesAsync: pushOp.async,
|
|
384
|
-
commitChanges: commitOp.sync,
|
|
385
|
-
commitChangesAsync: commitOp.async,
|
|
380
|
+
checkoutBranch: checkoutOp,
|
|
381
|
+
createBranch: createOp,
|
|
382
|
+
rebaseBranch: rebaseOp,
|
|
383
|
+
dropBranch: dropBranchOp,
|
|
384
|
+
dropCommit: dropCommitOp,
|
|
385
|
+
applyFixup: fixupOp,
|
|
386
|
+
rewordCommit: rewordOp,
|
|
387
|
+
pullChanges: pullOp,
|
|
388
|
+
pushChanges: pushOp,
|
|
389
|
+
commitChanges: commitOp,
|
|
390
|
+
updateCommit: updateOp,
|
|
386
391
|
};
|
|
@@ -3,22 +3,28 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
|
-
const deps = require('
|
|
6
|
+
const deps = require('../deps/deps.js');
|
|
7
7
|
const { foldDepItems, mergeProposedItems } = deps;
|
|
8
8
|
const { parseAuditReport, parseOutdatedReport } = deps;
|
|
9
9
|
const { collectUsedNames, collectExportMeta } = deps;
|
|
10
|
-
const
|
|
10
|
+
const { collectGroups } = require('../deps/diff.js');
|
|
11
|
+
const manifest = require('../deps/manifest.js');
|
|
11
12
|
const { MANIFEST, LOCKFILE, depFileMeta, mergeDepFile, relOf } = manifest;
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const { itemPath, isPathInScope } = require('../common/files.js');
|
|
14
|
+
const utilities = require('../common/utilities.js');
|
|
15
|
+
const {
|
|
16
|
+
INSTALL_MS,
|
|
17
|
+
IS_WIN,
|
|
18
|
+
abortError,
|
|
19
|
+
npmBin,
|
|
20
|
+
runProc,
|
|
21
|
+
} = require('../common/process.js');
|
|
22
|
+
const { oneLine } = utilities;
|
|
23
|
+
const worktree = require('./worktree.js');
|
|
24
|
+
const { gitOk, indexedText, worktreeText, readDepSides } = worktree;
|
|
18
25
|
const { revertOnePath, groupByPath, writeFile, writeIndex } = worktree;
|
|
19
26
|
|
|
20
27
|
const AUDIT_MS = 15000;
|
|
21
|
-
const INSTALL_MS = 120000;
|
|
22
28
|
|
|
23
29
|
const NPM_REPORT = {
|
|
24
30
|
audit: {
|
|
@@ -41,21 +47,11 @@ const npmOptions = (cwd, timeout, signal) => ({
|
|
|
41
47
|
});
|
|
42
48
|
|
|
43
49
|
const runNpm = (cwd, args) =>
|
|
44
|
-
runProcSync(npmBin(), args, npmOptions(cwd, INSTALL_MS));
|
|
45
|
-
|
|
46
|
-
const runNpmAsync = (cwd, args) =>
|
|
47
50
|
runProc(npmBin(), args, npmOptions(cwd, INSTALL_MS));
|
|
48
51
|
|
|
49
52
|
const npmOutput = (result) => (result.error ? '' : result.stdout);
|
|
50
53
|
|
|
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)));
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const collectReportAsync = async (top, kind, signal) => {
|
|
54
|
+
const collectReport = async (top, kind, signal) => {
|
|
59
55
|
const report = NPM_REPORT[kind];
|
|
60
56
|
if (!fs.existsSync(path.join(top, report.file))) return null;
|
|
61
57
|
const options = npmOptions(top, AUDIT_MS, signal);
|
|
@@ -67,13 +63,16 @@ const depPaths = (item) => item.dep?.files ?? [];
|
|
|
67
63
|
|
|
68
64
|
const depChange = (item) => item.dep?.change ?? null;
|
|
69
65
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
66
|
+
const isStagedOrCommit = (item) => {
|
|
67
|
+
const staged = item.origin === 'staged';
|
|
68
|
+
const commit = item.origin === 'commit';
|
|
69
|
+
return staged || commit;
|
|
70
|
+
};
|
|
74
71
|
|
|
75
|
-
const firstDepRel = (rels, kind) =>
|
|
76
|
-
|
|
72
|
+
const firstDepRel = (rels, kind) => {
|
|
73
|
+
const match = (rel) => depFileMeta(rel)?.kind === kind;
|
|
74
|
+
return rels.find(match) ?? '';
|
|
75
|
+
};
|
|
77
76
|
|
|
78
77
|
const proposedNpmPlan = (top, item) => {
|
|
79
78
|
const change = depChange(item);
|
|
@@ -118,8 +117,25 @@ const extrasNeeds = (items, rev, extra) => {
|
|
|
118
117
|
return { live, scan: live || hasDepDiff, audit };
|
|
119
118
|
};
|
|
120
119
|
|
|
121
|
-
const
|
|
122
|
-
|
|
120
|
+
const NO_SIDES = { oldText: '', newText: '' };
|
|
121
|
+
|
|
122
|
+
const sidesReader = async (items, top, rev) => {
|
|
123
|
+
const sides = new Map();
|
|
124
|
+
const reads = [];
|
|
125
|
+
for (const group of collectGroups(items).values()) {
|
|
126
|
+
for (const name of [MANIFEST, LOCKFILE]) {
|
|
127
|
+
const rel = relOf(group.dir, name);
|
|
128
|
+
const key = `${group.origin}\0${rel}`;
|
|
129
|
+
const keep = (found) => sides.set(key, found);
|
|
130
|
+
reads.push(readDepSides(top, rev, group.origin, rel).then(keep));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
await Promise.all(reads);
|
|
134
|
+
return (origin, rel) => sides.get(`${origin}\0${rel}`) ?? NO_SIDES;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const foldLoaded = async (items, top, rev, extra) => {
|
|
138
|
+
const readSides = await sidesReader(items, top, rev);
|
|
123
139
|
const needs = extrasNeeds(items, rev, extra);
|
|
124
140
|
const collect = extra.collect !== false;
|
|
125
141
|
const scan = collect && needs.scan;
|
|
@@ -129,10 +145,10 @@ const foldLoaded = (items, top, rev, extra) => {
|
|
|
129
145
|
exportEntries = collectExportMeta(top);
|
|
130
146
|
}
|
|
131
147
|
if (collect && needs.audit && audit === undefined) {
|
|
132
|
-
audit = collectReport(top, 'audit');
|
|
148
|
+
audit = await collectReport(top, 'audit', extra.signal);
|
|
133
149
|
}
|
|
134
150
|
if (collect && needs.live && outdated === undefined) {
|
|
135
|
-
outdated = collectReport(top, 'outdated');
|
|
151
|
+
outdated = await collectReport(top, 'outdated', extra.signal);
|
|
136
152
|
}
|
|
137
153
|
audit ??= null;
|
|
138
154
|
outdated ??= null;
|
|
@@ -141,16 +157,16 @@ const foldLoaded = (items, top, rev, extra) => {
|
|
|
141
157
|
exportEntries,
|
|
142
158
|
});
|
|
143
159
|
if (!needs.live || !isPathInScope(MANIFEST, extra.paths)) return folded;
|
|
144
|
-
const
|
|
145
|
-
const lockText =
|
|
160
|
+
const texts = [worktreeText(top, MANIFEST), worktreeText(top, LOCKFILE)];
|
|
161
|
+
const [pkgText, lockText] = await Promise.all(texts);
|
|
146
162
|
const options = { lockText, usedNames, root: top, exportEntries };
|
|
147
163
|
return mergeProposedItems(folded, pkgText, outdated, audit, options);
|
|
148
164
|
};
|
|
149
165
|
|
|
150
|
-
const finishLoad = (snapshot, extra) => {
|
|
166
|
+
const finishLoad = async (snapshot, extra) => {
|
|
151
167
|
const { top, rev, parsed } = snapshot;
|
|
152
168
|
const options = { ...extra, collect: !extra.deferExtras };
|
|
153
|
-
const items = foldLoaded(parsed, top, rev, options);
|
|
169
|
+
const items = await foldLoaded(parsed, top, rev, options);
|
|
154
170
|
const pending = extra.deferExtras && extrasNeeds(parsed, rev, extra).audit;
|
|
155
171
|
return { ...snapshot, items, pending };
|
|
156
172
|
};
|
|
@@ -167,21 +183,23 @@ const loadExtras = async (snapshot, extra = {}) => {
|
|
|
167
183
|
const { signal } = extra;
|
|
168
184
|
const needs = extrasNeeds(parsed, rev, extra);
|
|
169
185
|
const known = extra.exportEntries;
|
|
186
|
+
let exportMeta = known;
|
|
187
|
+
if (known === undefined && needs.scan) {
|
|
188
|
+
exportMeta = later(() => collectExportMeta(top));
|
|
189
|
+
}
|
|
170
190
|
const [usedNames, audit, outdated, exportEntries] = await Promise.all([
|
|
171
191
|
needs.scan ? later(() => collectUsedNames(top)) : null,
|
|
172
192
|
reportTask(needs.audit, extra.auditMap, () =>
|
|
173
|
-
|
|
193
|
+
collectReport(top, 'audit', signal),
|
|
174
194
|
),
|
|
175
195
|
reportTask(needs.live, extra.outdatedMap, () =>
|
|
176
|
-
|
|
196
|
+
collectReport(top, 'outdated', signal),
|
|
177
197
|
),
|
|
178
|
-
|
|
179
|
-
? later(() => collectExportMeta(top))
|
|
180
|
-
: known,
|
|
198
|
+
exportMeta,
|
|
181
199
|
]);
|
|
182
200
|
if (signal && signal.aborted) throw abortError();
|
|
183
201
|
const found = { usedNames, auditMap: audit, outdatedMap: outdated };
|
|
184
|
-
const items = foldLoaded(parsed, top, rev, {
|
|
202
|
+
const items = await foldLoaded(parsed, top, rev, {
|
|
185
203
|
...extra,
|
|
186
204
|
...found,
|
|
187
205
|
paths: extra.paths ?? [],
|
|
@@ -207,23 +225,23 @@ const restoreFile = (top, saved) => {
|
|
|
207
225
|
else fs.rmSync(abs, { force: true });
|
|
208
226
|
};
|
|
209
227
|
|
|
210
|
-
const mergeLive = (top, rel, item, base, side) => {
|
|
211
|
-
const sides = readDepSides(top, null, item.origin, rel);
|
|
228
|
+
const mergeLive = async (top, rel, item, base, side) => {
|
|
229
|
+
const sides = await readDepSides(top, null, item.origin, rel);
|
|
212
230
|
const { kind } = depFileMeta(rel);
|
|
213
231
|
const change = depChange(item);
|
|
214
232
|
return mergeDepFile(base, sides.oldText, sides.newText, change, side, kind);
|
|
215
233
|
};
|
|
216
234
|
|
|
217
|
-
const applyDepIndex = (top, rel, item, side) => {
|
|
235
|
+
const applyDepIndex = async (top, rel, item, side) => {
|
|
218
236
|
if (!depFileMeta(rel)) return;
|
|
219
|
-
const indexed =
|
|
220
|
-
writeIndex(top, rel, mergeLive(top, rel, item, indexed, side));
|
|
237
|
+
const indexed = await indexedText(top, rel);
|
|
238
|
+
await writeIndex(top, rel, await mergeLive(top, rel, item, indexed, side));
|
|
221
239
|
};
|
|
222
240
|
|
|
223
|
-
const applyDepWorktree = (top, rel, item, side) => {
|
|
241
|
+
const applyDepWorktree = async (top, rel, item, side) => {
|
|
224
242
|
if (!depFileMeta(rel)) return;
|
|
225
|
-
const
|
|
226
|
-
writeFile(top, rel,
|
|
243
|
+
const live = await worktreeText(top, rel);
|
|
244
|
+
await writeFile(top, rel, await mergeLive(top, rel, item, live, side));
|
|
227
245
|
};
|
|
228
246
|
|
|
229
247
|
const npmError = (result, fail) => {
|
|
@@ -232,12 +250,12 @@ const npmError = (result, fail) => {
|
|
|
232
250
|
return new Error(oneLine(msg || fail, fail));
|
|
233
251
|
};
|
|
234
252
|
|
|
235
|
-
const stagePlan = (top, plan) => {
|
|
253
|
+
const stagePlan = async (top, plan) => {
|
|
236
254
|
const toAdd = [];
|
|
237
255
|
if (!plan.lockOnly && plan.manifest) toAdd.push(plan.manifest);
|
|
238
256
|
if (fs.existsSync(path.join(top, plan.lockPath))) toAdd.push(plan.lockPath);
|
|
239
257
|
if (!toAdd.length) return;
|
|
240
|
-
|
|
258
|
+
await gitOk(['add', '--', ...toAdd], top);
|
|
241
259
|
};
|
|
242
260
|
|
|
243
261
|
const startPlan = (top, item) => {
|
|
@@ -248,104 +266,115 @@ const startPlan = (top, item) => {
|
|
|
248
266
|
return { plan, owned };
|
|
249
267
|
};
|
|
250
268
|
|
|
251
|
-
const finishPlan = (top, plan, result, stage = stagePlan) => {
|
|
269
|
+
const finishPlan = async (top, plan, result, stage = stagePlan) => {
|
|
252
270
|
const failed = result && (result.error || result.status !== 0);
|
|
253
271
|
if (failed) throw npmError(result, plan.fail);
|
|
254
|
-
stage(top, plan);
|
|
272
|
+
await stage(top, plan);
|
|
255
273
|
};
|
|
256
274
|
|
|
257
275
|
const restoreOwned = (top, owned) => {
|
|
258
276
|
for (const saved of owned) restoreFile(top, saved);
|
|
259
277
|
};
|
|
260
278
|
|
|
261
|
-
const
|
|
262
|
-
const started = startPlan(top, item);
|
|
263
|
-
if (!started) return;
|
|
264
|
-
const { plan, owned } = started;
|
|
265
|
-
try {
|
|
266
|
-
if (plan.prepareManifest) applyDepWorktree(top, plan.manifest, item, 'new');
|
|
267
|
-
const install = extra.run ?? plan.runner ?? runNpm;
|
|
268
|
-
finishPlan(top, plan, install(plan.cwd, plan.npmArgs), extra.stage);
|
|
269
|
-
} catch (error) {
|
|
270
|
-
restoreOwned(top, owned);
|
|
271
|
-
throw error;
|
|
272
|
-
}
|
|
273
|
-
};
|
|
279
|
+
const unlocked = (write) => write();
|
|
274
280
|
|
|
275
|
-
|
|
281
|
+
// The install can run for minutes, so only the file and index steps around
|
|
282
|
+
// it go through the caller's write lock
|
|
283
|
+
const applyProposedUpdate = async (top, item, extra = {}) => {
|
|
276
284
|
const started = startPlan(top, item);
|
|
277
285
|
if (!started) return;
|
|
278
286
|
const { plan, owned } = started;
|
|
287
|
+
const locked = extra.locked ?? unlocked;
|
|
279
288
|
try {
|
|
280
|
-
if (plan.prepareManifest)
|
|
281
|
-
|
|
282
|
-
|
|
289
|
+
if (plan.prepareManifest) {
|
|
290
|
+
await locked(() => applyDepWorktree(top, plan.manifest, item, 'new'));
|
|
291
|
+
}
|
|
292
|
+
const install = extra.run ?? plan.runner ?? runNpm;
|
|
293
|
+
const result = await install(plan.cwd, plan.npmArgs);
|
|
294
|
+
await locked(() => finishPlan(top, plan, result, extra.stage));
|
|
283
295
|
} catch (error) {
|
|
284
296
|
restoreOwned(top, owned);
|
|
285
297
|
throw error;
|
|
286
298
|
}
|
|
287
299
|
};
|
|
288
300
|
|
|
289
|
-
const applyDepChange = (top, item, mode) => {
|
|
301
|
+
const applyDepChange = async (top, item, mode, extra) => {
|
|
290
302
|
const change = depChange(item);
|
|
291
303
|
if (change.propose) {
|
|
292
|
-
if (mode === 'add')
|
|
304
|
+
if (mode === 'add') {
|
|
305
|
+
await applyProposedUpdate(top, item, extra);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
293
308
|
if (!change.unused) return;
|
|
294
309
|
}
|
|
295
310
|
for (const rel of depPaths(item)) {
|
|
296
311
|
if (mode !== 'revert') {
|
|
297
|
-
applyDepIndex(top, rel, item, mode === 'add' ? 'new' : 'old');
|
|
312
|
+
await applyDepIndex(top, rel, item, mode === 'add' ? 'new' : 'old');
|
|
298
313
|
continue;
|
|
299
314
|
}
|
|
300
|
-
if (item.origin === 'staged') applyDepIndex(top, rel, item, 'old');
|
|
301
|
-
applyDepWorktree(top, rel, item, 'old');
|
|
315
|
+
if (item.origin === 'staged') await applyDepIndex(top, rel, item, 'old');
|
|
316
|
+
await applyDepWorktree(top, rel, item, 'old');
|
|
302
317
|
}
|
|
303
318
|
};
|
|
304
319
|
|
|
305
|
-
const addItem = (top, item) => {
|
|
306
|
-
if (!item.dep)
|
|
320
|
+
const addItem = async (top, item, extra) => {
|
|
321
|
+
if (!item.dep) {
|
|
322
|
+
await worktree.addItem(top, item);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
307
325
|
if (isStagedOrCommit(item)) return;
|
|
308
|
-
if (depChange(item))
|
|
326
|
+
if (depChange(item)) {
|
|
327
|
+
await applyDepChange(top, item, 'add', extra);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
309
330
|
const rels = depPaths(item);
|
|
310
|
-
if (rels.length)
|
|
331
|
+
if (rels.length) await gitOk(['add', '--', ...rels], top);
|
|
311
332
|
};
|
|
312
333
|
|
|
313
|
-
const
|
|
314
|
-
if (
|
|
315
|
-
|
|
334
|
+
const unstageItem = async (top, item) => {
|
|
335
|
+
if (!item.dep) {
|
|
336
|
+
await worktree.unstageItem(top, item);
|
|
337
|
+
return;
|
|
316
338
|
}
|
|
317
|
-
await applyProposedUpdateAsync(top, item);
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
const unstageItem = (top, item) => {
|
|
321
|
-
if (!item.dep) return void worktree.unstageItem(top, item);
|
|
322
339
|
if (item.origin !== 'staged') return;
|
|
323
|
-
if (depChange(item))
|
|
340
|
+
if (depChange(item)) {
|
|
341
|
+
await applyDepChange(top, item, 'unstage');
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
324
344
|
const rels = depPaths(item);
|
|
325
345
|
if (!rels.length) return;
|
|
326
|
-
|
|
346
|
+
await gitOk(['restore', '--staged', '--', ...rels], top);
|
|
327
347
|
};
|
|
328
348
|
|
|
329
|
-
const revertItem = (top, item) => {
|
|
349
|
+
const revertItem = async (top, item) => {
|
|
330
350
|
if (item.origin === 'commit') return;
|
|
331
|
-
if (!item.dep)
|
|
332
|
-
|
|
351
|
+
if (!item.dep) {
|
|
352
|
+
await worktree.revertItem(top, item);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (depChange(item)) {
|
|
356
|
+
await applyDepChange(top, item, 'revert');
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
333
359
|
const byPath = groupByPath(item.dep.items ?? []);
|
|
334
|
-
for (const rel of byPath.keys())
|
|
360
|
+
for (const rel of byPath.keys()) {
|
|
361
|
+
await revertOnePath(top, rel, byPath.get(rel));
|
|
362
|
+
}
|
|
335
363
|
};
|
|
336
364
|
|
|
337
|
-
const revertFile = (top, rel, items) => {
|
|
365
|
+
const revertFile = async (top, rel, items) => {
|
|
338
366
|
const rels = new Set([rel]);
|
|
339
367
|
for (const item of items) {
|
|
340
368
|
for (const file of depPaths(item)) rels.add(file);
|
|
341
369
|
}
|
|
342
370
|
for (const file of rels) {
|
|
343
371
|
const group = itemsForPath(file, items);
|
|
344
|
-
revertOnePath(top, file, group.length ? group : items);
|
|
372
|
+
await revertOnePath(top, file, group.length ? group : items);
|
|
345
373
|
}
|
|
346
374
|
};
|
|
347
375
|
|
|
348
376
|
module.exports = {
|
|
377
|
+
collectReport,
|
|
349
378
|
finishLoad,
|
|
350
379
|
loadExtras,
|
|
351
380
|
captureFile,
|
|
@@ -353,7 +382,6 @@ module.exports = {
|
|
|
353
382
|
proposedNpmPlan,
|
|
354
383
|
applyProposedUpdate,
|
|
355
384
|
addItem,
|
|
356
|
-
addItemAsync,
|
|
357
385
|
unstageItem,
|
|
358
386
|
revertItem,
|
|
359
387
|
revertFile,
|
package/lib/git/git.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
capabilitiesFor,
|
|
5
|
+
attachCapabilities,
|
|
6
|
+
} = require('../source/capabilities.js');
|
|
7
|
+
const worktree = require('./worktree.js');
|
|
8
|
+
const branches = require('./branches.js');
|
|
9
|
+
const gitDeps = require('./deps.js');
|
|
10
|
+
|
|
11
|
+
const load = async (cwd, paths = [], options = {}) => {
|
|
12
|
+
const snapshot = await worktree.readSnapshot(cwd, paths, options);
|
|
13
|
+
const deferExtras = options.deferExtras ?? true;
|
|
14
|
+
return gitDeps.finishLoad(snapshot, { ...options, paths, deferExtras });
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const REPO_METHODS = {
|
|
18
|
+
load,
|
|
19
|
+
loadExtras: gitDeps.loadExtras,
|
|
20
|
+
toplevel: worktree.toplevel,
|
|
21
|
+
resolveRev: worktree.resolveRev,
|
|
22
|
+
listFiles: worktree.listScopeFiles,
|
|
23
|
+
fileText: worktree.fileText,
|
|
24
|
+
add: gitDeps.addItem,
|
|
25
|
+
unstage: gitDeps.unstageItem,
|
|
26
|
+
revert: gitDeps.revertItem,
|
|
27
|
+
revertFile: gitDeps.revertFile,
|
|
28
|
+
edit: worktree.editItem,
|
|
29
|
+
writeFile: worktree.writeFile,
|
|
30
|
+
stagePath: worktree.stagePath,
|
|
31
|
+
commit: branches.commitChanges,
|
|
32
|
+
updateCommit: branches.updateCommit,
|
|
33
|
+
hasStaged: branches.hasStaged,
|
|
34
|
+
lastMessage: branches.lastMessage,
|
|
35
|
+
commitMessage: branches.commitMessage,
|
|
36
|
+
listBranches: branches.listBranches,
|
|
37
|
+
listCommits: branches.listCommits,
|
|
38
|
+
currentBranch: worktree.currentBranch,
|
|
39
|
+
checkout: branches.checkoutBranch,
|
|
40
|
+
createBranch: branches.createBranch,
|
|
41
|
+
rebase: branches.rebaseBranch,
|
|
42
|
+
drop: branches.dropBranch,
|
|
43
|
+
dropCommit: branches.dropCommit,
|
|
44
|
+
applyFixup: branches.applyFixup,
|
|
45
|
+
reword: branches.rewordCommit,
|
|
46
|
+
pull: branches.pullChanges,
|
|
47
|
+
push: branches.pushChanges,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const createGitRepo = () =>
|
|
51
|
+
attachCapabilities(REPO_METHODS, capabilitiesFor('local'));
|
|
52
|
+
|
|
53
|
+
const listed = createGitRepo();
|
|
54
|
+
const unlisted = Object.keys(REPO_METHODS).filter(
|
|
55
|
+
(name) => !Object.hasOwn(listed, name),
|
|
56
|
+
);
|
|
57
|
+
if (unlisted.length) {
|
|
58
|
+
throw new Error(`git methods without a capability: ${unlisted.join(', ')}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
...branches,
|
|
63
|
+
currentBranch: worktree.currentBranch,
|
|
64
|
+
editItem: worktree.editItem,
|
|
65
|
+
load,
|
|
66
|
+
loadExtras: gitDeps.loadExtras,
|
|
67
|
+
addItem: gitDeps.addItem,
|
|
68
|
+
unstageItem: gitDeps.unstageItem,
|
|
69
|
+
revertItem: gitDeps.revertItem,
|
|
70
|
+
createGitRepo,
|
|
71
|
+
};
|