reslop 0.2.3 → 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 +14 -1
- package/README.md +8 -3
- package/bin/reslop.js +4 -2
- package/lib/api.js +2 -1
- package/lib/cli.js +19 -10
- package/lib/clipboard.js +6 -3
- package/lib/deps-diff.js +61 -115
- package/lib/deps.js +24 -61
- 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 +107 -154
- 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 +2 -2
- package/lib/manifest.js +4 -2
- package/lib/npm-commands.js +83 -4
- package/lib/remote.js +20 -13
- package/lib/render/branches.js +6 -15
- package/lib/render/chrome.js +27 -7
- package/lib/render/commits.js +158 -46
- package/lib/render/diff.js +130 -47
- package/lib/render/files.js +4 -12
- package/lib/render/frame.js +3 -9
- package/lib/render/notes.js +4 -11
- package/lib/render/npm.js +1 -13
- package/lib/render/present.js +2 -1
- package/lib/render/primitives.js +28 -15
- package/lib/render/render.js +2 -1
- package/lib/render/unit.js +14 -11
- package/lib/report-model.js +6 -4
- package/lib/report-parse.js +8 -13
- package/lib/report-render.js +38 -21
- package/lib/report-run.js +12 -10
- package/lib/report-test.js +5 -4
- package/lib/report-tools.js +44 -18
- package/lib/review-merge.js +2 -1
- package/lib/review.js +6 -2
- package/lib/session/actions.js +49 -65
- package/lib/session/branches.js +13 -49
- package/lib/session/change-actions.js +2 -1
- package/lib/session/commit.js +34 -49
- package/lib/session/compose.js +37 -11
- package/lib/session/dispatch.js +17 -25
- package/lib/session/files-pane.js +6 -27
- package/lib/session/items.js +6 -3
- package/lib/session/lifecycle.js +8 -4
- package/lib/session/load.js +4 -2
- package/lib/session/navigation.js +1 -0
- package/lib/session/npm.js +54 -1
- package/lib/session/ops.js +15 -1
- package/lib/session/pointer.js +22 -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 +29 -3
- package/lib/session.js +37 -23
- 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 +1 -1
package/lib/highlight/html.js
CHANGED
package/lib/highlight/json.js
CHANGED
package/lib/highlight/log.js
CHANGED
package/lib/highlight/txt.js
CHANGED
package/lib/highlight.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const core = require('./highlight/core.js');
|
|
7
|
+
const { tokensText } = core;
|
|
7
8
|
|
|
8
9
|
const PLUGIN_DIR = path.join(__dirname, 'highlight');
|
|
9
10
|
const SKIP = ['core.js'];
|
package/lib/keys.js
CHANGED
|
@@ -4,8 +4,8 @@ const actions = require('./session/actions.js');
|
|
|
4
4
|
|
|
5
5
|
const { ACTIONS, ACTION_IDS, BRANCH_ACTIONS, COMMIT_ACTIONS } = actions;
|
|
6
6
|
const { DIFF_DISABLED, FILES_DISABLED, FILES_GIT_DISABLED } = actions;
|
|
7
|
-
const { FILES_HIDDEN, FILES_TODO_DISABLED
|
|
8
|
-
|
|
7
|
+
const { FILES_HIDDEN, FILES_TODO_DISABLED } = actions;
|
|
8
|
+
const { TODO_DISABLED, BRANCHES_DISABLED } = actions;
|
|
9
9
|
const { COMMITS_DISABLED, UNIT_DISABLED, NPM_DISABLED } = actions;
|
|
10
10
|
const { disabledActions, diffGitDisabled, branchGitDisabled } = actions;
|
|
11
11
|
const { commitGitDisabled } = actions;
|
package/lib/manifest.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const path = require('node:path');
|
|
4
|
-
const
|
|
5
|
-
const {
|
|
4
|
+
const metautil = require('metautil');
|
|
5
|
+
const { isHashObject, jsonParse } = metautil;
|
|
6
|
+
const utilities = require('./utilities.js');
|
|
7
|
+
const { parseVersion, cmpVersion } = utilities;
|
|
6
8
|
|
|
7
9
|
const MANIFEST = 'package.json';
|
|
8
10
|
const LOCKFILE = 'package-lock.json';
|
package/lib/npm-commands.js
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
8
|
-
const {
|
|
5
|
+
const childProcess = require('node:child_process');
|
|
6
|
+
const { spawn } = childProcess;
|
|
7
|
+
const metautil = require('metautil');
|
|
8
|
+
const { jsonParse } = metautil;
|
|
9
|
+
const ansi = require('./ansi.js');
|
|
10
|
+
const { stripAnsi, visibleWidth } = ansi;
|
|
11
|
+
const utilities = require('./utilities.js');
|
|
12
|
+
const { npmBin, npmOpts, spawnBase } = utilities;
|
|
9
13
|
|
|
10
14
|
const MANIFEST = 'package.json';
|
|
11
15
|
const SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies'];
|
|
@@ -288,6 +292,78 @@ const dateStamp = (now) => {
|
|
|
288
292
|
};
|
|
289
293
|
|
|
290
294
|
const LOG_DIR = '.log';
|
|
295
|
+
const LOG_STALE_DAYS = 5;
|
|
296
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
297
|
+
const LOG_DATE = /^(\d{4})-(\d{2})-(\d{2})-/;
|
|
298
|
+
|
|
299
|
+
const logDay = (name) => {
|
|
300
|
+
const match = LOG_DATE.exec(name);
|
|
301
|
+
if (!match) return null;
|
|
302
|
+
const year = Number(match[1]);
|
|
303
|
+
const month = Number(match[2]);
|
|
304
|
+
const day = Number(match[3]);
|
|
305
|
+
if (month < 1 || month > 12 || day < 1 || day > 31) return null;
|
|
306
|
+
return Date.UTC(year, month - 1, day) / DAY_MS;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const todayIndex = (now) =>
|
|
310
|
+
Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()) / DAY_MS;
|
|
311
|
+
|
|
312
|
+
const isStaleLog = (name, now) => {
|
|
313
|
+
const day = logDay(name);
|
|
314
|
+
if (day === null) return false;
|
|
315
|
+
return todayIndex(now) - day > LOG_STALE_DAYS;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const readLogNames = (dir) => {
|
|
319
|
+
try {
|
|
320
|
+
return fs.readdirSync(dir);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
if (error.code === 'ENOENT') return [];
|
|
323
|
+
throw error;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const staleLogFiles = (root, now = new Date()) => {
|
|
328
|
+
const dir = path.join(root, LOG_DIR);
|
|
329
|
+
const names = readLogNames(dir);
|
|
330
|
+
const files = [];
|
|
331
|
+
let bytes = 0;
|
|
332
|
+
for (const name of names) {
|
|
333
|
+
if (!name.endsWith('.log') || !isStaleLog(name, now)) continue;
|
|
334
|
+
const full = path.join(dir, name);
|
|
335
|
+
let stat;
|
|
336
|
+
try {
|
|
337
|
+
stat = fs.statSync(full);
|
|
338
|
+
} catch (error) {
|
|
339
|
+
if (error.code === 'ENOENT') continue;
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
if (!stat.isFile()) continue;
|
|
343
|
+
bytes += stat.size;
|
|
344
|
+
files.push(full);
|
|
345
|
+
}
|
|
346
|
+
return { bytes, files };
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const removeStaleLogs = (root, now = new Date()) => {
|
|
350
|
+
const found = staleLogFiles(root, now);
|
|
351
|
+
for (const full of found.files) fs.rmSync(full, { force: true });
|
|
352
|
+
return found.bytes;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const scaled = (value) => {
|
|
356
|
+
if (value >= 10) return `${Math.round(value)}`;
|
|
357
|
+
return `${Math.round(value * 10) / 10}`;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const formatSize = (bytes) => {
|
|
361
|
+
const size = bytes > 0 ? bytes : 0;
|
|
362
|
+
if (size < 1024) return `${size}b`;
|
|
363
|
+
const kb = size / 1024;
|
|
364
|
+
if (kb < 1024) return `${scaled(kb)}k`;
|
|
365
|
+
return `${scaled(kb / 1024)}m`;
|
|
366
|
+
};
|
|
291
367
|
|
|
292
368
|
const nextLogFile = (root, command, now = new Date()) => {
|
|
293
369
|
const date = dateStamp(now);
|
|
@@ -497,6 +573,9 @@ module.exports = {
|
|
|
497
573
|
reduceOutput,
|
|
498
574
|
logFileName,
|
|
499
575
|
writeLog,
|
|
576
|
+
staleLogFiles,
|
|
577
|
+
removeStaleLogs,
|
|
578
|
+
formatSize,
|
|
500
579
|
parseScriptLine,
|
|
501
580
|
scriptLine,
|
|
502
581
|
saveScript,
|
package/lib/remote.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
3
|
+
const metautil = require('metautil');
|
|
4
|
+
const { delay, jsonParse, isHashObject } = metautil;
|
|
5
|
+
const utilities = require('./utilities.js');
|
|
6
|
+
const { oneLine } = utilities;
|
|
7
|
+
const files = require('./files.js');
|
|
8
|
+
const { itemPath, isPathInScope } = files;
|
|
9
|
+
const diff = require('./diff/diff.js');
|
|
10
|
+
const { parseDiff, itemsFromFiles, isCtxType } = diff;
|
|
11
|
+
const deps = require('./deps.js');
|
|
12
|
+
const { foldDepItems } = deps;
|
|
8
13
|
|
|
9
14
|
const RETRY_ATTEMPTS = 3;
|
|
10
15
|
const RETRY_DELAY_MS = 200;
|
|
@@ -323,13 +328,15 @@ const changeFiles = (items) => {
|
|
|
323
328
|
|
|
324
329
|
const loadChange = async (ref, options, source) => {
|
|
325
330
|
const { client, url, diffUrl = url, diffAccept } = source;
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
const {
|
|
332
|
+
cwd,
|
|
333
|
+
paths = [],
|
|
334
|
+
fetch = globalThis.fetch,
|
|
335
|
+
token = '',
|
|
336
|
+
retry = {},
|
|
337
|
+
signal,
|
|
338
|
+
} = options;
|
|
339
|
+
const request = { fetch, token, retry, signal };
|
|
333
340
|
const jsonPage = await client.request(url, request);
|
|
334
341
|
const data = client.parseJson(jsonPage.body, `${client.subject} payload`);
|
|
335
342
|
const diffPage = await client.request(diffUrl, {
|
|
@@ -348,7 +355,7 @@ const loadChange = async (ref, options, source) => {
|
|
|
348
355
|
if (isAbortError(error, request.signal)) throw error;
|
|
349
356
|
// Discussion import is optional.
|
|
350
357
|
}
|
|
351
|
-
return { top:
|
|
358
|
+
return { top: cwd, items, sourceLabel, change, imported };
|
|
352
359
|
};
|
|
353
360
|
|
|
354
361
|
module.exports = {
|
package/lib/render/branches.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
|
+
const files = require('../files.js');
|
|
4
5
|
const primitives = require('./primitives.js');
|
|
6
|
+
const { shortAge } = files;
|
|
5
7
|
const { measureColumns, padColumns, joinColumns, paintColumns } = primitives;
|
|
6
8
|
|
|
7
9
|
const { THEME, paint, visibleWidth, truncateVisible, seq, EL } = ansi;
|
|
8
|
-
const { FILE_MARK
|
|
10
|
+
const { FILE_MARK } = primitives;
|
|
9
11
|
const { paintCursorList, paintListEdit } = primitives;
|
|
10
12
|
const { branchLabel } = primitives;
|
|
11
13
|
|
|
@@ -61,7 +63,7 @@ const branchRowFg = (entry, selected) => {
|
|
|
61
63
|
|
|
62
64
|
const branchRowFields = (entry) => {
|
|
63
65
|
const sha = entry.sha ?? '';
|
|
64
|
-
const date = entry.date ?? '';
|
|
66
|
+
const date = shortAge(entry.date ?? '');
|
|
65
67
|
const gone = entry.gone ? 'gone' : '';
|
|
66
68
|
const ahead = entry.ahead ? `⇡${entry.ahead}` : '';
|
|
67
69
|
const behind = entry.behind ? `⇣${entry.behind}` : '';
|
|
@@ -110,9 +112,6 @@ const paintBranchRow = (entry, width, color, selected, cols) => {
|
|
|
110
112
|
return out;
|
|
111
113
|
};
|
|
112
114
|
|
|
113
|
-
const paintBranchEdit = (compose, width, color) =>
|
|
114
|
-
paintListEdit(compose, width, color);
|
|
115
|
-
|
|
116
115
|
const paintBodyBranches = (view, width, color, bodyH, headerLines) => {
|
|
117
116
|
const branches = view.branches ?? [];
|
|
118
117
|
const cols = measureColumns(branches, branchRowFields, BRANCH_COL_ALIGN);
|
|
@@ -124,10 +123,10 @@ const paintBodyBranches = (view, width, color, bodyH, headerLines) => {
|
|
|
124
123
|
const cursor = view.branchCursor ?? 0;
|
|
125
124
|
const compose = view.compose;
|
|
126
125
|
const editing = compose && compose.kind === 'branch';
|
|
127
|
-
const tail = editing ?
|
|
126
|
+
const tail = editing ? paintListEdit(compose, width, color) : null;
|
|
128
127
|
const paintRow = (entry, rowWidth, rowColor, selected) =>
|
|
129
128
|
paintBranchRow(entry, rowWidth, rowColor, selected, cols);
|
|
130
|
-
|
|
129
|
+
return paintCursorList(
|
|
131
130
|
branches,
|
|
132
131
|
cursor,
|
|
133
132
|
width,
|
|
@@ -138,14 +137,6 @@ const paintBodyBranches = (view, width, color, bodyH, headerLines) => {
|
|
|
138
137
|
tail,
|
|
139
138
|
view.listScroll,
|
|
140
139
|
);
|
|
141
|
-
return paneResult({
|
|
142
|
-
body: painted.body,
|
|
143
|
-
fileHits: painted.fileHits,
|
|
144
|
-
todoOwners: [],
|
|
145
|
-
cursor: painted.cursor,
|
|
146
|
-
splitBody: false,
|
|
147
|
-
listScroll: painted.offset,
|
|
148
|
-
});
|
|
149
140
|
};
|
|
150
141
|
|
|
151
142
|
module.exports = { paintBodyBranches };
|
package/lib/render/chrome.js
CHANGED
|
@@ -5,7 +5,7 @@ const keys = require('../keys.js');
|
|
|
5
5
|
const files = require('../files.js');
|
|
6
6
|
const primitives = require('./primitives.js');
|
|
7
7
|
|
|
8
|
-
const { itemPath,
|
|
8
|
+
const { itemPath, REPO_TODOS_LABEL, isTodosEntry, fileTotals } = files;
|
|
9
9
|
const { ACTION_IDS, BRANCH_ACTIONS, COMMIT_ACTIONS, actionLetter } = keys;
|
|
10
10
|
const { buttonWord } = keys;
|
|
11
11
|
const { FILES_HIDDEN, FILES_GIT_DISABLED, BRANCHES_DISABLED } = keys;
|
|
@@ -152,10 +152,10 @@ const headerScreen = (view) => {
|
|
|
152
152
|
if (pane === 'files') {
|
|
153
153
|
const files = view.files ?? [];
|
|
154
154
|
const entry = files[view.fileCursor ?? 0];
|
|
155
|
-
if (isTodosEntry(entry)) return
|
|
155
|
+
if (isTodosEntry(entry)) return REPO_TODOS_LABEL;
|
|
156
156
|
return '';
|
|
157
157
|
}
|
|
158
|
-
if (view.item && view.item.origin === 'todo') return
|
|
158
|
+
if (view.item && view.item.origin === 'todo') return REPO_TODOS_LABEL;
|
|
159
159
|
return '';
|
|
160
160
|
};
|
|
161
161
|
|
|
@@ -397,7 +397,9 @@ const statusStats = (view) => {
|
|
|
397
397
|
const fallbackN = counts.staged ?? 0;
|
|
398
398
|
const n = hasGit ? total.staged : fallbackN;
|
|
399
399
|
const m = hasGit ? total.remaining : counted;
|
|
400
|
-
|
|
400
|
+
const git = joinStatus([statusDelta(total), `${n}/${m}`]);
|
|
401
|
+
if (!view.npmLogs) return git;
|
|
402
|
+
return joinStatus([view.npmLogs, git]);
|
|
401
403
|
};
|
|
402
404
|
|
|
403
405
|
const statusLayout = (view, status, width) => {
|
|
@@ -454,6 +456,7 @@ const paintChoicePrompt = (choices, width, color, prefix = '') => {
|
|
|
454
456
|
};
|
|
455
457
|
|
|
456
458
|
const STATUS_MARK = /(\+[0-9]+(?:\/[0-9]+)?|-[0-9]+(?:\/[0-9]+)?)/g;
|
|
459
|
+
const OLD_LOGS = /old logs \S+/;
|
|
457
460
|
|
|
458
461
|
const paintStatusMark = (token, color) => {
|
|
459
462
|
const bgRgb = THEME.chromeBg;
|
|
@@ -467,6 +470,18 @@ const paintStatusLeft = (text, color) => {
|
|
|
467
470
|
const bgRgb = THEME.chromeBg;
|
|
468
471
|
const muted = THEME.mutedFg;
|
|
469
472
|
if (!color) return text;
|
|
473
|
+
const found = text.match(OLD_LOGS);
|
|
474
|
+
if (found) {
|
|
475
|
+
const at = found.index;
|
|
476
|
+
const token = found[0];
|
|
477
|
+
const before = text.slice(0, at);
|
|
478
|
+
const after = text.slice(at + token.length);
|
|
479
|
+
return (
|
|
480
|
+
paintStatusLeft(before, color) +
|
|
481
|
+
paint(token, THEME.shaFg, bgRgb, color) +
|
|
482
|
+
paintStatusLeft(after, color)
|
|
483
|
+
);
|
|
484
|
+
}
|
|
470
485
|
let out = '';
|
|
471
486
|
let last = 0;
|
|
472
487
|
for (const match of text.matchAll(STATUS_MARK)) {
|
|
@@ -621,13 +636,18 @@ const dimFooterActions = (view) => {
|
|
|
621
636
|
if (pane === 'commits') {
|
|
622
637
|
const entry = selectedCommit(view);
|
|
623
638
|
const staged = (view.counts?.staged ?? 0) > 0;
|
|
624
|
-
|
|
625
|
-
return commitGitDisabled({
|
|
639
|
+
const unstagedCurrent = !view.rev && !view.revShort;
|
|
640
|
+
return commitGitDisabled({
|
|
641
|
+
...entry,
|
|
642
|
+
canCommit: staged,
|
|
643
|
+
unstagedCurrent,
|
|
644
|
+
});
|
|
626
645
|
}
|
|
627
646
|
if (pane === 'npm' && view.npmView) {
|
|
628
647
|
if (view.npmRunning) return ['npmRerun'];
|
|
629
648
|
return [];
|
|
630
649
|
}
|
|
650
|
+
if (pane === 'npm' && !view.npmLogs) return ['npmLogs'];
|
|
631
651
|
return [];
|
|
632
652
|
};
|
|
633
653
|
|
|
@@ -641,7 +661,7 @@ const extraFooterActions = (view) => {
|
|
|
641
661
|
if (pane === 'commits') return Object.keys(COMMIT_ACTIONS);
|
|
642
662
|
if (pane === 'npm') {
|
|
643
663
|
if (view.npmView) return ['npmStop', 'npmRerun'];
|
|
644
|
-
return ['npmEdit', 'npmNew'];
|
|
664
|
+
return ['npmEdit', 'npmNew', 'npmDrop', 'npmLogs'];
|
|
645
665
|
}
|
|
646
666
|
return [];
|
|
647
667
|
};
|