reslop 0.2.5 → 0.2.7

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 (114) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/LICENSE +1 -1
  3. package/README.md +11 -11
  4. package/bin/reslop.js +3 -8
  5. package/lib/ansi.js +72 -19
  6. package/lib/api.js +1 -1
  7. package/lib/capabilities.js +8 -8
  8. package/lib/cli.js +61 -75
  9. package/lib/clipboard.js +39 -47
  10. package/lib/dashboard/git.js +128 -0
  11. package/lib/dashboard/model.js +357 -0
  12. package/lib/dashboard/npm.js +290 -0
  13. package/lib/dashboard/tree.js +278 -0
  14. package/lib/deps-diff.js +197 -402
  15. package/lib/deps.js +113 -240
  16. package/lib/detect.js +8 -18
  17. package/lib/diff/align.js +41 -113
  18. package/lib/diff/diff.js +12 -43
  19. package/lib/diff/display.js +25 -36
  20. package/lib/diff/edit.js +6 -20
  21. package/lib/diff/inline.js +76 -144
  22. package/lib/diff/items.js +33 -47
  23. package/lib/diff/parse.js +28 -56
  24. package/lib/diff/patch.js +12 -33
  25. package/lib/diff/unit.js +79 -178
  26. package/lib/editor.js +52 -69
  27. package/lib/files.js +79 -91
  28. package/lib/find.js +61 -0
  29. package/lib/git-branches.js +254 -416
  30. package/lib/git-deps.js +148 -272
  31. package/lib/git-reword-todo.js +5 -13
  32. package/lib/git-worktree.js +161 -214
  33. package/lib/git.js +21 -49
  34. package/lib/github.js +67 -181
  35. package/lib/gitlab.js +40 -90
  36. package/lib/highlight/bash.js +55 -177
  37. package/lib/highlight/core.js +155 -141
  38. package/lib/highlight/css.js +25 -58
  39. package/lib/highlight/csv.js +19 -37
  40. package/lib/highlight/dart.js +72 -213
  41. package/lib/highlight/dot.js +14 -14
  42. package/lib/highlight/html.js +42 -63
  43. package/lib/highlight/js.js +177 -506
  44. package/lib/highlight/json.js +17 -37
  45. package/lib/highlight/log.js +19 -25
  46. package/lib/highlight/txt.js +7 -10
  47. package/lib/highlight.js +17 -46
  48. package/lib/keys.js +75 -83
  49. package/lib/manifest.js +67 -126
  50. package/lib/npm-commands.js +120 -169
  51. package/lib/remote.js +140 -190
  52. package/lib/render/branches.js +63 -61
  53. package/lib/render/chrome.js +412 -347
  54. package/lib/render/commits.js +160 -361
  55. package/lib/render/dash-activity.js +212 -0
  56. package/lib/render/dash-blocks.js +496 -0
  57. package/lib/render/dash-table.js +295 -0
  58. package/lib/render/dashboard.js +62 -0
  59. package/lib/render/diff.js +123 -258
  60. package/lib/render/files.js +60 -86
  61. package/lib/render/frame.js +82 -117
  62. package/lib/render/notes.js +150 -210
  63. package/lib/render/npm.js +213 -177
  64. package/lib/render/packages.js +93 -0
  65. package/lib/render/present.js +4 -11
  66. package/lib/render/primitives.js +77 -172
  67. package/lib/render/render.js +5 -14
  68. package/lib/render/report.js +43 -0
  69. package/lib/render/repos.js +226 -0
  70. package/lib/render/search.js +25 -0
  71. package/lib/render/tiles.js +355 -0
  72. package/lib/render/unit.js +15 -31
  73. package/lib/report-model.js +110 -33
  74. package/lib/report-parse.js +56 -37
  75. package/lib/report-render.js +102 -59
  76. package/lib/report-run.js +87 -77
  77. package/lib/report-test.js +221 -99
  78. package/lib/report-tools.js +96 -154
  79. package/lib/review-merge.js +33 -41
  80. package/lib/review.js +69 -129
  81. package/lib/runs.js +225 -0
  82. package/lib/select.js +53 -91
  83. package/lib/session/accessors.js +14 -21
  84. package/lib/session/actions.js +189 -65
  85. package/lib/session/branches.js +164 -222
  86. package/lib/session/change-actions.js +49 -119
  87. package/lib/session/commit.js +298 -352
  88. package/lib/session/compose.js +572 -715
  89. package/lib/session/dashboard.js +530 -0
  90. package/lib/session/dispatch.js +197 -171
  91. package/lib/session/files-pane.js +92 -45
  92. package/lib/session/find.js +94 -0
  93. package/lib/session/items.js +6 -17
  94. package/lib/session/lifecycle.js +420 -466
  95. package/lib/session/load.js +1 -14
  96. package/lib/session/navigation.js +4 -2
  97. package/lib/session/npm.js +403 -420
  98. package/lib/session/ops.js +5 -13
  99. package/lib/session/packages.js +143 -0
  100. package/lib/session/pointer.js +30 -14
  101. package/lib/session/progress.js +39 -30
  102. package/lib/session/review.js +1 -3
  103. package/lib/session/tasks.js +277 -236
  104. package/lib/session/templates.js +51 -59
  105. package/lib/session/terminal.js +85 -132
  106. package/lib/session/view.js +59 -80
  107. package/lib/session/watch.js +201 -212
  108. package/lib/session/workspace.js +455 -0
  109. package/lib/session.js +95 -161
  110. package/lib/update.js +44 -111
  111. package/lib/utilities.js +46 -88
  112. package/lib/workspace.js +166 -0
  113. package/lib/wrap.js +29 -55
  114. package/package.json +1 -1
