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.
Files changed (80) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/README.md +8 -3
  3. package/bin/reslop.js +4 -2
  4. package/lib/api.js +2 -1
  5. package/lib/cli.js +19 -10
  6. package/lib/clipboard.js +6 -3
  7. package/lib/deps-diff.js +61 -115
  8. package/lib/deps.js +24 -61
  9. package/lib/detect.js +4 -2
  10. package/lib/diff/align.js +2 -1
  11. package/lib/diff/diff.js +6 -3
  12. package/lib/diff/display.js +2 -1
  13. package/lib/diff/inline.js +2 -1
  14. package/lib/diff/patch.js +2 -1
  15. package/lib/diff/unit.js +4 -2
  16. package/lib/editor.js +22 -4
  17. package/lib/files.js +38 -12
  18. package/lib/git-branches.js +107 -154
  19. package/lib/git-deps.js +20 -60
  20. package/lib/git-worktree.js +508 -0
  21. package/lib/git.js +23 -580
  22. package/lib/github.js +2 -1
  23. package/lib/gitlab.js +2 -1
  24. package/lib/highlight/bash.js +2 -1
  25. package/lib/highlight/css.js +2 -1
  26. package/lib/highlight/csv.js +2 -1
  27. package/lib/highlight/dot.js +2 -1
  28. package/lib/highlight/html.js +2 -1
  29. package/lib/highlight/json.js +2 -1
  30. package/lib/highlight/log.js +2 -1
  31. package/lib/highlight/txt.js +2 -1
  32. package/lib/highlight.js +2 -1
  33. package/lib/keys.js +2 -2
  34. package/lib/manifest.js +4 -2
  35. package/lib/npm-commands.js +83 -4
  36. package/lib/remote.js +20 -13
  37. package/lib/render/branches.js +6 -15
  38. package/lib/render/chrome.js +27 -7
  39. package/lib/render/commits.js +158 -46
  40. package/lib/render/diff.js +130 -47
  41. package/lib/render/files.js +4 -12
  42. package/lib/render/frame.js +3 -9
  43. package/lib/render/notes.js +4 -11
  44. package/lib/render/npm.js +1 -13
  45. package/lib/render/present.js +2 -1
  46. package/lib/render/primitives.js +28 -15
  47. package/lib/render/render.js +2 -1
  48. package/lib/render/unit.js +14 -11
  49. package/lib/report-model.js +6 -4
  50. package/lib/report-parse.js +8 -13
  51. package/lib/report-render.js +38 -21
  52. package/lib/report-run.js +12 -10
  53. package/lib/report-test.js +5 -4
  54. package/lib/report-tools.js +44 -18
  55. package/lib/review-merge.js +2 -1
  56. package/lib/review.js +6 -2
  57. package/lib/session/actions.js +49 -65
  58. package/lib/session/branches.js +13 -49
  59. package/lib/session/change-actions.js +2 -1
  60. package/lib/session/commit.js +34 -49
  61. package/lib/session/compose.js +37 -11
  62. package/lib/session/dispatch.js +17 -25
  63. package/lib/session/files-pane.js +6 -27
  64. package/lib/session/items.js +6 -3
  65. package/lib/session/lifecycle.js +8 -4
  66. package/lib/session/load.js +4 -2
  67. package/lib/session/navigation.js +1 -0
  68. package/lib/session/npm.js +54 -1
  69. package/lib/session/ops.js +15 -1
  70. package/lib/session/pointer.js +22 -3
  71. package/lib/session/review.js +2 -1
  72. package/lib/session/terminal.js +4 -2
  73. package/lib/session/update.js +2 -1
  74. package/lib/session/view.js +29 -3
  75. package/lib/session.js +37 -23
  76. package/lib/source.js +6 -3
  77. package/lib/update.js +5 -9
  78. package/lib/utilities.js +4 -2
  79. package/lib/wrap.js +3 -1
  80. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## [Unreleased][unreleased]
4
4
 
