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
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { stripAnsi } = require('./ansi.js');
3
+ const ansi = require('./ansi.js');
4
+ const { stripAnsi } = ansi;
4
5
  const commands = require('./npm-commands.js');
5
6
 
6
7
  const { withoutNodeFrame, indentStack, relativize } = commands;
@@ -108,9 +109,10 @@ const shortTail = (text) => {
108
109
  const envelope = (reports, exit, stderr) => {
109
110
  const status = exit === null || exit === undefined ? 1 : exit;
110
111
  const ok = status === 0;
111
- const items = [];
112
- for (const item of reports) {
113
- items.push(report(item.tool, { ...item, ok }));
112
+ const items = new Array(reports.length);
113
+ for (let i = 0; i < reports.length; i++) {
114
+ const item = reports[i];
115
+ items[i] = report(item.tool, { ...item, ok });
114
116
  }
115
117
  return { ok, exit: status, reports: items, stderr: stderr || '' };
116
118
  };
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { stripAnsi } = require('./ansi.js');
3
+ const ansi = require('./ansi.js');
4
+ const { stripAnsi } = ansi;
4
5
  const model = require('./report-model.js');
5
6
  const testParse = require('./report-test.js');
6
7
  const toolParse = require('./report-tools.js');
@@ -10,15 +11,7 @@ const { reduceStderr, shortTail, envelope } = model;
10
11
  const { parseTap, parseNodeJsonl, parseTestJson, parseSpec } = testParse;
11
12
  const { parseToolJson, parseToolText } = toolParse;
12
13
 
13
- const KNOWN = new Set([
14
- 'test',
15
- 'tsc',
16
- 'eslint',
17
- 'prettier',
18
- 'audit',
19
- 'outdated',
20
- 'ls',
21
- ]);
14
+ const KNOWN = ['test', 'tsc', 'eslint', 'prettier', 'audit', 'outdated', 'ls'];
22
15
 
23
16
  const toolHint = (program, args) => {
24
17
  const name = `${program ?? ''}`.split(/[/\\]/).pop();
@@ -75,20 +68,21 @@ const parseReports = (text, root) => {
75
68
 
76
69
  const withHint = (reports, text, exit, hint) => {
77
70
  if (reports.length || !`${text ?? ''}`.trim() || exit === 0) return reports;
78
- const tool = KNOWN.has(hint) ? hint : 'test';
71
+ const tool = KNOWN.includes(hint) ? hint : 'test';
79
72
  return [report(tool, { problems: [problem({ message: shortTail(text) })] })];
80
73
  };
81
74
 
82
75
  const buildDocument = (stdout, stderr, root, exit, hint) => {
83
76
  const text = stripAnsi(stdout);
84
77
  const reports = withHint(parseReports(text, root), text, exit, hint);
85
- const grouped = [];
86
- for (const item of reports) grouped.push(groupReport(item));
78
+ const grouped = new Array(reports.length);
79
+ for (let i = 0; i < reports.length; i++) {
80
+ grouped[i] = groupReport(reports[i]);
81
+ }
87
82
  return envelope(grouped, exit, reduceStderr(stderr, root));
88
83
  };
89
84
 
90
85
  module.exports = {
91
86
  toolHint,
92
- parseReports,
93
87
  buildDocument,
94
88
  };
@@ -10,34 +10,51 @@ const NOTICE = [
10
10
  'rerun the same command with RESLOP_OUTPUT=raw.',
11
11
  ].join('\n');
12
12
 
13
- const addRow = (rows, key, value) => {
14
- if (value === null || value === undefined || value === '') return;
15
- rows.push([key, `${value}`]);
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;
16
20
  };
17
21
 
18
22
  const summaryRows = (stats) => {
19
- const rows = [];
20
- addRow(rows, 'passed', stats.passed);
21
- addRow(rows, 'failed', stats.failed);
22
- addRow(rows, 'skipped', stats.skipped);
23
- addRow(rows, 'errors', stats.errors);
24
- addRow(rows, 'warnings', stats.warnings);
25
- addRow(rows, 'duration', stats.duration);
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;
26
32
  return rows;
27
33
  };
28
34
 
29
35
  const problemRows = (item) => {
30
- const rows = [];
31
- addRow(rows, 'severity', item.severity);
32
- addRow(rows, 'duration', item.duration);
33
- addRow(rows, 'code', item.code);
34
- addRow(rows, 'operator', item.operator);
35
- addRow(rows, 'expected', item.expected);
36
- addRow(rows, 'actual', item.actual);
37
- addRow(rows, 'message', item.message);
38
- for (const extra of item.extra) addRow(rows, extra[0], extra[1]);
39
- if (item.count > 1) addRow(rows, 'count', item.count);
40
- if (item.omitted > 0) addRow(rows, 'omitted', item.omitted);
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;
41
58
  return rows;
42
59
  };
43
60
 
package/lib/report-run.js CHANGED
@@ -1,12 +1,17 @@
1
1
  'use strict';
2
2
 
3
3
  const path = require('node:path');
4
- const { spawn } = require('node:child_process');
4
+ const childProcess = require('node:child_process');
5
+ const { spawn } = childProcess;
5
6
 
6
- const { spawnBase } = require('./utilities.js');
7
- const { writeLog } = require('./npm-commands.js');
8
- const { toolHint, buildDocument } = require('./report-parse.js');
9
- const { renderDocument } = require('./report-render.js');
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;
10
15
 
11
16
  const OUTPUTS = new Set(['md', 'raw']);
12
17
 
@@ -31,11 +36,8 @@ const winCommand = (program, args) => {
31
36
  };
32
37
 
33
38
  const spawnCommand = (program, args, options) => {
34
- const base = {
35
- cwd: options.cwd,
36
- env: options.env,
37
- stdio: options.stdio,
38
- };
39
+ const { cwd, env, stdio } = options;
40
+ const base = { cwd, env, stdio };
39
41
  if (process.platform !== 'win32') {
40
42
  return spawn(program, args, spawnBase(base));
41
43
  }
@@ -158,7 +160,6 @@ const runCapture = async (proc, deps = {}) => {
158
160
  };
159
161
 
160
162
  module.exports = {
161
- outputMode,
162
163
  winCommand,
163
164
  runCapture,
164
165
  };
@@ -1,11 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  const model = require('./report-model.js');
4
- const { isPassingTest } = require('./npm-commands.js');
4
+ const npmCommands = require('./npm-commands.js');
5
+ const { isPassingTest } = npmCommands;
6
+ const utilities = require('./utilities.js');
7
+ const { unquote } = utilities;
5
8
 
6
9
  const { summary, problem, report, userTrace } = model;
7
10
 
8
- const KNOWN = new Set([
11
+ const KNOWN = [
9
12
  'message',
10
13
  'error',
11
14
  'code',
@@ -19,7 +22,7 @@ const KNOWN = new Set([
19
22
  'location',
20
23
  'severity',
21
24
  'type',
22
- ]);
25
+ ];
23
26
 
24
27
  const show = (value) => {
25
28
  if (value === null || value === undefined) return '';
@@ -33,7 +36,7 @@ const show = (value) => {
33
36
  const extraFrom = (fields) => {
34
37
  const extra = [];
35
38
  for (const field of fields) {
36
- if (KNOWN.has(field[0])) continue;
39
+ if (KNOWN.includes(field[0])) continue;
37
40
  const value = show(field[1]);
38
41
  if (!value) continue;
39
42
  const shown = value.length > 500 ? `${value.slice(0, 500)}…` : value;
@@ -68,17 +71,6 @@ const problemFromFields = (name, fields, root) => {
68
71
  });
69
72
  };
70
73
 
71
- const yamlValue = (raw) => {
72
- const text = `${raw ?? ''}`.trim();
73
- if (text.length < 2) return text;
74
- const open = text[0];
75
- const close = text[text.length - 1];
76
- const code = open.charCodeAt(0);
77
- const quoted = open === close && (code === 34 || code === 39);
78
- if (quoted) return text.slice(1, -1);
79
- return text;
80
- };
81
-
82
74
  const yamlFields = (lines) => {
83
75
  const fields = [];
84
76
  let index = 0;
@@ -92,7 +84,7 @@ const yamlFields = (lines) => {
92
84
  const value = match[3].trim().replace(/,$/, '');
93
85
  const block = value === '|-' || value === '|' || value === '>';
94
86
  if (!block) {
95
- fields.push([key, yamlValue(value)]);
87
+ fields.push([key, unquote(value)]);
96
88
  index += 1;
97
89
  continue;
98
90
  }
@@ -123,16 +115,23 @@ const tapName = (line) => {
123
115
  return match[1].trim();
124
116
  };
125
117
 
118
+ const STAT_FIELD = {
119
+ tests: 'tests',
120
+ pass: 'passed',
121
+ fail: 'failed',
122
+ skipped: 'skipped',
123
+ skip: 'skipped',
124
+ };
125
+
126
+ const applyStat = (stats, key, value) => {
127
+ if (key === 'duration_ms') stats.duration = value;
128
+ const field = Object.hasOwn(STAT_FIELD, key) ? STAT_FIELD[key] : '';
129
+ if (field) stats[field] = Number(value);
130
+ };
131
+
126
132
  const applyTapComment = (stats, line) => {
127
133
  const match = /^#\s+(\w+)\s+(.+)$/.exec(line.trim());
128
- if (!match) return;
129
- const key = match[1];
130
- const value = match[2].trim();
131
- if (key === 'tests') stats.tests = Number(value);
132
- if (key === 'pass') stats.passed = Number(value);
133
- if (key === 'fail') stats.failed = Number(value);
134
- if (key === 'skipped' || key === 'skip') stats.skipped = Number(value);
135
- if (key === 'duration_ms') stats.duration = value;
134
+ if (match) applyStat(stats, match[1], match[2].trim());
136
135
  };
137
136
 
138
137
  const isTapLine = (line) => {
@@ -258,6 +257,8 @@ const jestTrace = (text, file, root) => {
258
257
  return userTrace(file, root);
259
258
  };
260
259
 
260
+ const durationText = (value) => (value === undefined ? '' : `${value}`);
261
+
261
262
  const jestProblem = (item, file, root) => {
262
263
  const messages = item.failureMessages || [];
263
264
  const text = messages.join('\n');
@@ -269,7 +270,7 @@ const jestProblem = (item, file, root) => {
269
270
  trace: jestTrace(text, file, root),
270
271
  expected: expected ? expected[1] : '',
271
272
  actual: received ? received[1] : '',
272
- duration: item.duration === undefined ? '' : `${item.duration}`,
273
+ duration: durationText(item.duration),
273
274
  });
274
275
  };
275
276
 
@@ -297,29 +298,27 @@ const parseJest = (data, root) => {
297
298
  return report('test', { summary: stats, problems });
298
299
  };
299
300
 
301
+ const PLAYWRIGHT_FAILED = ['failed', 'timedOut'];
302
+
303
+ const playwrightProblem = (spec, result, root) => {
304
+ const error = result.error || {};
305
+ return problem({
306
+ name: spec.title || '',
307
+ message: error.message || result.status,
308
+ trace: userTrace(error.stack || '', root),
309
+ duration: durationText(result.duration),
310
+ });
311
+ };
312
+
300
313
  const walkPlaywright = (suite, problems, root) => {
301
- const specs = suite.specs || [];
302
- for (const spec of specs) {
303
- const tests = spec.tests || [];
304
- for (const item of tests) {
305
- const results = item.results || [];
306
- for (const result of results) {
307
- const status = result.status;
308
- if (status !== 'failed' && status !== 'timedOut') continue;
309
- const error = result.error || {};
310
- problems.push(
311
- problem({
312
- name: spec.title || '',
313
- message: error.message || status,
314
- trace: userTrace(error.stack || '', root),
315
- duration: result.duration === undefined ? '' : `${result.duration}`,
316
- }),
317
- );
318
- }
314
+ for (const spec of suite.specs || []) {
315
+ const results = (spec.tests || []).flatMap((item) => item.results || []);
316
+ for (const result of results) {
317
+ if (!PLAYWRIGHT_FAILED.includes(result.status)) continue;
318
+ problems.push(playwrightProblem(spec, result, root));
319
319
  }
320
320
  }
321
- const children = suite.suites || [];
322
- for (const child of children) walkPlaywright(child, problems, root);
321
+ for (const child of suite.suites || []) walkPlaywright(child, problems, root);
323
322
  };
324
323
 
325
324
  const parsePlaywright = (data, root) => {
@@ -348,12 +347,7 @@ const specStats = (stats, line) => {
348
347
  const match = /^[ℹi]\s+(\w+)\s+(.+)$/.exec(line.trim());
349
348
  if (!match) return false;
350
349
  const key = match[1];
351
- const value = match[2].trim();
352
- if (key === 'tests') stats.tests = Number(value);
353
- if (key === 'pass') stats.passed = Number(value);
354
- if (key === 'fail') stats.failed = Number(value);
355
- if (key === 'skipped') stats.skipped = Number(value);
356
- if (key === 'duration_ms') stats.duration = value;
350
+ applyStat(stats, key, match[2].trim());
357
351
  return key === 'tests' || key === 'pass' || key === 'fail';
358
352
  };
359
353
 
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { parseAuditReport, parseOutdatedReport } = require('./deps.js');
3
+ const deps = require('./deps.js');
4
+ const { parseAuditReport, parseOutdatedReport } = deps;
4
5
  const model = require('./report-model.js');
5
6
 
6
7
  const { summary, problem, report, userTrace } = model;
@@ -160,21 +161,30 @@ const parsePrettier = (text, root) => {
160
161
  return report('prettier', { summary: stats, problems });
161
162
  };
162
163
 
164
+ const auditExtra = (item) => {
165
+ const size = (item.range ? 1 : 0) + (item.fix ? 1 : 0);
166
+ const extra = new Array(size);
167
+ let index = 0;
168
+ if (item.range) {
169
+ extra[index] = ['range', item.range];
170
+ index += 1;
171
+ }
172
+ if (item.fix) extra[index] = ['fix', item.fix];
173
+ return extra;
174
+ };
175
+
163
176
  const auditProblems = (text) => {
164
177
  const audit = parseAuditReport(text);
165
- const problems = [];
178
+ const problems = new Array(audit.size);
179
+ let index = 0;
166
180
  for (const item of audit.values()) {
167
- const extra = [];
168
- if (item.range) extra.push(['range', item.range]);
169
- if (item.fix) extra.push(['fix', item.fix]);
170
- problems.push(
171
- problem({
172
- severity: item.severity || 'error',
173
- name: item.name,
174
- message: item.title || '',
175
- extra,
176
- }),
177
- );
181
+ problems[index] = problem({
182
+ severity: item.severity || 'error',
183
+ name: item.name,
184
+ message: item.title || '',
185
+ extra: auditExtra(item),
186
+ });
187
+ index += 1;
178
188
  }
179
189
  return problems;
180
190
  };
@@ -285,27 +295,36 @@ const parseAuditText = (text) => {
285
295
  return report('audit', { summary: stats, problems });
286
296
  };
287
297
 
298
+ const TOOL_SLOTS = 4;
299
+
288
300
  const parseToolText = (text, root) => {
289
- const reports = [];
301
+ const reports = new Array(TOOL_SLOTS);
302
+ let index = 0;
290
303
  const eslint = parseEslintStylish(text, root);
291
- if (eslint) reports.push(eslint);
304
+ if (eslint) {
305
+ reports[index] = eslint;
306
+ index += 1;
307
+ }
292
308
  const tsc = parseTsc(text, root);
293
- if (tsc) reports.push(tsc);
309
+ if (tsc) {
310
+ reports[index] = tsc;
311
+ index += 1;
312
+ }
294
313
  const prettier = parsePrettier(text, root);
295
- if (prettier) reports.push(prettier);
314
+ if (prettier) {
315
+ reports[index] = prettier;
316
+ index += 1;
317
+ }
296
318
  const audit = parseAuditText(text);
297
- if (audit) reports.push(audit);
319
+ if (audit) {
320
+ reports[index] = audit;
321
+ index += 1;
322
+ }
323
+ reports.length = index;
298
324
  return reports;
299
325
  };
300
326
 
301
327
  module.exports = {
302
- parseEslintJson,
303
- parseEslintStylish,
304
- parseTsc,
305
- parsePrettier,
306
- parseAudit,
307
- parseOutdated,
308
- parseLs,
309
328
  parseToolJson,
310
329
  parseToolText,
311
330
  };