package/lib/manifest.js CHANGED
@@ -15,20 +15,17 @@ const SECTION_KEYS = [
15
15
  'optionalDependencies',
16
16
  'peerDependencies',
17
17
  ];
18
+ const LOCK_PREFIX = 'node_modules/';
18
19
 
19
- const posixRel = (rel) => {
20
- const raw = `${rel ?? ''}`;
21
- return raw.replaceAll('\\', '/');
22
- };
20
+ const isObject = (value) => !!value && typeof value === 'object';
21
+
22
+ const posixRel = (rel) => `${rel ?? ''}`.replaceAll('\\', '/');
23
23
 
24
24
  const depFileMeta = (rel) => {
25
25
  const normalized = posixRel(rel);
26
- if (!normalized) return null;
27
- const base = path.posix.basename(normalized);
28
- const kind = DEP_KIND[base];
29
- if (!kind) return null;
30
- const dir = path.posix.dirname(normalized);
31
- return { kind, dir, base, rel: normalized };
26
+ const kind = DEP_KIND[path.posix.basename(normalized)];
27
+ if (!normalized || !kind) return null;
28
+ return { kind, dir: path.posix.dirname(normalized) };
32
29
  };
33
30
 
34
31
  const relOf = (dir, name) => (dir === '.' ? name : `${dir}/${name}`);
@@ -59,28 +56,22 @@ const emptySectionMaps = () => readSections(null);
59
56
 
60
57
  const lockRootSections = (lock) => {
61
58
  if (!lock) return emptySectionMaps();
62
- const packages = lock.packages;
63
- const root = packages && typeof packages === 'object' ? packages[''] : null;
64
- const src = root && typeof root === 'object' ? root : lock;
65
- return readSections(src);
59
+ const root = isObject(lock.packages) ? lock.packages[''] : null;
60
+ return readSections(isObject(root) ? root : lock);
66
61
  };
67
62
 
