reslop 0.2.5 → 0.2.6

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 (105) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/LICENSE +1 -1
  3. package/README.md +11 -11
  4. package/bin/reslop.js +3 -8
  5. package/lib/ansi.js +32 -19
  6. package/lib/capabilities.js +8 -8
  7. package/lib/cli.js +40 -74
  8. package/lib/clipboard.js +39 -47
  9. package/lib/dashboard/git.js +128 -0
  10. package/lib/dashboard/model.js +332 -0
  11. package/lib/dashboard/npm.js +193 -0
  12. package/lib/dashboard/tree.js +278 -0
  13. package/lib/deps-diff.js +197 -402
  14. package/lib/deps.js +113 -240
  15. package/lib/detect.js +8 -18
  16. package/lib/diff/align.js +41 -113
  17. package/lib/diff/diff.js +12 -43
  18. package/lib/diff/display.js +25 -36
  19. package/lib/diff/edit.js +6 -20
  20. package/lib/diff/inline.js +76 -144
  21. package/lib/diff/items.js +33 -47
  22. package/lib/diff/parse.js +28 -56
  23. package/lib/diff/patch.js +12 -33
  24. package/lib/diff/unit.js +79 -178
  25. package/lib/editor.js +52 -69
  26. package/lib/files.js +71 -89
  27. package/lib/git-branches.js +254 -416
  28. package/lib/git-deps.js +147 -272
  29. package/lib/git-reword-todo.js +5 -13
  30. package/lib/git-worktree.js +141 -212
  31. package/lib/git.js +21 -49
  32. package/lib/github.js +67 -181
  33. package/lib/gitlab.js +40 -90
  34. package/lib/highlight/bash.js +55 -177
  35. package/lib/highlight/core.js +155 -141
  36. package/lib/highlight/css.js +25 -58
  37. package/lib/highlight/csv.js +19 -37
  38. package/lib/highlight/dart.js +72 -213
  39. package/lib/highlight/dot.js +14 -14
  40. package/lib/highlight/html.js +42 -63
  41. package/lib/highlight/js.js +177 -506
  42. package/lib/highlight/json.js +17 -37
  43. package/lib/highlight/log.js +19 -25
  44. package/lib/highlight/txt.js +7 -10
  45. package/lib/highlight.js +17 -46
  46. package/lib/keys.js +49 -82
  47. package/lib/manifest.js +67 -126
  48. package/lib/npm-commands.js +120 -169
  49. package/lib/remote.js +140 -190
  50. package/lib/render/branches.js +63 -61
  51. package/lib/render/chrome.js +220 -333
  52. package/lib/render/commits.js +160 -361
  53. package/lib/render/dash-activity.js +212 -0
  54. package/lib/render/dash-blocks.js +245 -0
  55. package/lib/render/dash-table.js +290 -0
  56. package/lib/render/dashboard.js +62 -0
  57. package/lib/render/diff.js +123 -258
  58. package/lib/render/files.js +40 -85
  59. package/lib/render/frame.js +78 -117
  60. package/lib/render/notes.js +139 -210
  61. package/lib/render/npm.js +213 -177
  62. package/lib/render/present.js +4 -11
  63. package/lib/render/primitives.js +72 -174
  64. package/lib/render/render.js +5 -14
  65. package/lib/render/report.js +43 -0
  66. package/lib/render/tiles.js +260 -0
  67. package/lib/render/unit.js +15 -31
  68. package/lib/report-model.js +110 -33
  69. package/lib/report-parse.js +56 -37
  70. package/lib/report-render.js +102 -59
  71. package/lib/report-run.js +87 -77
  72. package/lib/report-test.js +221 -99
  73. package/lib/report-tools.js +96 -154
  74. package/lib/review-merge.js +33 -41
  75. package/lib/review.js +68 -129
  76. package/lib/runs.js +225 -0
  77. package/lib/select.js +53 -91
  78. package/lib/session/accessors.js +14 -21
  79. package/lib/session/actions.js +61 -43
  80. package/lib/session/branches.js +166 -222
  81. package/lib/session/change-actions.js +49 -119
  82. package/lib/session/commit.js +274 -355
  83. package/lib/session/compose.js +556 -714
  84. package/lib/session/dashboard.js +385 -0
  85. package/lib/session/dispatch.js +162 -170
  86. package/lib/session/files-pane.js +76 -44
  87. package/lib/session/items.js +6 -17
  88. package/lib/session/lifecycle.js +393 -466
  89. package/lib/session/load.js +1 -14
  90. package/lib/session/navigation.js +2 -2
  91. package/lib/session/npm.js +403 -420
  92. package/lib/session/ops.js +5 -13
  93. package/lib/session/pointer.js +16 -14
  94. package/lib/session/progress.js +39 -30
  95. package/lib/session/review.js +1 -3
  96. package/lib/session/tasks.js +276 -235
  97. package/lib/session/templates.js +51 -59
  98. package/lib/session/terminal.js +85 -132
  99. package/lib/session/view.js +47 -81
  100. package/lib/session/watch.js +196 -212
  101. package/lib/session.js +81 -161
  102. package/lib/update.js +44 -111
  103. package/lib/utilities.js +46 -88
  104. package/lib/wrap.js +29 -55
  105. package/package.json +1 -1
