1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const chalk_1 = require("chalk");
|
4 | const cliTruncate = require("cli-truncate");
|
5 | const elegantSpinner = require("elegant-spinner");
|
6 | const figures = require("figures");
|
7 | const indentString = require("indent-string");
|
8 | const logSymbols = require("log-symbols");
|
9 | const logUpdate = require("log-update");
|
10 | const stringWidth = require("string-width");
|
11 | const stripAnsi = require("strip-ansi");
|
12 | const pointer = chalk_1.default.yellow(figures.pointer);
|
13 | const skipped = chalk_1.default.yellow(figures.arrowDown);
|
14 | const isDefined = x => x !== null && x !== undefined;
|
15 | function getSymbol(task, options) {
|
16 | if (!task.spinner) {
|
17 | task.spinner = elegantSpinner();
|
18 | }
|
19 | if (task.isPending()) {
|
20 | return options.showSubtasks !== false && task.subtasks.length > 0 ? pointer : chalk_1.default.yellow(task.spinner());
|
21 | }
|
22 | if (task.isCompleted()) {
|
23 | return logSymbols.success;
|
24 | }
|
25 | if (task.hasFailed()) {
|
26 | return task.subtasks.length > 0 ? pointer : logSymbols.error;
|
27 | }
|
28 | if (task.isSkipped()) {
|
29 | return skipped;
|
30 | }
|
31 | return '';
|
32 | }
|
33 | function getBranchStatus(porcelainLines) {
|
34 | const ret = {
|
35 | currentBranch: 'Unknown',
|
36 | };
|
37 | const branchLine = porcelainLines.find(str => str.startsWith('##'));
|
38 | const currentBranchRE = /## (.+?)\.\.\./gi;
|
39 | const upstreamRE = /\[(.+)\]$/gi;
|
40 | const currentBranchMatch = currentBranchRE.exec(branchLine);
|
41 | const upstreamMatch = upstreamRE.exec(branchLine);
|
42 | if (currentBranchMatch) {
|
43 | let currentBranch = currentBranchMatch[1];
|
44 | if (currentBranch === 'master') {
|
45 | currentBranch = chalk_1.default.red(currentBranch);
|
46 | }
|
47 | ret.currentBranch = currentBranch;
|
48 | }
|
49 | if (upstreamMatch) {
|
50 | ret.upstreamStatus = upstreamMatch[1];
|
51 | }
|
52 | return ret;
|
53 | }
|
54 | function getModifications(porcelainLines) {
|
55 | const modificationLines = porcelainLines.filter(str => !str.startsWith('##'));
|
56 | if (modificationLines.length === 0) {
|
57 | return '';
|
58 | }
|
59 | return modificationLines.reduce((acc, str) => {
|
60 | const symb = str.charAt(0);
|
61 | if (!acc.includes(symb)) {
|
62 | acc += symb;
|
63 | }
|
64 | return acc;
|
65 | }, '');
|
66 | }
|
67 | function getGitStatus(task) {
|
68 | if (!task.output) {
|
69 | return;
|
70 | }
|
71 | const porcelainLines = `${task.output}`.replace('git status:', '')
|
72 | .split('\u0000')
|
73 | .map(str => str.trim())
|
74 | .filter(str => str.length !== 0);
|
75 | if (porcelainLines.length === 0) {
|
76 | return;
|
77 | }
|
78 | return {
|
79 | branchStatus: getBranchStatus(porcelainLines),
|
80 | modifications: getModifications(porcelainLines),
|
81 | };
|
82 | }
|
83 | function stripHyperlinkANSI(str) {
|
84 | const OSC = '\u001B]';
|
85 | const BEL = '\u0007';
|
86 | const stripper1 = new RegExp(`${OSC}8;;.+?${BEL}(.+)${OSC}8;;${BEL}`, 'i');
|
87 | return str.replace(stripper1, '$1');
|
88 | }
|
89 | function padANSI(str, length, char, dir = 'start') {
|
90 | const stripped = stripHyperlinkANSI(str);
|
91 | const widthPad = (str.length - stringWidth(stripped)) + length;
|
92 | if (dir === 'end') {
|
93 | return str.padEnd(widthPad, char);
|
94 | }
|
95 | return str.padStart(widthPad, char);
|
96 | }
|
97 | const renderHelper = (tasks, options, level = 0, isFinal) => {
|
98 | let output = [];
|
99 | for (const task of tasks) {
|
100 | if (task.isEnabled()) {
|
101 | const { showSubtasks, collapse, padTitleLength, } = options;
|
102 | const skipped = task.isSkipped() ? ` ${chalk_1.default.dim('[skipped]')}` : '';
|
103 | const gitStatus = getGitStatus(task);
|
104 | let currentBranch = '';
|
105 | let upstreamStatus = '';
|
106 | if (gitStatus) {
|
107 | currentBranch = chalk_1.default.gray(gitStatus.branchStatus.currentBranch);
|
108 | if (gitStatus.branchStatus.upstreamStatus) {
|
109 | upstreamStatus = chalk_1.default.yellow(gitStatus.branchStatus.upstreamStatus)
|
110 | .replace('behind', figures.arrowDown)
|
111 | .replace('ahead', figures.arrowUp);
|
112 | }
|
113 | }
|
114 | let title = task.title;
|
115 | if (!isFinal) {
|
116 | title = stripHyperlinkANSI(title);
|
117 | }
|
118 | if (gitStatus) {
|
119 | title = gitStatus.modifications.length ? chalk_1.default.yellow(title) : chalk_1.default.gray(title);
|
120 | }
|
121 | if (padTitleLength) {
|
122 | title = padANSI(title, padTitleLength, ' ', 'end');
|
123 | }
|
124 | let lineSymbol;
|
125 | if (gitStatus && gitStatus.modifications.length) {
|
126 | lineSymbol = chalk_1.default `{yellow ${figures.warning}} {red ${gitStatus.modifications}}`;
|
127 | }
|
128 | else {
|
129 | lineSymbol = getSymbol(task, options);
|
130 | }
|
131 | lineSymbol = padANSI(lineSymbol, 4);
|
132 | output.push(indentString(`${lineSymbol} ${title}${skipped} ${currentBranch} ${upstreamStatus}`, level, ' '));
|
133 | if ((task.isPending() || task.isSkipped() || task.hasFailed()) && isDefined(task.output)) {
|
134 | let data = task.output;
|
135 | if (typeof data === 'string') {
|
136 | data = stripAnsi(data.trim().split('\n').filter(Boolean).pop());
|
137 | if (data === '') {
|
138 | data = undefined;
|
139 | }
|
140 | }
|
141 | if (isDefined(data)) {
|
142 | const out = indentString(`${figures.arrowRight} ${data}`, level, ' ');
|
143 | output.push(` ${chalk_1.default.gray(cliTruncate(out, process.stdout.columns - 3))}`);
|
144 | }
|
145 | }
|
146 | if ((task.isPending() || task.hasFailed() || collapse === false) && (task.hasFailed() || showSubtasks !== false) && task.subtasks.length > 0) {
|
147 | output = output.concat(renderHelper(task.subtasks, options, level + 1, isFinal));
|
148 | }
|
149 | }
|
150 | }
|
151 | return output.join('\n');
|
152 | };
|
153 | const render = (tasks, options, level, isFinal) => {
|
154 | logUpdate(renderHelper(tasks, options, level, isFinal));
|
155 | };
|
156 | class StatusRenderer {
|
157 | constructor(tasks, options) {
|
158 | this.nonTTY = false;
|
159 | this._tasks = tasks;
|
160 | this._options = Object.assign({ showSubtasks: true, collapse: true, clearOutput: false, padTitleLength: undefined, updateInterval: 100 }, options);
|
161 | }
|
162 | render() {
|
163 | if (this._id) {
|
164 |
|
165 | return;
|
166 | }
|
167 | this._id = setInterval(() => {
|
168 | render(this._tasks, this._options);
|
169 | }, this._options.updateInterval);
|
170 | }
|
171 | end(err) {
|
172 | if (this._id) {
|
173 | clearInterval(this._id);
|
174 | this._id = undefined;
|
175 | }
|
176 | render(this._tasks, this._options, 0, true);
|
177 | if (this._options.clearOutput && err === undefined) {
|
178 | logUpdate.clear();
|
179 | }
|
180 | else {
|
181 | logUpdate.done();
|
182 | }
|
183 | }
|
184 | }
|
185 | StatusRenderer.nonTTY = false;
|
186 | StatusRenderer.render = () => { };
|
187 | StatusRenderer.end = () => { };
|
188 | exports.default = StatusRenderer;
|