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.
Files changed (81) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +19 -4
  3. package/bin/reslop.js +4 -2
  4. package/lib/ansi.js +120 -4
  5. package/lib/api.js +2 -1
  6. package/lib/cli.js +21 -9
  7. package/lib/clipboard.js +6 -3
  8. package/lib/deps-diff.js +61 -115
  9. package/lib/deps.js +57 -54
  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 +2 -1
  15. package/lib/diff/patch.js +2 -1
  16. package/lib/diff/unit.js +4 -2
  17. package/lib/editor.js +22 -4
  18. package/lib/files.js +38 -12
  19. package/lib/git-branches.js +150 -165
  20. package/lib/git-deps.js +20 -60
  21. package/lib/git-worktree.js +508 -0
  22. package/lib/git.js +23 -580
  23. package/lib/github.js +2 -1
  24. package/lib/gitlab.js +2 -1
  25. package/lib/highlight/bash.js +2 -1
  26. package/lib/highlight/css.js +2 -1
  27. package/lib/highlight/csv.js +2 -1
  28. package/lib/highlight/dot.js +2 -1
  29. package/lib/highlight/html.js +2 -1
  30. package/lib/highlight/json.js +2 -1
  31. package/lib/highlight/log.js +2 -1
  32. package/lib/highlight/txt.js +2 -1
  33. package/lib/highlight.js +2 -1
  34. package/lib/keys.js +12 -6
  35. package/lib/manifest.js +4 -2
  36. package/lib/npm-commands.js +592 -0
  37. package/lib/remote.js +20 -13
  38. package/lib/render/branches.js +25 -41
  39. package/lib/render/chrome.js +163 -128
  40. package/lib/render/commits.js +727 -65
  41. package/lib/render/diff.js +130 -47
  42. package/lib/render/files.js +4 -12
  43. package/lib/render/frame.js +41 -13
  44. package/lib/render/notes.js +4 -11
  45. package/lib/render/npm.js +216 -0
  46. package/lib/render/present.js +2 -1
  47. package/lib/render/primitives.js +34 -50
  48. package/lib/render/render.js +2 -1
  49. package/lib/render/unit.js +14 -11
  50. package/lib/report-model.js +129 -0
  51. package/lib/report-parse.js +89 -0
  52. package/lib/report-render.js +104 -0
  53. package/lib/report-run.js +166 -0
  54. package/lib/report-test.js +507 -0
  55. package/lib/report-tools.js +337 -0
  56. package/lib/review-merge.js +2 -1
  57. package/lib/review.js +6 -2
  58. package/lib/session/actions.js +77 -44
  59. package/lib/session/branches.js +13 -49
  60. package/lib/session/change-actions.js +2 -1
  61. package/lib/session/commit.js +113 -53
  62. package/lib/session/compose.js +83 -14
  63. package/lib/session/dispatch.js +42 -22
  64. package/lib/session/files-pane.js +16 -21
  65. package/lib/session/items.js +6 -3
  66. package/lib/session/lifecycle.js +12 -5
  67. package/lib/session/load.js +4 -2
  68. package/lib/session/navigation.js +2 -0
  69. package/lib/session/npm.js +388 -0
  70. package/lib/session/ops.js +15 -1
  71. package/lib/session/pointer.js +30 -3
  72. package/lib/session/review.js +2 -1
  73. package/lib/session/terminal.js +4 -2
  74. package/lib/session/update.js +2 -1
  75. package/lib/session/view.js +41 -4
  76. package/lib/session.js +44 -22
  77. package/lib/source.js +6 -3
  78. package/lib/update.js +5 -9
  79. package/lib/utilities.js +4 -2
  80. package/lib/wrap.js +3 -1
  81. package/package.json +6 -6
