reslop 0.2.3 → 0.2.5

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +8 -4
  3. package/bin/reslop.js +4 -2
  4. package/lib/api.js +2 -1
  5. package/lib/capabilities.js +0 -4
  6. package/lib/cli.js +19 -10
  7. package/lib/clipboard.js +45 -4
  8. package/lib/deps-diff.js +61 -118
  9. package/lib/deps.js +166 -235
  10. package/lib/detect.js +4 -2
  11. package/lib/diff/align.js +2 -1
  12. package/lib/diff/diff.js +6 -3
  13. package/lib/diff/display.js +2 -1
  14. package/lib/diff/inline.js +68 -2
  15. package/lib/diff/patch.js +2 -1
  16. package/lib/diff/unit.js +15 -30
  17. package/lib/editor.js +91 -13
  18. package/lib/files.js +54 -27
  19. package/lib/git-branches.js +107 -154
  20. package/lib/git-deps.js +78 -155
  21. package/lib/git-worktree.js +508 -0
  22. package/lib/git.js +23 -580
  23. package/lib/github.js +9 -8
  24. package/lib/gitlab.js +5 -4
  25. package/lib/highlight/bash.js +2 -1
  26. package/lib/highlight/core.js +11 -0
  27. package/lib/highlight/css.js +2 -1
  28. package/lib/highlight/csv.js +2 -1
  29. package/lib/highlight/dot.js +3 -12
  30. package/lib/highlight/html.js +2 -1
  31. package/lib/highlight/json.js +2 -1
  32. package/lib/highlight/log.js +5 -11
  33. package/lib/highlight/txt.js +3 -12
  34. package/lib/highlight.js +2 -1
  35. package/lib/keys.js +22 -12
  36. package/lib/manifest.js +4 -6
  37. package/lib/npm-commands.js +82 -24
  38. package/lib/remote.js +21 -20
  39. package/lib/render/branches.js +9 -27
  40. package/lib/render/chrome.js +109 -35
  41. package/lib/render/commits.js +216 -216
  42. package/lib/render/diff.js +160 -47
  43. package/lib/render/files.js +9 -26
  44. package/lib/render/frame.js +97 -118
  45. package/lib/render/notes.js +74 -26
  46. package/lib/render/npm.js +104 -30
  47. package/lib/render/present.js +2 -1
  48. package/lib/render/primitives.js +128 -52
  49. package/lib/render/render.js +2 -1
  50. package/lib/render/unit.js +17 -11
  51. package/lib/report-model.js +6 -4
  52. package/lib/report-parse.js +8 -14
  53. package/lib/report-render.js +38 -21
  54. package/lib/report-run.js +12 -11
  55. package/lib/report-test.js +45 -51
  56. package/lib/report-tools.js +44 -25
  57. package/lib/review-merge.js +102 -131
  58. package/lib/review.js +69 -78
  59. package/lib/select.js +52 -0
  60. package/lib/session/actions.js +72 -87
  61. package/lib/session/branches.js +18 -52
  62. package/lib/session/change-actions.js +175 -147
  63. package/lib/session/commit.js +65 -61
  64. package/lib/session/compose.js +152 -83
  65. package/lib/session/dispatch.js +53 -46
  66. package/lib/session/edit-click.js +57 -0
  67. package/lib/session/files-pane.js +30 -51
  68. package/lib/session/items.js +73 -90
  69. package/lib/session/lifecycle.js +37 -98
  70. package/lib/session/load.js +52 -57
  71. package/lib/session/navigation.js +105 -95
  72. package/lib/session/npm.js +205 -71
  73. package/lib/session/ops.js +200 -45
  74. package/lib/session/pointer.js +33 -8
  75. package/lib/session/progress.js +9 -6
  76. package/lib/session/review.js +35 -53
  77. package/lib/session/tasks.js +292 -0
  78. package/lib/session/terminal.js +17 -19
  79. package/lib/session/unit.js +3 -4
  80. package/lib/session/update.js +76 -96
  81. package/lib/session/view.js +45 -16
  82. package/lib/session/watch.js +47 -27
  83. package/lib/session.js +66 -97
  84. package/lib/source.js +6 -3
  85. package/lib/update.js +5 -13
  86. package/lib/utilities.js +25 -3
  87. package/lib/wrap.js +18 -1
  88. package/package.json +1 -1
  89. package/lib/session/todos.js +0 -293