68
63
  const lockEntries = (lock) => {
69
64
  const versions = new Map();
70
65
  if (!lock) return versions;
71
- const packages = lock.packages;
72
- if (packages && typeof packages === 'object') {
73
- for (const key of Object.keys(packages)) {
74
- const entry = packages[key];
75
- if (!key.startsWith('node_modules/')) continue;
76
- if (key.includes('/node_modules/')) continue;
77
- if (!entry || typeof entry.version !== 'string') continue;
78
- const name = key.slice('node_modules/'.length);
79
- versions.set(name, entry.version);
80
- }
66
+ const packages = isObject(lock.packages) ? lock.packages : {};
67
+ for (const key of Object.keys(packages)) {
68
+ const entry = packages[key];
69
+ if (!key.startsWith(LOCK_PREFIX)) continue;
70
+ if (key.includes('/node_modules/')) continue;
71
+ if (!entry || typeof entry.version !== 'string') continue;
72
+ versions.set(key.slice(LOCK_PREFIX.length), entry.version);
81
73
  }
82
- const deps = lock.dependencies;
83
- if (!deps || typeof deps !== 'object') return versions;
74
+ const deps = isObject(lock.dependencies) ? lock.dependencies : {};
84
75
  for (const name of Object.keys(deps)) {
85
76
  const entry = deps[name];
86
77
  if (versions.has(name)) continue;
@@ -92,44 +83,31 @@ const lockEntries = (lock) => {
92
83
 
93
84
  const lockPackageCount = (lock) => {
94
85
  if (!lock) return 0;
95
- const packages = lock.packages;
96
- if (packages && typeof packages === 'object') {
97
- let count = 0;
98
- for (const key of Object.keys(packages)) {
99
- if (key !== '') count += 1;
100
- }
101
- return count;
86
+ if (isObject(lock.packages)) {
87
+ return Object.keys(lock.packages).filter((key) => key !== '').length;
102
88
  }
103
- const deps = lock.dependencies;
104
- if (!deps || typeof deps !== 'object') return 0;
105
- return Object.keys(deps).length;
89
+ if (!isObject(lock.dependencies)) return 0;
90
+ return Object.keys(lock.dependencies).length;
106
91
  };
107
92
 
108
93
  const cloneJson = (value) => JSON.parse(JSON.stringify(value ?? null));
109
94
 
110
- const detectIndent = (text) => {
111
- const match = /\n([ \t]+)"/.exec(text);
112
- if (!match) return 2;
113
- return match[1];
114
- };
115
-
116
95
  const stringifyJson = (value, original) => {
117
- const indent = detectIndent(original || '');
118
- const body = JSON.stringify(value, null, indent);
119
- return `${body}\n`;
96
+ const match = /\n([ \t]+)"/.exec(original);
97
+ const indent = match ? match[1] : 2;
98
+ return `${JSON.stringify(value, null, indent)}\n`;
120
99
  };
121
100
 
122
101
  const setSectionEntry = (pkg, section, name, version) => {
123
- if (!version) {
124
- const map = pkg[section];
125
- if (!map || typeof map !== 'object') return;
126
- delete map[name];
127
- if (!Object.keys(map).length) delete pkg[section];
102
+ const map = pkg[section];
103
+ if (version) {
104
+ if (!isObject(map)) pkg[section] = {};
105
+ pkg[section][name] = version;
128
106
  return;
129
107
  }
130
- const map = pkg[section];
131
- if (!map || typeof map !== 'object') pkg[section] = {};
132
- pkg[section][name] = version;
108
+ if (!isObject(map)) return;
109
+ delete map[name];
110
+ if (!Object.keys(map).length) delete pkg[section];
133
111
  };
134
112
 
135
113
  const dropsEntry = (change, side) => {
@@ -138,73 +116,41 @@ const dropsEntry = (change, side) => {
138
116
  };
139
117
 
140
118
  const applyManifestChange = (pkg, change, side) => {
141
- if (change.section === 'resolved') return pkg;
119
+ if (change.section === 'resolved') return;
142
120
  const value = side === 'new' ? change.to : change.from;
143
- if (change.section === 'field') {
144
- if (value === '') {
145
- delete pkg[change.name];
146
- return pkg;
147
- }
148
- pkg[change.name] = jsonParse(value ?? '') ?? value;
149
- return pkg;
150
- }
151
121
  const version = dropsEntry(change, side) ? '' : value;
152
122
  setSectionEntry(pkg, change.section, change.name, version);
153
- return pkg;
154
123
  };
155
124
 
156
- const ensureLockRoot = (lock) => {
157
- if (!lock.packages || typeof lock.packages !== 'object') {
158
- lock.packages = {};
159
- }
160
- const root = lock.packages[''];
161
- if (!root || typeof root !== 'object') lock.packages[''] = {};
125
+ const lockRoot = (lock) => {
126
+ if (!isObject(lock.packages)) lock.packages = {};
127
+ if (!isObject(lock.packages[''])) lock.packages[''] = {};
162
128
  return lock.packages[''];
163
129
  };
164
130
 
165
- const applyLegacyLockDep = (lock, change, side, src) => {
166
- const deps = lock.dependencies;
167
- if (!deps || typeof deps !== 'object') return;
168
- if (dropsEntry(change, side)) {
169
- delete deps[change.name];
170
- return;
171
- }
172
- const srcDeps = src && src.dependencies;
173
- if (!srcDeps || typeof srcDeps !== 'object') return;
174
- const entry = srcDeps[change.name];
175
- if (entry) deps[change.name] = cloneJson(entry);
131
+ const copyLockEntry = (target, source, key, drop) => {
132
+ if (!isObject(target)) return;
133
+ if (drop) return void delete target[key];
134
+ const entry = isObject(source) ? source[key] : null;
135
+ if (entry) target[key] = cloneJson(entry);
176
136
  };
177
137
 
178
138
  const applyLockChange = (lock, change, side, oldLock, newLock) => {
179
- if (change.section === 'field') return lock;
180
- const src = side === 'new' ? newLock : oldLock;
139
+ const src = (side === 'new' ? newLock : oldLock) ?? {};
140
+ const drop = dropsEntry(change, side);
181
141
  if (change.section !== 'resolved') {
182
- const root = ensureLockRoot(lock);
183
- applyManifestChange(root, change, side);
184
- applyLegacyLockDep(lock, change, side, src);
185
- }
186
- const packages = lock.packages;
187
- if (!packages || typeof packages !== 'object') return lock;
188
- const key = `node_modules/${change.name}`;
189
- if (dropsEntry(change, side)) {
190
- delete packages[key];
191
- return lock;
142
+ applyManifestChange(lockRoot(lock), change, side);
143
+ copyLockEntry(lock.dependencies, src.dependencies, change.name, drop);
192
144
  }
193
- const srcPkgs = src && src.packages;
194
- if (!srcPkgs || typeof srcPkgs !== 'object') return lock;
195
- const entry = srcPkgs[key];
196
- if (entry) packages[key] = cloneJson(entry);
197
- return lock;
145
+ const key = `${LOCK_PREFIX}${change.name}`;
146
+ copyLockEntry(lock.packages, src.packages, key, drop);
198
147
  };
199
148
 
200
149
  const seedManifest = (oldObj, newObj) => {
201
150
  const base = cloneJson(oldObj) || {};
202
- if (!newObj) return base;
203
- for (const key of Object.keys(newObj)) {
204
- const value = newObj[key];
205
- if (SECTION_KEYS.includes(key)) continue;
206
- if (Object.hasOwn(base, key)) continue;
207
- base[key] = cloneJson(value);
151
+ for (const key of Object.keys(newObj ?? {})) {
152
+ if (SECTION_KEYS.includes(key) || Object.hasOwn(base, key)) continue;
153
+ base[key] = cloneJson(newObj[key]);
208
154
  }
209
155
  return base;
210
156
  };
@@ -219,8 +165,7 @@ const mergeDepFile = (baseText, oldText, newText, change, side, kind) => {
219
165
  else base = cloneJson(oldObj) || {};
220
166
  if (kind === 'lockfile') applyLockChange(base, change, side, oldObj, newObj);
221
167
  else applyManifestChange(base, change, side);
222
- const original = baseText || newText || oldText || '';
223
- return stringifyJson(base, original);
168
+ return stringifyJson(base, baseText || newText || oldText || '');
224
169
  };
225
170
 
226
171
  const applyWantedRange = (declared, wanted) => {
@@ -229,25 +174,26 @@ const applyWantedRange = (declared, wanted) => {
229
174
  if (/^(file:|git[+@:]|workspace:|https?:|npm:)/.test(declared)) {
230
175
  return declared;
231
176
  }
232
- if (declared.includes(' ') || declared.includes('|')) return declared;
233
- if (declared.includes('<') || declared.includes('>')) return declared;
177
+ if (/[ |<>]/.test(declared)) return declared;
234
178
  const prefix = declared.startsWith('^') || declared.startsWith('~');
235
179
  const mark = prefix ? declared[0] : '';
236
180
  return `${mark}${wanted}`;
237
181
  };
238
182
 
183
+ const declaredIn = (pkg, section, name) => {
184
+ const map = pkg[section];
185
+ if (!map || typeof map[name] !== 'string') return null;
186
+ return { section, version: map[name] };
187
+ };
188
+
239
189
  const findDeclared = (pkg, name, type) => {
240
190
  if (type && SECTION_KEYS.includes(type)) {
241
- const map = pkg[type];
242
- if (map && typeof map[name] === 'string') {
243
- return { section: type, version: map[name] };
244
- }
191
+ const found = declaredIn(pkg, type, name);
192
+ if (found) return found;
245
193
  }
246
194
  for (const section of SECTION_KEYS) {
247
- const map = pkg[section];
248
- if (map && typeof map[name] === 'string') {
249
- return { section, version: map[name] };
250
- }
195
+ const found = declaredIn(pkg, section, name);
196
+ if (found) return found;
251
197
  }
252
198
  return null;
253
199
  };
@@ -255,15 +201,12 @@ const findDeclared = (pkg, name, type) => {
255
201
  const fmtVer = (parts) => `${parts.major}.${parts.minor}.${parts.patch}`;
256
202
 
257
203
  const clauseUpper = (clause) => {
258
- const pattern = /(<=?)\s*(\d+\.\d+\.\d+)/g;
259
- const matches = clause.matchAll(pattern);
260
204
  let best = null;
261
- for (const match of matches) {
262
- const op = match[1];
205
+ for (const match of clause.matchAll(/(<=?)\s*(\d+\.\d+\.\d+)/g)) {
263
206
  const parsed = parseVersion(match[2]);
264
207
  if (!parsed) continue;
265
208
  if (best && cmpVersion(parsed, best.ver) >= 0) continue;
266
- best = { op, ver: parsed };
209
+ best = { op: match[1], ver: parsed };
267
210
  }
268
211
  return best;
269
212
  };
@@ -272,8 +215,7 @@ const patchedFromRange = (current, range) => {
272
215
  if (!current || !range) return '';
273
216
  const now = parseVersion(current);
274
217
  if (!now) return '';
275
- const clauses = `${range}`.split('||');
276
- for (const clause of clauses) {
218
+ for (const clause of `${range}`.split('||')) {
277
219
  const upper = clauseUpper(clause);
278
220
  if (!upper) continue;
279
221
  if (now.major !== upper.ver.major) continue;
@@ -287,8 +229,7 @@ const patchedFromRange = (current, range) => {
287
229
  const sameMajor = (from, to) => {
288
230
  const left = parseVersion(from);
289
231
  const right = parseVersion(to);
290
- if (!left || !right) return false;
291
- return left.major === right.major;
232
+ return !!left && !!right && left.major === right.major;
292
233
  };
293
234
 
294
235
  const proposedLockVersion = (current, vul) => {
@@ -297,8 +238,7 @@ const proposedLockVersion = (current, vul) => {
297
238
  const fix = vul.fix;
298
239
  if (fix && sameMajor(current, fix) && fix !== current) return fix;
299
240
  const patched = patchedFromRange(current, vul.range);
300
- if (patched && patched !== current) return patched;
301
- return current;
241
+ return patched || current;
302
242
  };
303
243
 
304
244
  const proposedWanted = (out, vul) => {
@@ -311,6 +251,7 @@ module.exports = {
311
251
  MANIFEST,
312
252
  LOCKFILE,
313
253
  SECTION_KEYS,
254
+ isObject,
314
255
  depFileMeta,
315
256
  relOf,
316
257
  listFilePath,