reslop 0.2.1 → 0.2.4
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 +19 -4
- package/bin/reslop.js +4 -2
- package/lib/ansi.js +120 -4
- package/lib/api.js +2 -1
- package/lib/cli.js +21 -9
- package/lib/clipboard.js +6 -3
- package/lib/deps-diff.js +61 -115
- package/lib/deps.js +57 -54
- package/lib/detect.js +4 -2
- package/lib/diff/align.js +2 -1
- package/lib/diff/diff.js +6 -3
- package/lib/diff/display.js +2 -1
- package/lib/diff/inline.js +2 -1
- package/lib/diff/patch.js +2 -1
- package/lib/diff/unit.js +4 -2
- package/lib/editor.js +22 -4
- package/lib/files.js +38 -12
- package/lib/git-branches.js +150 -165
- package/lib/git-deps.js +20 -60
- package/lib/git-worktree.js +508 -0
- package/lib/git.js +23 -580
- package/lib/github.js +2 -1
- package/lib/gitlab.js +2 -1
- package/lib/highlight/bash.js +2 -1
- package/lib/highlight/css.js +2 -1
- package/lib/highlight/csv.js +2 -1
- package/lib/highlight/dot.js +2 -1
- package/lib/highlight/html.js +2 -1
- package/lib/highlight/json.js +2 -1
- package/lib/highlight/log.js +2 -1
- package/lib/highlight/txt.js +2 -1
- package/lib/highlight.js +2 -1
- package/lib/keys.js +12 -6
- package/lib/manifest.js +4 -2
- package/lib/npm-commands.js +592 -0
- package/lib/remote.js +20 -13
- package/lib/render/branches.js +25 -41
- package/lib/render/chrome.js +163 -128
- package/lib/render/commits.js +727 -65
- package/lib/render/diff.js +130 -47
- package/lib/render/files.js +4 -12
- package/lib/render/frame.js +41 -13
- package/lib/render/notes.js +4 -11
- package/lib/render/npm.js +216 -0
- package/lib/render/present.js +2 -1
- package/lib/render/primitives.js +34 -50
- package/lib/render/render.js +2 -1
- package/lib/render/unit.js +14 -11
- package/lib/report-model.js +129 -0
- package/lib/report-parse.js +89 -0
- package/lib/report-render.js +104 -0
- package/lib/report-run.js +166 -0
- package/lib/report-test.js +507 -0
- package/lib/report-tools.js +337 -0
- package/lib/review-merge.js +2 -1
- package/lib/review.js +6 -2
- package/lib/session/actions.js +77 -44
- package/lib/session/branches.js +13 -49
- package/lib/session/change-actions.js +2 -1
- package/lib/session/commit.js +113 -53
- package/lib/session/compose.js +83 -14
- package/lib/session/dispatch.js +42 -22
- package/lib/session/files-pane.js +16 -21
- package/lib/session/items.js +6 -3
- package/lib/session/lifecycle.js +12 -5
- package/lib/session/load.js +4 -2
- package/lib/session/navigation.js +2 -0
- package/lib/session/npm.js +388 -0
- package/lib/session/ops.js +15 -1
- package/lib/session/pointer.js +30 -3
- package/lib/session/review.js +2 -1
- package/lib/session/terminal.js +4 -2
- package/lib/session/update.js +2 -1
- package/lib/session/view.js +41 -4
- package/lib/session.js +44 -22
- package/lib/source.js +6 -3
- package/lib/update.js +5 -9
- package/lib/utilities.js +4 -2
- package/lib/wrap.js +3 -1
- package/package.json +6 -6
package/lib/deps.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
5
|
+
const metautil = require('metautil');
|
|
6
|
+
const { jsonParse, isHashObject } = metautil;
|
|
7
|
+
const files = require('./files.js');
|
|
8
|
+
const { itemPath } = files;
|
|
7
9
|
const manifest = require('./manifest.js');
|
|
8
10
|
const { MANIFEST, LOCKFILE, SECTION_KEYS, depFileMeta, relOf } = manifest;
|
|
9
11
|
const { listFilePath, readSections, lockEntries, lockPackageCount } = manifest;
|
|
@@ -73,6 +75,39 @@ const pkgHasExportEntry = (pkg) => {
|
|
|
73
75
|
return Object.keys(exported).length > 0;
|
|
74
76
|
};
|
|
75
77
|
|
|
78
|
+
const isRuntimeFile = (value) => {
|
|
79
|
+
if (typeof value !== 'string' || !value) return false;
|
|
80
|
+
return !value.endsWith('.d.ts');
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const hasRuntimeExport = (value) => {
|
|
84
|
+
if (isRuntimeFile(value)) return true;
|
|
85
|
+
if (Array.isArray(value)) return value.some(hasRuntimeExport);
|
|
86
|
+
if (!value || typeof value !== 'object') return false;
|
|
87
|
+
const keys = Object.keys(value);
|
|
88
|
+
for (const key of keys) {
|
|
89
|
+
if (key === 'types' || key === 'typings') continue;
|
|
90
|
+
if (hasRuntimeExport(value[key])) return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const pkgHasBin = (pkg) => {
|
|
96
|
+
if (!pkg || typeof pkg !== 'object') return false;
|
|
97
|
+
const bin = pkg.bin;
|
|
98
|
+
if (typeof bin === 'string') return !!bin;
|
|
99
|
+
if (!bin || typeof bin !== 'object' || Array.isArray(bin)) return false;
|
|
100
|
+
return Object.keys(bin).length > 0;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const pkgExportsLibrary = (pkg) => {
|
|
104
|
+
if (!pkg || typeof pkg !== 'object') return false;
|
|
105
|
+
if (pkgHasBin(pkg)) return false;
|
|
106
|
+
if (isRuntimeFile(pkg.main)) return true;
|
|
107
|
+
if (isRuntimeFile(pkg.module)) return true;
|
|
108
|
+
return hasRuntimeExport(pkg.exports);
|
|
109
|
+
};
|
|
110
|
+
|
|
76
111
|
const SKIP_WALK = ['node_modules', '.git', '.review', 'coverage', 'dist'];
|
|
77
112
|
|
|
78
113
|
const collectUsedFromManifest = (used, rel) => {
|
|
@@ -156,8 +191,8 @@ const readDepPkg = (root, pkgDir, name) => {
|
|
|
156
191
|
|
|
157
192
|
const depHasExportEntry = (root, pkgDir, name) => {
|
|
158
193
|
const pkg = readDepPkg(root, pkgDir, name);
|
|
159
|
-
if (!pkg) return
|
|
160
|
-
return
|
|
194
|
+
if (!pkg) return false;
|
|
195
|
+
return pkgExportsLibrary(pkg);
|
|
161
196
|
};
|
|
162
197
|
|
|
163
198
|
const collectExportEntries = (root, pkgDir, names) => {
|
|
@@ -180,14 +215,7 @@ const declaredDepNames = (root, pkgDir) => {
|
|
|
180
215
|
return [];
|
|
181
216
|
}
|
|
182
217
|
const maps = readSections(jsonParse(text));
|
|
183
|
-
|
|
184
|
-
for (const key of SECTION_KEYS) size += maps[key].size;
|
|
185
|
-
const names = new Array(size);
|
|
186
|
-
let i = 0;
|
|
187
|
-
for (const key of SECTION_KEYS) {
|
|
188
|
-
for (const name of maps[key].keys()) names[i++] = name;
|
|
189
|
-
}
|
|
190
|
-
return names;
|
|
218
|
+
return SECTION_KEYS.flatMap((key) => [...maps[key].keys()]);
|
|
191
219
|
};
|
|
192
220
|
|
|
193
221
|
const collectExportMeta = (root, extra = {}) => {
|
|
@@ -342,16 +370,7 @@ const markChanges = (changes, report, kind) => {
|
|
|
342
370
|
});
|
|
343
371
|
};
|
|
344
372
|
|
|
345
|
-
const
|
|
346
|
-
markChanges(changes, audit, 'audit');
|
|
347
|
-
|
|
348
|
-
const markOutdatedChanges = (changes, outdated) =>
|
|
349
|
-
markChanges(changes, outdated, 'outdated');
|
|
350
|
-
|
|
351
|
-
const depExports = (exportEntries, name) => {
|
|
352
|
-
if (!exportEntries || !exportEntries.has(name)) return true;
|
|
353
|
-
return exportEntries.get(name) === true;
|
|
354
|
-
};
|
|
373
|
+
const depExports = (exportEntries, name) => exportEntries?.get(name) === true;
|
|
355
374
|
|
|
356
375
|
const isAddedDep = (change) => {
|
|
357
376
|
if (change.action !== 'added') return false;
|
|
@@ -389,23 +408,12 @@ const unusedDeclaredNames = (pkg, used) => {
|
|
|
389
408
|
};
|
|
390
409
|
|
|
391
410
|
const markUnusedChanges = (changes, used, pkg, exportEntries) => {
|
|
392
|
-
if (!pkgHasExportEntry(pkg)) return changes;
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
if (!isAddedDep(change) || used.has(change.name)) {
|
|
399
|
-
marked[i] = change;
|
|
400
|
-
continue;
|
|
401
|
-
}
|
|
402
|
-
if (!depExports(exportEntries, change.name)) {
|
|
403
|
-
marked[i] = change;
|
|
404
|
-
continue;
|
|
405
|
-
}
|
|
406
|
-
marked[i] = { ...change, unused: true, propose: true };
|
|
407
|
-
}
|
|
408
|
-
return marked;
|
|
411
|
+
if (!pkgHasExportEntry(pkg) || !used?.size) return changes;
|
|
412
|
+
return changes.map((change) => {
|
|
413
|
+
if (!isAddedDep(change) || used.has(change.name)) return change;
|
|
414
|
+
if (!depExports(exportEntries, change.name)) return change;
|
|
415
|
+
return { ...change, unused: true, propose: true };
|
|
416
|
+
});
|
|
409
417
|
};
|
|
410
418
|
|
|
411
419
|
const unusedDeclaredChanges = (pkg, used, exportEntries) => {
|
|
@@ -511,18 +519,16 @@ const loadExportEntries = (names, extra = {}, pkgDir) => {
|
|
|
511
519
|
if (!missing.length) return supplied ?? new Map();
|
|
512
520
|
const found = collectExportEntries(extra.root, dir, missing);
|
|
513
521
|
if (!supplied) return found;
|
|
514
|
-
|
|
515
|
-
for (const name of found.keys()) merged.set(name, found.get(name));
|
|
516
|
-
return merged;
|
|
522
|
+
return new Map([...supplied, ...found]);
|
|
517
523
|
};
|
|
518
524
|
|
|
519
525
|
const groupedSynthetics = (synthetic) => {
|
|
520
526
|
const byPath = new Map();
|
|
521
|
-
for (const key of synthetic
|
|
527
|
+
for (const [key, items] of synthetic) {
|
|
522
528
|
const dir = key.slice(key.indexOf('\0') + 1);
|
|
523
529
|
const rel = relOf(dir, MANIFEST);
|
|
524
530
|
const list = byPath.get(rel) ?? [];
|
|
525
|
-
for (const item of
|
|
531
|
+
for (const item of items) list.push(item);
|
|
526
532
|
byPath.set(rel, list);
|
|
527
533
|
}
|
|
528
534
|
return byPath;
|
|
@@ -530,12 +536,10 @@ const groupedSynthetics = (synthetic) => {
|
|
|
530
536
|
|
|
531
537
|
const spliceAtSlots = (out, synthetic, slot) => {
|
|
532
538
|
const byPath = groupedSynthetics(synthetic);
|
|
533
|
-
const inserts =
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
inserts[i++] = { at, items: byPath.get(rel) };
|
|
538
|
-
}
|
|
539
|
+
const inserts = [...byPath].map(([rel, items]) => ({
|
|
540
|
+
at: slot.get(rel) ?? out.length,
|
|
541
|
+
items,
|
|
542
|
+
}));
|
|
539
543
|
inserts.sort((left, right) => right.at - left.at);
|
|
540
544
|
for (const insert of inserts) {
|
|
541
545
|
out.splice(insert.at, 0, ...insert.items);
|
|
@@ -570,8 +574,7 @@ const foldDepItems = (
|
|
|
570
574
|
const folded = new Set();
|
|
571
575
|
const synthetic = new Map();
|
|
572
576
|
const leftoverNames = new Map();
|
|
573
|
-
for (const key of groups
|
|
574
|
-
const group = groups.get(key);
|
|
577
|
+
for (const [key, group] of groups) {
|
|
575
578
|
const summary = summarizeGroup(group, readSides);
|
|
576
579
|
const unusedNames = unusedChangeNames(summary.changes, usedNames);
|
|
577
580
|
const exportEntries = loadExportEntries(unusedNames, extra, group.dir);
|
|
@@ -581,8 +584,8 @@ const foldDepItems = (
|
|
|
581
584
|
summary.pkg,
|
|
582
585
|
exportEntries,
|
|
583
586
|
);
|
|
584
|
-
const audited =
|
|
585
|
-
const changes =
|
|
587
|
+
const audited = markChanges(unused, audit, 'audit');
|
|
588
|
+
const changes = markChanges(audited, outdated, 'outdated');
|
|
586
589
|
const next = { ...summary, changes };
|
|
587
590
|
leftoverNames.set(key, foldedDepNames(next.changes));
|
|
588
591
|
const taken = takeManifestItems(next);
|
package/lib/detect.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const path = require('node:path');
|
|
4
|
-
const
|
|
4
|
+
const metautil = require('metautil');
|
|
5
|
+
const { fileExt } = metautil;
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
+
const highlight = require('./highlight.js');
|
|
8
|
+
const { mapLang } = highlight;
|
|
7
9
|
|
|
8
10
|
const PROSE_BASENAMES = [
|
|
9
11
|
'license',
|
package/lib/diff/align.js
CHANGED
package/lib/diff/diff.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
3
|
+
const inline = require('./inline.js');
|
|
4
|
+
const { diffChars } = inline;
|
|
5
|
+
const align = require('./align.js');
|
|
6
|
+
const { isCtxType, pairIndices, attachInline } = align;
|
|
7
|
+
const parse = require('./parse.js');
|
|
8
|
+
const { parseDiff } = parse;
|
|
6
9
|
const patch = require('./patch.js');
|
|
7
10
|
const { splitHunk, flattenBlock, countSides, formatPatch } = patch;
|
|
8
11
|
const display = require('./display.js');
|
package/lib/diff/display.js
CHANGED
package/lib/diff/inline.js
CHANGED
package/lib/diff/patch.js
CHANGED
package/lib/diff/unit.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
3
|
+
const align = require('./align.js');
|
|
4
|
+
const { attachInline } = align;
|
|
5
|
+
const files = require('../files.js');
|
|
6
|
+
const { listPath } = files;
|
|
5
7
|
|
|
6
8
|
const splitEditor = (text) => {
|
|
7
9
|
const lines = `${text}`.split('\n');
|
package/lib/editor.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
3
|
+
const ansi = require('./ansi.js');
|
|
4
|
+
const { graphemes, visibleWidth } = ansi;
|
|
5
|
+
const wrap = require('./wrap.js');
|
|
6
|
+
const { wrapMove } = wrap;
|
|
5
7
|
|
|
6
8
|
const UNDO_CAP = 100;
|
|
7
9
|
|
|
@@ -59,6 +61,7 @@ class Editor {
|
|
|
59
61
|
this.undo = [];
|
|
60
62
|
this.redo = [];
|
|
61
63
|
this.wantCol = null;
|
|
64
|
+
this.scrollCol = 0;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
snapshot() {
|
|
@@ -132,6 +135,19 @@ class Editor {
|
|
|
132
135
|
return { line, col };
|
|
133
136
|
}
|
|
134
137
|
|
|
138
|
+
visibleLineCol() {
|
|
139
|
+
const { col, line } = this.linePos();
|
|
140
|
+
const row = this.text.split('\n')[line] ?? '';
|
|
141
|
+
return visibleWidth(row.slice(0, col));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
reveal(width) {
|
|
145
|
+
const col = this.visibleLineCol();
|
|
146
|
+
const room = Math.max(1, width);
|
|
147
|
+
if (col < this.scrollCol) this.scrollCol = col;
|
|
148
|
+
if (col >= this.scrollCol + room) this.scrollCol = col - room + 1;
|
|
149
|
+
}
|
|
150
|
+
|
|
135
151
|
moveLine(delta, width) {
|
|
136
152
|
if (width !== undefined) {
|
|
137
153
|
const col = this.wantCol;
|
|
@@ -142,8 +158,10 @@ class Editor {
|
|
|
142
158
|
}
|
|
143
159
|
const rows = this.text.split('\n');
|
|
144
160
|
const { line, col } = this.linePos();
|
|
145
|
-
|
|
146
|
-
if (next < 0
|
|
161
|
+
let next = line + delta;
|
|
162
|
+
if (next < 0) next = 0;
|
|
163
|
+
if (next >= rows.length) next = rows.length - 1;
|
|
164
|
+
if (next === line) return;
|
|
147
165
|
const row = rows[next];
|
|
148
166
|
const c = Math.min(col, row.length);
|
|
149
167
|
let cursor = 0;
|
package/lib/files.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const manifest = require('./manifest.js');
|
|
4
|
+
const { listFilePath } = manifest;
|
|
4
5
|
|
|
5
6
|
const REVIEW_DIR = '.review';
|
|
6
7
|
const ORIGIN_ORDER = ['staged', 'unstaged', 'untracked', 'commit', 'pr'];
|
|
7
8
|
const TODO_FILE = 'TODOs';
|
|
8
|
-
const REPO_TODOS_LABEL = '
|
|
9
|
+
const REPO_TODOS_LABEL = 'Project Backlog and Issues';
|
|
9
10
|
const TOTAL_LABEL = 'total';
|
|
10
11
|
|
|
11
12
|
const isPathInScope = (rel, paths) => {
|
|
@@ -73,23 +74,47 @@ const lineDelta = ({ hunk, blockId }) => {
|
|
|
73
74
|
return delta;
|
|
74
75
|
};
|
|
75
76
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const AGE_UNIT = {
|
|
78
|
+
second: 's',
|
|
79
|
+
minute: 'm',
|
|
80
|
+
hour: 'h',
|
|
81
|
+
day: 'd',
|
|
82
|
+
week: 'w',
|
|
83
|
+
month: 'mo',
|
|
84
|
+
year: 'y',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const AGE_AGO = /^(\d+)\s+(second|minute|hour|day|week|month|year)s?\s+ago$/i;
|
|
88
|
+
const AGE_SHORT = /^(\d+)(mo|[smhdwy])(?: ago)?$/i;
|
|
89
|
+
|
|
90
|
+
const shortUnit = (n, unit) => `${n}${AGE_UNIT[unit]} ago`;
|
|
91
|
+
|
|
92
|
+
const shortAge = (text) => {
|
|
93
|
+
const raw = `${text ?? ''}`.trim();
|
|
94
|
+
if (!raw) return '';
|
|
95
|
+
if (/^yesterday$/i.test(raw)) return '1d ago';
|
|
96
|
+
const match = AGE_AGO.exec(raw);
|
|
97
|
+
if (match) {
|
|
98
|
+
const unit = AGE_UNIT[match[2].toLowerCase()];
|
|
99
|
+
if (unit) return `${match[1]}${unit} ago`;
|
|
100
|
+
}
|
|
101
|
+
const brief = AGE_SHORT.exec(raw);
|
|
102
|
+
if (brief) return `${brief[1]}${brief[2].toLowerCase()} ago`;
|
|
103
|
+
return raw;
|
|
79
104
|
};
|
|
80
105
|
|
|
81
106
|
const relativeAge = (thenMs, nowMs = Date.now()) => {
|
|
82
107
|
const diff = Math.max(0, Math.round((nowMs - thenMs) / 1000));
|
|
83
|
-
if (diff < 90) return
|
|
108
|
+
if (diff < 90) return shortUnit(diff, 'second');
|
|
84
109
|
const minutes = Math.round(diff / 60);
|
|
85
|
-
if (minutes < 90) return
|
|
110
|
+
if (minutes < 90) return shortUnit(minutes, 'minute');
|
|
86
111
|
const hours = Math.round(minutes / 60);
|
|
87
|
-
if (hours < 36) return
|
|
112
|
+
if (hours < 36) return shortUnit(hours, 'hour');
|
|
88
113
|
const days = Math.round(hours / 24);
|
|
89
|
-
if (days < 14) return
|
|
90
|
-
if (days < 70) return
|
|
91
|
-
if (days < 365) return
|
|
92
|
-
return
|
|
114
|
+
if (days < 14) return shortUnit(days, 'day');
|
|
115
|
+
if (days < 70) return shortUnit(Math.round(days / 7), 'week');
|
|
116
|
+
if (days < 365) return shortUnit(Math.round(days / 30), 'month');
|
|
117
|
+
return shortUnit(Math.round(days / 365), 'year');
|
|
93
118
|
};
|
|
94
119
|
|
|
95
120
|
const addLineDelta = (group, origin, delta) => {
|
|
@@ -231,4 +256,5 @@ module.exports = {
|
|
|
231
256
|
fileEntries,
|
|
232
257
|
fileTotals,
|
|
233
258
|
relativeAge,
|
|
259
|
+
shortAge,
|
|
234
260
|
};
|