Skip to content

Commit a570770

Browse files
authored
fix(start): fix stdio freezing issue on Windows (#3725)
Any time BottomBar is used, it uses a readline instance which hijacks the stdio streams and has some strange issues on Windows. log-update uses terminal ANSI codes to clear the line--should be the same functionality without the readline weirdness.
1 parent 78dbda8 commit a570770

File tree

4 files changed

+66
-6
lines changed

4 files changed

+66
-6
lines changed

packages/@ionic/cli-framework/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"debug": "^4.0.0",
3333
"inquirer": "^6.0.0",
3434
"lodash": "^4.17.5",
35+
"log-update": "^2.3.0",
3536
"minimist": "^1.2.0",
3637
"rimraf": "^2.6.2",
3738
"slice-ansi": "^2.0.0",
@@ -50,6 +51,7 @@
5051
"@types/inquirer": "0.0.43",
5152
"@types/jest": "^23.3.5",
5253
"@types/lodash": "^4.14.104",
54+
"@types/log-update": "^2.0.0",
5355
"@types/minimist": "^1.2.0",
5456
"@types/node": "^6.0.101",
5557
"@types/rimraf": "^2.0.2",

packages/@ionic/cli-framework/src/lib/output.ts

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as Debug from 'debug';
22
import * as ζinquirer from 'inquirer';
3+
import * as ζlogUpdate from 'log-update';
34

45
import { Colors, DEFAULT_COLORS } from './colors';
56
import { ICON_FAILURE, ICON_SUCCESS, Spinner, TaskChain } from './tasks';
@@ -13,8 +14,6 @@ export interface OutputStrategy {
1314

1415
export interface RedrawLine {
1516
redrawLine(msg?: string): void;
16-
open(): void;
17-
close(): void;
1817
}
1918

2019
export interface StreamOutputStrategyOptions {
@@ -50,6 +49,63 @@ export class StreamOutputStrategy implements OutputStrategy {
5049
}
5150
}
5251

52+
export interface LogUpdateOutputStrategyOptions {
53+
readonly LogUpdate: typeof ζlogUpdate;
54+
readonly stream?: NodeJS.WritableStream;
55+
readonly colors?: Colors;
56+
}
57+
58+
export class LogUpdateOutputStrategy implements OutputStrategy, RedrawLine {
59+
readonly stream: NodeJS.WritableStream;
60+
61+
protected readonly colors: Colors;
62+
protected readonly logUpdate: typeof ζlogUpdate;
63+
64+
constructor({ LogUpdate, stream = process.stdout, colors = DEFAULT_COLORS }: LogUpdateOutputStrategyOptions) {
65+
this.stream = stream;
66+
this.colors = colors;
67+
this.logUpdate = LogUpdate.create(stream);
68+
}
69+
70+
redrawLine(msg = ''): void {
71+
this.logUpdate(msg);
72+
}
73+
74+
createTaskChain(): TaskChain {
75+
const { failure, strong, success } = this.colors;
76+
const chain = new TaskChain({ taskOptions: { tickInterval: 50 } });
77+
78+
chain.on('next', task => {
79+
task.on('success', () => {
80+
this.stream.write(`${success(ICON_SUCCESS)} ${task.msg} - done!\n`);
81+
});
82+
83+
task.on('failure', () => {
84+
this.stream.write(`${failure(ICON_FAILURE)} ${task.msg} - failed!\n`);
85+
});
86+
87+
const spinner = new Spinner();
88+
89+
task.on('tick', () => {
90+
const progress = task.progressRatio ? (task.progressRatio * 100).toFixed(2) : '';
91+
const frame = spinner.frame();
92+
93+
this.redrawLine(`${strong(frame)} ${task.msg}${progress ? ' (' + strong(String(progress) + '%') + ')' : ''} `);
94+
});
95+
96+
task.on('clear', () => {
97+
this.logUpdate.clear();
98+
});
99+
});
100+
101+
chain.on('end', () => {
102+
this.logUpdate.done();
103+
});
104+
105+
return chain;
106+
}
107+
}
108+
53109
export interface BottomBarOutputStrategyOptions {
54110
readonly BottomBar: typeof ζinquirer.ui.BottomBar;
55111
readonly input?: NodeJS.ReadableStream;
@@ -112,7 +168,7 @@ export class BottomBarOutputStrategy implements OutputStrategy, RedrawLine {
112168
}
113169
}
114170

115-
createTaskChain() {
171+
createTaskChain(): TaskChain {
116172
const { failure, strong, success } = this.colors;
117173
const chain = new TaskChain({ taskOptions: { tickInterval: 50 } });
118174

packages/ionic/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"elementtree": "^0.1.7",
5959
"leek": "0.0.24",
6060
"lodash": "^4.17.5",
61+
"log-update": "^2.3.0",
6162
"opn": "^5.2.0",
6263
"os-name": "^2.0.1",
6364
"semver": "^5.5.0",
@@ -78,6 +79,7 @@
7879
"@types/elementtree": "^0.1.0",
7980
"@types/jest": "^23.3.5",
8081
"@types/lodash": "^4.14.104",
82+
"@types/log-update": "^2.0.0",
8183
"@types/node": "^6.0.101",
8284
"@types/opn": "^5.1.0",
8385
"@types/os-name": "^2.0.0",

packages/ionic/src/lib/command.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { BaseCommand, BottomBarOutputStrategy, LOGGER_LEVELS, OutputStrategy, StreamHandler, StreamOutputStrategy, TaskChain, generateCommandPath, unparseArgs } from '@ionic/cli-framework';
1+
import { BaseCommand, LOGGER_LEVELS, LogUpdateOutputStrategy, OutputStrategy, StreamHandler, StreamOutputStrategy, TaskChain, generateCommandPath, unparseArgs } from '@ionic/cli-framework';
22
import chalk from 'chalk';
3+
import * as LogUpdate from 'log-update';
34

45
import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataInput, CommandMetadataOption, ICommand, INamespace, IProject, IonicEnvironment } from '../definitions';
56
import { isCommandPreRun } from '../guards';
@@ -27,8 +28,7 @@ export abstract class Command extends BaseCommand<ICommand, INamespace, CommandM
2728
const formatter = createFormatter();
2829

2930
if (this.env.flags.interactive) {
30-
const { ui: { BottomBar } } = this.env.prompt._inquirer;
31-
output = new BottomBarOutputStrategy({ BottomBar });
31+
output = new LogUpdateOutputStrategy({ LogUpdate });
3232
this.env.log.handlers = new Set([new StreamHandler({ stream: output.stream, formatter })]);
3333
} else {
3434
this.env.log.handlers = createDefaultLoggerHandlers();

0 commit comments

Comments
 (0)