5
+ ## [0.2.4][] - 2026-09-28
6
+
7
+ - Add AI instructions to run `npm t` or `reslop t -- node --test`
8
+ - Cleanup old logs (older than 5 days)
9
+ - Rename TODO list to Project Backlog and Issues
10
+ - Word wrap in view and horizontal scrolling in edit mode
11
+ - Return to uncommitted changes when viewing history in read-only mode
12
+ - Improve TUI/TUX for commits and npm commands
13
+ - Remove circular dependencies between Git modules
14
+ - Unify list rendering and keyboard navigation
15
+ - Simplify dependency transformations and remove redundant wrappers
16
+
5
17
  ## [0.2.3][] - 2026-09-27
6
18
 
7
19
  - Add brief and full commit lists, toggled with `v`
@@ -129,7 +141,8 @@
129
141
  - Feedback and todos that produce a repair plan for an agent
130
142
  - Intra-line highlighting in unified, mixed, and side-by-side layouts
131
143
 
132
- [unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.2.3...HEAD
144
+ [unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.2.4...HEAD
145
+ [0.2.4]: https://github.com/tshemsedinov/reslop/compare/v0.2.3...v0.2.4
133
146
  [0.2.3]: https://github.com/tshemsedinov/reslop/compare/v0.2.1...v0.2.3
134
147
  [0.2.1]: https://github.com/tshemsedinov/reslop/compare/v0.2.0...v0.2.1
135
148
  [0.2.0]: https://github.com/tshemsedinov/reslop/compare/v0.1.9...v0.2.0
package/README.md CHANGED
@@ -11,21 +11,24 @@
11
11
 
12
12
  > Turn generated changes into owned changes
13
13
 
14
- Open a change, leave findings and todos, and prepare a repair plan
14
+ Open a change, leave findings and a backlog, and prepare a repair plan
15
15
  for the agent.
16
16
 
17
17
  ```text
18
18
  Review → Plan → Repair → Verify
19
19
  ```
20
20
 
21
- - Leave feedback, issues/todos, and in-place code proposals as a repair plan
21
+ - Leave feedback, a project backlog, and in-place code proposals as a repair plan
22
22
  - Review uncommitted git diffs, a given commit, a GitHub PR, or a GitLab MR
23
23
  - File-scope review mode: not just diffs, with editor mode
24
24
  - Stage, unstage, or revert each contiguous block of diff lines
25
25
  - Auto-reload local diffs when files change, keeping the current screen
26
26
  - Import GitHub PR and GitLab MR review comments into the local plan for AI
27
- - Commits list: commit, amend, fixup, reword, drop, pull, and push
27
+ - Commits list: brief and full, view a commit's diff, commit, amend, fixup, reword, drop, pull, and push
28
28
  - Branches: checkout, create, rebase, drop, pull, and push
29
+ - npm scripts and bins: run, edit, and reorder; double-click to run; clean logs older than 5 days
30
+ - Long lines wrap in the diff view; the editor scrolls horizontally
31
+ - Reduced test and lint output for agents: failures and a short summary, not passing noise
29
32
  - Review each npm dependency once across `package.json` and the lockfile
30
33
  - Propose unused removals, npm audit fixes, and outdated updates as diffs
31
34
  - Code highlighting and intra-line diffs: unified, mixed, and side-by-side
@@ -61,6 +64,8 @@ Prefix each program in an npm script with `reslop t --`. Keep `&&` between those
61
64
 
62
65
  The report is Markdown: failures, diagnostics, and a short summary. Passing results are omitted and similar problems are grouped. The same text is saved under `.log/`. Set `RESLOP_OUTPUT=raw` to pass the command through unchanged. The review screen runs scripts in raw mode and applies its own filter once.
63
66
 
67
+ Run the full check with `npm t`. Run one or more test files with `reslop t -- node --test <files>`. New reviews include those two commands in the agent instructions.
68
+
64
69
  Reviews go in `.review/YYYY-MM-DD-NN.md` with frontmatter `status`:
65
70
 
66
71
  - `editing`: still writing the review in reslop
package/bin/reslop.js CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- const { run, errorMessage } = require('../lib/cli.js');
5
- const { LEAVE_TERM } = require('../lib/session.js');
4
+ const cli = require('../lib/cli.js');
5
+ const { run, errorMessage } = cli;
6
+ const session = require('../lib/session.js');
7
+ const { LEAVE_TERM } = session;
6
8
 
7
9
  const fail = (reason) => {
8
10
  try {
package/lib/api.js CHANGED
@@ -10,7 +10,8 @@ const detect = require('./detect.js');
10
10
  const github = require('./github.js');
11
11
  const gitlab = require('./gitlab.js');
12
12
  const source = require('./source.js');
13
- const { Session } = require('./session.js');
13
+ const session = require('./session.js');
14
+ const { Session } = session;
14
15
  const cli = require('./cli.js');
15
16
 
16
17
  module.exports = {
package/lib/cli.js CHANGED
@@ -2,16 +2,25 @@
2
2
 
3
3
  const path = require('node:path');
4
4
 
5
- const { exists } = require('metautil');
6
-
7
- const { createGitRepo } = require('./git.js');
8
- const { githubToken, loadPullRequest } = require('./github.js');
9
- const { gitlabToken, loadMergeRequest } = require('./gitlab.js');
10
- const { selectChangeSource, createLoadedSource } = require('./source.js');
11
- const { Session } = require('./session.js');
12
- const { capabilitiesFor, attachCapabilities } = require('./capabilities.js');
13
- const { setTheme } = require('./ansi.js');
14
- const { runCapture } = require('./report-run.js');
5
+ const metautil = require('metautil');
6
+ const { exists } = metautil;
7
+
8
+ const git = require('./git.js');
9
+ const { createGitRepo } = git;
10
+ const github = require('./github.js');
11
+ const { githubToken, loadPullRequest } = github;
12
+ const gitlab = require('./gitlab.js');
13
+ const { gitlabToken, loadMergeRequest } = gitlab;
14
+ const source = require('./source.js');
15
+ const { selectChangeSource, createLoadedSource } = source;
16
+ const session = require('./session.js');
17
+ const { Session } = session;
18
+ const capabilities = require('./capabilities.js');
19
+ const { capabilitiesFor, attachCapabilities } = capabilities;
20
+ const ansi = require('./ansi.js');
21
+ const { setTheme } = ansi;
22
+ const reportRun = require('./report-run.js');
23
+ const { runCapture } = reportRun;
15
24
 
16
25
  const HEX_REV = /^[0-9a-f]{4,40}$/i;
17
26
  const HEAD_REV = /^HEAD([~^].*)?$/i;
package/lib/clipboard.js CHANGED
@@ -1,9 +1,12 @@
1
1
  'use strict';
2
2
 
3
- const { spawnSync } = require('node:child_process');
3
+ const childProcess = require('node:child_process');
4
+ const { spawnSync } = childProcess;
4
5
 
5
- const { ESC } = require('./ansi.js');
6
- const { clipTools, spawnBase } = require('./utilities.js');
6
+ const ansi = require('./ansi.js');
7
+ const { ESC } = ansi;
8
+ const utilities = require('./utilities.js');
9
+ const { clipTools, spawnBase } = utilities;
7
10
 
8
11
  const CLIP_TIMEOUT_MS = 1000;
9
12
 
package/lib/deps-diff.js CHANGED
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- const { jsonParse } = require('metautil');
4
- const { itemPath } = require('./files.js');
5
- const {
6
- countSides,
7
- formatHunkHeader,
8
- formatPatch,
9
- } = require('./diff/patch.js');
10
- const { isCtxType } = require('./diff/diff.js');
3
+ const metautil = require('metautil');
4
+ const { jsonParse } = metautil;
5
+ const files = require('./files.js');
6
+ const { itemPath } = files;
7
+ const patch = require('./diff/patch.js');
8
+ const { countSides, formatHunkHeader, formatPatch } = patch;
9
+ const diff = require('./diff/diff.js');
10
+ const { isCtxType } = diff;
11
11
  const manifest = require('./manifest.js');
12
12
  const { MANIFEST, LOCKFILE, SECTION_KEYS, depFileMeta, relOf } = manifest;
13
13
  const { listFilePath, readSections, emptySectionMaps } = manifest;
@@ -37,14 +37,8 @@ const diffMaps = (oldMap, newMap, section) => {
37
37
  return changes;
38
38
  };
39
39
 
40
- const diffSections = (oldMaps, newMaps) => {
41
- const changes = [];
42
- for (const key of SECTION_KEYS) {
43
- const found = diffMaps(oldMaps[key], newMaps[key], key);
44
- for (const change of found) changes.push(change);
45
- }
46
- return changes;
47
- };
40
+ const diffSections = (oldMaps, newMaps) =>
41
+ SECTION_KEYS.flatMap((key) => diffMaps(oldMaps[key], newMaps[key], key));
48
42
 
49
43
  const fieldText = (value) => {
50
44
  if (value === undefined) return '';
@@ -263,15 +257,8 @@ const compareChangeOrder = (left, right) => {
263
257
 
264
258
  const orderedChanges = (changes) => [...changes].sort(compareChangeOrder);
265
259
 
266
- const kindItems = (items, kind) => {
267
- const matched = [];
268
- for (const item of items) {
269
- const rel = itemPath(item);
270
- const meta = depFileMeta(rel);
271
- if (meta && meta.kind === kind) matched.push(item);
272
- }
273
- return matched;
274
- };
260
+ const kindItems = (items, kind) =>
261
+ items.filter((item) => depFileMeta(itemPath(item))?.kind === kind);
275
262
 
276
263
  const readGroupSnapshot = (group, readSides) => {
277
264
  const origin = group.origin;
@@ -457,26 +444,10 @@ const entryText = (change, side) => {
457
444
  return `"${change.name}": "${version}"`;
458
445
  };
459
446
 
460
- const ctxLine = (text) => {
461
- const type = 'ctx';
462
- return { type, text };
463
- };
464
-
465
- const warnLine = (text) => {
466
- const type = 'warn';
467
- return { type, text };
468
- };
469
-
470
- const noteLine = (text) => {
471
- const type = 'note';
472
- return { type, text };
473
- };
474
-
475
- const noteSepLine = () => {
476
- const type = 'noteSep';
477
- const text = '';
478
- return { type, text };
479
- };
447
+ const ctxLine = (text) => ({ type: 'ctx', text });
448
+ const warnLine = (text) => ({ type: 'warn', text });
449
+ const noteLine = (text) => ({ type: 'note', text });
450
+ const noteSepLine = () => ({ type: 'noteSep', text: '' });
480
451
 
481
452
  const changeLine = (type, change, side) => {
482
453
  const text = entryText(change, side);
@@ -522,12 +493,7 @@ const auditNoteTexts = (audit) => {
522
493
  }
523
494
  const severity = audit.severity;
524
495
  if (!notes.length) return [`npm audit ${severity}`];
525
- const { length } = notes;
526
- const texts = new Array(length);
527
- for (let i = 0; i < length; i++) {
528
- texts[i] = `npm audit ${severity} ${notes[i]}`;
529
- }
530
- return texts;
496
+ return notes.map((note) => `npm audit ${severity} ${note}`);
531
497
  };
532
498
 
533
499
  const pushAuditNotes = (rows, audit) => {
@@ -562,13 +528,12 @@ const buildRows = (change, oldCount, newCount) => {
562
528
  return rows;
563
529
  };
564
530
 
565
- const toHunkLine = (row, blockId) => {
566
- const type = row.type;
567
- const text = row.text;
568
- const noNl = false;
569
- const id = isCtxType(type) ? null : blockId;
570
- return { type, text, noNl, blockId: id };
571
- };
531
+ const toHunkLine = (row, blockId) => ({
532
+ type: row.type,
533
+ text: row.text,
534
+ noNl: false,
535
+ blockId: isCtxType(row.type) ? null : blockId,
536
+ });
572
537
 
573
538
  const makeHunk = (rows, blockId) => {
574
539
  const lines = rows.map((row) => toHunkLine(row, blockId));
@@ -600,21 +565,13 @@ const filesForChange = (change, files) => {
600
565
  if (meta.kind === 'manifest') manifest.push(rel);
601
566
  else lockfile.push(rel);
602
567
  }
603
- if (change.section === 'field') {
604
- if (manifest.length) return manifest;
605
- return files;
606
- }
607
- if (change.section === 'resolved') {
608
- if (lockfile.length) return lockfile;
609
- return files;
610
- }
611
- if (change.propose) {
612
- if (manifest.length) return manifest;
613
- return files;
568
+ if (change.section === 'resolved') return lockfile.length ? lockfile : files;
569
+ if (change.section === 'field' || change.propose) {
570
+ return manifest.length ? manifest : files;
614
571
  }
615
- const both = [...manifest, ...lockfile];
616
- if (both.length) return both;
617
- return files;
572
+ return manifest.length || lockfile.length
573
+ ? [...manifest, ...lockfile]
574
+ : files;
618
575
  };
619
576
 
620
577
  const pickPrimary = (items) => {
@@ -630,36 +587,28 @@ const makeDepItem = (origin, fileItems, hidden, change, extra, blockId) => {
630
587
  const allFiles = uniquePaths(fileItems);
631
588
  const files = change ? filesForChange(change, allFiles) : allFiles;
632
589
  const primary = pickPrimary(fileItems);
633
- const oldPath = listFilePath(itemPath(primary));
634
- const newPath = oldPath;
635
- const isNew = fileItems.some((item) => item.file && item.file.isNew);
636
- const isDeleted = fileItems.every((item) => item.file && item.file.isDeleted);
637
- let oldCount = 0;
638
- let newCount = 0;
639
- if (!change) {
640
- oldCount = extra.oldCount ?? 0;
641
- newCount = extra.newCount ?? 0;
642
- }
643
- const rows = buildRows(change, oldCount, newCount);
590
+ const rel = listFilePath(itemPath(primary));
591
+ const counts = change ? {} : extra;
592
+ const rows = buildRows(change, counts.oldCount ?? 0, counts.newCount ?? 0);
644
593
  const hunk = makeHunk(rows, blockId);
645
- const isBinary = false;
646
- const preamble = [];
647
- const hunks = [hunk];
648
- const file = {
649
- oldPath,
650
- newPath,
651
- isNew,
652
- isDeleted,
653
- isBinary,
654
- preamble,
655
- hunks,
594
+ return {
595
+ origin,
596
+ file: {
597
+ oldPath: rel,
598
+ newPath: rel,
599
+ isNew: fileItems.some((item) => item.file?.isNew),
600
+ isDeleted: fileItems.every((item) => item.file?.isDeleted),
601
+ isBinary: false,
602
+ preamble: [],
603
+ hunks: [hunk],
604
+ },
605
+ hunk,
606
+ blockId,
607
+ patchAdd: '',
608
+ patchRevert: '',
609
+ dep: { files, items: hidden, change },
610
+ reload: !!change?.propose,
656
611
  };
657
- const patchAdd = '';
658
- const patchRevert = '';
659
- const items = hidden;
660
- const dep = { files, items, change };
661
- const reload = !!(change && change.propose);
662
- return { origin, file, hunk, blockId, patchAdd, patchRevert, dep, reload };
663
612
  };
664
613
 
665
614
  const makeDepItems = (origin, fileItems, hidden, summary) => {
@@ -670,16 +619,14 @@ const makeDepItems = (origin, fileItems, hidden, summary) => {
670
619
  if (oldCount === newCount) return [];
671
620
  return [makeDepItem(origin, fileItems, hidden, null, summary, 0)];
672
621
  }
673
- const { length } = ordered;
674
- const items = new Array(length);
675
- for (let i = 0; i < length; i++) {
676
- items[i] = makeDepItem(origin, fileItems, hidden, ordered[i], summary, i);
677
- }
678
- return items;
622
+ return ordered.map((change, index) =>
623
+ makeDepItem(origin, fileItems, hidden, change, summary, index),
624
+ );
679
625
  };
680
626
 
681
- const stubFileItem = (rel, origin) => {
682
- const file = {
627
+ const stubFileItem = (rel, origin) => ({
628
+ origin,
629
+ file: {
683
630
  oldPath: rel,
684
631
  newPath: rel,
685
632
  isNew: false,
@@ -687,13 +634,12 @@ const stubFileItem = (rel, origin) => {
687
634
  isBinary: false,
688
635
  preamble: [],
689
636
  hunks: [],
690
- };
691
- const hunk = null;
692
- const blockId = 0;
693
- const patchAdd = '';
694
- const patchRevert = '';
695
- return { origin, file, hunk, blockId, patchAdd, patchRevert };
696
- };
637
+ },
638
+ hunk: null,
639
+ blockId: 0,
640
+ patchAdd: '',
641
+ patchRevert: '',
642
+ });
697
643
 
698
644
  module.exports = {
699
645
  DEP_CAPTION,
package/lib/deps.js CHANGED
@@ -2,8 +2,10 @@
2
2
 
3
3
  const fs = require('node:fs');
4
4
  const path = require('node:path');
5
- const { jsonParse, isHashObject } = require('metautil');
6
- const { itemPath } = require('./files.js');
5
+ const metautil = require('metautil');
6
+ const { jsonParse, isHashObject } = metautil;
7
+ const files = require('./files.js');
8
+ const { itemPath } = files;
7
9
  const manifest = require('./manifest.js');
8
10
  const { MANIFEST, LOCKFILE, SECTION_KEYS, depFileMeta, relOf } = manifest;
9
11
  const { listFilePath, readSections, lockEntries, lockPackageCount } = manifest;
@@ -73,8 +75,6 @@ const pkgHasExportEntry = (pkg) => {
73
75
  return Object.keys(exported).length > 0;
74
76
  };
75
77
 
76
- const isTypeKey = (key) => key === 'types' || key === 'typings';
77
-
78
78
  const isRuntimeFile = (value) => {
79
79
  if (typeof value !== 'string' || !value) return false;
80
80
  return !value.endsWith('.d.ts');
@@ -82,16 +82,11 @@ const isRuntimeFile = (value) => {
82
82
 
83
83
  const hasRuntimeExport = (value) => {
84
84
  if (isRuntimeFile(value)) return true;
85
- if (Array.isArray(value)) {
86
- for (const item of value) {
87
- if (hasRuntimeExport(item)) return true;
88
- }
89
- return false;
90
- }
85
+ if (Array.isArray(value)) return value.some(hasRuntimeExport);
91
86
  if (!value || typeof value !== 'object') return false;
92
87
  const keys = Object.keys(value);
93
88
  for (const key of keys) {
94
- if (isTypeKey(key)) continue;
89
+ if (key === 'types' || key === 'typings') continue;
95
90
  if (hasRuntimeExport(value[key])) return true;
96
91
  }
97
92
  return false;
@@ -220,14 +215,7 @@ const declaredDepNames = (root, pkgDir) => {
220
215
  return [];
221
216
  }
222
217
  const maps = readSections(jsonParse(text));
223
- let size = 0;
224
- for (const key of SECTION_KEYS) size += maps[key].size;
225
- const names = new Array(size);
226
- let i = 0;
227
- for (const key of SECTION_KEYS) {
228
- for (const name of maps[key].keys()) names[i++] = name;
229
- }
230
- return names;
218
+ return SECTION_KEYS.flatMap((key) => [...maps[key].keys()]);
231
219
  };
232
220
 
233
221
  const collectExportMeta = (root, extra = {}) => {
@@ -382,16 +370,7 @@ const markChanges = (changes, report, kind) => {
382
370
  });
383
371
  };
384
372
 
385
- const markAuditChanges = (changes, audit) =>
386
- markChanges(changes, audit, 'audit');
387
-
388
- const markOutdatedChanges = (changes, outdated) =>
389
- markChanges(changes, outdated, 'outdated');
390
-
391
- const depExports = (exportEntries, name) => {
392
- if (!exportEntries || !exportEntries.has(name)) return false;
393
- return exportEntries.get(name) === true;
394
- };
373
+ const depExports = (exportEntries, name) => exportEntries?.get(name) === true;
395
374
 
396
375
  const isAddedDep = (change) => {
397
376
  if (change.action !== 'added') return false;
@@ -429,23 +408,12 @@ const unusedDeclaredNames = (pkg, used) => {
429
408
  };
430
409
 
431
410
  const markUnusedChanges = (changes, used, pkg, exportEntries) => {
432
- if (!pkgHasExportEntry(pkg)) return changes;
433
- if (!used || !used.size) return changes;
434
- const { length } = changes;
435
- const marked = new Array(length);
436
- for (let i = 0; i < length; i++) {
437
- const change = changes[i];
438
- if (!isAddedDep(change) || used.has(change.name)) {
439
- marked[i] = change;
440
- continue;
441
- }
442
- if (!depExports(exportEntries, change.name)) {
443
- marked[i] = change;
444
- continue;
445
- }
446
- marked[i] = { ...change, unused: true, propose: true };
447
- }
448
- return marked;
411
+ if (!pkgHasExportEntry(pkg) || !used?.size) return changes;
412
+ return changes.map((change) => {
413
+ if (!isAddedDep(change) || used.has(change.name)) return change;
414
+ if (!depExports(exportEntries, change.name)) return change;
415
+ return { ...change, unused: true, propose: true };
416
+ });
449
417
  };
450
418
 
451
419
  const unusedDeclaredChanges = (pkg, used, exportEntries) => {
@@ -551,18 +519,16 @@ const loadExportEntries = (names, extra = {}, pkgDir) => {
551
519
  if (!missing.length) return supplied ?? new Map();
552
520
  const found = collectExportEntries(extra.root, dir, missing);
553
521
  if (!supplied) return found;
554
- const merged = new Map(supplied);
555
- for (const name of found.keys()) merged.set(name, found.get(name));
556
- return merged;
522
+ return new Map([...supplied, ...found]);
557
523
  };
558
524
 
559
525
  const groupedSynthetics = (synthetic) => {
560
526
  const byPath = new Map();
561
- for (const key of synthetic.keys()) {
527
+ for (const [key, items] of synthetic) {
562
528
  const dir = key.slice(key.indexOf('\0') + 1);
563
529
  const rel = relOf(dir, MANIFEST);
564
530
  const list = byPath.get(rel) ?? [];
565
- for (const item of synthetic.get(key)) list.push(item);
531
+ for (const item of items) list.push(item);
566
532
  byPath.set(rel, list);
567
533
  }
568
534
  return byPath;
@@ -570,12 +536,10 @@ const groupedSynthetics = (synthetic) => {
570
536
 
571
537
  const spliceAtSlots = (out, synthetic, slot) => {
572
538
  const byPath = groupedSynthetics(synthetic);
573
- const inserts = new Array(byPath.size);
574
- let i = 0;
575
- for (const rel of byPath.keys()) {
576
- const at = slot.has(rel) ? slot.get(rel) : out.length;
577
- inserts[i++] = { at, items: byPath.get(rel) };
578
- }
539
+ const inserts = [...byPath].map(([rel, items]) => ({
540
+ at: slot.get(rel) ?? out.length,
541
+ items,
542
+ }));
579
543
  inserts.sort((left, right) => right.at - left.at);
580
544
  for (const insert of inserts) {
581
545
  out.splice(insert.at, 0, ...insert.items);
@@ -610,8 +574,7 @@ const foldDepItems = (
610
574
  const folded = new Set();
611
575
  const synthetic = new Map();
612
576
  const leftoverNames = new Map();
613
- for (const key of groups.keys()) {
614
- const group = groups.get(key);
577
+ for (const [key, group] of groups) {
615
578
  const summary = summarizeGroup(group, readSides);
616
579
  const unusedNames = unusedChangeNames(summary.changes, usedNames);
617
580
  const exportEntries = loadExportEntries(unusedNames, extra, group.dir);
@@ -621,8 +584,8 @@ const foldDepItems = (
621
584
  summary.pkg,
622
585
  exportEntries,
623
586
  );
624
- const audited = markAuditChanges(unused, audit);
625
- const changes = markOutdatedChanges(audited, outdated);
587
+ const audited = markChanges(unused, audit, 'audit');
588
+ const changes = markChanges(audited, outdated, 'outdated');
626
589
  const next = { ...summary, changes };
627
590
  leftoverNames.set(key, foldedDepNames(next.changes));
628
591
  const taken = takeManifestItems(next);
package/lib/detect.js CHANGED
@@ -1,9 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  const path = require('node:path');
4
- const { fileExt } = require('metautil');
4
+ const metautil = require('metautil');
5
+ const { fileExt } = metautil;
5
6
 
6
- const { mapLang } = require('./highlight.js');
7
+ const highlight = require('./highlight.js');
8
+ const { mapLang } = highlight;
7
9
 
8
10
  const PROSE_BASENAMES = [
9
11
  'license',
package/lib/diff/align.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { diffChars, tokenSimilarity } = require('./inline.js');
3
+ const inline = require('./inline.js');
4
+ const { diffChars, tokenSimilarity } = inline;
4
5
 
5
6
  const PAIR_MIN_SIM = 0.3;
6
7
  const PAIR_HEAD_WEIGHT = 0.4;
package/lib/diff/diff.js CHANGED
@@ -1,8 +1,11 @@
1
1
  'use strict';
2
2
 
3
- const { diffChars } = require('./inline.js');
4
- const { isCtxType, pairIndices, attachInline } = require('./align.js');
5
- const { parseDiff } = require('./parse.js');
3
+ const inline = require('./inline.js');
4
+ const { diffChars } = inline;
5
+ const align = require('./align.js');
6
+ const { isCtxType, pairIndices, attachInline } = align;
7
+ const parse = require('./parse.js');
8
+ const { parseDiff } = parse;
6
9
  const patch = require('./patch.js');
7
10
  const { splitHunk, flattenBlock, countSides, formatPatch } = patch;
8
11
  const display = require('./display.js');
@@ -2,7 +2,8 @@
2
2
 
3
3
  const align = require('./align.js');
4
4
  const { isCtxType, attachInline, LAYOUT_ALIGN } = align;
5
- const { flattenBlock } = require('./patch.js');
5
+ const patch = require('./patch.js');
6
+ const { flattenBlock } = patch;
6
7
 
7
8
  const DISPLAY_CONTEXT = 3;
8
9
 
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { graphemes } = require('../ansi.js');
3
+ const ansi = require('../ansi.js');
4
+ const { graphemes } = ansi;
4
5
 
5
6
  const LCS_CELL_LIMIT = 250000;
6
7
  const NOISY_TINY_SPANS = 6;
package/lib/diff/patch.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { isCtxType } = require('./align.js');
3
+ const align = require('./align.js');
4
+ const { isCtxType } = align;
4
5
 
5
6
  const LINE_MARK = {
6
7
  add: '+',
package/lib/diff/unit.js CHANGED
@@ -1,7 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const { attachInline } = require('./align.js');
4
- const { listPath } = require('../files.js');
3
+ const align = require('./align.js');
4
+ const { attachInline } = align;
5
+ const files = require('../files.js');
6
+ const { listPath } = files;
5
7
 
6
8
  const splitEditor = (text) => {
7
9
  const lines = `${text}`.split('\n');