@@ -0,0 +1,104 @@
1
+ 'use strict';
2
+
3
+ const NOTICE_HEAD =
4
+ 'reslop: reduced output; passing results are omitted ' +
5
+ 'and similar problems are grouped';
6
+
7
+ const NOTICE = [
8
+ NOTICE_HEAD,
9
+ 'If reduced output hides information needed for debugging,',
10
+ 'rerun the same command with RESLOP_OUTPUT=raw.',
11
+ ].join('\n');
12
+
13
+ const SUMMARY_ROWS = 6;
14
+ const PROBLEM_FIELDS = 7;
15
+
16
+ const writeRow = (rows, index, key, value) => {
17
+ if (value === null || value === undefined || value === '') return index;
18
+ rows[index] = [key, `${value}`];
19
+ return index + 1;
20
+ };
21
+
22
+ const summaryRows = (stats) => {
23
+ const rows = new Array(SUMMARY_ROWS);
24
+ let index = 0;
25
+ index = writeRow(rows, index, 'passed', stats.passed);
26
+ index = writeRow(rows, index, 'failed', stats.failed);
27
+ index = writeRow(rows, index, 'skipped', stats.skipped);
28
+ index = writeRow(rows, index, 'errors', stats.errors);
29
+ index = writeRow(rows, index, 'warnings', stats.warnings);
30
+ index = writeRow(rows, index, 'duration', stats.duration);
31
+ rows.length = index;
32
+ return rows;
33
+ };
34
+
35
+ const problemRows = (item) => {
36
+ const extra = item.extra;
37
+ let size = PROBLEM_FIELDS + extra.length;
38
+ if (item.count > 1) size += 1;
39
+ if (item.omitted > 0) size += 1;
40
+ const rows = new Array(size);
41
+ let index = 0;
42
+ index = writeRow(rows, index, 'severity', item.severity);
43
+ index = writeRow(rows, index, 'duration', item.duration);
44
+ index = writeRow(rows, index, 'code', item.code);
45
+ index = writeRow(rows, index, 'operator', item.operator);
46
+ index = writeRow(rows, index, 'expected', item.expected);
47
+ index = writeRow(rows, index, 'actual', item.actual);
48
+ index = writeRow(rows, index, 'message', item.message);
49
+ for (let i = 0; i < extra.length; i++) {
50
+ const pair = extra[i];
51
+ index = writeRow(rows, index, pair[0], pair[1]);
52
+ }
53
+ if (item.count > 1) index = writeRow(rows, index, 'count', item.count);
54
+ if (item.omitted > 0) {
55
+ index = writeRow(rows, index, 'omitted', item.omitted);
56
+ }
57
+ rows.length = index;
58
+ return rows;
59
+ };
60
+
61
+ const markdownValue = (value) => {
62
+ const text = `${value}`;
63
+ if (!text.includes('\n')) return text;
64
+ return `\n${text}`;
65
+ };
66
+
67
+ const markdownProblem = (item) => {
68
+ const title = item.name || item.message || item.code || 'problem';
69
+ const lines = ['', `## ${title}`, ''];
70
+ for (const row of problemRows(item)) {
71
+ lines.push(`- ${row[0]}: ${markdownValue(row[1])}`);
72
+ }
73
+ for (const trace of item.traces) {
74
+ if (!trace) continue;
75
+ lines.push(`- trace:\n${trace}`);
76
+ }
77
+ return lines;
78
+ };
79
+
80
+ const markdownReport = (item) => {
81
+ const lines = ['', `# ${item.tool}`, '', `ok: ${item.ok}`];
82
+ for (const row of summaryRows(item.summary)) {
83
+ lines.push(`${row[0]}: ${row[1]}`);
84
+ }
85
+ for (const problem of item.problems) {
86
+ lines.push(...markdownProblem(problem));
87
+ }
88
+ return lines;
89
+ };
90
+
91
+ const renderMarkdown = (doc) => {
92
+ const lines = [NOTICE, '', `exit: ${doc.exit}`];
93
+ for (const item of doc.reports) lines.push(...markdownReport(item));
94
+ if (doc.stderr) lines.push('', '# stderr', '', doc.stderr);
95
+ return `${lines.join('\n').replace(/\n+$/, '')}\n`;
96
+ };
97
+
98
+ const renderDocument = (doc) => renderMarkdown(doc);
99
+
100
+ module.exports = {
101
+ NOTICE,
102
+ renderMarkdown,
103
+ renderDocument,
104
+ };
@@ -0,0 +1,166 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const childProcess = require('node:child_process');
5
+ const { spawn } = childProcess;
6
+
7
+ const utilities = require('./utilities.js');
8
+ const { spawnBase } = utilities;
9
+ const npmCommands = require('./npm-commands.js');
10
+ const { writeLog } = npmCommands;
11
+ const reportParse = require('./report-parse.js');
12
+ const { toolHint, buildDocument } = reportParse;
13
+ const reportRender = require('./report-render.js');
14
+ const { renderDocument } = reportRender;
15
+
16
+ const OUTPUTS = new Set(['md', 'raw']);
17
+
18
+ const outputMode = (env) => {
19
+ const value = env.RESLOP_OUTPUT;
20
+ if (value) return OUTPUTS.has(value) ? value : '';
21
+ return 'md';
22
+ };
23
+
24
+ const CMD_SAFE = /^[\w./:=@+-]+$/;
25
+
26
+ const quoteCmdArg = (arg) => {
27
+ const text = `${arg}`;
28
+ if (text && CMD_SAFE.test(text)) return text;
29
+ const escaped = text.replaceAll('"', '""').replaceAll('%', '"%"');
30
+ return `"${escaped}"`;
31
+ };
32
+
33
+ const winCommand = (program, args) => {
34
+ const parts = [program, ...args].map(quoteCmdArg);
35
+ return `"${parts.join(' ')}"`;
36
+ };
37
+
38
+ const spawnCommand = (program, args, options) => {
39
+ const { cwd, env, stdio } = options;
40
+ const base = { cwd, env, stdio };
41
+ if (process.platform !== 'win32') {
42
+ return spawn(program, args, spawnBase(base));
43
+ }
44
+ const command = winCommand(program, args);
45
+ const shell = process.env.comspec || 'cmd.exe';
46
+ return spawn(
47
+ shell,
48
+ ['/d', '/s', '/c', command],
49
+ spawnBase({ ...base, windowsVerbatimArguments: true }),
50
+ );
51
+ };
52
+
53
+ const waitStatus = (child) =>
54
+ new Promise((resolve) => {
55
+ let settled = false;
56
+ const finish = (status) => {
57
+ if (settled) return;
58
+ settled = true;
59
+ resolve(status);
60
+ };
61
+ child.on('error', () => finish(1));
62
+ child.on('close', (status) => finish(status === null ? 1 : status));
63
+ });
64
+
65
+ const collect = (child) =>
66
+ new Promise((resolve) => {
67
+ let out = '';
68
+ let err = '';
69
+ let settled = false;
70
+ const finish = (status) => {
71
+ if (settled) return;
72
+ settled = true;
73
+ resolve({ status, stdout: out, stderr: err });
74
+ };
75
+ if (child.stdout) {
76
+ child.stdout.setEncoding('utf8');
77
+ child.stdout.on('data', (chunk) => {
78
+ out += chunk;
79
+ });
80
+ }
81
+ if (child.stderr) {
82
+ child.stderr.setEncoding('utf8');
83
+ child.stderr.on('data', (chunk) => {
84
+ err += chunk;
85
+ });
86
+ }
87
+ child.on('error', (error) => {
88
+ err += `${error.message}\n`;
89
+ finish(1);
90
+ });
91
+ child.on('close', (status) => finish(status === null ? 1 : status));
92
+ });
93
+
94
+ const commandArgs = (argv) => {
95
+ if (argv[0] !== 't' || argv[1] !== '--' || !argv[2]) return null;
96
+ return { program: argv[2], args: argv.slice(3) };
97
+ };
98
+
99
+ const usage = (stderr) => {
100
+ stderr.write('reslop: missing command\n');
101
+ stderr.write('Usage: reslop t -- <program> [args]\n');
102
+ return 1;
103
+ };
104
+
105
+ const runInherit = async (spawnFn, program, args, options) => {
106
+ let child;
107
+ try {
108
+ child = spawnFn(program, args, { ...options, stdio: 'inherit' });
109
+ } catch {
110
+ return 1;
111
+ }
112
+ return waitStatus(child);
113
+ };
114
+
115
+ const runCapture = async (proc, deps = {}) => {
116
+ const env = proc.env || {};
117
+ const argv = proc.argv.slice(2);
118
+ const command = commandArgs(argv);
119
+ if (!command) return usage(proc.stderr);
120
+ const mode = outputMode(env);
121
+ if (!mode) {
122
+ proc.stderr.write(`reslop: unknown RESLOP_OUTPUT ${env.RESLOP_OUTPUT}\n`);
123
+ return 1;
124
+ }
125
+ const cwd = deps.cwd ? deps.cwd : proc.cwd();
126
+ const spawnFn = deps.spawn || spawnCommand;
127
+ const capture = env.RESLOP_CAPTURE === '1';
128
+ if (mode === 'raw' || capture) {
129
+ return runInherit(spawnFn, command.program, command.args, { cwd, env });
130
+ }
131
+ const childEnv = { ...env, RESLOP_CAPTURE: '1' };
132
+ let child;
133
+ try {
134
+ child = spawnFn(command.program, command.args, {
135
+ cwd,
136
+ env: childEnv,
137
+ stdio: ['ignore', 'pipe', 'pipe'],
138
+ });
139
+ } catch (error) {
140
+ proc.stderr.write(`reslop: ${error.message}\n`);
141
+ return 1;
142
+ }
143
+ const result = await collect(child);
144
+ const hint = toolHint(command.program, command.args);
145
+ const doc = buildDocument(
146
+ result.stdout,
147
+ result.stderr,
148
+ cwd,
149
+ result.status,
150
+ hint,
151
+ );
152
+ const text = renderDocument(doc);
153
+ proc.stdout.write(text);
154
+ try {
155
+ writeLog(cwd, path.basename(command.program), text);
156
+ } catch (error) {
157
+ proc.stderr.write(`reslop: ${error.message}\n`);
158
+ }
159
+ return result.status;
160
+ };
161
+
162
+ module.exports = {
163
+ outputMode,
164
+ winCommand,
165
+ runCapture,
166
+ };