@pnpm/cli.default-reporter 1100.3.1 → 1100.3.2

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.
package/lib/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import createDiffer from 'ansi-diff';
2
1
  import * as Rx from 'rxjs';
3
2
  import { filter, map, mergeAll } from 'rxjs/operators';
4
3
  import { EOL } from './constants.js';
@@ -6,6 +5,11 @@ import { mergeOutputs } from './mergeOutputs.js';
6
5
  import { reporterForClient } from './reporterForClient/index.js';
7
6
  import { formatWarn } from './reporterForClient/utils/formatWarn.js';
8
7
  export { formatWarn };
8
+ // ANSI "erase from cursor to end of display". Emitted before reprinting each
9
+ // frame so that anything an external process (e.g. an SSH passphrase prompt)
10
+ // wrote to the terminal between progress updates is cleared instead of bleeding
11
+ // into the progress output.
12
+ const ERASE_TO_END_OF_DISPLAY = '\x1b[0J';
9
13
  export function initDefaultReporter(opts) {
10
14
  const proc = opts.context.process ?? process;
11
15
  const outputMaxWidth = opts.reportingOptions?.outputMaxWidth ?? (proc.stdout.columns && proc.stdout.columns - 2) ?? 80;
@@ -32,10 +36,6 @@ export function initDefaultReporter(opts) {
32
36
  subscription.unsubscribe();
33
37
  };
34
38
  }
35
- const diff = createDiffer({
36
- height: proc.stdout.rows,
37
- outputMaxWidth,
38
- });
39
39
  const subscription = output$
40
40
  .subscribe({
41
41
  complete() { }, // eslint-disable-line:no-empty
@@ -47,18 +47,39 @@ export function initDefaultReporter(opts) {
47
47
  const write = opts.useStderr
48
48
  ? proc.stderr.write.bind(proc.stderr)
49
49
  : proc.stdout.write.bind(proc.stdout);
50
+ let prevRows = 0;
50
51
  function logUpdate(view) {
51
52
  // A new line should always be appended in case a prompt needs to appear.
52
53
  // Without a new line the prompt will be joined with the previous output.
53
54
  // An example of such prompt may be seen by running: pnpm update --interactive
54
55
  if (!view.endsWith(EOL))
55
56
  view += EOL;
56
- write(diff.update(view));
57
+ // Redraw the whole frame in place: return the cursor to the top-left of the
58
+ // previous frame, erase everything below it, then reprint. The `\r` resets
59
+ // the column to 0 (cursor-up alone keeps the column) so the redraw starts
60
+ // cleanly even when an external process left the cursor mid-line. Doing it
61
+ // in a single write keeps the redraw atomic (no flicker) and clears any
62
+ // characters an external process wrote in between.
63
+ const moveToFrameTop = prevRows > 0 ? `\x1b[${prevRows}A\r` : '\r';
64
+ write(`${moveToFrameTop}${ERASE_TO_END_OF_DISPLAY}${view}`);
65
+ prevRows = countRows(view);
57
66
  }
58
67
  return () => {
59
68
  subscription.unsubscribe();
60
69
  };
61
70
  }
71
+ // Number of terminal rows a frame occupies. The frame always ends with a
72
+ // newline, so this also equals how far below the frame's top the cursor rests
73
+ // after printing it. Lines are assumed not to soft-wrap, matching how the
74
+ // progress output is width-constrained before it reaches here.
75
+ function countRows(frame) {
76
+ let rows = 0;
77
+ for (let i = 0; i < frame.length; i++) {
78
+ if (frame.charCodeAt(i) === 10 /* \n */)
79
+ rows++;
80
+ }
81
+ return rows;
82
+ }
62
83
  export function toOutput$(opts) {
63
84
  opts = opts || {};
64
85
  const contextPushStream = new Rx.Subject();
@@ -7,8 +7,8 @@ import { map } from 'rxjs/operators';
7
7
  export function reportLockfileVerification(lockfileVerification$, opts) {
8
8
  const expectedDir = opts.workspaceDir ?? opts.cwd;
9
9
  // A single inner observable so the `done` message overwrites the
10
- // transient `started` message in ansi-diff mode. In appendOnly mode
11
- // both lines are printed.
10
+ // transient `started` message when the reporter redraws in place. In
11
+ // appendOnly mode both lines are printed.
12
12
  return Rx.of(lockfileVerification$.pipe(map((log) => {
13
13
  const path_ = formatLockfilePath(log.lockfilePath, opts.cwd, expectedDir);
14
14
  if (log.status === 'cached') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/cli.default-reporter",
3
- "version": "1100.3.1",
3
+ "version": "1100.3.2",
4
4
  "description": "The default reporter of pnpm",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -33,7 +33,6 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@pnpm/util.lex-comparator": "^4.0.1",
36
- "ansi-diff": "^1.2.0",
37
36
  "boxen": "npm:@zkochan/boxen@5.1.2",
38
37
  "chalk": "^5.6.2",
39
38
  "cli-truncate": "^6.0.0",
@@ -45,14 +44,14 @@
45
44
  "semver": "^7.8.4",
46
45
  "stacktracey": "^2.2.0",
47
46
  "string-length": "^7.0.1",
48
- "@pnpm/deps.inspection.peers-issues-renderer": "1100.0.6",
49
- "@pnpm/error": "1100.0.0",
50
- "@pnpm/installing.dedupe.types": "1100.0.1",
51
- "@pnpm/types": "1101.3.2",
52
47
  "@pnpm/cli.meta": "1100.0.8",
53
- "@pnpm/config.reader": "1101.9.0",
48
+ "@pnpm/config.reader": "1101.10.0",
49
+ "@pnpm/core-loggers": "1100.2.1",
50
+ "@pnpm/types": "1101.3.2",
54
51
  "@pnpm/installing.dedupe.issues-renderer": "1100.0.1",
55
- "@pnpm/core-loggers": "1100.2.1"
52
+ "@pnpm/installing.dedupe.types": "1100.0.1",
53
+ "@pnpm/error": "1100.0.0",
54
+ "@pnpm/deps.inspection.peers-issues-renderer": "1100.0.6"
56
55
  },
57
56
  "peerDependencies": {
58
57
  "@pnpm/logger": "^1100.0.0"
@@ -65,7 +64,7 @@
65
64
  "ghooks": "2.0.4",
66
65
  "load-json-file": "^7.0.1",
67
66
  "normalize-newline": "5.0.0",
68
- "@pnpm/cli.default-reporter": "1100.3.1",
67
+ "@pnpm/cli.default-reporter": "1100.3.2",
69
68
  "@pnpm/logger": "1100.0.0"
70
69
  },
71
70
  "engines": {