package/lib/git-deps.js CHANGED
@@ -8,12 +8,14 @@ const { foldDepItems, mergeDepFile, depFileMeta } = deps;
8
8
  const { parseAuditReport, parseOutdatedReport } = deps;
9
9
  const { mergeProposedItems, MANIFEST, LOCKFILE } = deps;
10
10
  const { collectUsedNames, collectExportMeta } = deps;
11
- const { itemPath, isPathInScope } = require('./files.js');
11
+ const files = require('./files.js');
12
+ const { itemPath, isPathInScope } = files;
12
13
  const proc = require('./utilities.js');
13
14
  const { abortError, npmBin, npmOpts, runProc, runProcSync } = proc;
14
- const git = require('./git.js');
15
+ const git = require('./git-worktree.js');
15
16
  const { oneLine, requireOk, runGit } = git;
16
17
  const { gitText, worktreeText, readDepSides, revertOnePath } = git;
18
+ const { writeFile, writeIndex } = git;
17
19
 
18
20
  const AUDIT_MS = 15000;
19
21
  const INSTALL_MS = 120000;
@@ -30,12 +32,12 @@ const npmProcOptions = (cwd, extra = {}) => ({
30
32
  const runNpm = (cwd, args) =>
31
33
  runProcSync(npmBin(), args, npmProcOptions(cwd, { timeout: INSTALL_MS }));
32
34
 
33
- const runNpmAsync = (cwd, args, extra = {}) =>
34
- runProc(
35
- npmBin(),
36
- args,
37
- npmProcOptions(cwd, { timeout: INSTALL_MS, signal: extra.signal }),
38
- );
35
+ const runNpmAsync = (cwd, args, extra = {}) => {
36
+ const bin = npmBin();
37
+ const signal = extra.signal;
38
+ const options = npmProcOptions(cwd, { timeout: INSTALL_MS, signal });
39
+ return runProc(bin, args, options);
40
+ };
39
41
 
40
42
  const runNpmJson = (top, args, timeout) => {
41
43
  const result = runProcSync(npmBin(), args, npmProcOptions(top, { timeout }));
@@ -104,18 +106,6 @@ const hasProposed = (item) => {
104
106
  return !!(change && change.propose);
105
107
  };
106
108
 
107
- const relatedFiles = (items) => {
108
- let size = 0;
109
- for (const item of items) size += depPaths(item).length;
110
- const files = new Array(size);
111
- let i = 0;
112
- for (const item of items) {
113
- const paths = depPaths(item);
114
- for (let j = 0; j < paths.length; j++) files[i++] = paths[j];
115
- }
116
- return files;
117
- };
118
-
119
109
  const itemsForPath = (rel, items) => {
120
110
  const group = [];
121
111
  for (const item of items) {
@@ -137,73 +127,61 @@ const hasDepFile = (items) => {
137
127
  return false;
138
128
  };
139
129
 
140
- const collectAudit = (top) => {
141
- const lock = path.join(top, LOCKFILE);
142
- if (!fs.existsSync(lock)) return null;
143
- const args = ['audit', '--json', '--package-lock-only'];
144
- const text = runNpmJson(top, args, AUDIT_MS);
145
- return parseAuditReport(text);
146
- };
147
-
148
- const collectOutdated = (top) => {
149
- const manifest = path.join(top, MANIFEST);
150
- if (!fs.existsSync(manifest)) return null;
151
- const args = ['outdated', '--json', '--long'];
152
- const text = runNpmJson(top, args, AUDIT_MS);
153
- return parseOutdatedReport(text);
130
+ const NPM_REPORT = {
131
+ audit: {
132
+ file: LOCKFILE,
133
+ args: ['audit', '--json', '--package-lock-only'],
134
+ parse: parseAuditReport,
135
+ },
136
+ outdated: {
137
+ file: MANIFEST,
138
+ args: ['outdated', '--json', '--long'],
139
+ parse: parseOutdatedReport,
140
+ },
154
141
  };
155
142
 
156
- const collectAuditAsync = async (top, extra, signal) => {
157
- if (extra.auditMap !== undefined) return extra.auditMap;
158
- const lock = path.join(top, LOCKFILE);
159
- if (!fs.existsSync(lock)) return null;
160
- const args = ['audit', '--json', '--package-lock-only'];
161
- const text = await runNpmJsonAsync(top, args, AUDIT_MS, signal);
162
- return parseAuditReport(text);
143
+ const collectReport = (top, kind) => {
144
+ const { file, args, parse } = NPM_REPORT[kind];
145
+ if (!fs.existsSync(path.join(top, file))) return null;
146
+ return parse(runNpmJson(top, args, AUDIT_MS));
163
147
  };
164
148
 
165
- const collectOutdatedAsync = async (top, extra, signal) => {
166
- if (extra.outdatedMap !== undefined) return extra.outdatedMap;
167
- const manifest = path.join(top, MANIFEST);
168
- if (!fs.existsSync(manifest)) return null;
169
- const args = ['outdated', '--json', '--long'];
170
- const text = await runNpmJsonAsync(top, args, AUDIT_MS, signal);
171
- return parseOutdatedReport(text);
149
+ const collectReportAsync = async (top, kind, signal) => {
150
+ const { file, args, parse } = NPM_REPORT[kind];
151
+ if (!fs.existsSync(path.join(top, file))) return null;
152
+ return parse(await runNpmJsonAsync(top, args, AUDIT_MS, signal));
172
153
  };
173
154
 
174
- const pickNpmMap = (override, collect, top) => {
175
- if (override !== undefined) return override;
176
- return collect(top);
155
+ const extrasNeeds = (items, rev, extra) => {
156
+ const live = extra.audit === true && !rev;
157
+ const hasDepDiff = hasDepFile(items);
158
+ const audit = live || (Boolean(extra.audit) && hasDepDiff);
159
+ return { live, scan: live || hasDepDiff, audit };
177
160
  };
178
161
 
179
162
  const foldLoaded = (items, top, rev, extra = {}) => {
180
163
  const readSides = (origin, rel) => readDepSides(top, rev, origin, rel);
181
- const live = extra.audit === true && !rev;
182
- const hasDepDiff = hasDepFile(items);
164
+ const needs = extrasNeeds(items, rev, extra);
183
165
  const collect = extra.collect !== false;
184
- let usedNames = extra.usedNames ?? null;
185
- let audit = extra.auditMap;
186
- let outdated = extra.outdatedMap;
187
- let exportEntries = extra.exportEntries;
188
- if (collect && usedNames === null && (hasDepDiff || live)) {
189
- usedNames = collectUsedNames(top);
190
- }
191
- if (collect && exportEntries === undefined && (hasDepDiff || live)) {
166
+ const scan = collect && needs.scan;
167
+ const usedNames = extra.usedNames ?? (scan ? collectUsedNames(top) : null);
168
+ let { exportEntries, auditMap: audit, outdatedMap: outdated } = extra;
169
+ if (scan && exportEntries === undefined) {
192
170
  exportEntries = collectExportMeta(top);
193
171
  }
194
- if (collect && live) {
195
- audit = pickNpmMap(extra.auditMap, collectAudit, top);
196
- outdated = pickNpmMap(extra.outdatedMap, collectOutdated, top);
197
- } else if (collect && extra.audit && hasDepDiff) {
198
- audit = pickNpmMap(extra.auditMap, collectAudit, top);
172
+ if (collect && needs.audit && audit === undefined) {
173
+ audit = collectReport(top, 'audit');
174
+ }
175
+ if (collect && needs.live && outdated === undefined) {
176
+ outdated = collectReport(top, 'outdated');
199
177
  }
200
- if (audit === undefined) audit = null;
201
- if (outdated === undefined) outdated = null;
178
+ audit ??= null;
179
+ outdated ??= null;
202
180
  const folded = foldDepItems(items, readSides, usedNames, audit, outdated, {
203
181
  root: top,
204
182
  exportEntries,
205
183
  });
206
- if (!live) return folded;
184
+ if (!needs.live) return folded;
207
185
  if (!isPathInScope(MANIFEST, extra.paths ?? [])) return folded;
208
186
  const pkgText = worktreeText(top, MANIFEST);
209
187
  const lockText = worktreeText(top, LOCKFILE);
@@ -211,12 +189,8 @@ const foldLoaded = (items, top, rev, extra = {}) => {
211
189
  return mergeProposedItems(folded, pkgText, outdated, audit, options);
212
190
  };
213
191
 
214
- const needsExtras = (parsed, rev, extra) => {
215
- const live = extra.audit === true && !rev;
216
- if (live) return true;
217
- if (!extra.audit) return false;
218
- return hasDepFile(parsed);
219
- };
192
+ const needsExtras = (parsed, rev, extra) =>
193
+ extrasNeeds(parsed, rev, extra).audit;
220
194
 
221
195
  const finishLoad = (snapshot, extra) => {
222
196
  const options = { ...extra, paths: extra.paths ?? [] };
@@ -229,42 +203,29 @@ const finishLoad = (snapshot, extra) => {
229
203
  return { ...snapshot, items, pending };
230
204
  };
231
205
 
232
- const extrasMetaOf = (parsed, rev, extra, top, signal) => {
233
- const live = extra.audit === true && !rev;
234
- const hasDepDiff = hasDepFile(parsed);
235
- const needScan = live || hasDepDiff;
236
- let usedNames = Promise.resolve(null);
237
- if (needScan) {
238
- usedNames = Promise.resolve().then(() => collectUsedNames(top));
239
- }
240
- let exportEntries = Promise.resolve(undefined);
241
- if (extra.exportEntries !== undefined) {
242
- exportEntries = Promise.resolve(extra.exportEntries);
243
- } else if (needScan) {
244
- exportEntries = Promise.resolve().then(() => collectExportMeta(top));
245
- }
246
- let audit = Promise.resolve(null);
247
- if (live || (extra.audit && hasDepDiff)) {
248
- audit = collectAuditAsync(top, extra, signal);
249
- }
250
- let outdated = Promise.resolve(null);
251
- if (live) {
252
- outdated = collectOutdatedAsync(top, extra, signal);
253
- }
254
- return { usedNames, audit, outdated, exportEntries };
206
+ const later = (fn) => Promise.resolve().then(fn);
207
+
208
+ const reportTask = (wanted, known, collect) => {
209
+ if (!wanted) return null;
210
+ return known === undefined ? collect() : known;
255
211
  };
256
212
 
257
213
  const loadExtras = async (snapshot, extra = {}) => {
258
- const top = snapshot.top;
259
- const rev = snapshot.rev;
260
- const parsed = snapshot.parsed;
261
- const signal = extra.signal;
262
- const tasks = extrasMetaOf(parsed, rev, extra, top, signal);
214
+ const { top, rev, parsed } = snapshot;
215
+ const { signal } = extra;
216
+ const needs = extrasNeeds(parsed, rev, extra);
217
+ const known = extra.exportEntries;
263
218
  const [usedNames, audit, outdated, exportEntries] = await Promise.all([
264
- tasks.usedNames,
265
- tasks.audit,
266
- tasks.outdated,
267
- tasks.exportEntries,
219
+ needs.scan ? later(() => collectUsedNames(top)) : null,
220
+ reportTask(needs.audit, extra.auditMap, () =>
221
+ collectReportAsync(top, 'audit', signal),
222
+ ),
223
+ reportTask(needs.live, extra.outdatedMap, () =>
224
+ collectReportAsync(top, 'outdated', signal),
225
+ ),
226
+ known === undefined && needs.scan
227
+ ? later(() => collectExportMeta(top))
228
+ : known,
268
229
  ]);
269
230
  if (signal && signal.aborted) throw abortError();
270
231
  const folded = foldLoaded(parsed, top, rev, {
@@ -287,12 +248,6 @@ const loadExtras = async (snapshot, extra = {}) => {
287
248
  };
288
249
  };
289
250
 
290
- const writeWorktreeFile = (top, rel, text) => {
291
- const abs = path.join(top, rel);
292
- fs.mkdirSync(path.dirname(abs), { recursive: true });
293
- fs.writeFileSync(abs, text);
294
- };
295
-
296
251
  const captureFile = (top, rel) => {
297
252
  const abs = path.join(top, rel);
298
253
  try {
@@ -327,16 +282,6 @@ const restoreFiles = (top, owned) => {
327
282
  for (const saved of owned) restoreFile(top, saved);
328
283
  };
329
284
 
330
- const writeIndexFile = (top, rel, text) => {
331
- const saved = captureFile(top, rel);
332
- writeWorktreeFile(top, rel, text);
333
- try {
334
- requireOk(runGit(['add', '--', rel], top));
335
- } finally {
336
- restoreFile(top, saved);
337
- }
338
- };
339
-
340
285
  const mergeDepLive = (top, rel, change, origin, base, side, kind) => {
341
286
  const sides = readDepSides(top, null, origin, rel);
342
287
  return mergeDepFile(base, sides.oldText, sides.newText, change, side, kind);
@@ -347,7 +292,7 @@ const applyDepIndex = (top, rel, change, origin, side) => {
347
292
  if (!meta) return;
348
293
  const indexed = gitText(top, `:${rel}`) || gitText(top, `HEAD:${rel}`);
349
294
  const text = mergeDepLive(top, rel, change, origin, indexed, side, meta.kind);
350
- writeIndexFile(top, rel, text);
295
+ writeIndex(top, rel, text);
351
296
  };
352
297
 
353
298
  const applyDepWorktree = (top, rel, change, origin) => {
@@ -363,7 +308,7 @@ const applyDepWorktree = (top, rel, change, origin) => {
363
308
  'old',
364
309
  meta.kind,
365
310
  );
366
- writeWorktreeFile(top, rel, text);
311
+ writeFile(top, rel, text);
367
312
  };
368
313
 
369
314
  const ownedRelsOf = (plan) => {
@@ -399,7 +344,7 @@ const applyPreparation = (top, item, plan) => {
399
344
  'new',
400
345
  'manifest',
401
346
  );
402
- writeWorktreeFile(top, plan.manifest, text);
347
+ writeFile(top, plan.manifest, text);
403
348
  };
404
349
 
405
350
  const stagePlan = (top, plan) => {
@@ -418,26 +363,14 @@ const finishPlan = (top, plan, result, stage) => {
418
363
  stageFn(top, plan);
419
364
  };
420
365
 
421
- const syncInstaller = (plan, extra) => {
422
- if (extra.run) return extra.run;
423
- if (plan.runner) return plan.runner;
424
- return (dir) => runNpm(dir, plan.npmArgs);
425
- };
426
-
427
- const asyncInstaller = (plan, extra) => {
428
- if (extra.run) return extra.run;
429
- if (plan.runner) return (dir) => Promise.resolve(plan.runner(dir));
430
- return (dir) => runNpmAsync(dir, plan.npmArgs);
431
- };
432
-
433
366
  const applyProposedUpdate = (top, item, extra = {}) => {
434
367
  const plan = proposedNpmPlan(top, item);
435
368
  if (!plan) return;
436
369
  const owned = captureFiles(top, ownedRelsOf(plan));
437
370
  try {
438
371
  applyPreparation(top, item, plan);
439
- const install = syncInstaller(plan, extra);
440
- const result = install(plan.cwd);
372
+ const install = extra.run ?? plan.runner ?? runNpm;
373
+ const result = install(plan.cwd, plan.npmArgs);
441
374
  finishPlan(top, plan, result, extra.stage);
442
375
  } catch (error) {
443
376
  restoreFiles(top, owned);
@@ -451,8 +384,8 @@ const applyProposedUpdateAsync = async (top, item, extra = {}) => {
451
384
  const owned = captureFiles(top, ownedRelsOf(plan));
452
385
  try {
453
386
  applyPreparation(top, item, plan);
454
- const run = asyncInstaller(plan, extra);
455
- const result = await run(plan.cwd);
387
+ const run = extra.run ?? plan.runner ?? runNpmAsync;
388
+ const result = await run(plan.cwd, plan.npmArgs);
456
389
  finishPlan(top, plan, result, extra.stage);
457
390
  } catch (error) {
458
391
  restoreFiles(top, owned);
@@ -525,7 +458,9 @@ const revertDepItem = (top, item) => {
525
458
 
526
459
  const revertFile = (top, rel, items) => {
527
460
  const paths = new Set([rel]);
528
- for (const file of relatedFiles(items)) paths.add(file);
461
+ for (const item of items) {
462
+ for (const file of depPaths(item)) paths.add(file);
463
+ }
529
464
  for (const file of paths) {
530
465
  const group = itemsForPath(file, items);
531
466
  const targets = group.length ? group : items;
@@ -533,30 +468,18 @@ const revertFile = (top, rel, items) => {
533
468
  }
534
469
  };
535
470
 
536
- const handles = (item) => !!(item && item.dep);
537
-
538
471
  module.exports = {
539
- hasProposed,
540
- handles,
541
- relatedFiles,
542
472
  itemsForPath,
543
473
  finishLoad,
544
474
  loadExtras,
545
475
  captureFile,
546
476
  restoreFile,
547
477
  applyProposedUpdate,
548
- applyProposedUpdateAsync,
549
- applyDepChange,
550
478
  addItem,
551
479
  addItemAsync,
552
480
  unstageItem,
553
481
  revertDepItem,
554
482
  revertFile,
555
- AUDIT_MS,
556
483
  INSTALL_MS,
557
- runNpm,
558
- runNpmAsync,
559
- runNpmJson,
560
- runNpmJsonAsync,
561
484
  proposedNpmPlan,
562
485
  };