1 |
|
2 | import * as ansi from '@bevry/ansi';
|
3 | import figures from '@bevry/figures';
|
4 | import versionClean from 'version-clean';
|
5 |
|
6 | import { runCommand, runVersion, runInstall, uniq, trim, loadVersion, lastLine, } from './util.js';
|
7 | function getTime() {
|
8 | return Date.now();
|
9 | }
|
10 |
|
11 | export class Version {
|
12 |
|
13 | version;
|
14 |
|
15 | listeners = [];
|
16 |
|
17 | aliases = [];
|
18 |
|
19 | status = 'pending';
|
20 | |
21 |
|
22 |
|
23 |
|
24 | loadedVersion = null;
|
25 |
|
26 | success = null;
|
27 |
|
28 | error = null;
|
29 |
|
30 | stdout = null;
|
31 |
|
32 | stderr = null;
|
33 |
|
34 | started = null;
|
35 |
|
36 | finished = null;
|
37 |
|
38 | messageCache = null;
|
39 |
|
40 | constructor(version, listeners = []) {
|
41 | this.listeners.push(...listeners);
|
42 | this.version = String(version);
|
43 |
|
44 | if (!versionClean(this.version)) {
|
45 |
|
46 | this.alias = this.version;
|
47 | }
|
48 | }
|
49 |
|
50 | get alias() {
|
51 | return this.aliases[0];
|
52 | }
|
53 | set alias(alias) {
|
54 | if (alias) {
|
55 | const aliases = this.aliases.concat(alias);
|
56 | this.aliases = uniq(aliases);
|
57 | }
|
58 | }
|
59 |
|
60 | reset() {
|
61 | this.success = null;
|
62 | this.error = null;
|
63 | this.stdout = null;
|
64 | this.stderr = null;
|
65 | this.started = null;
|
66 | this.finished = null;
|
67 | this.messageCache = null;
|
68 | return this;
|
69 | }
|
70 | |
71 |
|
72 |
|
73 |
|
74 |
|
75 | async update(status) {
|
76 | if (status)
|
77 | this.status = status;
|
78 | await Promise.all(this.listeners.map((listener) => listener(this)));
|
79 | return this;
|
80 | }
|
81 |
|
82 | async load() {
|
83 | this.status = 'loading';
|
84 | this.reset();
|
85 | await this.update();
|
86 | const result = await loadVersion(this.version);
|
87 | if (result.error) {
|
88 | if ((result.error || '').toString().includes('not yet installed')) {
|
89 | this.status = 'missing';
|
90 | this.success = false;
|
91 | }
|
92 | else {
|
93 | this.status = 'failed';
|
94 | this.success = false;
|
95 | }
|
96 | this.error = result.error;
|
97 | this.stdout = (result.stdout || '').toString();
|
98 | this.stderr = (result.stderr || '').toString();
|
99 | }
|
100 | else {
|
101 | const result = await runVersion(this.version);
|
102 | if (result.error) {
|
103 | this.status = 'failed';
|
104 | this.success = false;
|
105 | this.error = result.error;
|
106 | this.stdout = (result.stdout || '').toString();
|
107 | this.stderr = (result.stderr || '').toString();
|
108 | }
|
109 | else {
|
110 | this.loadedVersion = this.loadedVersion || this.version;
|
111 | this.version = lastLine(result.stdout);
|
112 | this.status = 'loaded';
|
113 | }
|
114 | }
|
115 | await this.update();
|
116 | return this;
|
117 | }
|
118 | |
119 |
|
120 |
|
121 |
|
122 | async install() {
|
123 | if (this.status !== 'missing')
|
124 | return this;
|
125 | this.status = 'installing';
|
126 | this.reset();
|
127 | await this.update();
|
128 | const result = await runInstall(this.version);
|
129 | if (result.error) {
|
130 | this.error = result.error;
|
131 | this.status = 'missing';
|
132 | this.success = false;
|
133 | this.stdout = (result.stdout || '').toString();
|
134 | this.stderr = (result.stderr || '').toString();
|
135 | }
|
136 | else {
|
137 | await this.update('installed');
|
138 | await this.load();
|
139 | }
|
140 | return this;
|
141 | }
|
142 | |
143 |
|
144 |
|
145 |
|
146 | async test(command) {
|
147 | if (!command) {
|
148 | throw new Error('no command provided to the testen version runner');
|
149 | }
|
150 | if (this.status !== 'loaded')
|
151 | return this;
|
152 | this.status = 'running';
|
153 | this.reset();
|
154 | await this.update();
|
155 | this.started = getTime();
|
156 | const result = await runCommand(this.loadedVersion || this.version, command);
|
157 | this.finished = getTime();
|
158 | this.error = result.error;
|
159 | this.stdout = (result.stdout || '').toString();
|
160 | this.stderr = (result.stderr || '').toString();
|
161 | this.success = Boolean(result.error) === false;
|
162 | await this.update(this.success ? 'passed' : 'failed');
|
163 | return this;
|
164 | }
|
165 | |
166 |
|
167 |
|
168 |
|
169 | get row() {
|
170 | const indicator = this.success === null
|
171 | ? ansi.dim(figures.circle)
|
172 | : this.success
|
173 | ? ansi.green(figures.tick)
|
174 | : ansi.red(figures.cross);
|
175 | const result = this.success === null
|
176 | ? ansi.dim(this.status)
|
177 | : this.success
|
178 | ? ansi.green(this.status)
|
179 | : ansi.red(this.status);
|
180 |
|
181 | const ms = this.started ? (this.finished || getTime()) - this.started : 0;
|
182 | const duration = this.started
|
183 | ? ansi.dim(ms > 1000 ? `${Math.round(ms / 1000)}s` : `${ms}ms`)
|
184 | : '';
|
185 | const aliases = this.aliases.length
|
186 | ? ansi.dim(` [${this.aliases.join('|')}]`)
|
187 | : '';
|
188 | const row = [
|
189 | ' ' + indicator,
|
190 | this.version + aliases,
|
191 | result,
|
192 | duration,
|
193 | ];
|
194 | return row;
|
195 | }
|
196 | |
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 | get message() {
|
203 |
|
204 | if (this.messageCache && this.messageCache[0] === this.status) {
|
205 | return this.messageCache[1];
|
206 | }
|
207 |
|
208 | const parts = [];
|
209 |
|
210 | const heading = `Node version ${ansi.underline(this.version)} ${this.status}`;
|
211 | if (this.status === 'missing') {
|
212 | parts.push(ansi.bold(ansi.red(heading)));
|
213 | }
|
214 | else if (this.success === true) {
|
215 | parts.push(ansi.bold(ansi.green(heading)));
|
216 | }
|
217 | else if (this.success === false) {
|
218 | parts.push(ansi.bold(ansi.red(heading)));
|
219 | }
|
220 | else {
|
221 |
|
222 | parts.push(ansi.bold(ansi.dim(heading)));
|
223 | }
|
224 |
|
225 | if (this.error) {
|
226 | parts.push(ansi.red(this.error.message.split('\n')[0]));
|
227 | }
|
228 |
|
229 | if (this.status === 'missing') {
|
230 | parts.push(ansi.red(`You need to run: nvm install ${this.version}`));
|
231 | }
|
232 | else {
|
233 | const stdout = trim(this.stdout || '');
|
234 | const stderr = trim(this.stderr || '');
|
235 | if (!stdout && !stderr) {
|
236 | parts.push(ansi.dim('no output'));
|
237 | }
|
238 | else {
|
239 | if (stdout) {
|
240 | parts.push(stdout);
|
241 | }
|
242 | if (stderr) {
|
243 | parts.push(ansi.red(stderr));
|
244 | }
|
245 | }
|
246 | }
|
247 |
|
248 | const message = parts.join('\n');
|
249 |
|
250 | this.messageCache = [this.status, message];
|
251 | return message;
|
252 | }
|
253 | }
|
254 | export default Version;
|