UNPKG

10.4 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 var desc = Object.getOwnPropertyDescriptor(m, k);
5 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6 desc = { enumerable: true, get: function() { return m[k]; } };
7 }
8 Object.defineProperty(o, k2, desc);
9}) : (function(o, m, k, k2) {
10 if (k2 === undefined) k2 = k;
11 o[k2] = m[k];
12}));
13var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14 Object.defineProperty(o, "default", { enumerable: true, value: v });
15}) : function(o, v) {
16 o["default"] = v;
17});
18var __importStar = (this && this.__importStar) || function (mod) {
19 if (mod && mod.__esModule) return mod;
20 var result = {};
21 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22 __setModuleDefault(result, mod);
23 return result;
24};
25var __importDefault = (this && this.__importDefault) || function (mod) {
26 return (mod && mod.__esModule) ? mod : { "default": mod };
27};
28Object.defineProperty(exports, "__esModule", { value: true });
29exports.Version = void 0;
30// external
31const ansi = __importStar(require("@bevry/ansi"));
32const figures_1 = __importDefault(require("@bevry/figures"));
33const version_clean_1 = __importDefault(require("version-clean"));
34// local
35const util_js_1 = require("./util.js");
36function getTime() {
37 return Date.now();
38}
39/** Version */
40class Version {
41 /** The precise version number, or at least the WIP version number/alias until it is resolved further. */
42 version;
43 /** The list of listeners we will call when updates happen. */
44 listeners = [];
45 /** An array of aliases for this version if any were used. */
46 aliases = [];
47 /** The current status of this version, initially it is `pending`. */
48 status = 'pending';
49 /**
50 * The version resolution that was successfully loaded.
51 * For instance, if a nvm alias is used such as "current" which resolves to 18.18.2 which is the system Node.js version, but is not installed via nvm itself, then trying to resolve "18.18.2" will fail with [version "v18.18.2" is not yet installed] but the original "current" resolution will work.
52 */
53 loadedVersion = null;
54 /** Whether or not this version has been successful. */
55 success = null;
56 /** Any error that occurred against this version. */
57 error = null;
58 /** The last stdout value that occurred against this version. */
59 stdout = null;
60 /** The last stderr value that occurred against this version. */
61 stderr = null;
62 /** The time the run started. */
63 started = null;
64 /** The time the run finished. */
65 finished = null;
66 /** Cache of the message. */
67 messageCache = null;
68 /** Create our Version instance */
69 constructor(version, listeners = []) {
70 this.listeners.push(...listeners);
71 this.version = String(version);
72 // If it fails to pass, then it is an alias, not a version
73 if (!(0, version_clean_1.default)(this.version)) {
74 // this uses a setter to add to this.aliases
75 this.alias = this.version;
76 }
77 }
78 /** The alias for this version if any were provided. E.g. `system` or `current` */
79 get alias() {
80 return this.aliases[0];
81 }
82 set alias(alias) {
83 if (alias) {
84 const aliases = this.aliases.concat(alias);
85 this.aliases = (0, util_js_1.uniq)(aliases);
86 }
87 }
88 /** Reset the version state. */
89 reset() {
90 this.success = null;
91 this.error = null;
92 this.stdout = null;
93 this.stderr = null;
94 this.started = null;
95 this.finished = null;
96 this.messageCache = null;
97 return this;
98 }
99 /** Notify that an update has occurred.
100 * @param {string?} status
101 * @returns {this}
102 * @private
103 */
104 async update(status) {
105 if (status)
106 this.status = status;
107 await Promise.all(this.listeners.map((listener) => listener(this)));
108 return this;
109 }
110 /** Load the version, which resolves the precise version number and determines if it is available or not. */
111 async load() {
112 this.status = 'loading';
113 this.reset();
114 await this.update();
115 const result = await (0, util_js_1.loadVersion)(this.version);
116 if (result.error) {
117 if ((result.error || '').toString().includes('not yet installed')) {
118 this.status = 'missing';
119 this.success = false;
120 }
121 else {
122 this.status = 'failed';
123 this.success = false;
124 }
125 this.error = result.error;
126 this.stdout = (result.stdout || '').toString();
127 this.stderr = (result.stderr || '').toString();
128 }
129 else {
130 const result = await (0, util_js_1.runVersion)(this.version);
131 if (result.error) {
132 this.status = 'failed';
133 this.success = false;
134 this.error = result.error;
135 this.stdout = (result.stdout || '').toString();
136 this.stderr = (result.stderr || '').toString();
137 }
138 else {
139 this.loadedVersion = this.loadedVersion || this.version;
140 this.version = (0, util_js_1.lastLine)(result.stdout); // resolve the version
141 this.status = 'loaded';
142 }
143 }
144 await this.update();
145 return this;
146 }
147 /**
148 * Install the version if it was missing.
149 * Requires the current state to be `missing`.
150 */
151 async install() {
152 if (this.status !== 'missing')
153 return this;
154 this.status = 'installing';
155 this.reset();
156 await this.update();
157 const result = await (0, util_js_1.runInstall)(this.version);
158 if (result.error) {
159 this.error = result.error;
160 this.status = 'missing';
161 this.success = false;
162 this.stdout = (result.stdout || '').toString();
163 this.stderr = (result.stderr || '').toString();
164 }
165 else {
166 await this.update('installed');
167 await this.load();
168 }
169 return this;
170 }
171 /**
172 * Run the command against the version.
173 * Requires the current state to be `loaded`.
174 */
175 async test(command) {
176 if (!command) {
177 throw new Error('no command provided to the testen version runner');
178 }
179 if (this.status !== 'loaded')
180 return this;
181 this.status = 'running';
182 this.reset();
183 await this.update();
184 this.started = getTime();
185 const result = await (0, util_js_1.runCommand)(this.loadedVersion || this.version, command);
186 this.finished = getTime();
187 this.error = result.error;
188 this.stdout = (result.stdout || '').toString();
189 this.stderr = (result.stderr || '').toString();
190 this.success = Boolean(result.error) === false;
191 await this.update(this.success ? 'passed' : 'failed');
192 return this;
193 }
194 /**
195 * Converts the version properties into an array for use of displaying in a neat table.
196 * Doesn't cache as we want to refresh timers.
197 */
198 get row() {
199 const indicator = this.success === null
200 ? ansi.dim(figures_1.default.circle)
201 : this.success
202 ? ansi.green(figures_1.default.tick)
203 : ansi.red(figures_1.default.cross);
204 const result = this.success === null
205 ? ansi.dim(this.status)
206 : this.success
207 ? ansi.green(this.status)
208 : ansi.red(this.status);
209 // note that caching prevents realtime updates of duration time
210 const ms = this.started ? (this.finished || getTime()) - this.started : 0;
211 const duration = this.started
212 ? ansi.dim(ms > 1000 ? `${Math.round(ms / 1000)}s` : `${ms}ms`)
213 : '';
214 const aliases = this.aliases.length
215 ? ansi.dim(` [${this.aliases.join('|')}]`)
216 : '';
217 const row = [
218 ' ' + indicator,
219 this.version + aliases,
220 result,
221 duration,
222 ];
223 return row;
224 }
225 /**
226 * Converts the version properties a detailed message of what has occurred with this version.
227 * Caches for each status change.
228 * @property {string} message
229 * @public
230 */
231 get message() {
232 // Cache
233 if (this.messageCache && this.messageCache[0] === this.status) {
234 return this.messageCache[1];
235 }
236 // Prepare
237 const parts = [];
238 // fetch heading
239 const heading = `Node version ${ansi.underline(this.version)} ${this.status}`;
240 if (this.status === 'missing') {
241 parts.push(ansi.bold(ansi.red(heading)));
242 }
243 else if (this.success === true) {
244 parts.push(ansi.bold(ansi.green(heading)));
245 }
246 else if (this.success === false) {
247 parts.push(ansi.bold(ansi.red(heading)));
248 }
249 else {
250 // running, loading, etc - shown in verbose mode
251 parts.push(ansi.bold(ansi.dim(heading)));
252 }
253 // Output the command that was run
254 if (this.error) {
255 parts.push(ansi.red(this.error.message.split('\n')[0]));
256 }
257 // Output stdout and stderr
258 if (this.status === 'missing') {
259 parts.push(ansi.red(`You need to run: nvm install ${this.version}`));
260 }
261 else {
262 const stdout = (0, util_js_1.trim)(this.stdout || '');
263 const stderr = (0, util_js_1.trim)(this.stderr || '');
264 if (!stdout && !stderr) {
265 parts.push(ansi.dim('no output'));
266 }
267 else {
268 if (stdout) {
269 parts.push(stdout);
270 }
271 if (stderr) {
272 parts.push(ansi.red(stderr));
273 }
274 }
275 }
276 // Join it all together
277 const message = parts.join('\n');
278 // Cache
279 this.messageCache = [this.status, message];
280 return message;
281 }
282}
283exports.Version = Version;
284exports.default = Version;