@@ -6,41 +6,52 @@ const model = require('./report-model.js');
6
6
 
7
7
  const { summary, problem, report, userTrace } = model;
8
8
 
9
+ const PRETTIER_SUMMARY = [
10
+ 'Code style issues',
11
+ 'All matched files use Prettier',
12
+ ];
13
+
9
14
  const place = (file, line, column, root) =>
10
15
  userTrace(`${file}:${line}:${column}`, root);
11
16
 
17
+ const errorReport = (tool, problems) => {
18
+ const stats = summary();
19
+ stats.errors = problems.length;
20
+ return report(tool, { summary: stats, problems });
21
+ };
22
+
23
+ const eslintReport = (problems) => {
24
+ const stats = summary();
25
+ const isWarning = (item) => item.severity === 'warning';
26
+ stats.warnings = problems.filter(isWarning).length;
27
+ stats.errors = problems.length - stats.warnings;
28
+ return report('eslint', { summary: stats, problems });
29
+ };
30
+
12
31
  const parseEslintJson = (data, root) => {
13
32
  const rows = Array.isArray(data) ? data : data && data.results;
14
33
  if (!Array.isArray(rows) || !rows.length) return null;
15
34
  if (!rows[0] || !Array.isArray(rows[0].messages)) return null;
16
- const stats = summary();
17
- let errors = 0;
18
- let warnings = 0;
19
35
  const problems = [];
20
36
  for (const file of rows) {
21
- const messages = file.messages || [];
22
- for (const item of messages) {
23
- const severity = item.severity === 1 ? 'warning' : 'error';
24
- if (severity === 'warning') warnings += 1;
25
- else errors += 1;
37
+ for (const item of file.messages || []) {
38
+ const trace = place(
39
+ file.filePath || '',
40
+ item.line || 0,
41
+ item.column || 0,
42
+ root,
43
+ );
26
44
  problems.push(
27
45
  problem({
28
- severity,
46
+ severity: item.severity === 1 ? 'warning' : 'error',
29
47
  message: item.message || '',
30
48
  code: item.ruleId || '',
31
- trace: place(
32
- file.filePath || '',
33
- item.line || 0,
34
- item.column || 0,
35
- root,
36
- ),
49
+ trace,
37
50
  }),
38
51
  );
39
52
  }
40
53
  }
41
- stats.errors = errors;
42
- stats.warnings = warnings;
43
- return report('eslint', { summary: stats, problems });
54
+ return eslintReport(problems);
44
55
  };
45
56
 
46
57
  const eslintLine = (line) => {
@@ -67,69 +78,35 @@ const isPathLine = (line) => {
67
78
  };
68
79
 
69
80
  const parseEslintStylish = (text, root) => {
70
- const lines = `${text ?? ''}`.split(/\r?\n/);
71
81
  const problems = [];
72
82
  let file = '';
73
- let errors = 0;
74
- let warnings = 0;
75
83
  let seen = false;
76
- for (const line of lines) {
84
+ for (const line of `${text ?? ''}`.split(/\r?\n/)) {
77
85
  const item = eslintLine(line);
78
86
  if (item) {
79
87
  seen = true;
80
- if (item.severity === 'warning') warnings += 1;
81
- else errors += 1;
82
- problems.push(
83
- problem({
84
- severity: item.severity,
85
- message: item.message,
86
- code: item.code,
87
- trace: place(file, item.line, item.column, root),
88
- }),
89
- );
88
+ const { severity, message, code } = item;
89
+ const trace = place(file, item.line, item.column, root);
90
+ problems.push(problem({ severity, message, code, trace }));
90
91
  continue;
91
92
  }
92
93
  if (/✖\s+\d+\s+problems?\b/.test(line)) seen = true;
93
94
  if (isPathLine(line)) file = line.trim();
94
95
  }
95
- if (!seen) return null;
96
- const stats = summary();
97
- stats.errors = errors;
98
- stats.warnings = warnings;
99
- return report('eslint', { summary: stats, problems });
96
+ return seen ? eslintReport(problems) : null;
100
97
  };
101
98
 
102
- const tscLine = (line) => {
103
- const match = /^(.*)\((\d+),(\d+)\):\s+error\s+(TS\d+):\s+(.*)$/.exec(
104
- line.trim(),
105
- );
106
- if (!match) return null;
107
- return {
108
- file: match[1],
109
- line: match[2],
110
- column: match[3],
111
- code: match[4],
112
- message: match[5],
113
- };
114
- };
99
+ const TSC_LINE = /^(.*)\((\d+),(\d+)\):\s+error\s+(TS\d+):\s+(.*)$/;
115
100
 
116
101
  const parseTsc = (text, root) => {
117
102
  const problems = [];
118
103
  for (const line of `${text ?? ''}`.split(/\r?\n/)) {
119
- const item = tscLine(line);
120
- if (!item) continue;
121
- problems.push(
122
- problem({
123
- message: item.message,
124
- code: item.code,
125
- trace: place(item.file, item.line, item.column, root),
126
- }),
127
- );
104
+ const match = TSC_LINE.exec(line.trim());
105
+ if (!match) continue;
106
+ const trace = place(match[1], match[2], match[3], root);
107
+ problems.push(problem({ message: match[5], code: match[4], trace }));
128
108
  }
129
- if (!problems.length) return null;
130
- const stats = summary();
131
- stats.errors = problems.length;
132
- return report('tsc', { summary: stats, problems });
109
+ return problems.length ? errorReport('tsc', problems) : null;
133
110
  };
134
111
 
135
112
  const prettierFile = (line) => {
@@ -138,76 +115,53 @@ const prettierFile = (line) => {
138
115
  return '';
139
116
  };
140
117
 
118
+ const isPrettierSummary = (line) =>
119
+ PRETTIER_SUMMARY.some((text) => line.includes(text));
120
+
141
121
  const parsePrettier = (text, root) => {
142
- const lines = `${text ?? ''}`.split(/\r?\n/);
143
122
  const problems = [];
144
123
  let seen = false;
145
- for (const line of lines) {
146
- if (line.includes('Code style issues')) seen = true;
147
- if (line.includes('All matched files use Prettier')) seen = true;
124
+ for (const line of `${text ?? ''}`.split(/\r?\n/)) {
125
+ if (isPrettierSummary(line)) seen = true;
148
126
  const file = prettierFile(line);
149
127
  if (!file) continue;
150
128
  seen = true;
151
- problems.push(
152
- problem({
153
- message: 'format',
154
- trace: userTrace(file, root),
155
- }),
156
- );
129
+ problems.push(problem({ message: 'format', trace: userTrace(file, root) }));
157
130
  }
158
- if (!seen) return null;
159
- const stats = summary();
160
- stats.errors = problems.length;
161
- return report('prettier', { summary: stats, problems });
131
+ return seen ? errorReport('prettier', problems) : null;
162
132
  };
163
133
 
164
134
  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];
135
+ const extra = [];
136
+ if (item.range) extra.push(['range', item.range]);
137
+ if (item.fix) extra.push(['fix', item.fix]);
173
138
  return extra;
174
139
  };
175
140
 
176
- const auditProblems = (text) => {
177
- const audit = parseAuditReport(text);
178
- const problems = new Array(audit.size);
179
- let index = 0;
180
- for (const item of audit.values()) {
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;
188
- }
189
- return problems;
190
- };
191
-
192
141
  const parseAudit = (text) => {
193
- const problems = auditProblems(text);
194
- if (!problems.length) return null;
195
- const stats = summary();
196
- stats.errors = problems.length;
197
- return report('audit', { summary: stats, problems });
142
+ const problems = [];
143
+ for (const item of parseAuditReport(text).values()) {
144
+ problems.push(
145
+ problem({
146
+ severity: item.severity || 'error',
147
+ name: item.name,
148
+ message: item.title || '',
149
+ extra: auditExtra(item),
150
+ }),
151
+ );
152
+ }
153
+ return problems.length ? errorReport('audit', problems) : null;
198
154
  };
199
155
 
200
156
  const behind = (item) => {
201
157
  if (!item.current) return true;
202
158
  if (item.wanted && item.wanted !== item.current) return true;
203
- if (item.latest && item.latest !== item.current) return true;
204
- return false;
159
+ return !!item.latest && item.latest !== item.current;
205
160
  };
206
161
 
207
162
  const parseOutdated = (text) => {
208
- const outdated = parseOutdatedReport(text);
209
163
  const problems = [];
210
- for (const [name, item] of outdated) {
164
+ for (const [name, item] of parseOutdatedReport(text)) {
211
165
  if (!behind(item)) continue;
212
166
  problems.push(
213
167
  problem({
@@ -230,8 +184,7 @@ const parseOutdated = (text) => {
230
184
 
231
185
  const pushLs = (problems, text) => {
232
186
  const message = `${text ?? ''}`.trim();
233
- if (!message) return;
234
- problems.push(problem({ message }));
187
+ if (message) problems.push(problem({ message }));
235
188
  };
236
189
 
237
190
  const walkLs = (node, problems) => {
@@ -251,13 +204,12 @@ const walkLs = (node, problems) => {
251
204
 
252
205
  const parseLs = (data) => {
253
206
  if (!data || typeof data !== 'object' || Array.isArray(data)) return null;
254
- if (!data.dependencies && !Array.isArray(data.problems)) return null;
207
+ const listed = Array.isArray(data.problems);
208
+ if (!data.dependencies && !listed) return null;
255
209
  const problems = [];
256
210
  walkLs(data, problems);
257
- if (!problems.length && !Array.isArray(data.problems)) return null;
258
- const stats = summary();
259
- stats.errors = problems.length;
260
- return report('ls', { summary: stats, problems });
211
+ if (!problems.length && !listed) return null;
212
+ return errorReport('ls', problems);
261
213
  };
262
214
 
263
215
  const parseToolJson = (data, root) => {
@@ -281,50 +233,40 @@ const parseAuditText = (text) => {
281
233
  const match = /^Severity:\s+(\w+)\s*$/.exec(lines[index].trim());
282
234
  if (!match) continue;
283
235
  const name = (lines[index - 1] || '').trim();
284
- problems.push(
285
- problem({
286
- severity: match[1].toLowerCase(),
287
- name,
288
- message: name,
289
- }),
290
- );
236
+ const severity = match[1].toLowerCase();
237
+ problems.push(problem({ severity, name, message: name }));
291
238
  }
292
- if (!problems.length) return null;
293
- const stats = summary();
294
- stats.errors = problems.length;
295
- return report('audit', { summary: stats, problems });
239
+ return problems.length ? errorReport('audit', problems) : null;
296
240
  };
297
241
 
298
- const TOOL_SLOTS = 4;
299
-
300
242
  const parseToolText = (text, root) => {
301
- const reports = new Array(TOOL_SLOTS);
302
- let index = 0;
303
- const eslint = parseEslintStylish(text, root);
304
- if (eslint) {
305
- reports[index] = eslint;
306
- index += 1;
307
- }
308
- const tsc = parseTsc(text, root);
309
- if (tsc) {
310
- reports[index] = tsc;
311
- index += 1;
312
- }
313
- const prettier = parsePrettier(text, root);
314
- if (prettier) {
315
- reports[index] = prettier;
316
- index += 1;
317
- }
318
- const audit = parseAuditText(text);
319
- if (audit) {
320
- reports[index] = audit;
321
- index += 1;
322
- }
323
- reports.length = index;
324
- return reports;
243
+ const reports = [
244
+ parseEslintStylish(text, root),
245
+ parseTsc(text, root),
246
+ parsePrettier(text, root),
247
+ parseAuditText(text),
248
+ ];
249
+ return reports.filter(Boolean);
250
+ };
251
+
252
+ const isPrettierLine = (line) => {
253
+ const text = line.trim();
254
+ if (!text) return false;
255
+ if (prettierFile(text) || isPrettierSummary(text)) return true;
256
+ return text.startsWith('Checking formatting');
257
+ };
258
+
259
+ const stderrRest = (text, reports) => {
260
+ if (!reports.some((item) => item.tool === 'prettier')) return text;
261
+ const lines = `${text ?? ''}`.split(/\r?\n/);
262
+ return lines
263
+ .filter((line) => !isPrettierLine(line))
264
+ .join('\n')
265
+ .trim();
325
266
  };
326
267
 
327
268
  module.exports = {
328
269
  parseToolJson,
329
270
  parseToolText,
271
+ stderrRest,
330
272
  };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const files = require('./files.js');
4
- const { TASKS_FILE } = files;
4
+ const { TASKS_FILE, taskKind } = files;
5
5
 
6
6
  const flatText = (text) => `${text ?? ''}`.trim().replace(/\n/g, ' ');
7
7
 
@@ -9,21 +9,13 @@ const snapTodo = (todo) => ({
9
9
  file: todo.file ?? '',
10
10
  text: flatText(todo.text),
11
11
  done: todo.done === true,
12
+ kind: taskKind(todo),
12
13
  });
13
14
 
14
- const snapList = (todos) => {
15
- const list = [];
16
- for (const todo of todos ?? []) {
17
- const snap = snapTodo(todo);
18
- if (!snap.text) continue;
19
- list.push(snap);
20
- }
21
- return list;
22
- };
15
+ const snapList = (todos) => todos.map(snapTodo).filter((snap) => snap.text);
23
16
 
24
17
  const copyMap = (map) => {
25
18
  const next = new Map();
26
- if (!map) return next;
27
19
  for (const [key, note] of map) next.set(key, { ...note });
28
20
  return next;
29
21
  };
@@ -58,27 +50,31 @@ const pickDone = (baseDone, ourDone, theirDone) => {
58
50
 
59
51
  const withDone = (todo, done) => {
60
52
  if (todo.done === done) return todo;
61
- return { id: todo.id, file: todo.file, text: todo.text, done };
53
+ return { ...todo, done };
62
54
  };
63
55
 
64
56
  const liveText = (todo) => flatText(todo.text);
65
57
 
66
58
  const snapText = (snap) => snap.text;
67
59
 
60
+ const liveKey = (todo) => `${taskKind(todo)}\0${liveText(todo)}`;
61
+
62
+ const snapKey = (snap) => `${taskKind(snap)}\0${snapText(snap)}`;
63
+
68
64
  const flags = (n) => new Array(n).fill(false);
69
65
 
70
66
  const adoptTodo = (store, snap) => {
71
67
  const id = store.nextTaskId;
72
68
  store.nextTaskId += 1;
73
69
  const file = snap.file || TASKS_FILE;
74
- return { id, file, text: snap.text, done: snap.done };
70
+ return { id, file, text: snap.text, done: snap.done, kind: taskKind(snap) };
75
71
  };
76
72
 
77
73
  const replaceTodo = (todo, snap) => ({
78
- id: todo.id,
79
- file: todo.file,
74
+ ...todo,
80
75
  text: snap.text,
81
76
  done: snap.done,
77
+ kind: taskKind(snap),
82
78
  });
83
79
 
84
80
  class TodoMerge {
@@ -94,23 +90,23 @@ class TodoMerge {
94
90
  this.alias = new Map();
95
91
  }
96
92
 
97
- keepOurs(index, text) {
93
+ keepOurs(index) {
98
94
  this.kept[index] = true;
99
- this.alias.set(text, this.ours[index]);
95
+ this.alias.set(liveKey(this.ours[index]), this.ours[index]);
100
96
  }
101
97
 
102
98
  mergeBase(base) {
103
99
  const { ours, theirs } = this;
104
- const text = base.text;
105
- const oi = claim(ours, this.usedOurs, text, liveText);
106
- const ti = claim(theirs, this.usedTheirs, text, snapText);
100
+ const key = snapKey(base);
101
+ const oi = claim(ours, this.usedOurs, key, liveKey);
102
+ const ti = claim(theirs, this.usedTheirs, key, snapKey);
107
103
  if (oi >= 0 && ti >= 0) {
108
104
  const done = pickDone(base.done, ours[oi].done === true, theirs[ti].done);
109
105
  ours[oi] = withDone(ours[oi], done);
110
- this.keepOurs(oi, text);
106
+ this.keepOurs(oi);
111
107
  } else if (oi >= 0) {
112
108
  if (ours[oi].done === base.done) this.dropped[oi] = true;
113
- else this.keepOurs(oi, text);
109
+ else this.keepOurs(oi);
114
110
  } else if (ti >= 0 && theirs[ti].done !== base.done) {
115
111
  this.theirKeep[ti] = true;
116
112
  }
@@ -118,32 +114,33 @@ class TodoMerge {
118
114
 
119
115
  mergeFresh(index) {
120
116
  const { ours, theirs } = this;
121
- const text = liveText(ours[index]);
122
- const ti = claim(theirs, this.usedTheirs, text, snapText);
117
+ const key = liveKey(ours[index]);
118
+ const ti = claim(theirs, this.usedTheirs, key, snapKey);
123
119
  if (ti < 0) {
124
120
  this.kept[index] = true;
125
121
  return;
126
122
  }
127
123
  const done = ours[index].done === true || theirs[ti].done === true;
128
124
  ours[index] = withDone(ours[index], done);
129
- this.keepOurs(index, text);
125
+ this.keepOurs(index);
130
126
  }
131
127
 
132
- findMerged(merged, text) {
133
- const linked = this.alias.get(text);
128
+ findMerged(merged, snap) {
129
+ const key = snapKey(snap);
130
+ const linked = this.alias.get(key);
134
131
  const at = linked ? merged.indexOf(linked) : -1;
135
132
  if (at >= 0) return at;
136
- return merged.findIndex((todo) => liveText(todo) === text);
133
+ return merged.findIndex((todo) => liveKey(todo) === key);
137
134
  }
138
135
 
139
136
  theirPlace(merged, index) {
140
137
  const { theirs } = this;
141
138
  for (let j = index - 1; j >= 0; j--) {
142
- const prev = this.findMerged(merged, theirs[j].text);
139
+ const prev = this.findMerged(merged, theirs[j]);
143
140
  if (prev >= 0) return prev + 1;
144
141
  }
145
142
  for (let j = index + 1; j < theirs.length; j++) {
146
- const next = this.findMerged(merged, theirs[j].text);
143
+ const next = this.findMerged(merged, theirs[j]);
147
144
  if (next >= 0) return next;
148
145
  }
149
146
  return merged.length;
@@ -151,7 +148,7 @@ class TodoMerge {
151
148
 
152
149
  place(merged, at, index, item) {
153
150
  merged.splice(at, 0, item);
154
- this.alias.set(this.theirs[index].text, item);
151
+ this.alias.set(snapKey(this.theirs[index]), item);
155
152
  this.theirKeep[index] = false;
156
153
  }
157
154
 
@@ -214,10 +211,7 @@ const pickNote = (ours, base, theirs) => {
214
211
  };
215
212
 
216
213
  const mergeMap = (ours, base, theirs) => {
217
- const keys = new Set();
218
- for (const key of ours.keys()) keys.add(key);
219
- for (const key of base.keys()) keys.add(key);
220
- for (const key of theirs.keys()) keys.add(key);
214
+ const keys = new Set([...ours.keys(), ...base.keys(), ...theirs.keys()]);
221
215
  for (const key of keys) {
222
216
  const next = pickNote(ours.get(key), base.get(key), theirs.get(key));
223
217
  if (!next) ours.delete(key);
@@ -227,13 +221,11 @@ const mergeMap = (ours, base, theirs) => {
227
221
 
228
222
  const mergeReview = (store, disk) => {
229
223
  const base = store.baseline;
230
- const baseTodos = base?.tasks ?? base?.todos ?? [];
231
224
  const baseStatus = base ? base.status : store.status;
232
- store.tasks = new TodoMerge(store, snapList(disk.tasks)).merge(baseTodos);
233
- const feedback = base ? base.feedback : new Map();
234
- const code = base ? base.code : new Map();
235
- mergeMap(store.feedback, feedback, disk.feedback);
236
- mergeMap(store.code, code, disk.code);
225
+ const theirs = snapList(disk.tasks);
226
+ store.tasks = new TodoMerge(store, theirs).merge(base?.tasks ?? []);
227
+ mergeMap(store.feedback, base?.feedback ?? new Map(), disk.feedback);
228
+ mergeMap(store.code, base?.code ?? new Map(), disk.code);
237
229
  if (store.status === baseStatus && disk.status) store.status = disk.status;
238
230
  store.baseline = snapshotSide(disk);
239
231
  };