@decaf-ts/utils 0.4.1 → 0.4.3
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/dist/utils.cjs +1 -4224
- package/dist/utils.esm.cjs +1 -4163
- package/lib/bin/build-scripts.cjs +10 -2
- package/lib/cli/command.cjs +2 -2
- package/lib/cli/commands/build-scripts.cjs +1 -1
- package/lib/esm/bin/build-scripts.js +10 -2
- package/lib/esm/cli/command.js +2 -2
- package/lib/esm/cli/commands/build-scripts.js +1 -1
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/index.cjs +1 -1
- package/lib/index.d.ts +1 -1
- package/package.json +5 -5
package/dist/utils.cjs
CHANGED
|
@@ -1,4224 +1 @@
|
|
|
1
|
-
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('prompts'), require('util'), require('@decaf-ts/logging'), require('fs'), require('path'), require('child_process'), require('styled-string-builder'), require('https'), require('rollup'), require('@rollup/plugin-typescript'), require('@rollup/plugin-commonjs'), require('@rollup/plugin-node-resolve'), require('@rollup/plugin-json'), require('module'), require('typescript')) :
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', 'prompts', 'util', '@decaf-ts/logging', 'fs', 'path', 'child_process', 'styled-string-builder', 'https', 'rollup', '@rollup/plugin-typescript', '@rollup/plugin-commonjs', '@rollup/plugin-node-resolve', '@rollup/plugin-json', 'module', 'typescript'], factory) :
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.utils = {}, global.prompts, global.util, global.decafTsLogging, global.fs, global.path, global.childProcess, global.styledStringBuilder, global.https, global.rollup, global.rollupPluginTypescript, global.rollupPluginCommonjs, global.rollupPluginNodeResolve, global.rollupPluginJson, global.module, global.typescript));
|
|
5
|
-
})(this, (function (exports, prompts, util, logging, fs, path, child_process, styledStringBuilder, https, rollup, typescript, commonjs, pluginNodeResolve, json, module, ts) { 'use strict';
|
|
6
|
-
|
|
7
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
|
-
|
|
9
|
-
function _interopNamespace(e) {
|
|
10
|
-
if (e && e.__esModule) return e;
|
|
11
|
-
var n = Object.create(null);
|
|
12
|
-
if (e) {
|
|
13
|
-
Object.keys(e).forEach(function (k) {
|
|
14
|
-
if (k !== 'default') {
|
|
15
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
-
enumerable: true,
|
|
18
|
-
get: function () { return e[k]; }
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
n["default"] = e;
|
|
24
|
-
return Object.freeze(n);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
var prompts__default = /*#__PURE__*/_interopDefaultLegacy(prompts);
|
|
28
|
-
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
29
|
-
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
30
|
-
var https__default = /*#__PURE__*/_interopDefaultLegacy(https);
|
|
31
|
-
var typescript__default = /*#__PURE__*/_interopDefaultLegacy(typescript);
|
|
32
|
-
var commonjs__default = /*#__PURE__*/_interopDefaultLegacy(commonjs);
|
|
33
|
-
var json__default = /*#__PURE__*/_interopDefaultLegacy(json);
|
|
34
|
-
var ts__namespace = /*#__PURE__*/_interopNamespace(ts);
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @description Represents a user input prompt with various configuration options.
|
|
38
|
-
* @summary This class provides a flexible interface for creating and managing user input prompts.
|
|
39
|
-
* It implements the PromptObject interface from the 'prompts' library and offers methods to set
|
|
40
|
-
* various properties of the prompt. The class also includes static methods for common input scenarios
|
|
41
|
-
* and argument parsing.
|
|
42
|
-
*
|
|
43
|
-
* @template R - The type of the prompt name, extending string.
|
|
44
|
-
*
|
|
45
|
-
* @param name - The name of the prompt, used as the key in the returned answers object.
|
|
46
|
-
*
|
|
47
|
-
* @class
|
|
48
|
-
* @example
|
|
49
|
-
* ```typescript
|
|
50
|
-
* import { UserInput } from '@decaf-ts/utils';
|
|
51
|
-
*
|
|
52
|
-
* // Create a simple text input
|
|
53
|
-
* const nameInput = new UserInput('name')
|
|
54
|
-
* .setMessage('What is your name?')
|
|
55
|
-
* .setInitial('User');
|
|
56
|
-
*
|
|
57
|
-
* // Create a number input with validation
|
|
58
|
-
* const ageInput = new UserInput('age')
|
|
59
|
-
* .setType('number')
|
|
60
|
-
* .setMessage('How old are you?')
|
|
61
|
-
* .setMin(0)
|
|
62
|
-
* .setMax(120);
|
|
63
|
-
*
|
|
64
|
-
* // Ask for input and process the results
|
|
65
|
-
* async function getUserInfo() {
|
|
66
|
-
* const answers = await UserInput.ask([nameInput, ageInput]);
|
|
67
|
-
* console.log(`Hello ${answers.name}, you are ${answers.age} years old.`);
|
|
68
|
-
* }
|
|
69
|
-
*
|
|
70
|
-
* getUserInfo();
|
|
71
|
-
* ```
|
|
72
|
-
*
|
|
73
|
-
* @mermaid
|
|
74
|
-
* sequenceDiagram
|
|
75
|
-
* participant Client
|
|
76
|
-
* participant UserInput
|
|
77
|
-
* participant PromptLibrary
|
|
78
|
-
*
|
|
79
|
-
* Client->>UserInput: new UserInput(name)
|
|
80
|
-
* Client->>UserInput: setMessage(message)
|
|
81
|
-
* Client->>UserInput: setType(type)
|
|
82
|
-
* Client->>UserInput: setInitial(initial)
|
|
83
|
-
* Client->>UserInput: Other configuration methods
|
|
84
|
-
*
|
|
85
|
-
* Client->>UserInput: ask()
|
|
86
|
-
* UserInput->>PromptLibrary: prompts(question)
|
|
87
|
-
* PromptLibrary->>Client: Display prompt
|
|
88
|
-
* Client->>PromptLibrary: User provides input
|
|
89
|
-
* PromptLibrary->>UserInput: Return answers
|
|
90
|
-
* UserInput->>Client: Return processed answers
|
|
91
|
-
*/
|
|
92
|
-
class UserInput {
|
|
93
|
-
static { this.logger = logging.Logging.for(UserInput); }
|
|
94
|
-
constructor(name) {
|
|
95
|
-
/**
|
|
96
|
-
* @description The type of the prompt.
|
|
97
|
-
* @summary Determines the input method (e.g., text, number, confirm).
|
|
98
|
-
*/
|
|
99
|
-
this.type = "text";
|
|
100
|
-
this.name = name;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* @description Sets the type of the prompt.
|
|
104
|
-
* @summary Configures the input method for the prompt.
|
|
105
|
-
*
|
|
106
|
-
* @param type - The type of the prompt.
|
|
107
|
-
* @returns This UserInput instance for method chaining.
|
|
108
|
-
*/
|
|
109
|
-
setType(type) {
|
|
110
|
-
UserInput.logger.verbose(`Setting type to: ${type}`);
|
|
111
|
-
this.type = type;
|
|
112
|
-
return this;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* @description Sets the message of the prompt.
|
|
116
|
-
* @summary Configures the question or instruction presented to the user.
|
|
117
|
-
*
|
|
118
|
-
* @param value - The message to be displayed.
|
|
119
|
-
* @returns This UserInput instance for method chaining.
|
|
120
|
-
*/
|
|
121
|
-
setMessage(value) {
|
|
122
|
-
UserInput.logger.verbose(`Setting message to: ${value}`);
|
|
123
|
-
this.message = value;
|
|
124
|
-
return this;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* @description Sets the initial value of the prompt.
|
|
128
|
-
* @summary Configures the default value presented to the user.
|
|
129
|
-
*
|
|
130
|
-
* @param value - The initial value.
|
|
131
|
-
* @returns This UserInput instance for method chaining.
|
|
132
|
-
*/
|
|
133
|
-
setInitial(value) {
|
|
134
|
-
UserInput.logger.verbose(`Setting initial value to: ${value}`);
|
|
135
|
-
this.initial = value;
|
|
136
|
-
return this;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* @description Sets the style of the prompt.
|
|
140
|
-
* @summary Configures the visual style of the prompt.
|
|
141
|
-
*
|
|
142
|
-
* @param value - The style to be applied.
|
|
143
|
-
* @returns This UserInput instance for method chaining.
|
|
144
|
-
*/
|
|
145
|
-
setStyle(value) {
|
|
146
|
-
UserInput.logger.verbose(`Setting style to: ${value}`);
|
|
147
|
-
this.style = value;
|
|
148
|
-
return this;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* @description Sets the format function of the prompt.
|
|
152
|
-
* @summary Configures a function to format the user's input before it's returned.
|
|
153
|
-
*
|
|
154
|
-
* @param value - The format function.
|
|
155
|
-
* @returns This UserInput instance for method chaining.
|
|
156
|
-
*/
|
|
157
|
-
setFormat(value) {
|
|
158
|
-
UserInput.logger.verbose(`Setting format function`);
|
|
159
|
-
this.format = value;
|
|
160
|
-
return this;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* @description Sets the validation function of the prompt.
|
|
164
|
-
* @summary Configures a function to validate the user's input.
|
|
165
|
-
*
|
|
166
|
-
* @param value - The validation function.
|
|
167
|
-
* @returns This UserInput instance for method chaining.
|
|
168
|
-
*/
|
|
169
|
-
setValidate(value) {
|
|
170
|
-
UserInput.logger.verbose(`Setting validate function`);
|
|
171
|
-
this.validate = value;
|
|
172
|
-
return this;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* @description Sets the onState callback of the prompt.
|
|
176
|
-
* @summary Configures a function to be called when the state of the prompt changes.
|
|
177
|
-
*
|
|
178
|
-
* @param value - The onState callback function.
|
|
179
|
-
* @returns This UserInput instance for method chaining.
|
|
180
|
-
*/
|
|
181
|
-
setOnState(value) {
|
|
182
|
-
UserInput.logger.verbose(`Setting onState callback`);
|
|
183
|
-
this.onState = value;
|
|
184
|
-
return this;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* @description Sets the minimum value for number inputs.
|
|
188
|
-
* @summary Configures the lowest number the user can input.
|
|
189
|
-
*
|
|
190
|
-
* @param value - The minimum value.
|
|
191
|
-
* @returns This UserInput instance for method chaining.
|
|
192
|
-
*/
|
|
193
|
-
setMin(value) {
|
|
194
|
-
UserInput.logger.verbose(`Setting min value to: ${value}`);
|
|
195
|
-
this.min = value;
|
|
196
|
-
return this;
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* @description Sets the maximum value for number inputs.
|
|
200
|
-
* @summary Configures the highest number the user can input.
|
|
201
|
-
*
|
|
202
|
-
* @param value - The maximum value.
|
|
203
|
-
* @returns This UserInput instance for method chaining.
|
|
204
|
-
*/
|
|
205
|
-
setMax(value) {
|
|
206
|
-
UserInput.logger.verbose(`Setting max value to: ${value}`);
|
|
207
|
-
this.max = value;
|
|
208
|
-
return this;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* @description Sets whether to allow float values for number inputs.
|
|
212
|
-
* @summary Configures whether decimal numbers are allowed.
|
|
213
|
-
*
|
|
214
|
-
* @param value - Whether to allow float values.
|
|
215
|
-
* @returns This UserInput instance for method chaining.
|
|
216
|
-
*/
|
|
217
|
-
setFloat(value) {
|
|
218
|
-
UserInput.logger.verbose(`Setting float to: ${value}`);
|
|
219
|
-
this.float = value;
|
|
220
|
-
return this;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* @description Sets the number of decimal places to round to for float inputs.
|
|
224
|
-
* @summary Configures the precision of float inputs.
|
|
225
|
-
*
|
|
226
|
-
* @param value - The number of decimal places.
|
|
227
|
-
* @returns This UserInput instance for method chaining.
|
|
228
|
-
*/
|
|
229
|
-
setRound(value) {
|
|
230
|
-
UserInput.logger.verbose(`Setting round to: ${value}`);
|
|
231
|
-
this.round = value;
|
|
232
|
-
return this;
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* @description Sets the instructions for the user.
|
|
236
|
-
* @summary Configures additional guidance provided to the user.
|
|
237
|
-
*
|
|
238
|
-
* @param value - The instructions.
|
|
239
|
-
* @returns This UserInput instance for method chaining.
|
|
240
|
-
*/
|
|
241
|
-
setInstructions(value) {
|
|
242
|
-
UserInput.logger.verbose(`Setting instructions to: ${value}`);
|
|
243
|
-
this.instructions = value;
|
|
244
|
-
return this;
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* @description Sets the increment value for number inputs.
|
|
248
|
-
* @summary Configures the step size when increasing or decreasing the number.
|
|
249
|
-
*
|
|
250
|
-
* @param value - The increment value.
|
|
251
|
-
* @returns This UserInput instance for method chaining.
|
|
252
|
-
*/
|
|
253
|
-
setIncrement(value) {
|
|
254
|
-
UserInput.logger.verbose(`Setting increment to: ${value}`);
|
|
255
|
-
this.increment = value;
|
|
256
|
-
return this;
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* @description Sets the separator for list inputs.
|
|
260
|
-
* @summary Configures the character used to separate list items.
|
|
261
|
-
*
|
|
262
|
-
* @param value - The separator character.
|
|
263
|
-
* @returns This UserInput instance for method chaining.
|
|
264
|
-
*/
|
|
265
|
-
setSeparator(value) {
|
|
266
|
-
UserInput.logger.verbose(`Setting separator to: ${value}`);
|
|
267
|
-
this.separator = value;
|
|
268
|
-
return this;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* @description Sets the active option style for select inputs.
|
|
272
|
-
* @summary Configures the style applied to the currently selected option.
|
|
273
|
-
*
|
|
274
|
-
* @param value - The active option style.
|
|
275
|
-
* @returns This UserInput instance for method chaining.
|
|
276
|
-
*/
|
|
277
|
-
setActive(value) {
|
|
278
|
-
UserInput.logger.verbose(`Setting active style to: ${value}`);
|
|
279
|
-
this.active = value;
|
|
280
|
-
return this;
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* @description Sets the inactive option style for select inputs.
|
|
284
|
-
* @summary Configures the style applied to non-selected options.
|
|
285
|
-
*
|
|
286
|
-
* @param value - The inactive option style.
|
|
287
|
-
* @returns This UserInput instance for method chaining.
|
|
288
|
-
*/
|
|
289
|
-
setInactive(value) {
|
|
290
|
-
UserInput.logger.verbose(`Setting inactive style to: ${value}`);
|
|
291
|
-
this.inactive = value;
|
|
292
|
-
return this;
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* @description Sets the choices for select, multiselect, or autocomplete inputs.
|
|
296
|
-
* @summary Configures the available options that the user can select from in choice-based prompts.
|
|
297
|
-
*
|
|
298
|
-
* @param value - The array of choices or a function to determine the choices.
|
|
299
|
-
* @returns This UserInput instance for method chaining.
|
|
300
|
-
*/
|
|
301
|
-
setChoices(value) {
|
|
302
|
-
UserInput.logger.verbose(`Setting choices: ${JSON.stringify(value)}`);
|
|
303
|
-
this.choices = value;
|
|
304
|
-
return this;
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* @description Sets the hint text for the prompt.
|
|
308
|
-
* @summary Configures additional information displayed to the user.
|
|
309
|
-
*
|
|
310
|
-
* @param value - The hint text.
|
|
311
|
-
* @returns This UserInput instance for method chaining.
|
|
312
|
-
*/
|
|
313
|
-
setHint(value) {
|
|
314
|
-
UserInput.logger.verbose(`Setting hint to: ${value}`);
|
|
315
|
-
this.hint = value;
|
|
316
|
-
return this;
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* @description Sets the warning text for the prompt.
|
|
320
|
-
* @summary Configures a warning message displayed to the user.
|
|
321
|
-
*
|
|
322
|
-
* @param value - The warning text.
|
|
323
|
-
* @returns This UserInput instance for method chaining.
|
|
324
|
-
*/
|
|
325
|
-
setWarn(value) {
|
|
326
|
-
UserInput.logger.verbose(`Setting warn to: ${value}`);
|
|
327
|
-
this.warn = value;
|
|
328
|
-
return this;
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* @description Sets the suggestion function for autocomplete inputs.
|
|
332
|
-
* @summary Configures a function that provides suggestions based on the user's input and available choices.
|
|
333
|
-
*
|
|
334
|
-
* @param value - A function that takes the current input and available choices and returns a Promise resolving to suggestions.
|
|
335
|
-
* @returns This UserInput instance for method chaining.
|
|
336
|
-
*/
|
|
337
|
-
setSuggest(value) {
|
|
338
|
-
UserInput.logger.verbose(`Setting suggest function`);
|
|
339
|
-
this.suggest = value;
|
|
340
|
-
return this;
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* @description Sets the limit for list inputs.
|
|
344
|
-
* @summary Configures the maximum number of items that can be selected in list-type prompts.
|
|
345
|
-
* @template R - The type of the prompt name, extending string.
|
|
346
|
-
* @param value - The maximum number of items that can be selected, or a function to determine this value.
|
|
347
|
-
* @return This UserInput instance for method chaining.
|
|
348
|
-
*/
|
|
349
|
-
setLimit(value) {
|
|
350
|
-
UserInput.logger.verbose(`Setting limit to: ${value}`);
|
|
351
|
-
this.limit = value;
|
|
352
|
-
return this;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* @description Sets the mask for password inputs.
|
|
356
|
-
* @summary Configures the character used to hide the user's input in password-type prompts.
|
|
357
|
-
* @template R - The type of the prompt name, extending string.
|
|
358
|
-
* @param value - The character used to mask the input, or a function to determine this value.
|
|
359
|
-
* @return This UserInput instance for method chaining.
|
|
360
|
-
*/
|
|
361
|
-
setMask(value) {
|
|
362
|
-
UserInput.logger.verbose(`Setting mask to: ${value}`);
|
|
363
|
-
this.mask = value;
|
|
364
|
-
return this;
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* @description Sets the stdout stream for the prompt.
|
|
368
|
-
* @summary Configures the output stream used by the prompt for displaying messages and results.
|
|
369
|
-
* @param value - The Writable stream to be used as stdout.
|
|
370
|
-
* @return This UserInput instance for method chaining.
|
|
371
|
-
*/
|
|
372
|
-
setStdout(value) {
|
|
373
|
-
UserInput.logger.verbose(`Setting stdout stream`);
|
|
374
|
-
this.stdout = value;
|
|
375
|
-
return this;
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* @description Sets the stdin stream for the prompt.
|
|
379
|
-
* @summary Configures the input stream used by the prompt for receiving user input.
|
|
380
|
-
* @param value - The Readable stream to be used as stdin.
|
|
381
|
-
* @return This UserInput instance for method chaining.
|
|
382
|
-
*/
|
|
383
|
-
setStdin(value) {
|
|
384
|
-
this.stdin = value;
|
|
385
|
-
return this;
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* @description Asks the user for input based on the current UserInput configuration.
|
|
389
|
-
* @summary Prompts the user and returns their response as a single value.
|
|
390
|
-
* @template R - The type of the prompt name, extending string.
|
|
391
|
-
* @return A Promise that resolves to the user's answer.
|
|
392
|
-
*/
|
|
393
|
-
async ask() {
|
|
394
|
-
return (await UserInput.ask(this))[this.name];
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* @description Asks the user one or more questions based on the provided UserInput configurations.
|
|
398
|
-
* @summary Prompts the user with one or more questions and returns their answers as an object.
|
|
399
|
-
* @template R - The type of the prompt name, extending string.
|
|
400
|
-
* @param question - A single UserInput instance or an array of UserInput instances.
|
|
401
|
-
* @return A Promise that resolves to an object containing the user's answers.
|
|
402
|
-
* @mermaid
|
|
403
|
-
* sequenceDiagram
|
|
404
|
-
* participant U as User
|
|
405
|
-
* participant A as ask method
|
|
406
|
-
* participant P as prompts library
|
|
407
|
-
* A->>P: Call prompts with question(s)
|
|
408
|
-
* P->>U: Display prompt(s)
|
|
409
|
-
* U->>P: Provide input
|
|
410
|
-
* P->>A: Return answers
|
|
411
|
-
* A->>A: Process answers
|
|
412
|
-
* A-->>Caller: Return processed answers
|
|
413
|
-
*/
|
|
414
|
-
static async ask(question) {
|
|
415
|
-
const log = UserInput.logger.for(this.ask);
|
|
416
|
-
if (!Array.isArray(question)) {
|
|
417
|
-
question = [question];
|
|
418
|
-
}
|
|
419
|
-
let answers;
|
|
420
|
-
try {
|
|
421
|
-
log.verbose(`Asking questions: ${question.map((q) => q.name).join(", ")}`);
|
|
422
|
-
answers = await prompts__default["default"](question);
|
|
423
|
-
log.verbose(`Received answers: ${JSON.stringify(answers, null, 2)}`);
|
|
424
|
-
}
|
|
425
|
-
catch (error) {
|
|
426
|
-
throw new Error(`Error while getting input: ${error}`);
|
|
427
|
-
}
|
|
428
|
-
return answers;
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* @description Asks the user for a number input.
|
|
432
|
-
* @summary Prompts the user to enter a number, with optional minimum, maximum, and initial values.
|
|
433
|
-
* @param name - The name of the prompt, used as the key in the returned answers object.
|
|
434
|
-
* @param question - The message displayed to the user.
|
|
435
|
-
* @param min - The minimum allowed value (optional).
|
|
436
|
-
* @param max - The maximum allowed value (optional).
|
|
437
|
-
* @param initial - The initial value presented to the user (optional).
|
|
438
|
-
* @return A Promise that resolves to the number entered by the user.
|
|
439
|
-
*/
|
|
440
|
-
static async askNumber(name, question, min, max, initial) {
|
|
441
|
-
const log = UserInput.logger.for(this.askNumber);
|
|
442
|
-
log.verbose(`Asking number input: undefined, question: ${question}, min: ${min}, max: ${max}, initial: ${initial}`);
|
|
443
|
-
const userInput = new UserInput(name)
|
|
444
|
-
.setMessage(question)
|
|
445
|
-
.setType("number");
|
|
446
|
-
if (typeof min === "number")
|
|
447
|
-
userInput.setMin(min);
|
|
448
|
-
if (typeof max === "number")
|
|
449
|
-
userInput.setMax(max);
|
|
450
|
-
if (typeof initial === "number")
|
|
451
|
-
userInput.setInitial(initial);
|
|
452
|
-
return (await this.ask(userInput))[name];
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* @description Asks the user for a text input.
|
|
456
|
-
* @summary Prompts the user to enter text, with optional masking and initial value.
|
|
457
|
-
* @param name - The name of the prompt, used as the key in the returned answers object.
|
|
458
|
-
* @param question - The message displayed to the user.
|
|
459
|
-
* @param mask - The character used to mask the input (optional, for password-like inputs).
|
|
460
|
-
* @param initial - The initial value presented to the user (optional).
|
|
461
|
-
* @return A Promise that resolves to the text entered by the user.
|
|
462
|
-
*/
|
|
463
|
-
static async askText(name, question, mask = undefined, initial) {
|
|
464
|
-
const log = UserInput.logger.for(this.askText);
|
|
465
|
-
log.verbose(`Asking text input: undefined, question: ${question}, mask: ${mask}, initial: ${initial}`);
|
|
466
|
-
const userInput = new UserInput(name).setMessage(question);
|
|
467
|
-
if (mask)
|
|
468
|
-
userInput.setMask(mask);
|
|
469
|
-
if (typeof initial === "string")
|
|
470
|
-
userInput.setInitial(initial);
|
|
471
|
-
return (await this.ask(userInput))[name];
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* @description Asks the user for a confirmation (yes/no).
|
|
475
|
-
* @summary Prompts the user with a yes/no question and returns a boolean result.
|
|
476
|
-
* @param name - The name of the prompt, used as the key in the returned answers object.
|
|
477
|
-
* @param question - The message displayed to the user.
|
|
478
|
-
* @param initial - The initial value presented to the user (optional).
|
|
479
|
-
* @return A Promise that resolves to a boolean representing the user's answer.
|
|
480
|
-
*/
|
|
481
|
-
static async askConfirmation(name, question, initial) {
|
|
482
|
-
const log = UserInput.logger.for(this.askConfirmation);
|
|
483
|
-
log.verbose(`Asking confirmation input: undefined, question: ${question}, initial: ${initial}`);
|
|
484
|
-
const userInput = new UserInput(name)
|
|
485
|
-
.setMessage(question)
|
|
486
|
-
.setType("confirm");
|
|
487
|
-
if (typeof initial !== "undefined")
|
|
488
|
-
userInput.setInitial(initial);
|
|
489
|
-
return (await this.ask(userInput))[name];
|
|
490
|
-
}
|
|
491
|
-
/**
|
|
492
|
-
* @description Repeatedly asks for input until a valid response is given or the limit is reached.
|
|
493
|
-
* @summary This method insists on getting a valid input from the user, allowing for a specified number of attempts.
|
|
494
|
-
*
|
|
495
|
-
* @template R - The type of the expected result.
|
|
496
|
-
* @param input - The UserInput instance to use for prompting.
|
|
497
|
-
* @param {function(string):boolean} test - Validator function receiving the user input and returning whether it is valid.
|
|
498
|
-
* @param defaultConfirmation - The default value for the confirmation prompt (true for yes, false for no).
|
|
499
|
-
* @param limit - The maximum number of attempts allowed (default is 1).
|
|
500
|
-
* @return A Promise that resolves to the valid input or undefined if the limit is reached.
|
|
501
|
-
*
|
|
502
|
-
* @mermaid
|
|
503
|
-
* sequenceDiagram
|
|
504
|
-
* participant U as User
|
|
505
|
-
* participant I as insist method
|
|
506
|
-
* participant A as ask method
|
|
507
|
-
* participant T as test function
|
|
508
|
-
* participant C as askConfirmation method
|
|
509
|
-
* loop Until valid input or limit reached
|
|
510
|
-
* I->>A: Call ask with input
|
|
511
|
-
* A->>U: Prompt user
|
|
512
|
-
* U->>A: Provide input
|
|
513
|
-
* A->>I: Return result
|
|
514
|
-
* I->>T: Test result
|
|
515
|
-
* alt Test passes
|
|
516
|
-
* I->>C: Ask for confirmation
|
|
517
|
-
* C->>U: Confirm input
|
|
518
|
-
* U->>C: Provide confirmation
|
|
519
|
-
* C->>I: Return confirmation
|
|
520
|
-
* alt Confirmed
|
|
521
|
-
* I-->>Caller: Return valid result
|
|
522
|
-
* else Not confirmed
|
|
523
|
-
* I->>I: Continue loop
|
|
524
|
-
* end
|
|
525
|
-
* else Test fails
|
|
526
|
-
* I->>I: Continue loop
|
|
527
|
-
* end
|
|
528
|
-
* end
|
|
529
|
-
* I-->>Caller: Return undefined if limit reached
|
|
530
|
-
*/
|
|
531
|
-
static async insist(input, test, defaultConfirmation, limit = 1) {
|
|
532
|
-
const log = UserInput.logger.for(this.insist);
|
|
533
|
-
log.verbose(`Insisting on input: ${input.name}, test: ${test.toString()}, defaultConfirmation: ${defaultConfirmation}, limit: ${limit}`);
|
|
534
|
-
let result = undefined;
|
|
535
|
-
let count = 0;
|
|
536
|
-
let confirmation;
|
|
537
|
-
try {
|
|
538
|
-
do {
|
|
539
|
-
result = (await UserInput.ask(input))[input.name];
|
|
540
|
-
if (!test(result)) {
|
|
541
|
-
result = undefined;
|
|
542
|
-
continue;
|
|
543
|
-
}
|
|
544
|
-
confirmation = await UserInput.askConfirmation(`${input.name}-confirm`, `Is the ${input.type} correct?`, defaultConfirmation);
|
|
545
|
-
if (!confirmation)
|
|
546
|
-
result = undefined;
|
|
547
|
-
} while (typeof result === "undefined" && limit > 1 && count++ < limit);
|
|
548
|
-
}
|
|
549
|
-
catch (e) {
|
|
550
|
-
log.error(`Error while insisting: ${e}`);
|
|
551
|
-
throw e;
|
|
552
|
-
}
|
|
553
|
-
if (typeof result === "undefined")
|
|
554
|
-
log.info("no selection...");
|
|
555
|
-
return result;
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* @description Repeatedly asks for text input until a valid response is given or the limit is reached.
|
|
559
|
-
* @summary This method insists on getting a valid text input from the user, allowing for a specified number of attempts.
|
|
560
|
-
*
|
|
561
|
-
* @param name - The name of the prompt, used as the key in the returned answers object.
|
|
562
|
-
* @param question - The message displayed to the user.
|
|
563
|
-
* @param {function(number):boolean} test - Validator function receiving the user input and returning whether it is valid.
|
|
564
|
-
* @param mask - The character used to mask the input (optional, for password-like inputs).
|
|
565
|
-
* @param initial - The initial value presented to the user (optional).
|
|
566
|
-
* @param defaultConfirmation - The default value for the confirmation prompt (true for yes, false for no).
|
|
567
|
-
* @param limit - The maximum number of attempts allowed (default is -1, meaning unlimited).
|
|
568
|
-
* @return A Promise that resolves to the valid input or undefined if the limit is reached.
|
|
569
|
-
*/
|
|
570
|
-
static async insistForText(name, question, test, mask = undefined, initial, defaultConfirmation = false, limit = -1) {
|
|
571
|
-
const log = UserInput.logger.for(this.insistForText);
|
|
572
|
-
log.verbose(`Insisting for text input: undefined, question: ${question}, test: ${test.toString()}, mask: ${mask}, initial: ${initial}, defaultConfirmation: ${defaultConfirmation}, limit: ${limit}`);
|
|
573
|
-
const userInput = new UserInput(name).setMessage(question);
|
|
574
|
-
if (mask)
|
|
575
|
-
userInput.setMask(mask);
|
|
576
|
-
if (typeof initial === "string")
|
|
577
|
-
userInput.setInitial(initial);
|
|
578
|
-
return (await this.insist(userInput, test, defaultConfirmation, limit));
|
|
579
|
-
}
|
|
580
|
-
/**
|
|
581
|
-
* @description Repeatedly asks for number input until a valid response is given or the limit is reached.
|
|
582
|
-
* @summary This method insists on getting a valid number input from the user, allowing for a specified number of attempts.
|
|
583
|
-
*
|
|
584
|
-
* @param name - The name of the prompt, used as the key in the returned answers object.
|
|
585
|
-
* @param question - The message displayed to the user.
|
|
586
|
-
* @param test - A function to validate the user's input.
|
|
587
|
-
* @param min - The minimum allowed value (optional).
|
|
588
|
-
* @param max - The maximum allowed value (optional).
|
|
589
|
-
* @param initial - The initial value presented to the user (optional).
|
|
590
|
-
* @param defaultConfirmation - The default value for the confirmation prompt (true for yes, false for no).
|
|
591
|
-
* @param limit - The maximum number of attempts allowed (default is -1, meaning unlimited).
|
|
592
|
-
* @return A Promise that resolves to the valid input or undefined if the limit is reached.
|
|
593
|
-
*/
|
|
594
|
-
static async insistForNumber(name, question, test, min, max, initial, defaultConfirmation = false, limit = -1) {
|
|
595
|
-
const log = UserInput.logger.for(this.insistForNumber);
|
|
596
|
-
log.verbose(`Insisting for number input: undefined, question: ${question}, test: ${test.toString()}, min: ${min}, max: ${max}, initial: ${initial}, defaultConfirmation: ${defaultConfirmation}, limit: ${limit}`);
|
|
597
|
-
const userInput = new UserInput(name)
|
|
598
|
-
.setMessage(question)
|
|
599
|
-
.setType("number");
|
|
600
|
-
if (typeof min === "number")
|
|
601
|
-
userInput.setMin(min);
|
|
602
|
-
if (typeof max === "number")
|
|
603
|
-
userInput.setMax(max);
|
|
604
|
-
if (typeof initial === "number")
|
|
605
|
-
userInput.setInitial(initial);
|
|
606
|
-
return (await this.insist(userInput, test, defaultConfirmation, limit));
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* @description Parses command-line arguments based on the provided options.
|
|
610
|
-
* @summary Uses Node.js's util.parseArgs to parse command-line arguments and return the result.
|
|
611
|
-
* @param options - Configuration options for parsing arguments.
|
|
612
|
-
* @return An object containing the parsed arguments.
|
|
613
|
-
* @mermaid
|
|
614
|
-
* sequenceDiagram
|
|
615
|
-
* participant C as Caller
|
|
616
|
-
* participant P as parseArgs method
|
|
617
|
-
* participant U as util.parseArgs
|
|
618
|
-
* C->>P: Call with options
|
|
619
|
-
* P->>P: Prepare args object
|
|
620
|
-
* P->>U: Call parseArgs with prepared args
|
|
621
|
-
* U->>P: Return parsed result
|
|
622
|
-
* P-->>C: Return ParseArgsResult
|
|
623
|
-
*/
|
|
624
|
-
static parseArgs(options) {
|
|
625
|
-
const log = UserInput.logger.for(this.parseArgs);
|
|
626
|
-
const args = {
|
|
627
|
-
args: process.argv.slice(2),
|
|
628
|
-
options: options,
|
|
629
|
-
};
|
|
630
|
-
log.debug(`Parsing arguments: ${JSON.stringify(args, null, 2)}`);
|
|
631
|
-
try {
|
|
632
|
-
return util.parseArgs(args);
|
|
633
|
-
}
|
|
634
|
-
catch (error) {
|
|
635
|
-
log.debug(`Error while parsing arguments:\n${JSON.stringify(args, null, 2)}\n | options\n${JSON.stringify(options, null, 2)}\n | ${error}`);
|
|
636
|
-
throw new Error(`Error while parsing arguments: ${error}`);
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
/**
|
|
642
|
-
* @description Default command options for CLI commands.
|
|
643
|
-
* @summary Defines the structure and default values for common command-line options used across various CLI commands.
|
|
644
|
-
* @const DefaultCommandOptions
|
|
645
|
-
* @typedef {Object} DefaultCommandOptions
|
|
646
|
-
* @property {Object} verbose - Verbosity level option.
|
|
647
|
-
* @property {string} verbose.type - The type of the verbose option (number).
|
|
648
|
-
* @property {string} verbose.short - The short flag for the verbose option (V).
|
|
649
|
-
* @property {number} verbose.default - The default value for verbosity (0).
|
|
650
|
-
* @property {Object} version - Version display option.
|
|
651
|
-
* @property {string} version.type - The type of the version option (boolean).
|
|
652
|
-
* @property {string} version.short - The short flag for the version option (v).
|
|
653
|
-
* @property {undefined} version.default - The default value for version display (undefined).
|
|
654
|
-
* @property {Object} help - Help display option.
|
|
655
|
-
* @property {string} help.type - The type of the help option (boolean).
|
|
656
|
-
* @property {string} help.short - The short flag for the help option (h).
|
|
657
|
-
* @property {boolean} help.default - The default value for help display (false).
|
|
658
|
-
* @property {Object} logLevel - Log level option.
|
|
659
|
-
* @property {string} logLevel.type - The type of the logLevel option (string).
|
|
660
|
-
* @property {string} logLevel.default - The default value for log level ("info").
|
|
661
|
-
* @property {Object} logStyle - Log styling option.
|
|
662
|
-
* @property {string} logStyle.type - The type of the logStyle option (boolean).
|
|
663
|
-
* @property {boolean} logStyle.default - The default value for log styling (true).
|
|
664
|
-
* @property {Object} timestamp - Timestamp display option.
|
|
665
|
-
* @property {string} timestamp.type - The type of the timestamp option (boolean).
|
|
666
|
-
* @property {boolean} timestamp.default - The default value for timestamp display (true).
|
|
667
|
-
* @property {Object} banner - Banner display option.
|
|
668
|
-
* @property {string} banner.type - The type of the banner option (boolean).
|
|
669
|
-
* @property {boolean} banner.default - The default value for banner display (false).
|
|
670
|
-
* @memberOf module:utils
|
|
671
|
-
*/
|
|
672
|
-
const DefaultCommandOptions = {
|
|
673
|
-
verbose: {
|
|
674
|
-
type: "boolean",
|
|
675
|
-
short: "V",
|
|
676
|
-
default: undefined,
|
|
677
|
-
},
|
|
678
|
-
version: {
|
|
679
|
-
type: "boolean",
|
|
680
|
-
short: "v",
|
|
681
|
-
default: undefined,
|
|
682
|
-
},
|
|
683
|
-
help: {
|
|
684
|
-
type: "boolean",
|
|
685
|
-
short: "h",
|
|
686
|
-
default: false,
|
|
687
|
-
},
|
|
688
|
-
logLevel: {
|
|
689
|
-
type: "string",
|
|
690
|
-
default: "info",
|
|
691
|
-
},
|
|
692
|
-
logStyle: {
|
|
693
|
-
type: "boolean",
|
|
694
|
-
default: true,
|
|
695
|
-
},
|
|
696
|
-
timestamp: {
|
|
697
|
-
type: "boolean",
|
|
698
|
-
default: true,
|
|
699
|
-
},
|
|
700
|
-
banner: {
|
|
701
|
-
type: "boolean",
|
|
702
|
-
default: true,
|
|
703
|
-
},
|
|
704
|
-
};
|
|
705
|
-
/**
|
|
706
|
-
* @description Default command values derived from DefaultCommandOptions.
|
|
707
|
-
* @summary Creates an object with the default values of all options defined in DefaultCommandOptions.
|
|
708
|
-
* @const DefaultCommandValues
|
|
709
|
-
* @typedef {Object} DefaultCommandValues
|
|
710
|
-
* @property {unknown} [key: string] - The default value for each option in DefaultCommandOptions.
|
|
711
|
-
* @memberOf module:utils
|
|
712
|
-
*/
|
|
713
|
-
const DefaultCommandValues = Object.keys(DefaultCommandOptions).reduce((acc, key) => {
|
|
714
|
-
acc[key] =
|
|
715
|
-
DefaultCommandOptions[key].default;
|
|
716
|
-
return acc;
|
|
717
|
-
}, {});
|
|
718
|
-
|
|
719
|
-
/**
|
|
720
|
-
* @description Default encoding for text operations.
|
|
721
|
-
* @summary The standard UTF-8 encoding used for text processing.
|
|
722
|
-
* @const {string} Encoding
|
|
723
|
-
* @memberOf module:utils
|
|
724
|
-
*/
|
|
725
|
-
const Encoding = "utf-8";
|
|
726
|
-
/**
|
|
727
|
-
* @description Regular expression for semantic versioning.
|
|
728
|
-
* @summary A regex pattern to match and parse semantic version strings.
|
|
729
|
-
* @const {RegExp} SemVersionRegex
|
|
730
|
-
* @memberOf module:utils
|
|
731
|
-
*/
|
|
732
|
-
const SemVersionRegex = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z])))/g;
|
|
733
|
-
/**
|
|
734
|
-
* @description Enum for semantic version components.
|
|
735
|
-
* @summary Defines the three levels of semantic versioning: PATCH, MINOR, and MAJOR.
|
|
736
|
-
* @enum {string}
|
|
737
|
-
* @memberOf module:utils
|
|
738
|
-
*/
|
|
739
|
-
exports.SemVersion = void 0;
|
|
740
|
-
(function (SemVersion) {
|
|
741
|
-
/** Patch version for backwards-compatible bug fixes. */
|
|
742
|
-
SemVersion["PATCH"] = "patch";
|
|
743
|
-
/** Minor version for backwards-compatible new features. */
|
|
744
|
-
SemVersion["MINOR"] = "minor";
|
|
745
|
-
/** Major version for changes that break backwards compatibility. */
|
|
746
|
-
SemVersion["MAJOR"] = "major";
|
|
747
|
-
})(exports.SemVersion || (exports.SemVersion = {}));
|
|
748
|
-
/**
|
|
749
|
-
* @description Flag to indicate non-CI environment.
|
|
750
|
-
* @summary Used to specify that a command should run outside of a Continuous Integration environment.
|
|
751
|
-
* @const {string} NoCIFLag
|
|
752
|
-
* @memberOf module:utils
|
|
753
|
-
*/
|
|
754
|
-
const NoCIFLag = "-no-ci";
|
|
755
|
-
/**
|
|
756
|
-
* @description Key for the setup script in package.json.
|
|
757
|
-
* @summary Identifies the script that runs after package installation.
|
|
758
|
-
* @const {string} SetupScriptKey
|
|
759
|
-
* @memberOf module:utils
|
|
760
|
-
*/
|
|
761
|
-
const SetupScriptKey = "postinstall";
|
|
762
|
-
/**
|
|
763
|
-
* @description Enum for various authentication tokens.
|
|
764
|
-
* @summary Defines the file names for storing different types of authentication tokens.
|
|
765
|
-
* @enum {string}
|
|
766
|
-
* @memberOf module:utils
|
|
767
|
-
*/
|
|
768
|
-
exports.Tokens = void 0;
|
|
769
|
-
(function (Tokens) {
|
|
770
|
-
/** Git authentication token file name. */
|
|
771
|
-
Tokens["GIT"] = ".token";
|
|
772
|
-
/** NPM authentication token file name. */
|
|
773
|
-
Tokens["NPM"] = ".npmtoken";
|
|
774
|
-
/** Docker authentication token file name. */
|
|
775
|
-
Tokens["DOCKER"] = ".dockertoken";
|
|
776
|
-
/** Confluence authentication token file name. */
|
|
777
|
-
Tokens["CONFLUENCE"] = ".confluence-token";
|
|
778
|
-
})(exports.Tokens || (exports.Tokens = {}));
|
|
779
|
-
/**
|
|
780
|
-
* @description Code used to indicate an operation was aborted.
|
|
781
|
-
* @summary Standard message used when a process is manually terminated.
|
|
782
|
-
* @const {string} AbortCode
|
|
783
|
-
* @memberOf module:utils
|
|
784
|
-
*/
|
|
785
|
-
const AbortCode = "Aborted";
|
|
786
|
-
|
|
787
|
-
/**
|
|
788
|
-
* @description A standard output writer for handling command execution output.
|
|
789
|
-
* @summary This class implements the OutputWriter interface and provides methods for
|
|
790
|
-
* handling various types of output from command execution, including standard output,
|
|
791
|
-
* error output, and exit codes. It also includes utility methods for parsing commands
|
|
792
|
-
* and resolving or rejecting promises based on execution results.
|
|
793
|
-
*
|
|
794
|
-
* @template R - The type of the resolved value, defaulting to string.
|
|
795
|
-
*
|
|
796
|
-
* @param cmd - The command string to be executed.
|
|
797
|
-
* @param lock - A PromiseExecutor to control the asynchronous flow.
|
|
798
|
-
* @param args - Additional arguments (unused in the current implementation).
|
|
799
|
-
*
|
|
800
|
-
* @class
|
|
801
|
-
* @example
|
|
802
|
-
* ```typescript
|
|
803
|
-
* import { StandardOutputWriter } from '@decaf-ts/utils';
|
|
804
|
-
* import { PromiseExecutor } from '@decaf-ts/utils';
|
|
805
|
-
*
|
|
806
|
-
* // Create a promise executor
|
|
807
|
-
* const executor: PromiseExecutor<string> = {
|
|
808
|
-
* resolve: (value) => console.log(`Resolved: ${value}`),
|
|
809
|
-
* reject: (error) => console.error(`Rejected: ${error.message}`)
|
|
810
|
-
* };
|
|
811
|
-
*
|
|
812
|
-
* // Create a standard output writer
|
|
813
|
-
* const writer = new StandardOutputWriter('ls -la', executor);
|
|
814
|
-
*
|
|
815
|
-
* // Use the writer to handle command output
|
|
816
|
-
* writer.data('File list output...');
|
|
817
|
-
* writer.exit(0, ['Command executed successfully']);
|
|
818
|
-
* ```
|
|
819
|
-
*
|
|
820
|
-
* @mermaid
|
|
821
|
-
* sequenceDiagram
|
|
822
|
-
* participant Client
|
|
823
|
-
* participant StandardOutputWriter
|
|
824
|
-
* participant Logger
|
|
825
|
-
* participant PromiseExecutor
|
|
826
|
-
*
|
|
827
|
-
* Client->>StandardOutputWriter: new StandardOutputWriter(cmd, lock)
|
|
828
|
-
* StandardOutputWriter->>Logger: Logging.for(cmd)
|
|
829
|
-
*
|
|
830
|
-
* Client->>StandardOutputWriter: data(chunk)
|
|
831
|
-
* StandardOutputWriter->>StandardOutputWriter: log("stdout", chunk)
|
|
832
|
-
* StandardOutputWriter->>Logger: logger.info(log)
|
|
833
|
-
*
|
|
834
|
-
* Client->>StandardOutputWriter: error(chunk)
|
|
835
|
-
* StandardOutputWriter->>StandardOutputWriter: log("stderr", chunk)
|
|
836
|
-
* StandardOutputWriter->>Logger: logger.info(log)
|
|
837
|
-
*
|
|
838
|
-
* Client->>StandardOutputWriter: exit(code, logs)
|
|
839
|
-
* StandardOutputWriter->>StandardOutputWriter: log("stdout", exitMessage)
|
|
840
|
-
* alt code === 0
|
|
841
|
-
* StandardOutputWriter->>StandardOutputWriter: resolve(logs)
|
|
842
|
-
* StandardOutputWriter->>PromiseExecutor: lock.resolve(reason)
|
|
843
|
-
* else code !== 0
|
|
844
|
-
* StandardOutputWriter->>StandardOutputWriter: reject(error)
|
|
845
|
-
* StandardOutputWriter->>PromiseExecutor: lock.reject(reason)
|
|
846
|
-
* end
|
|
847
|
-
*/
|
|
848
|
-
class StandardOutputWriter {
|
|
849
|
-
constructor(cmd, lock,
|
|
850
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
851
|
-
...args) {
|
|
852
|
-
this.cmd = cmd;
|
|
853
|
-
this.lock = lock;
|
|
854
|
-
this.logger = logging.Logging.for(this.cmd);
|
|
855
|
-
}
|
|
856
|
-
/**
|
|
857
|
-
* @description Logs output to the console.
|
|
858
|
-
* @summary Formats and logs the given data with a timestamp and type indicator.
|
|
859
|
-
*
|
|
860
|
-
* @param type - The type of output (stdout or stderr).
|
|
861
|
-
* @param data - The data to be logged.
|
|
862
|
-
*/
|
|
863
|
-
log(type, data) {
|
|
864
|
-
data = Buffer.isBuffer(data) ? data.toString(Encoding) : data;
|
|
865
|
-
const log = type === "stderr" ? styledStringBuilder.style(data).red.text : data;
|
|
866
|
-
this.logger.info(log);
|
|
867
|
-
}
|
|
868
|
-
/**
|
|
869
|
-
* @description Handles standard output data.
|
|
870
|
-
* @summary Logs the given chunk as standard output.
|
|
871
|
-
*
|
|
872
|
-
* @param chunk - The data chunk to be logged.
|
|
873
|
-
*/
|
|
874
|
-
data(chunk) {
|
|
875
|
-
this.log("stdout", String(chunk));
|
|
876
|
-
}
|
|
877
|
-
/**
|
|
878
|
-
* @description Handles error output data.
|
|
879
|
-
* @summary Logs the given chunk as error output.
|
|
880
|
-
*
|
|
881
|
-
* @param chunk - The error data chunk to be logged.
|
|
882
|
-
*/
|
|
883
|
-
error(chunk) {
|
|
884
|
-
this.log("stderr", String(chunk));
|
|
885
|
-
}
|
|
886
|
-
/**
|
|
887
|
-
* @description Handles error objects.
|
|
888
|
-
* @summary Logs the error message from the given Error object.
|
|
889
|
-
*
|
|
890
|
-
* @param err - The Error object to be logged.
|
|
891
|
-
*/
|
|
892
|
-
errors(err) {
|
|
893
|
-
this.log("stderr", `Error executing command exited : ${err}`);
|
|
894
|
-
}
|
|
895
|
-
/**
|
|
896
|
-
* @description Handles the exit of a command.
|
|
897
|
-
* @summary Logs the exit code and resolves or rejects the promise based on the code.
|
|
898
|
-
*
|
|
899
|
-
* @param code - The exit code of the command.
|
|
900
|
-
* @param logs - Array of log messages to be processed before exiting.
|
|
901
|
-
*/
|
|
902
|
-
exit(code, logs) {
|
|
903
|
-
this.log("stdout", `command exited code : ${code === 0 ? styledStringBuilder.style(code.toString()).green.text : styledStringBuilder.style(code === null ? "null" : code.toString()).red.text}`);
|
|
904
|
-
if (code === 0) {
|
|
905
|
-
this.resolve(logs.map((l) => l.trim()).join("\n"));
|
|
906
|
-
}
|
|
907
|
-
else {
|
|
908
|
-
this.reject(new Error(logs.length ? logs.join("\n") : code.toString()));
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
/**
|
|
912
|
-
* @description Parses a command string or array into components.
|
|
913
|
-
* @summary Converts the command into a consistent format and stores it, then returns it split into command and arguments.
|
|
914
|
-
*
|
|
915
|
-
* @param command - The command as a string or array of strings.
|
|
916
|
-
* @return A tuple containing the command and its arguments as separate elements.
|
|
917
|
-
*/
|
|
918
|
-
parseCommand(command) {
|
|
919
|
-
command = typeof command === "string" ? command.split(" ") : command;
|
|
920
|
-
this.cmd = command.join(" ");
|
|
921
|
-
return [command[0], command.slice(1)];
|
|
922
|
-
}
|
|
923
|
-
/**
|
|
924
|
-
* @description Resolves the promise with a success message.
|
|
925
|
-
* @summary Logs a success message and resolves the promise with the given reason.
|
|
926
|
-
*
|
|
927
|
-
* @param reason - The reason for resolving the promise.
|
|
928
|
-
*/
|
|
929
|
-
resolve(reason) {
|
|
930
|
-
this.log("stdout", `${this.cmd} executed successfully: ${styledStringBuilder.style(reason ? "ran to completion" : reason).green}`);
|
|
931
|
-
this.lock.resolve(reason);
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* @description Rejects the promise with an error message.
|
|
935
|
-
* @summary Logs an error message and rejects the promise with the given reason.
|
|
936
|
-
*
|
|
937
|
-
* @param reason - The reason for rejecting the promise, either a number (exit code) or a string.
|
|
938
|
-
*/
|
|
939
|
-
reject(reason) {
|
|
940
|
-
if (!(reason instanceof Error)) {
|
|
941
|
-
reason = new Error(typeof reason === "number" ? `Exit code ${reason}` : reason);
|
|
942
|
-
}
|
|
943
|
-
this.log("stderr", `${this.cmd} failed to execute: ${styledStringBuilder.style(reason.message).red}`);
|
|
944
|
-
this.lock.reject(reason);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
/**
|
|
949
|
-
* @description Creates a locked version of a function.
|
|
950
|
-
* @summary This higher-order function takes a function and returns a new function that ensures
|
|
951
|
-
* sequential execution of the original function, even when called multiple times concurrently.
|
|
952
|
-
* It uses a Promise-based locking mechanism to queue function calls.
|
|
953
|
-
*
|
|
954
|
-
* @template R - The return type of the input function.
|
|
955
|
-
*
|
|
956
|
-
* @param f - The function to be locked. It can take any number of parameters and return a value of type R.
|
|
957
|
-
* @return A new function with the same signature as the input function, but with sequential execution guaranteed.
|
|
958
|
-
*
|
|
959
|
-
* @function lockify
|
|
960
|
-
*
|
|
961
|
-
* @mermaid
|
|
962
|
-
* sequenceDiagram
|
|
963
|
-
* participant Caller
|
|
964
|
-
* participant LockedFunction
|
|
965
|
-
* participant OriginalFunction
|
|
966
|
-
* Caller->>LockedFunction: Call with params
|
|
967
|
-
* LockedFunction->>LockedFunction: Check current lock
|
|
968
|
-
* alt Lock is resolved
|
|
969
|
-
* LockedFunction->>OriginalFunction: Execute with params
|
|
970
|
-
* OriginalFunction-->>LockedFunction: Return result
|
|
971
|
-
* LockedFunction-->>Caller: Return result
|
|
972
|
-
* else Lock is pending
|
|
973
|
-
* LockedFunction->>LockedFunction: Queue execution
|
|
974
|
-
* LockedFunction-->>Caller: Return promise
|
|
975
|
-
* Note over LockedFunction: Wait for previous execution
|
|
976
|
-
* LockedFunction->>OriginalFunction: Execute with params
|
|
977
|
-
* OriginalFunction-->>LockedFunction: Return result
|
|
978
|
-
* LockedFunction-->>Caller: Resolve promise with result
|
|
979
|
-
* end
|
|
980
|
-
* LockedFunction->>LockedFunction: Update lock
|
|
981
|
-
*
|
|
982
|
-
* @memberOf module:utils
|
|
983
|
-
*/
|
|
984
|
-
function lockify(f) {
|
|
985
|
-
let lock = Promise.resolve();
|
|
986
|
-
return (...params) => {
|
|
987
|
-
const result = lock.then(() => f(...params));
|
|
988
|
-
lock = result.catch(() => { });
|
|
989
|
-
return result;
|
|
990
|
-
};
|
|
991
|
-
}
|
|
992
|
-
function chainAbortController(argument0, ...remainder) {
|
|
993
|
-
let signals;
|
|
994
|
-
let controller;
|
|
995
|
-
// normalize args
|
|
996
|
-
if (argument0 instanceof AbortSignal) {
|
|
997
|
-
controller = new AbortController();
|
|
998
|
-
signals = [argument0, ...remainder];
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
controller = argument0;
|
|
1002
|
-
signals = remainder;
|
|
1003
|
-
}
|
|
1004
|
-
// if the controller is already aborted, exit early
|
|
1005
|
-
if (controller.signal.aborted) {
|
|
1006
|
-
return controller;
|
|
1007
|
-
}
|
|
1008
|
-
const handler = () => controller.abort();
|
|
1009
|
-
for (const signal of signals) {
|
|
1010
|
-
// check before adding! (and assume there is no possible way that the signal could
|
|
1011
|
-
// abort between the `if` check and adding the event listener)
|
|
1012
|
-
if (signal.aborted) {
|
|
1013
|
-
controller.abort();
|
|
1014
|
-
break;
|
|
1015
|
-
}
|
|
1016
|
-
signal.addEventListener("abort", handler, {
|
|
1017
|
-
once: true,
|
|
1018
|
-
signal: controller.signal,
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
return controller;
|
|
1022
|
-
}
|
|
1023
|
-
/**
|
|
1024
|
-
* @description Spawns a command as a child process with output handling.
|
|
1025
|
-
* @summary Creates a child process to execute a command with support for piping multiple commands,
|
|
1026
|
-
* custom output handling, and abort control. This function handles the low-level details of
|
|
1027
|
-
* spawning processes and connecting their inputs/outputs when piping is used.
|
|
1028
|
-
*
|
|
1029
|
-
* @template R - The type of the processed output, defaulting to string.
|
|
1030
|
-
* @param {StandardOutputWriter<R>} output - The output writer to handle command output.
|
|
1031
|
-
* @param {string} command - The command to execute, can include pipe operators.
|
|
1032
|
-
* @param {SpawnOptionsWithoutStdio} opts - Options for the spawned process.
|
|
1033
|
-
* @param {AbortController} abort - Controller to abort the command execution.
|
|
1034
|
-
* @param {Logger} logger - Logger for recording command execution details.
|
|
1035
|
-
* @return {ChildProcessWithoutNullStreams} The spawned child process.
|
|
1036
|
-
*
|
|
1037
|
-
* @function spawnCommand
|
|
1038
|
-
*
|
|
1039
|
-
* @memberOf module:utils
|
|
1040
|
-
*/
|
|
1041
|
-
function spawnCommand(output, command, opts, abort, logger) {
|
|
1042
|
-
function spawnInner(command, controller) {
|
|
1043
|
-
const [cmd, argz] = output.parseCommand(command);
|
|
1044
|
-
logger.info(`Running command: ${cmd}`);
|
|
1045
|
-
logger.debug(`with args: ${argz.join(" ")}`);
|
|
1046
|
-
const childProcess = child_process.spawn(cmd, argz, {
|
|
1047
|
-
...opts,
|
|
1048
|
-
cwd: opts.cwd || process.cwd(),
|
|
1049
|
-
env: Object.assign({}, process.env, opts.env, { PATH: process.env.PATH }),
|
|
1050
|
-
shell: opts.shell || false,
|
|
1051
|
-
signal: controller.signal,
|
|
1052
|
-
});
|
|
1053
|
-
logger.verbose(`pid : ${childProcess.pid}`);
|
|
1054
|
-
return childProcess;
|
|
1055
|
-
}
|
|
1056
|
-
const m = command.match(/[<>$#]/g);
|
|
1057
|
-
if (m)
|
|
1058
|
-
throw new Error(`Invalid command: ${command}. contains invalid characters: ${m}`);
|
|
1059
|
-
if (command.includes(" | ")) {
|
|
1060
|
-
const cmds = command.split(" | ");
|
|
1061
|
-
const spawns = [];
|
|
1062
|
-
const controllers = new Array(cmds.length);
|
|
1063
|
-
controllers[0] = abort;
|
|
1064
|
-
for (let i = 0; i < cmds.length; i++) {
|
|
1065
|
-
if (i !== 0)
|
|
1066
|
-
controllers[i] = chainAbortController(controllers[i - 1].signal);
|
|
1067
|
-
spawns.push(spawnInner(cmds[i], controllers[i]));
|
|
1068
|
-
if (i === 0)
|
|
1069
|
-
continue;
|
|
1070
|
-
spawns[i - 1].stdout.pipe(spawns[i].stdin);
|
|
1071
|
-
}
|
|
1072
|
-
return spawns[cmds.length - 1];
|
|
1073
|
-
}
|
|
1074
|
-
return spawnInner(command, abort);
|
|
1075
|
-
}
|
|
1076
|
-
/**
|
|
1077
|
-
* @description Executes a command asynchronously with customizable output handling.
|
|
1078
|
-
* @summary This function runs a shell command as a child process, providing fine-grained
|
|
1079
|
-
* control over its execution and output handling. It supports custom output writers,
|
|
1080
|
-
* allows for command abortion, and captures both stdout and stderr.
|
|
1081
|
-
*
|
|
1082
|
-
* @template R - The type of the resolved value from the command execution.
|
|
1083
|
-
*
|
|
1084
|
-
* @param command - The command to run, either as a string or an array of strings.
|
|
1085
|
-
* @param opts - Spawn options for the child process. Defaults to an empty object.
|
|
1086
|
-
* @param outputConstructor - Constructor for the output writer. Defaults to StandardOutputWriter.
|
|
1087
|
-
* @param args - Additional arguments to pass to the output constructor.
|
|
1088
|
-
* @return {CommandResult} A promise that resolves to the command result of type R.
|
|
1089
|
-
*
|
|
1090
|
-
* @function runCommand
|
|
1091
|
-
*
|
|
1092
|
-
* @mermaid
|
|
1093
|
-
* sequenceDiagram
|
|
1094
|
-
* participant Caller
|
|
1095
|
-
* participant runCommand
|
|
1096
|
-
* participant OutputWriter
|
|
1097
|
-
* participant ChildProcess
|
|
1098
|
-
* Caller->>runCommand: Call with command and options
|
|
1099
|
-
* runCommand->>OutputWriter: Create new instance
|
|
1100
|
-
* runCommand->>OutputWriter: Parse command
|
|
1101
|
-
* runCommand->>ChildProcess: Spawn process
|
|
1102
|
-
* ChildProcess-->>runCommand: Return process object
|
|
1103
|
-
* runCommand->>ChildProcess: Set up event listeners
|
|
1104
|
-
* loop For each stdout data
|
|
1105
|
-
* ChildProcess->>runCommand: Emit stdout data
|
|
1106
|
-
* runCommand->>OutputWriter: Handle stdout data
|
|
1107
|
-
* end
|
|
1108
|
-
* loop For each stderr data
|
|
1109
|
-
* ChildProcess->>runCommand: Emit stderr data
|
|
1110
|
-
* runCommand->>OutputWriter: Handle stderr data
|
|
1111
|
-
* end
|
|
1112
|
-
* ChildProcess->>runCommand: Emit error (if any)
|
|
1113
|
-
* runCommand->>OutputWriter: Handle error
|
|
1114
|
-
* ChildProcess->>runCommand: Emit exit
|
|
1115
|
-
* runCommand->>OutputWriter: Handle exit
|
|
1116
|
-
* OutputWriter-->>runCommand: Resolve or reject promise
|
|
1117
|
-
* runCommand-->>Caller: Return CommandResult
|
|
1118
|
-
*
|
|
1119
|
-
* @memberOf module:utils
|
|
1120
|
-
*/
|
|
1121
|
-
function runCommand(command, opts = {}, outputConstructor = (StandardOutputWriter), ...args) {
|
|
1122
|
-
const logger = logging.Logging.for(runCommand);
|
|
1123
|
-
const abort = new AbortController();
|
|
1124
|
-
const result = {
|
|
1125
|
-
abort: abort,
|
|
1126
|
-
command: command,
|
|
1127
|
-
logs: [],
|
|
1128
|
-
errs: [],
|
|
1129
|
-
};
|
|
1130
|
-
const lock = new Promise((resolve, reject) => {
|
|
1131
|
-
let output;
|
|
1132
|
-
try {
|
|
1133
|
-
output = new outputConstructor(command, {
|
|
1134
|
-
resolve,
|
|
1135
|
-
reject,
|
|
1136
|
-
}, ...args);
|
|
1137
|
-
result.cmd = spawnCommand(output, command, opts, abort, logger);
|
|
1138
|
-
}
|
|
1139
|
-
catch (e) {
|
|
1140
|
-
return reject(new Error(`Error running command ${command}: ${e}`));
|
|
1141
|
-
}
|
|
1142
|
-
result.cmd.stdout.setEncoding("utf8");
|
|
1143
|
-
result.cmd.stdout.on("data", (chunk) => {
|
|
1144
|
-
chunk = chunk.toString();
|
|
1145
|
-
result.logs.push(chunk);
|
|
1146
|
-
output.data(chunk);
|
|
1147
|
-
});
|
|
1148
|
-
result.cmd.stderr.on("data", (data) => {
|
|
1149
|
-
data = data.toString();
|
|
1150
|
-
result.errs.push(data);
|
|
1151
|
-
output.error(data);
|
|
1152
|
-
});
|
|
1153
|
-
result.cmd.once("error", (err) => {
|
|
1154
|
-
output.exit(err.message, result.errs);
|
|
1155
|
-
});
|
|
1156
|
-
result.cmd.once("exit", (code = 0) => {
|
|
1157
|
-
if (abort.signal.aborted && code === null)
|
|
1158
|
-
code = AbortCode;
|
|
1159
|
-
output.exit(code, code === 0 ? result.logs : result.errs);
|
|
1160
|
-
});
|
|
1161
|
-
});
|
|
1162
|
-
Object.assign(result, {
|
|
1163
|
-
promise: lock,
|
|
1164
|
-
pipe: async (cb) => {
|
|
1165
|
-
const l = logger.for("pipe");
|
|
1166
|
-
try {
|
|
1167
|
-
l.verbose(`Executing pipe function ${command}...`);
|
|
1168
|
-
const result = await lock;
|
|
1169
|
-
l.verbose(`Piping output to ${cb.name}: ${result}`);
|
|
1170
|
-
return cb(result);
|
|
1171
|
-
}
|
|
1172
|
-
catch (e) {
|
|
1173
|
-
l.error(`Error piping command output: ${e}`);
|
|
1174
|
-
throw e;
|
|
1175
|
-
}
|
|
1176
|
-
},
|
|
1177
|
-
});
|
|
1178
|
-
return result;
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
const logger = logging.Logging.for("fs");
|
|
1182
|
-
/**
|
|
1183
|
-
* @description Patches a file with given values.
|
|
1184
|
-
* @summary Reads a file, applies patches using TextUtils, and writes the result back to the file.
|
|
1185
|
-
*
|
|
1186
|
-
* @param {string} path - The path to the file to be patched.
|
|
1187
|
-
* @param {Record<string, number | string>} values - The values to patch into the file.
|
|
1188
|
-
* @return {void}
|
|
1189
|
-
*
|
|
1190
|
-
* @function patchFile
|
|
1191
|
-
*
|
|
1192
|
-
* @mermaid
|
|
1193
|
-
* sequenceDiagram
|
|
1194
|
-
* participant Caller
|
|
1195
|
-
* participant patchFile
|
|
1196
|
-
* participant fs
|
|
1197
|
-
* participant readFile
|
|
1198
|
-
* participant TextUtils
|
|
1199
|
-
* participant writeFile
|
|
1200
|
-
* Caller->>patchFile: Call with path and values
|
|
1201
|
-
* patchFile->>fs: Check if file exists
|
|
1202
|
-
* patchFile->>readFile: Read file content
|
|
1203
|
-
* readFile->>fs: Read file
|
|
1204
|
-
* fs-->>readFile: Return file content
|
|
1205
|
-
* readFile-->>patchFile: Return file content
|
|
1206
|
-
* patchFile->>TextUtils: Patch string
|
|
1207
|
-
* TextUtils-->>patchFile: Return patched content
|
|
1208
|
-
* patchFile->>writeFile: Write patched content
|
|
1209
|
-
* writeFile->>fs: Write to file
|
|
1210
|
-
* fs-->>writeFile: File written
|
|
1211
|
-
* writeFile-->>patchFile: File written
|
|
1212
|
-
* patchFile-->>Caller: Patching complete
|
|
1213
|
-
*
|
|
1214
|
-
* @memberOf module:utils
|
|
1215
|
-
*/
|
|
1216
|
-
function patchFile(path, values) {
|
|
1217
|
-
const log = logger.for(patchFile);
|
|
1218
|
-
if (!fs__default["default"].existsSync(path))
|
|
1219
|
-
throw new Error(`File not found at path "${path}".`);
|
|
1220
|
-
let content = readFile(path);
|
|
1221
|
-
try {
|
|
1222
|
-
log.verbose(`Patching file "${path}"...`);
|
|
1223
|
-
log.debug(`with value: ${JSON.stringify(values)}`);
|
|
1224
|
-
content = logging.patchString(content, values);
|
|
1225
|
-
}
|
|
1226
|
-
catch (error) {
|
|
1227
|
-
throw new Error(`Error patching file: ${error}`);
|
|
1228
|
-
}
|
|
1229
|
-
writeFile(path, content);
|
|
1230
|
-
}
|
|
1231
|
-
/**
|
|
1232
|
-
* @description Reads a file and returns its content.
|
|
1233
|
-
* @summary Reads the content of a file at the specified path and returns it as a string.
|
|
1234
|
-
*
|
|
1235
|
-
* @param {string} path - The path to the file to be read.
|
|
1236
|
-
* @return {string} The content of the file.
|
|
1237
|
-
*
|
|
1238
|
-
* @function readFile
|
|
1239
|
-
*
|
|
1240
|
-
* @memberOf module:utils
|
|
1241
|
-
*/
|
|
1242
|
-
function readFile(path) {
|
|
1243
|
-
const log = logger.for(readFile);
|
|
1244
|
-
try {
|
|
1245
|
-
log.verbose(`Reading file "${path}"...`);
|
|
1246
|
-
return fs__default["default"].readFileSync(path, "utf8");
|
|
1247
|
-
}
|
|
1248
|
-
catch (error) {
|
|
1249
|
-
log.verbose(`Error reading file "${path}": ${error}`);
|
|
1250
|
-
throw new Error(`Error reading file "${path}": ${error}`);
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* @description Writes data to a file.
|
|
1255
|
-
* @summary Writes the provided data to a file at the specified path.
|
|
1256
|
-
*
|
|
1257
|
-
* @param {string} path - The path to the file to be written.
|
|
1258
|
-
* @param {string | Buffer} data - The data to be written to the file.
|
|
1259
|
-
* @return {void}
|
|
1260
|
-
*
|
|
1261
|
-
* @function writeFile
|
|
1262
|
-
*
|
|
1263
|
-
* @memberOf module:utils
|
|
1264
|
-
*/
|
|
1265
|
-
function writeFile(path, data) {
|
|
1266
|
-
const log = logger.for(writeFile);
|
|
1267
|
-
try {
|
|
1268
|
-
log.verbose(`Writing file "${path} with ${data.length} bytes...`);
|
|
1269
|
-
fs__default["default"].writeFileSync(path, data, "utf8");
|
|
1270
|
-
}
|
|
1271
|
-
catch (error) {
|
|
1272
|
-
log.verbose(`Error writing file "${path}": ${error}`);
|
|
1273
|
-
throw new Error(`Error writing file "${path}": ${error}`);
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
/**
|
|
1277
|
-
* @description Retrieves all files recursively from a directory.
|
|
1278
|
-
* @summary Traverses through directories and subdirectories to collect all file paths.
|
|
1279
|
-
*
|
|
1280
|
-
* @param {string} p - The path to start searching from.
|
|
1281
|
-
* @param {function} [filter] - Optional function to filter files by name or index.
|
|
1282
|
-
* @return {string[]} Array of file paths.
|
|
1283
|
-
*
|
|
1284
|
-
* @function getAllFiles
|
|
1285
|
-
*
|
|
1286
|
-
* @memberOf module:utils
|
|
1287
|
-
*/
|
|
1288
|
-
function getAllFiles(p, filter) {
|
|
1289
|
-
const log = logger.for(getAllFiles);
|
|
1290
|
-
const files = [];
|
|
1291
|
-
try {
|
|
1292
|
-
log.verbose(`Retrieving all files from "${p}"...`);
|
|
1293
|
-
const entries = fs__default["default"].readdirSync(p);
|
|
1294
|
-
entries.forEach((entry) => {
|
|
1295
|
-
const fullPath = path__default["default"].join(p, entry);
|
|
1296
|
-
const stat = fs__default["default"].statSync(fullPath);
|
|
1297
|
-
if (stat.isFile()) {
|
|
1298
|
-
files.push(fullPath);
|
|
1299
|
-
}
|
|
1300
|
-
else if (stat.isDirectory()) {
|
|
1301
|
-
files.push(...getAllFiles(fullPath));
|
|
1302
|
-
}
|
|
1303
|
-
});
|
|
1304
|
-
if (!filter)
|
|
1305
|
-
return files;
|
|
1306
|
-
return files.filter(filter);
|
|
1307
|
-
}
|
|
1308
|
-
catch (error) {
|
|
1309
|
-
log.verbose(`Error retrieving files from "${p}": ${error}`);
|
|
1310
|
-
throw new Error(`Error retrieving files from "${p}": ${error}`);
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
/**
|
|
1314
|
-
* @description Renames a file or directory.
|
|
1315
|
-
* @summary Moves a file or directory from the source path to the destination path.
|
|
1316
|
-
*
|
|
1317
|
-
* @param {string} source - The source path of the file or directory.
|
|
1318
|
-
* @param {string} dest - The destination path for the file or directory.
|
|
1319
|
-
* @return {Promise<void>} A promise that resolves when the rename operation is complete.
|
|
1320
|
-
*
|
|
1321
|
-
* @function renameFile
|
|
1322
|
-
*
|
|
1323
|
-
* @memberOf module:utils
|
|
1324
|
-
*/
|
|
1325
|
-
async function renameFile(source, dest) {
|
|
1326
|
-
const log = logger.for(renameFile);
|
|
1327
|
-
let descriptorSource, descriptorDest;
|
|
1328
|
-
try {
|
|
1329
|
-
descriptorSource = fs__default["default"].statSync(source);
|
|
1330
|
-
}
|
|
1331
|
-
catch (error) {
|
|
1332
|
-
log.verbose(`Source path "${source}" does not exist: ${error}`);
|
|
1333
|
-
throw new Error(`Source path "${source}" does not exist: ${error}`);
|
|
1334
|
-
}
|
|
1335
|
-
try {
|
|
1336
|
-
descriptorDest = fs__default["default"].statSync(dest);
|
|
1337
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1338
|
-
}
|
|
1339
|
-
catch (e) {
|
|
1340
|
-
// do nothing. its ok
|
|
1341
|
-
}
|
|
1342
|
-
if (descriptorDest) {
|
|
1343
|
-
log.verbose(`Destination path "${dest}" already exists`);
|
|
1344
|
-
throw new Error(`Destination path "${dest}" already exists`);
|
|
1345
|
-
}
|
|
1346
|
-
try {
|
|
1347
|
-
log.verbose(`Renaming ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}...`);
|
|
1348
|
-
fs__default["default"].renameSync(source, dest);
|
|
1349
|
-
log.verbose(`Successfully renamed to "${dest}"`);
|
|
1350
|
-
}
|
|
1351
|
-
catch (error) {
|
|
1352
|
-
log.verbose(`Error renaming ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}": ${error}`);
|
|
1353
|
-
throw new Error(`Error renaming ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}": ${error}`);
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
/**
|
|
1357
|
-
* @description Copies a file or directory.
|
|
1358
|
-
* @summary Creates a copy of a file or directory from the source path to the destination path.
|
|
1359
|
-
*
|
|
1360
|
-
* @param {string} source - The source path of the file or directory.
|
|
1361
|
-
* @param {string} dest - The destination path for the file or directory.
|
|
1362
|
-
* @return {void}
|
|
1363
|
-
*
|
|
1364
|
-
* @function copyFile
|
|
1365
|
-
*
|
|
1366
|
-
* @memberOf module:utils
|
|
1367
|
-
*/
|
|
1368
|
-
function copyFile(source, dest) {
|
|
1369
|
-
const log = logger.for(copyFile);
|
|
1370
|
-
let descriptorSource, descriptorDest;
|
|
1371
|
-
try {
|
|
1372
|
-
descriptorSource = fs__default["default"].statSync(source);
|
|
1373
|
-
}
|
|
1374
|
-
catch (error) {
|
|
1375
|
-
log.verbose(`Source path "${source}" does not exist: ${error}`);
|
|
1376
|
-
throw new Error(`Source path "${source}" does not exist: ${error}`);
|
|
1377
|
-
}
|
|
1378
|
-
try {
|
|
1379
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1380
|
-
descriptorDest = fs__default["default"].statSync(dest);
|
|
1381
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1382
|
-
}
|
|
1383
|
-
catch (error) {
|
|
1384
|
-
if (descriptorSource.isDirectory()) {
|
|
1385
|
-
log.verbose(`Dest path "${dest}" does not exist. creating`);
|
|
1386
|
-
fs__default["default"].mkdirSync(dest, { recursive: true });
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
try {
|
|
1390
|
-
log.verbose(`Copying ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}...`);
|
|
1391
|
-
fs__default["default"].cpSync(source, dest, { recursive: true });
|
|
1392
|
-
}
|
|
1393
|
-
catch (error) {
|
|
1394
|
-
log.verbose(`Error copying ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}: ${error}`);
|
|
1395
|
-
throw new Error(`Error copying ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}: ${error}`);
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
/**
|
|
1399
|
-
* @description Deletes a file or directory.
|
|
1400
|
-
* @summary Removes a file or directory at the specified path, with recursive and force options enabled.
|
|
1401
|
-
*
|
|
1402
|
-
* @param {string} p - The path to the file or directory to delete.
|
|
1403
|
-
* @return {void}
|
|
1404
|
-
*
|
|
1405
|
-
* @function deletePath
|
|
1406
|
-
*
|
|
1407
|
-
* @memberOf module:utils
|
|
1408
|
-
*/
|
|
1409
|
-
function deletePath(p) {
|
|
1410
|
-
const log = logger.for(deletePath);
|
|
1411
|
-
try {
|
|
1412
|
-
const descriptor = fs__default["default"].statSync(p);
|
|
1413
|
-
if (descriptor.isFile()) {
|
|
1414
|
-
log.verbose(`Deleting file "${p}...`);
|
|
1415
|
-
fs__default["default"].rmSync(p, { recursive: true, force: true });
|
|
1416
|
-
}
|
|
1417
|
-
else if (descriptor.isDirectory())
|
|
1418
|
-
fs__default["default"].rmSync(p, { recursive: true, force: true });
|
|
1419
|
-
}
|
|
1420
|
-
catch (error) {
|
|
1421
|
-
log.verbose(`Error Deleting "${p}": ${error}`);
|
|
1422
|
-
throw new Error(`Error Deleting "${p}": ${error}`);
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
|
-
* @description Retrieves package information from package.json.
|
|
1427
|
-
* @summary Loads and parses the package.json file from a specified directory or the current working directory. Can return the entire package object or a specific property.
|
|
1428
|
-
* @param {string} [p=process.cwd()] - The directory path where the package.json file is located.
|
|
1429
|
-
* @param {string} [property] - Optional. The specific property to retrieve from package.json.
|
|
1430
|
-
* @return {object | string} The parsed contents of package.json or the value of the specified property.
|
|
1431
|
-
* @function getPackage
|
|
1432
|
-
* @mermaid
|
|
1433
|
-
* sequenceDiagram
|
|
1434
|
-
* participant Caller
|
|
1435
|
-
* participant getPackage
|
|
1436
|
-
* participant readFile
|
|
1437
|
-
* participant JSON
|
|
1438
|
-
* Caller->>getPackage: Call with path and optional property
|
|
1439
|
-
* getPackage->>readFile: Read package.json
|
|
1440
|
-
* readFile-->>getPackage: Return file content
|
|
1441
|
-
* getPackage->>JSON: Parse file content
|
|
1442
|
-
* JSON-->>getPackage: Return parsed object
|
|
1443
|
-
* alt property specified
|
|
1444
|
-
* getPackage->>getPackage: Check if property exists
|
|
1445
|
-
* alt property exists
|
|
1446
|
-
* getPackage-->>Caller: Return property value
|
|
1447
|
-
* else property doesn't exist
|
|
1448
|
-
* getPackage-->>Caller: Throw Error
|
|
1449
|
-
* end
|
|
1450
|
-
* else no property specified
|
|
1451
|
-
* getPackage-->>Caller: Return entire package object
|
|
1452
|
-
* end
|
|
1453
|
-
* @memberOf module:utils
|
|
1454
|
-
*/
|
|
1455
|
-
function getPackage(p = process.cwd(), property) {
|
|
1456
|
-
let pkg;
|
|
1457
|
-
try {
|
|
1458
|
-
pkg = JSON.parse(readFile(path__default["default"].join(p, `package.json`)));
|
|
1459
|
-
}
|
|
1460
|
-
catch (error) {
|
|
1461
|
-
throw new Error(`Failed to retrieve package information" ${error}`);
|
|
1462
|
-
}
|
|
1463
|
-
if (property) {
|
|
1464
|
-
if (!(property in pkg))
|
|
1465
|
-
throw new Error(`Property "${property}" not found in package.json`);
|
|
1466
|
-
return pkg[property];
|
|
1467
|
-
}
|
|
1468
|
-
return pkg;
|
|
1469
|
-
}
|
|
1470
|
-
/**
|
|
1471
|
-
* @description Sets an attribute in the package.json file.
|
|
1472
|
-
* @summary Updates a specific attribute in the package.json file with the provided value.
|
|
1473
|
-
*
|
|
1474
|
-
* @param {string} attr - The attribute name to set in package.json.
|
|
1475
|
-
* @param {string | number | object} value - The value to set for the attribute.
|
|
1476
|
-
* @param {string} [p=process.cwd()] - The directory path where the package.json file is located.
|
|
1477
|
-
* @return {void}
|
|
1478
|
-
*
|
|
1479
|
-
* @function setPackageAttribute
|
|
1480
|
-
*
|
|
1481
|
-
* @memberOf module:utils
|
|
1482
|
-
*/
|
|
1483
|
-
function setPackageAttribute(attr, value, p = process.cwd()) {
|
|
1484
|
-
const pkg = getPackage(p);
|
|
1485
|
-
pkg[attr] = value;
|
|
1486
|
-
writeFile(path__default["default"].join(p, `package.json`), JSON.stringify(pkg, null, 2));
|
|
1487
|
-
}
|
|
1488
|
-
/**
|
|
1489
|
-
* @description Retrieves the version from package.json.
|
|
1490
|
-
* @summary A convenience function that calls getPackage to retrieve the "version" property from package.json.
|
|
1491
|
-
* @param {string} [p=process.cwd()] - The directory path where the package.json file is located.
|
|
1492
|
-
* @return {string} The version string from package.json.
|
|
1493
|
-
* @function getPackageVersion
|
|
1494
|
-
* @memberOf module:utils
|
|
1495
|
-
*/
|
|
1496
|
-
function getPackageVersion(p = process.cwd()) {
|
|
1497
|
-
return getPackage(p, "version");
|
|
1498
|
-
}
|
|
1499
|
-
/**
|
|
1500
|
-
* @description Retrieves all dependencies from the project.
|
|
1501
|
-
* @summary Executes 'npm ls --json' command to get a detailed list of all dependencies (production, development, and peer) and their versions.
|
|
1502
|
-
* @param {string} [path=process.cwd()] - The directory path of the project.
|
|
1503
|
-
* @return {Promise<{prod: Array<{name: string, version: string}>, dev: Array<{name: string, version: string}>, peer: Array<{name: string, version: string}>}>} An object containing arrays of production, development, and peer dependencies.
|
|
1504
|
-
* @function getDependencies
|
|
1505
|
-
* @mermaid
|
|
1506
|
-
* sequenceDiagram
|
|
1507
|
-
* participant Caller
|
|
1508
|
-
* participant getDependencies
|
|
1509
|
-
* participant runCommand
|
|
1510
|
-
* participant JSON
|
|
1511
|
-
* Caller->>getDependencies: Call with optional path
|
|
1512
|
-
* getDependencies->>runCommand: Execute 'npm ls --json'
|
|
1513
|
-
* runCommand-->>getDependencies: Return command output
|
|
1514
|
-
* getDependencies->>JSON: Parse command output
|
|
1515
|
-
* JSON-->>getDependencies: Return parsed object
|
|
1516
|
-
* getDependencies->>getDependencies: Process dependencies
|
|
1517
|
-
* getDependencies-->>Caller: Return processed dependencies
|
|
1518
|
-
* @memberOf module:utils
|
|
1519
|
-
*/
|
|
1520
|
-
async function getDependencies(path = process.cwd()) {
|
|
1521
|
-
let pkg;
|
|
1522
|
-
try {
|
|
1523
|
-
pkg = JSON.parse(await runCommand(`npm ls --json`, { cwd: path }).promise);
|
|
1524
|
-
}
|
|
1525
|
-
catch (e) {
|
|
1526
|
-
throw new Error(`Failed to retrieve dependencies: ${e}`);
|
|
1527
|
-
}
|
|
1528
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1529
|
-
const mapper = (entry, index) => ({
|
|
1530
|
-
name: entry[0],
|
|
1531
|
-
version: entry[1].version,
|
|
1532
|
-
});
|
|
1533
|
-
return {
|
|
1534
|
-
prod: Object.entries(pkg.dependencies || {}).map(mapper),
|
|
1535
|
-
dev: Object.entries(pkg.devDependencies || {}).map(mapper),
|
|
1536
|
-
peer: Object.entries(pkg.peerDependencies || {}).map(mapper),
|
|
1537
|
-
};
|
|
1538
|
-
}
|
|
1539
|
-
/**
|
|
1540
|
-
* @description Updates project dependencies to their latest versions.
|
|
1541
|
-
* @summary Runs npm-check-updates to update package.json and then installs the updated dependencies.
|
|
1542
|
-
*
|
|
1543
|
-
* @return {Promise<void>} A promise that resolves when dependencies are updated.
|
|
1544
|
-
*
|
|
1545
|
-
* @function updateDependencies
|
|
1546
|
-
*
|
|
1547
|
-
* @memberOf module:utils
|
|
1548
|
-
*/
|
|
1549
|
-
async function updateDependencies() {
|
|
1550
|
-
const log = logger.for(updateDependencies);
|
|
1551
|
-
log.info("checking for updates...");
|
|
1552
|
-
await runCommand("npx npm-check-updates -u").promise;
|
|
1553
|
-
log.info("updating...");
|
|
1554
|
-
await runCommand("npx npm run do-install").promise;
|
|
1555
|
-
}
|
|
1556
|
-
/**
|
|
1557
|
-
* @description Installs dependencies if they are not already available.
|
|
1558
|
-
* @summary Checks if specified dependencies are installed and installs any that are missing.
|
|
1559
|
-
*
|
|
1560
|
-
* @param {string[] | string} deps - The dependencies to check and potentially install.
|
|
1561
|
-
* @param {SimpleDependencyMap} [dependencies] - Optional map of existing dependencies.
|
|
1562
|
-
* @return {Promise<SimpleDependencyMap>} Updated map of dependencies.
|
|
1563
|
-
*
|
|
1564
|
-
* @function installIfNotAvailable
|
|
1565
|
-
*
|
|
1566
|
-
* @memberOf module:utils
|
|
1567
|
-
*/
|
|
1568
|
-
async function installIfNotAvailable(deps, dependencies) {
|
|
1569
|
-
if (!dependencies) {
|
|
1570
|
-
const d = await getDependencies();
|
|
1571
|
-
dependencies = {
|
|
1572
|
-
prod: d.prod?.map((p) => p.name) || [],
|
|
1573
|
-
dev: d.dev?.map((d) => d.name) || [],
|
|
1574
|
-
peer: d.peer?.map((p) => p.name) || [],
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
const { prod, dev, peer } = dependencies;
|
|
1578
|
-
const installed = Array.from(new Set([...(prod || []), ...(dev || []), ...(peer || [])]));
|
|
1579
|
-
deps = typeof deps === "string" ? [deps] : deps;
|
|
1580
|
-
const toInstall = deps.filter((d) => !installed.includes(d));
|
|
1581
|
-
if (toInstall.length)
|
|
1582
|
-
await installDependencies({ dev: toInstall });
|
|
1583
|
-
dependencies.dev = dependencies.dev || [];
|
|
1584
|
-
dependencies.dev.push(...toInstall);
|
|
1585
|
-
return dependencies;
|
|
1586
|
-
}
|
|
1587
|
-
/**
|
|
1588
|
-
* @description Pushes changes to Git repository.
|
|
1589
|
-
* @summary Temporarily changes Git user configuration, commits all changes, pushes to remote, and restores original user configuration.
|
|
1590
|
-
*
|
|
1591
|
-
* @return {Promise<void>} A promise that resolves when changes are pushed.
|
|
1592
|
-
*
|
|
1593
|
-
* @function pushToGit
|
|
1594
|
-
*
|
|
1595
|
-
* @memberOf module:utils
|
|
1596
|
-
*/
|
|
1597
|
-
async function pushToGit() {
|
|
1598
|
-
const log = logger.for(pushToGit);
|
|
1599
|
-
const gitUser = await runCommand("git config user.name").promise;
|
|
1600
|
-
const gitEmail = await runCommand("git config user.email").promise;
|
|
1601
|
-
log.verbose(`cached git id: ${gitUser}/${gitEmail}. changing to automation`);
|
|
1602
|
-
await runCommand('git config user.email "automation@decaf.ts"').promise;
|
|
1603
|
-
await runCommand('git config user.name "decaf"').promise;
|
|
1604
|
-
log.info("Pushing changes to git...");
|
|
1605
|
-
await runCommand("git add .").promise;
|
|
1606
|
-
await runCommand(`git commit -m "refs #1 - after repo setup"`).promise;
|
|
1607
|
-
await runCommand("git push").promise;
|
|
1608
|
-
await runCommand(`git config user.email "${gitEmail}"`).promise;
|
|
1609
|
-
await runCommand(`git config user.name "${gitUser}"`).promise;
|
|
1610
|
-
log.verbose(`reverted to git id: ${gitUser}/${gitEmail}`);
|
|
1611
|
-
}
|
|
1612
|
-
/**
|
|
1613
|
-
* @description Installs project dependencies.
|
|
1614
|
-
* @summary Installs production, development, and peer dependencies as specified.
|
|
1615
|
-
*
|
|
1616
|
-
* @param {object} dependencies - Object containing arrays of dependencies to install.
|
|
1617
|
-
* @param {string[]} [dependencies.prod] - Production dependencies to install.
|
|
1618
|
-
* @param {string[]} [dependencies.dev] - Development dependencies to install.
|
|
1619
|
-
* @param {string[]} [dependencies.peer] - Peer dependencies to install.
|
|
1620
|
-
* @return {Promise<void>} A promise that resolves when all dependencies are installed.
|
|
1621
|
-
*
|
|
1622
|
-
* @function installDependencies
|
|
1623
|
-
*
|
|
1624
|
-
* @memberOf module:utils
|
|
1625
|
-
*/
|
|
1626
|
-
async function installDependencies(dependencies) {
|
|
1627
|
-
const log = logger.for(installDependencies);
|
|
1628
|
-
const prod = dependencies.prod || [];
|
|
1629
|
-
const dev = dependencies.dev || [];
|
|
1630
|
-
const peer = dependencies.peer || [];
|
|
1631
|
-
if (prod.length) {
|
|
1632
|
-
log.info(`Installing dependencies ${prod.join(", ")}...`);
|
|
1633
|
-
await runCommand(`npm install ${prod.join(" ")}`, { cwd: process.cwd() })
|
|
1634
|
-
.promise;
|
|
1635
|
-
}
|
|
1636
|
-
if (dev.length) {
|
|
1637
|
-
log.info(`Installing devDependencies ${dev.join(", ")}...`);
|
|
1638
|
-
await runCommand(`npm install --save-dev ${dev.join(" ")}`, {
|
|
1639
|
-
cwd: process.cwd(),
|
|
1640
|
-
}).promise;
|
|
1641
|
-
}
|
|
1642
|
-
if (peer.length) {
|
|
1643
|
-
log.info(`Installing peerDependencies ${peer.join(", ")}...`);
|
|
1644
|
-
await runCommand(`npm install --save-peer ${peer.join(" ")}`, {
|
|
1645
|
-
cwd: process.cwd(),
|
|
1646
|
-
}).promise;
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
/**
|
|
1650
|
-
* @description Normalizes imports to handle both CommonJS and ESModule formats.
|
|
1651
|
-
* @summary Utility function to handle module import differences between formats.
|
|
1652
|
-
*
|
|
1653
|
-
* @template T - Type of the imported module.
|
|
1654
|
-
* @param {Promise<T>} importPromise - Promise returned by dynamic import.
|
|
1655
|
-
* @return {Promise<T>} Normalized module.
|
|
1656
|
-
*
|
|
1657
|
-
* @function normalizeImport
|
|
1658
|
-
*
|
|
1659
|
-
* @memberOf module:utils
|
|
1660
|
-
*/
|
|
1661
|
-
async function normalizeImport(importPromise) {
|
|
1662
|
-
// CommonJS's `module.exports` is wrapped as `default` in ESModule.
|
|
1663
|
-
return importPromise.then((m) => (m.default || m));
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
/**
|
|
1667
|
-
* @description Definition of a slogan item.
|
|
1668
|
-
* @summary Represents a single slogan entry with text and tags.
|
|
1669
|
-
* @typedef {Object} SloganItem
|
|
1670
|
-
* @property {string} Slogan - The slogan text.
|
|
1671
|
-
* @property {string} Tags - Comma-separated tags describing the slogan.
|
|
1672
|
-
* @memberOf module:utils
|
|
1673
|
-
*/
|
|
1674
|
-
/**
|
|
1675
|
-
* @description List of available slogans for banners and messages.
|
|
1676
|
-
* @summary Immutable array of slogan entries used by {@link getSlogan} and banner rendering.
|
|
1677
|
-
* @type {SloganItem[]}
|
|
1678
|
-
* @const slogans
|
|
1679
|
-
* @memberOf module:utils
|
|
1680
|
-
*/
|
|
1681
|
-
const slogans = [
|
|
1682
|
-
{
|
|
1683
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
1684
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
1685
|
-
},
|
|
1686
|
-
{
|
|
1687
|
-
Slogan: "Full flavor, no jitters. That's Decaf-TS.",
|
|
1688
|
-
Tags: "Coffee-themed, Cheerful",
|
|
1689
|
-
},
|
|
1690
|
-
{
|
|
1691
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
1692
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
1693
|
-
},
|
|
1694
|
-
{
|
|
1695
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
1696
|
-
Tags: "Coffee-themed, Branding",
|
|
1697
|
-
},
|
|
1698
|
-
{
|
|
1699
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
1700
|
-
Tags: "Coffee-themed, Chill",
|
|
1701
|
-
},
|
|
1702
|
-
{
|
|
1703
|
-
Slogan: "All the kick, none of the crash.",
|
|
1704
|
-
Tags: "Coffee-themed, Energetic",
|
|
1705
|
-
},
|
|
1706
|
-
{
|
|
1707
|
-
Slogan: "Sip back and ship faster.",
|
|
1708
|
-
Tags: "Coffee-themed, Fun",
|
|
1709
|
-
},
|
|
1710
|
-
{
|
|
1711
|
-
Slogan: "Keep calm and code Decaf.",
|
|
1712
|
-
Tags: "Coffee-themed, Playful",
|
|
1713
|
-
},
|
|
1714
|
-
{
|
|
1715
|
-
Slogan: "Code without the caffeine shakes.",
|
|
1716
|
-
Tags: "Coffee-themed, Humorous",
|
|
1717
|
-
},
|
|
1718
|
-
{
|
|
1719
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
1720
|
-
Tags: "Coffee-themed, Technical",
|
|
1721
|
-
},
|
|
1722
|
-
{
|
|
1723
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
1724
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
1725
|
-
},
|
|
1726
|
-
{
|
|
1727
|
-
Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
|
|
1728
|
-
Tags: "Coffee-themed, Cheerful",
|
|
1729
|
-
},
|
|
1730
|
-
{
|
|
1731
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
1732
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
1733
|
-
},
|
|
1734
|
-
{
|
|
1735
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
1736
|
-
Tags: "Coffee-themed, Branding",
|
|
1737
|
-
},
|
|
1738
|
-
{
|
|
1739
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
1740
|
-
Tags: "Coffee-themed, Chill",
|
|
1741
|
-
},
|
|
1742
|
-
{
|
|
1743
|
-
Slogan: "All the kick, none of the crash.",
|
|
1744
|
-
Tags: "Coffee-themed, Energetic",
|
|
1745
|
-
},
|
|
1746
|
-
{
|
|
1747
|
-
Slogan: "Sip back and ship faster.",
|
|
1748
|
-
Tags: "Coffee-themed, Fun",
|
|
1749
|
-
},
|
|
1750
|
-
{
|
|
1751
|
-
Slogan: "Keep calm and code Decaf.",
|
|
1752
|
-
Tags: "Coffee-themed, Playful",
|
|
1753
|
-
},
|
|
1754
|
-
{
|
|
1755
|
-
Slogan: "Code without the caffeine shakes.",
|
|
1756
|
-
Tags: "Coffee-themed, Humorous",
|
|
1757
|
-
},
|
|
1758
|
-
{
|
|
1759
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
1760
|
-
Tags: "Coffee-themed, Technical",
|
|
1761
|
-
},
|
|
1762
|
-
{
|
|
1763
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
1764
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
1765
|
-
},
|
|
1766
|
-
{
|
|
1767
|
-
Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
|
|
1768
|
-
Tags: "Coffee-themed, Cheerful",
|
|
1769
|
-
},
|
|
1770
|
-
{
|
|
1771
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
1772
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
1773
|
-
},
|
|
1774
|
-
{
|
|
1775
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
1776
|
-
Tags: "Coffee-themed, Branding",
|
|
1777
|
-
},
|
|
1778
|
-
{
|
|
1779
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
1780
|
-
Tags: "Coffee-themed, Chill",
|
|
1781
|
-
},
|
|
1782
|
-
{
|
|
1783
|
-
Slogan: "All the kick, none of the crash.",
|
|
1784
|
-
Tags: "Coffee-themed, Energetic",
|
|
1785
|
-
},
|
|
1786
|
-
{
|
|
1787
|
-
Slogan: "Sip back and ship faster.",
|
|
1788
|
-
Tags: "Coffee-themed, Fun",
|
|
1789
|
-
},
|
|
1790
|
-
{
|
|
1791
|
-
Slogan: "Keep calm and code Decaf.",
|
|
1792
|
-
Tags: "Coffee-themed, Playful",
|
|
1793
|
-
},
|
|
1794
|
-
{
|
|
1795
|
-
Slogan: "Code without the caffeine shakes.",
|
|
1796
|
-
Tags: "Coffee-themed, Humorous",
|
|
1797
|
-
},
|
|
1798
|
-
{
|
|
1799
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
1800
|
-
Tags: "Coffee-themed, Technical",
|
|
1801
|
-
},
|
|
1802
|
-
{
|
|
1803
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
1804
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
1805
|
-
},
|
|
1806
|
-
{
|
|
1807
|
-
Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
|
|
1808
|
-
Tags: "Coffee-themed, Cheerful",
|
|
1809
|
-
},
|
|
1810
|
-
{
|
|
1811
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
1812
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
1813
|
-
},
|
|
1814
|
-
{
|
|
1815
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
1816
|
-
Tags: "Coffee-themed, Branding",
|
|
1817
|
-
},
|
|
1818
|
-
{
|
|
1819
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
1820
|
-
Tags: "Coffee-themed, Chill",
|
|
1821
|
-
},
|
|
1822
|
-
{
|
|
1823
|
-
Slogan: "All the kick, none of the crash.",
|
|
1824
|
-
Tags: "Coffee-themed, Energetic",
|
|
1825
|
-
},
|
|
1826
|
-
{
|
|
1827
|
-
Slogan: "Sip back and ship faster.",
|
|
1828
|
-
Tags: "Coffee-themed, Fun",
|
|
1829
|
-
},
|
|
1830
|
-
{
|
|
1831
|
-
Slogan: "Keep calm and code Decaf.",
|
|
1832
|
-
Tags: "Coffee-themed, Playful",
|
|
1833
|
-
},
|
|
1834
|
-
{
|
|
1835
|
-
Slogan: "Code without the caffeine shakes.",
|
|
1836
|
-
Tags: "Coffee-themed, Humorous",
|
|
1837
|
-
},
|
|
1838
|
-
{
|
|
1839
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
1840
|
-
Tags: "Coffee-themed, Technical",
|
|
1841
|
-
},
|
|
1842
|
-
{
|
|
1843
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
1844
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
1845
|
-
},
|
|
1846
|
-
{
|
|
1847
|
-
Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
|
|
1848
|
-
Tags: "Coffee-themed, Cheerful",
|
|
1849
|
-
},
|
|
1850
|
-
{
|
|
1851
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
1852
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
1853
|
-
},
|
|
1854
|
-
{
|
|
1855
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
1856
|
-
Tags: "Coffee-themed, Branding",
|
|
1857
|
-
},
|
|
1858
|
-
{
|
|
1859
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
1860
|
-
Tags: "Coffee-themed, Chill",
|
|
1861
|
-
},
|
|
1862
|
-
{
|
|
1863
|
-
Slogan: "All the kick, none of the crash.",
|
|
1864
|
-
Tags: "Coffee-themed, Energetic",
|
|
1865
|
-
},
|
|
1866
|
-
{
|
|
1867
|
-
Slogan: "Sip back and ship faster.",
|
|
1868
|
-
Tags: "Coffee-themed, Fun",
|
|
1869
|
-
},
|
|
1870
|
-
{
|
|
1871
|
-
Slogan: "Keep calm and code Decaf.",
|
|
1872
|
-
Tags: "Coffee-themed, Playful",
|
|
1873
|
-
},
|
|
1874
|
-
{
|
|
1875
|
-
Slogan: "Code without the caffeine shakes.",
|
|
1876
|
-
Tags: "Coffee-themed, Humorous",
|
|
1877
|
-
},
|
|
1878
|
-
{
|
|
1879
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
1880
|
-
Tags: "Coffee-themed, Technical",
|
|
1881
|
-
},
|
|
1882
|
-
{
|
|
1883
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
1884
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
1885
|
-
},
|
|
1886
|
-
{
|
|
1887
|
-
Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
|
|
1888
|
-
Tags: "Coffee-themed, Cheerful",
|
|
1889
|
-
},
|
|
1890
|
-
{
|
|
1891
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
1892
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
1893
|
-
},
|
|
1894
|
-
{
|
|
1895
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
1896
|
-
Tags: "Coffee-themed, Branding",
|
|
1897
|
-
},
|
|
1898
|
-
{
|
|
1899
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
1900
|
-
Tags: "Coffee-themed, Chill",
|
|
1901
|
-
},
|
|
1902
|
-
{
|
|
1903
|
-
Slogan: "All the kick, none of the crash.",
|
|
1904
|
-
Tags: "Coffee-themed, Energetic",
|
|
1905
|
-
},
|
|
1906
|
-
{
|
|
1907
|
-
Slogan: "Sip back and ship faster.",
|
|
1908
|
-
Tags: "Coffee-themed, Fun",
|
|
1909
|
-
},
|
|
1910
|
-
{
|
|
1911
|
-
Slogan: "Keep calm and code Decaf.",
|
|
1912
|
-
Tags: "Coffee-themed, Playful",
|
|
1913
|
-
},
|
|
1914
|
-
{
|
|
1915
|
-
Slogan: "Code without the caffeine shakes.",
|
|
1916
|
-
Tags: "Coffee-themed, Humorous",
|
|
1917
|
-
},
|
|
1918
|
-
{
|
|
1919
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
1920
|
-
Tags: "Coffee-themed, Technical",
|
|
1921
|
-
},
|
|
1922
|
-
{
|
|
1923
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
1924
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
1925
|
-
},
|
|
1926
|
-
{
|
|
1927
|
-
Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
|
|
1928
|
-
Tags: "Coffee-themed, Cheerful",
|
|
1929
|
-
},
|
|
1930
|
-
{
|
|
1931
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
1932
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
1933
|
-
},
|
|
1934
|
-
{
|
|
1935
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
1936
|
-
Tags: "Coffee-themed, Branding",
|
|
1937
|
-
},
|
|
1938
|
-
{
|
|
1939
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
1940
|
-
Tags: "Coffee-themed, Chill",
|
|
1941
|
-
},
|
|
1942
|
-
{
|
|
1943
|
-
Slogan: "All the kick, none of the crash.",
|
|
1944
|
-
Tags: "Coffee-themed, Energetic",
|
|
1945
|
-
},
|
|
1946
|
-
{
|
|
1947
|
-
Slogan: "Sip back and ship faster.",
|
|
1948
|
-
Tags: "Coffee-themed, Fun",
|
|
1949
|
-
},
|
|
1950
|
-
{
|
|
1951
|
-
Slogan: "Keep calm and code Decaf.",
|
|
1952
|
-
Tags: "Coffee-themed, Playful",
|
|
1953
|
-
},
|
|
1954
|
-
{
|
|
1955
|
-
Slogan: "Code without the caffeine shakes.",
|
|
1956
|
-
Tags: "Coffee-themed, Humorous",
|
|
1957
|
-
},
|
|
1958
|
-
{
|
|
1959
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
1960
|
-
Tags: "Coffee-themed, Technical",
|
|
1961
|
-
},
|
|
1962
|
-
{
|
|
1963
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
1964
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
1965
|
-
},
|
|
1966
|
-
{
|
|
1967
|
-
Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
|
|
1968
|
-
Tags: "Coffee-themed, Cheerful",
|
|
1969
|
-
},
|
|
1970
|
-
{
|
|
1971
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
1972
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
1973
|
-
},
|
|
1974
|
-
{
|
|
1975
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
1976
|
-
Tags: "Coffee-themed, Branding",
|
|
1977
|
-
},
|
|
1978
|
-
{
|
|
1979
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
1980
|
-
Tags: "Coffee-themed, Chill",
|
|
1981
|
-
},
|
|
1982
|
-
{
|
|
1983
|
-
Slogan: "All the kick, none of the crash.",
|
|
1984
|
-
Tags: "Coffee-themed, Energetic",
|
|
1985
|
-
},
|
|
1986
|
-
{
|
|
1987
|
-
Slogan: "Sip back and ship faster.",
|
|
1988
|
-
Tags: "Coffee-themed, Fun",
|
|
1989
|
-
},
|
|
1990
|
-
{
|
|
1991
|
-
Slogan: "Keep calm and code Decaf.",
|
|
1992
|
-
Tags: "Coffee-themed, Playful",
|
|
1993
|
-
},
|
|
1994
|
-
{
|
|
1995
|
-
Slogan: "Code without the caffeine shakes.",
|
|
1996
|
-
Tags: "Coffee-themed, Humorous",
|
|
1997
|
-
},
|
|
1998
|
-
{
|
|
1999
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
2000
|
-
Tags: "Coffee-themed, Technical",
|
|
2001
|
-
},
|
|
2002
|
-
{
|
|
2003
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
2004
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
2005
|
-
},
|
|
2006
|
-
{
|
|
2007
|
-
Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
|
|
2008
|
-
Tags: "Coffee-themed, Cheerful",
|
|
2009
|
-
},
|
|
2010
|
-
{
|
|
2011
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
2012
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
2013
|
-
},
|
|
2014
|
-
{
|
|
2015
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
2016
|
-
Tags: "Coffee-themed, Branding",
|
|
2017
|
-
},
|
|
2018
|
-
{
|
|
2019
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
2020
|
-
Tags: "Coffee-themed, Chill",
|
|
2021
|
-
},
|
|
2022
|
-
{
|
|
2023
|
-
Slogan: "All the kick, none of the crash.",
|
|
2024
|
-
Tags: "Coffee-themed, Energetic",
|
|
2025
|
-
},
|
|
2026
|
-
{
|
|
2027
|
-
Slogan: "Sip back and ship faster.",
|
|
2028
|
-
Tags: "Coffee-themed, Fun",
|
|
2029
|
-
},
|
|
2030
|
-
{
|
|
2031
|
-
Slogan: "Keep calm and code Decaf.",
|
|
2032
|
-
Tags: "Coffee-themed, Playful",
|
|
2033
|
-
},
|
|
2034
|
-
{
|
|
2035
|
-
Slogan: "Code without the caffeine shakes.",
|
|
2036
|
-
Tags: "Coffee-themed, Humorous",
|
|
2037
|
-
},
|
|
2038
|
-
{
|
|
2039
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
2040
|
-
Tags: "Coffee-themed, Technical",
|
|
2041
|
-
},
|
|
2042
|
-
{
|
|
2043
|
-
Slogan: "No caffeine, no chaos. Just clean code.",
|
|
2044
|
-
Tags: "Coffee-themed, Calm, Tech",
|
|
2045
|
-
},
|
|
2046
|
-
{
|
|
2047
|
-
Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
|
|
2048
|
-
Tags: "Coffee-themed, Cheerful",
|
|
2049
|
-
},
|
|
2050
|
-
{
|
|
2051
|
-
Slogan: "Chill fullstack. Powered by Decaf.",
|
|
2052
|
-
Tags: "Coffee-themed, Fun, Tech",
|
|
2053
|
-
},
|
|
2054
|
-
{
|
|
2055
|
-
Slogan: "Decaf-TS: Brewed for calm code.",
|
|
2056
|
-
Tags: "Coffee-themed, Branding",
|
|
2057
|
-
},
|
|
2058
|
-
{
|
|
2059
|
-
Slogan: "Smooth as your morning Decaf.",
|
|
2060
|
-
Tags: "Coffee-themed, Chill",
|
|
2061
|
-
},
|
|
2062
|
-
{
|
|
2063
|
-
Slogan: "All the kick, none of the crash.",
|
|
2064
|
-
Tags: "Coffee-themed, Energetic",
|
|
2065
|
-
},
|
|
2066
|
-
{
|
|
2067
|
-
Slogan: "Sip back and ship faster.",
|
|
2068
|
-
Tags: "Coffee-themed, Fun",
|
|
2069
|
-
},
|
|
2070
|
-
{
|
|
2071
|
-
Slogan: "Keep calm and code Decaf.",
|
|
2072
|
-
Tags: "Coffee-themed, Playful",
|
|
2073
|
-
},
|
|
2074
|
-
{
|
|
2075
|
-
Slogan: "Code without the caffeine shakes.",
|
|
2076
|
-
Tags: "Coffee-themed, Humorous",
|
|
2077
|
-
},
|
|
2078
|
-
{
|
|
2079
|
-
Slogan: "Your fullstack, decaffeinated.",
|
|
2080
|
-
Tags: "Coffee-themed, Technical",
|
|
2081
|
-
},
|
|
2082
|
-
{
|
|
2083
|
-
Slogan: "Decaf-TS: Where smart contracts meet smart interfaces.",
|
|
2084
|
-
Tags: "Blockchain, Smart Contracts, Tech",
|
|
2085
|
-
},
|
|
2086
|
-
{
|
|
2087
|
-
Slogan: "Ship dApps without the stress.",
|
|
2088
|
-
Tags: "Blockchain, Cheerful, Developer",
|
|
2089
|
-
},
|
|
2090
|
-
{
|
|
2091
|
-
Slogan: "No CRUD, no problem \u2014 Decaf your data.",
|
|
2092
|
-
Tags: "Data, No-CRUD, Chill",
|
|
2093
|
-
},
|
|
2094
|
-
{
|
|
2095
|
-
Slogan: "From DID to UI, without breaking a sweat.",
|
|
2096
|
-
Tags: "DID, SSI, UI, Calm",
|
|
2097
|
-
},
|
|
2098
|
-
{
|
|
2099
|
-
Slogan: "Decaf-TS: Your frontend already understands your smart contract.",
|
|
2100
|
-
Tags: "Smart Contracts, DX, Magic",
|
|
2101
|
-
},
|
|
2102
|
-
{
|
|
2103
|
-
Slogan: "Self-sovereign by design. Productive by default.",
|
|
2104
|
-
Tags: "SSI, Developer, Calm",
|
|
2105
|
-
},
|
|
2106
|
-
{
|
|
2107
|
-
Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
|
|
2108
|
-
Tags: "Blockchain, Multi-platform, Happy",
|
|
2109
|
-
},
|
|
2110
|
-
{
|
|
2111
|
-
Slogan: "Data that defines its own destiny.",
|
|
2112
|
-
Tags: "SSI, Data-driven, Empowerment",
|
|
2113
|
-
},
|
|
2114
|
-
{
|
|
2115
|
-
Slogan: "Goodbye CRUD, hello intent-based interfaces.",
|
|
2116
|
-
Tags: "No-CRUD, UI, Technical",
|
|
2117
|
-
},
|
|
2118
|
-
{
|
|
2119
|
-
Slogan: "The smoothest path from DID to done.",
|
|
2120
|
-
Tags: "DID, Workflow, Chill",
|
|
2121
|
-
},
|
|
2122
|
-
{
|
|
2123
|
-
Slogan: "Because your dApp deserves more than boilerplate.",
|
|
2124
|
-
Tags: "Blockchain, DevX, Efficiency",
|
|
2125
|
-
},
|
|
2126
|
-
{
|
|
2127
|
-
Slogan: "Own your data. Own your flow.",
|
|
2128
|
-
Tags: "SSI, Control, Ownership",
|
|
2129
|
-
},
|
|
2130
|
-
{
|
|
2131
|
-
Slogan: "Write logic like it belongs with the data \u2014 because it does.",
|
|
2132
|
-
Tags: "Data Logic, Developer, Smart",
|
|
2133
|
-
},
|
|
2134
|
-
{
|
|
2135
|
-
Slogan: "From smart contracts to smarter frontends.",
|
|
2136
|
-
Tags: "Smart Contracts, UI, DX",
|
|
2137
|
-
},
|
|
2138
|
-
{
|
|
2139
|
-
Slogan: "No caffeine. No CRUD. Just the future.",
|
|
2140
|
-
Tags: "No-CRUD, Coffee-themed, Futuristic",
|
|
2141
|
-
},
|
|
2142
|
-
{
|
|
2143
|
-
Slogan: "The future of web3 UX is Decaf.",
|
|
2144
|
-
Tags: "Blockchain, UX, Vision",
|
|
2145
|
-
},
|
|
2146
|
-
{
|
|
2147
|
-
Slogan: "Code with confidence. Govern with clarity.",
|
|
2148
|
-
Tags: "Blockchain, Governance, Calm",
|
|
2149
|
-
},
|
|
2150
|
-
{
|
|
2151
|
-
Slogan: "Interfaces that obey the data, not the other way around.",
|
|
2152
|
-
Tags: "UI, Data Logic, Self-aware",
|
|
2153
|
-
},
|
|
2154
|
-
{
|
|
2155
|
-
Slogan: "Brew business logic right into your bytes.",
|
|
2156
|
-
Tags: "Data Logic, Coffee-themed, Fun",
|
|
2157
|
-
},
|
|
2158
|
-
{
|
|
2159
|
-
Slogan: "DIDs done differently \u2014 and delightfully.",
|
|
2160
|
-
Tags: "DID, Self-Sovereign, Playful",
|
|
2161
|
-
},
|
|
2162
|
-
{
|
|
2163
|
-
Slogan: "Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",
|
|
2164
|
-
Tags: "Blockchain, Smart Contracts, Tech",
|
|
2165
|
-
},
|
|
2166
|
-
{
|
|
2167
|
-
Slogan: "Ship dApps without the stress.",
|
|
2168
|
-
Tags: "Blockchain, Cheerful, Developer",
|
|
2169
|
-
},
|
|
2170
|
-
{
|
|
2171
|
-
Slogan: "No boilerplate, no problem \u2014 Decaf-TS your data.",
|
|
2172
|
-
Tags: "Data, No-CRUD, Chill",
|
|
2173
|
-
},
|
|
2174
|
-
{
|
|
2175
|
-
Slogan: "From DID to UI, without breaking a sweat.",
|
|
2176
|
-
Tags: "DID, SSI, UI, Calm",
|
|
2177
|
-
},
|
|
2178
|
-
{
|
|
2179
|
-
Slogan: "Decaf-TS-TS: Your frontend already understands your blockchain contract.",
|
|
2180
|
-
Tags: "Smart Contracts, DX, Magic",
|
|
2181
|
-
},
|
|
2182
|
-
{
|
|
2183
|
-
Slogan: "Self-sovereign by design. Productive by default.",
|
|
2184
|
-
Tags: "SSI, Developer, Calm",
|
|
2185
|
-
},
|
|
2186
|
-
{
|
|
2187
|
-
Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
|
|
2188
|
-
Tags: "Blockchain, Multi-platform, Happy",
|
|
2189
|
-
},
|
|
2190
|
-
{
|
|
2191
|
-
Slogan: "Data that defines its own destiny.",
|
|
2192
|
-
Tags: "SSI, Data-driven, Empowerment",
|
|
2193
|
-
},
|
|
2194
|
-
{
|
|
2195
|
-
Slogan: "Goodbye boilerplate, hello intent-based interfaces.",
|
|
2196
|
-
Tags: "No-CRUD, UI, Technical",
|
|
2197
|
-
},
|
|
2198
|
-
{
|
|
2199
|
-
Slogan: "The smoothest path from DID to done.",
|
|
2200
|
-
Tags: "DID, Workflow, Chill",
|
|
2201
|
-
},
|
|
2202
|
-
{
|
|
2203
|
-
Slogan: "Because your dApp deserves more than boilerplate.",
|
|
2204
|
-
Tags: "Blockchain, DevX, Efficiency",
|
|
2205
|
-
},
|
|
2206
|
-
{
|
|
2207
|
-
Slogan: "Own your data. Own your flow.",
|
|
2208
|
-
Tags: "SSI, Control, Ownership",
|
|
2209
|
-
},
|
|
2210
|
-
{
|
|
2211
|
-
Slogan: "Write logic like it belongs with the data \u2014 because it does.",
|
|
2212
|
-
Tags: "Data Logic, Developer, Smart",
|
|
2213
|
-
},
|
|
2214
|
-
{
|
|
2215
|
-
Slogan: "From blockchain contracts to smarter frontends.",
|
|
2216
|
-
Tags: "Smart Contracts, UI, DX",
|
|
2217
|
-
},
|
|
2218
|
-
{
|
|
2219
|
-
Slogan: "No caffeine. No boilerplate. Just the future.",
|
|
2220
|
-
Tags: "No-CRUD, Coffee-themed, Futuristic",
|
|
2221
|
-
},
|
|
2222
|
-
{
|
|
2223
|
-
Slogan: "The future of web3 UX is Decaf-TS.",
|
|
2224
|
-
Tags: "Blockchain, UX, Vision",
|
|
2225
|
-
},
|
|
2226
|
-
{
|
|
2227
|
-
Slogan: "Code with confidence. Govern with clarity.",
|
|
2228
|
-
Tags: "Blockchain, Governance, Calm",
|
|
2229
|
-
},
|
|
2230
|
-
{
|
|
2231
|
-
Slogan: "Interfaces that obey the data, not the other way around.",
|
|
2232
|
-
Tags: "UI, Data Logic, Self-aware",
|
|
2233
|
-
},
|
|
2234
|
-
{
|
|
2235
|
-
Slogan: "Brew business logic right into your bytes.",
|
|
2236
|
-
Tags: "Data Logic, Coffee-themed, Fun",
|
|
2237
|
-
},
|
|
2238
|
-
{
|
|
2239
|
-
Slogan: "DIDs done differently \u2014 and delightfully.",
|
|
2240
|
-
Tags: "DID, Self-Sovereign, Playful",
|
|
2241
|
-
},
|
|
2242
|
-
{
|
|
2243
|
-
Slogan: "Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",
|
|
2244
|
-
Tags: "Blockchain, Smart Contracts, Tech",
|
|
2245
|
-
},
|
|
2246
|
-
{
|
|
2247
|
-
Slogan: "Ship dApps without the stress.",
|
|
2248
|
-
Tags: "Blockchain, Cheerful, Developer",
|
|
2249
|
-
},
|
|
2250
|
-
{
|
|
2251
|
-
Slogan: "No boilerplate, no problem \u2014 Decaf-TS your data.",
|
|
2252
|
-
Tags: "Data, No-CRUD, Chill",
|
|
2253
|
-
},
|
|
2254
|
-
{
|
|
2255
|
-
Slogan: "From DID to UI, without breaking a sweat.",
|
|
2256
|
-
Tags: "DID, SSI, UI, Calm",
|
|
2257
|
-
},
|
|
2258
|
-
{
|
|
2259
|
-
Slogan: "Decaf-TS-TS: Your frontend already understands your blockchain contract.",
|
|
2260
|
-
Tags: "Smart Contracts, DX, Magic",
|
|
2261
|
-
},
|
|
2262
|
-
{
|
|
2263
|
-
Slogan: "Self-sovereign by design. Productive by default.",
|
|
2264
|
-
Tags: "SSI, Developer, Calm",
|
|
2265
|
-
},
|
|
2266
|
-
{
|
|
2267
|
-
Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
|
|
2268
|
-
Tags: "Blockchain, Multi-platform, Happy",
|
|
2269
|
-
},
|
|
2270
|
-
{
|
|
2271
|
-
Slogan: "Data that defines its own destiny.",
|
|
2272
|
-
Tags: "SSI, Data-driven, Empowerment",
|
|
2273
|
-
},
|
|
2274
|
-
{
|
|
2275
|
-
Slogan: "Goodbye boilerplate, hello intent-based interfaces.",
|
|
2276
|
-
Tags: "No-CRUD, UI, Technical",
|
|
2277
|
-
},
|
|
2278
|
-
{
|
|
2279
|
-
Slogan: "The smoothest path from DID to done.",
|
|
2280
|
-
Tags: "DID, Workflow, Chill",
|
|
2281
|
-
},
|
|
2282
|
-
{
|
|
2283
|
-
Slogan: "Because your dApp deserves more than boilerplate.",
|
|
2284
|
-
Tags: "Blockchain, DevX, Efficiency",
|
|
2285
|
-
},
|
|
2286
|
-
{
|
|
2287
|
-
Slogan: "Own your data. Own your flow.",
|
|
2288
|
-
Tags: "SSI, Control, Ownership",
|
|
2289
|
-
},
|
|
2290
|
-
{
|
|
2291
|
-
Slogan: "Write logic like it belongs with the data \u2014 because it does.",
|
|
2292
|
-
Tags: "Data Logic, Developer, Smart",
|
|
2293
|
-
},
|
|
2294
|
-
{
|
|
2295
|
-
Slogan: "From blockchain contracts to smarter frontends.",
|
|
2296
|
-
Tags: "Smart Contracts, UI, DX",
|
|
2297
|
-
},
|
|
2298
|
-
{
|
|
2299
|
-
Slogan: "No caffeine. No boilerplate. Just the future.",
|
|
2300
|
-
Tags: "No-CRUD, Coffee-themed, Futuristic",
|
|
2301
|
-
},
|
|
2302
|
-
{
|
|
2303
|
-
Slogan: "The future of web3 UX is Decaf-TS.",
|
|
2304
|
-
Tags: "Blockchain, UX, Vision",
|
|
2305
|
-
},
|
|
2306
|
-
{
|
|
2307
|
-
Slogan: "Code with confidence. Govern with clarity.",
|
|
2308
|
-
Tags: "Blockchain, Governance, Calm",
|
|
2309
|
-
},
|
|
2310
|
-
{
|
|
2311
|
-
Slogan: "Interfaces that obey the data, not the other way around.",
|
|
2312
|
-
Tags: "UI, Data Logic, Self-aware",
|
|
2313
|
-
},
|
|
2314
|
-
{
|
|
2315
|
-
Slogan: "Brew business logic right into your bytes.",
|
|
2316
|
-
Tags: "Data Logic, Coffee-themed, Fun",
|
|
2317
|
-
},
|
|
2318
|
-
{
|
|
2319
|
-
Slogan: "DIDs done differently \u2014 and delightfully.",
|
|
2320
|
-
Tags: "DID, Self-Sovereign, Playful",
|
|
2321
|
-
},
|
|
2322
|
-
{
|
|
2323
|
-
Slogan: "Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",
|
|
2324
|
-
Tags: "Blockchain, Smart Contracts, Tech",
|
|
2325
|
-
},
|
|
2326
|
-
{
|
|
2327
|
-
Slogan: "Ship dApps without the stress.",
|
|
2328
|
-
Tags: "Blockchain, Cheerful, Developer",
|
|
2329
|
-
},
|
|
2330
|
-
{
|
|
2331
|
-
Slogan: "No boilerplate, no problem \u2014 Decaf-TS your data.",
|
|
2332
|
-
Tags: "Data, No-CRUD, Chill",
|
|
2333
|
-
},
|
|
2334
|
-
{
|
|
2335
|
-
Slogan: "From DID to UI, without breaking a sweat.",
|
|
2336
|
-
Tags: "DID, SSI, UI, Calm",
|
|
2337
|
-
},
|
|
2338
|
-
{
|
|
2339
|
-
Slogan: "Decaf-TS-TS: Your frontend already understands your blockchain contract.",
|
|
2340
|
-
Tags: "Smart Contracts, DX, Magic",
|
|
2341
|
-
},
|
|
2342
|
-
{
|
|
2343
|
-
Slogan: "Self-sovereign by design. Productive by default.",
|
|
2344
|
-
Tags: "SSI, Developer, Calm",
|
|
2345
|
-
},
|
|
2346
|
-
{
|
|
2347
|
-
Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
|
|
2348
|
-
Tags: "Blockchain, Multi-platform, Happy",
|
|
2349
|
-
},
|
|
2350
|
-
{
|
|
2351
|
-
Slogan: "Data that defines its own destiny.",
|
|
2352
|
-
Tags: "SSI, Data-driven, Empowerment",
|
|
2353
|
-
},
|
|
2354
|
-
{
|
|
2355
|
-
Slogan: "Goodbye boilerplate, hello intent-based interfaces.",
|
|
2356
|
-
Tags: "No-CRUD, UI, Technical",
|
|
2357
|
-
},
|
|
2358
|
-
{
|
|
2359
|
-
Slogan: "The smoothest path from DID to done.",
|
|
2360
|
-
Tags: "DID, Workflow, Chill",
|
|
2361
|
-
},
|
|
2362
|
-
{
|
|
2363
|
-
Slogan: "Because your dApp deserves more than boilerplate.",
|
|
2364
|
-
Tags: "Blockchain, DevX, Efficiency",
|
|
2365
|
-
},
|
|
2366
|
-
{
|
|
2367
|
-
Slogan: "Own your data. Own your flow.",
|
|
2368
|
-
Tags: "SSI, Control, Ownership",
|
|
2369
|
-
},
|
|
2370
|
-
{
|
|
2371
|
-
Slogan: "Write logic like it belongs with the data \u2014 because it does.",
|
|
2372
|
-
Tags: "Data Logic, Developer, Smart",
|
|
2373
|
-
},
|
|
2374
|
-
{
|
|
2375
|
-
Slogan: "From blockchain contracts to smarter frontends.",
|
|
2376
|
-
Tags: "Smart Contracts, UI, DX",
|
|
2377
|
-
},
|
|
2378
|
-
{
|
|
2379
|
-
Slogan: "No caffeine. No boilerplate. Just the future.",
|
|
2380
|
-
Tags: "No-CRUD, Coffee-themed, Futuristic",
|
|
2381
|
-
},
|
|
2382
|
-
{
|
|
2383
|
-
Slogan: "The future of web3 UX is Decaf-TS.",
|
|
2384
|
-
Tags: "Blockchain, UX, Vision",
|
|
2385
|
-
},
|
|
2386
|
-
{
|
|
2387
|
-
Slogan: "Code with confidence. Govern with clarity.",
|
|
2388
|
-
Tags: "Blockchain, Governance, Calm",
|
|
2389
|
-
},
|
|
2390
|
-
{
|
|
2391
|
-
Slogan: "Interfaces that obey the data, not the other way around.",
|
|
2392
|
-
Tags: "UI, Data Logic, Self-aware",
|
|
2393
|
-
},
|
|
2394
|
-
{
|
|
2395
|
-
Slogan: "Brew business logic right into your bytes.",
|
|
2396
|
-
Tags: "Data Logic, Coffee-themed, Fun",
|
|
2397
|
-
},
|
|
2398
|
-
{
|
|
2399
|
-
Slogan: "DIDs done differently \u2014 and delightfully.",
|
|
2400
|
-
Tags: "DID, Self-Sovereign, Playful",
|
|
2401
|
-
},
|
|
2402
|
-
{
|
|
2403
|
-
Slogan: "Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",
|
|
2404
|
-
Tags: "Blockchain, Smart Contracts, Tech",
|
|
2405
|
-
},
|
|
2406
|
-
{
|
|
2407
|
-
Slogan: "Ship dApps without the stress.",
|
|
2408
|
-
Tags: "Blockchain, Cheerful, Developer",
|
|
2409
|
-
},
|
|
2410
|
-
{
|
|
2411
|
-
Slogan: "No boilerplate, no problem \u2014 Decaf-TS your data.",
|
|
2412
|
-
Tags: "Data, No-CRUD, Chill",
|
|
2413
|
-
},
|
|
2414
|
-
{
|
|
2415
|
-
Slogan: "From DID to UI, without breaking a sweat.",
|
|
2416
|
-
Tags: "DID, SSI, UI, Calm",
|
|
2417
|
-
},
|
|
2418
|
-
{
|
|
2419
|
-
Slogan: "Decaf-TS-TS: Your frontend already understands your blockchain contract.",
|
|
2420
|
-
Tags: "Smart Contracts, DX, Magic",
|
|
2421
|
-
},
|
|
2422
|
-
{
|
|
2423
|
-
Slogan: "Self-sovereign by design. Productive by default.",
|
|
2424
|
-
Tags: "SSI, Developer, Calm",
|
|
2425
|
-
},
|
|
2426
|
-
{
|
|
2427
|
-
Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
|
|
2428
|
-
Tags: "Blockchain, Multi-platform, Happy",
|
|
2429
|
-
},
|
|
2430
|
-
{
|
|
2431
|
-
Slogan: "Data that defines its own destiny.",
|
|
2432
|
-
Tags: "SSI, Data-driven, Empowerment",
|
|
2433
|
-
},
|
|
2434
|
-
{
|
|
2435
|
-
Slogan: "Goodbye boilerplate, hello intent-based interfaces.",
|
|
2436
|
-
Tags: "No-CRUD, UI, Technical",
|
|
2437
|
-
},
|
|
2438
|
-
{
|
|
2439
|
-
Slogan: "The smoothest path from DID to done.",
|
|
2440
|
-
Tags: "DID, Workflow, Chill",
|
|
2441
|
-
},
|
|
2442
|
-
{
|
|
2443
|
-
Slogan: "Because your dApp deserves more than boilerplate.",
|
|
2444
|
-
Tags: "Blockchain, DevX, Efficiency",
|
|
2445
|
-
},
|
|
2446
|
-
{
|
|
2447
|
-
Slogan: "Own your data. Own your flow.",
|
|
2448
|
-
Tags: "SSI, Control, Ownership",
|
|
2449
|
-
},
|
|
2450
|
-
{
|
|
2451
|
-
Slogan: "Write logic like it belongs with the data \u2014 because it does.",
|
|
2452
|
-
Tags: "Data Logic, Developer, Smart",
|
|
2453
|
-
},
|
|
2454
|
-
{
|
|
2455
|
-
Slogan: "From blockchain contracts to smarter frontends.",
|
|
2456
|
-
Tags: "Smart Contracts, UI, DX",
|
|
2457
|
-
},
|
|
2458
|
-
{
|
|
2459
|
-
Slogan: "No caffeine. No boilerplate. Just the future.",
|
|
2460
|
-
Tags: "No-CRUD, Coffee-themed, Futuristic",
|
|
2461
|
-
},
|
|
2462
|
-
{
|
|
2463
|
-
Slogan: "The future of web3 UX is Decaf-TS.",
|
|
2464
|
-
Tags: "Blockchain, UX, Vision",
|
|
2465
|
-
},
|
|
2466
|
-
{
|
|
2467
|
-
Slogan: "Code with confidence. Govern with clarity.",
|
|
2468
|
-
Tags: "Blockchain, Governance, Calm",
|
|
2469
|
-
},
|
|
2470
|
-
{
|
|
2471
|
-
Slogan: "Interfaces that obey the data, not the other way around.",
|
|
2472
|
-
Tags: "UI, Data Logic, Self-aware",
|
|
2473
|
-
},
|
|
2474
|
-
{
|
|
2475
|
-
Slogan: "Brew business logic right into your bytes.",
|
|
2476
|
-
Tags: "Data Logic, Coffee-themed, Fun",
|
|
2477
|
-
},
|
|
2478
|
-
{
|
|
2479
|
-
Slogan: "DIDs done differently \u2014 and delightfully.",
|
|
2480
|
-
Tags: "DID, Self-Sovereign, Playful",
|
|
2481
|
-
},
|
|
2482
|
-
];
|
|
2483
|
-
|
|
2484
|
-
/**
|
|
2485
|
-
* @description Array of ANSI color codes for banner styling.
|
|
2486
|
-
* @summary Defines a set of ANSI color codes used to style the banner text.
|
|
2487
|
-
* @memberOf module:utils
|
|
2488
|
-
*/
|
|
2489
|
-
const colors = [
|
|
2490
|
-
"\x1b[38;5;215m", // soft orange
|
|
2491
|
-
"\x1b[38;5;209m", // coral
|
|
2492
|
-
"\x1b[38;5;205m", // pink
|
|
2493
|
-
"\x1b[38;5;210m", // peachy
|
|
2494
|
-
"\x1b[38;5;217m", // salmon
|
|
2495
|
-
"\x1b[38;5;216m", // light coral
|
|
2496
|
-
"\x1b[38;5;224m", // light peach
|
|
2497
|
-
"\x1b[38;5;230m", // soft cream
|
|
2498
|
-
"\x1b[38;5;230m", // soft cream
|
|
2499
|
-
];
|
|
2500
|
-
/**
|
|
2501
|
-
* @description Prints a styled banner to the console.
|
|
2502
|
-
* @summary Generates and prints a colorful ASCII art banner with a random slogan.
|
|
2503
|
-
* @param {Logger} [logger] - Optional logger for verbose output.
|
|
2504
|
-
* @memberOf module:utils
|
|
2505
|
-
* @function printBanner
|
|
2506
|
-
* @mermaid
|
|
2507
|
-
* sequenceDiagram
|
|
2508
|
-
* participant printBanner
|
|
2509
|
-
* participant getSlogan
|
|
2510
|
-
* participant padEnd
|
|
2511
|
-
* participant console
|
|
2512
|
-
* printBanner->>getSlogan: Call getSlogan()
|
|
2513
|
-
* getSlogan-->>printBanner: Return random slogan
|
|
2514
|
-
* printBanner->>printBanner: Create banner ASCII art
|
|
2515
|
-
* printBanner->>printBanner: Split banner into lines
|
|
2516
|
-
* printBanner->>printBanner: Calculate max line length
|
|
2517
|
-
* printBanner->>padEnd: Call padEnd with slogan
|
|
2518
|
-
* padEnd-->>printBanner: Return padded slogan line
|
|
2519
|
-
* loop For each banner line
|
|
2520
|
-
* printBanner->>style: Call style(line)
|
|
2521
|
-
* style-->>printBanner: Return styled line
|
|
2522
|
-
* printBanner->>console: Log styled line
|
|
2523
|
-
* end
|
|
2524
|
-
*/
|
|
2525
|
-
function printBanner(logger) {
|
|
2526
|
-
const message = getSlogan();
|
|
2527
|
-
const banner = `# ░▒▓███████▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓██████▓▒░ ░▒▓████████▓▒░ ░▒▓████████▓▒░ ░▒▓███████▓▒░
|
|
2528
|
-
# ( ( ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
|
|
2529
|
-
# ) ) ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
|
|
2530
|
-
# [=======] ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓██████▓▒░
|
|
2531
|
-
# \`-----´ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
|
|
2532
|
-
# ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
|
|
2533
|
-
# ░▒▓███████▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓███████▓▒░
|
|
2534
|
-
#`.split("\n");
|
|
2535
|
-
const maxLength = banner.reduce((max, line) => Math.max(max, line.length), 0);
|
|
2536
|
-
banner.push(`# ${message.padStart(maxLength - 3)}`);
|
|
2537
|
-
banner.forEach((line, index) => {
|
|
2538
|
-
(logger ? logger.info.bind(logger) : console.log.bind(console))(styledStringBuilder.style(line || "").raw(colors[index]).text);
|
|
2539
|
-
});
|
|
2540
|
-
}
|
|
2541
|
-
/**
|
|
2542
|
-
* @description Retrieves a slogan from the predefined list.
|
|
2543
|
-
* @summary Fetches a random slogan or a specific one by index from the slogans list.
|
|
2544
|
-
* @param {number} [i] - Optional index to retrieve a specific slogan.
|
|
2545
|
-
* @return {string} The selected slogan.
|
|
2546
|
-
* @function getSlogan
|
|
2547
|
-
* @memberOf module:utils
|
|
2548
|
-
* @mermaid
|
|
2549
|
-
* sequenceDiagram
|
|
2550
|
-
* participant getSlogan
|
|
2551
|
-
* participant Math.random
|
|
2552
|
-
* participant slogans
|
|
2553
|
-
* alt i is undefined
|
|
2554
|
-
* getSlogan->>Math.random: Generate random index
|
|
2555
|
-
* Math.random-->>getSlogan: Return random index
|
|
2556
|
-
* else i is defined
|
|
2557
|
-
* Note over getSlogan: Use provided index
|
|
2558
|
-
* end
|
|
2559
|
-
* getSlogan->>slogans: Access slogan at index
|
|
2560
|
-
* slogans-->>getSlogan: Return slogan
|
|
2561
|
-
* alt Error occurs
|
|
2562
|
-
* getSlogan->>getSlogan: Throw error
|
|
2563
|
-
* end
|
|
2564
|
-
* getSlogan-->>Caller: Return slogan
|
|
2565
|
-
*/
|
|
2566
|
-
function getSlogan(i) {
|
|
2567
|
-
try {
|
|
2568
|
-
i =
|
|
2569
|
-
typeof i === "undefined" ? Math.floor(Math.random() * slogans.length) : i;
|
|
2570
|
-
return slogans[i].Slogan;
|
|
2571
|
-
}
|
|
2572
|
-
catch (error) {
|
|
2573
|
-
throw new Error(`Failed to retrieve slogans: ${error}`);
|
|
2574
|
-
}
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
/**
|
|
2578
|
-
* @class Command
|
|
2579
|
-
* @abstract
|
|
2580
|
-
* @template I - The type of input options for the command.
|
|
2581
|
-
* @template R - The return type of the command execution.
|
|
2582
|
-
* @memberOf module:utils
|
|
2583
|
-
* @description Abstract base class for command implementation.
|
|
2584
|
-
* @summary Provides a structure for creating command-line interface commands with input handling, logging, and execution flow.
|
|
2585
|
-
*
|
|
2586
|
-
* @param {string} name - The name of the command.
|
|
2587
|
-
* @param {CommandOptions<I>} [inputs] - The input options for the command.
|
|
2588
|
-
* @param {string[]} [requirements] - The list of required dependencies for the command.
|
|
2589
|
-
*/
|
|
2590
|
-
class Command extends logging.LoggedClass {
|
|
2591
|
-
constructor(name, inputs = {}, requirements = []) {
|
|
2592
|
-
super();
|
|
2593
|
-
this.name = name;
|
|
2594
|
-
this.inputs = inputs;
|
|
2595
|
-
this.requirements = requirements;
|
|
2596
|
-
if (!Command.log) {
|
|
2597
|
-
Object.defineProperty(Command, "log", {
|
|
2598
|
-
writable: false,
|
|
2599
|
-
value: logging.Logging.for(Command.name),
|
|
2600
|
-
});
|
|
2601
|
-
}
|
|
2602
|
-
this.inputs = Object.assign({}, DefaultCommandOptions, inputs);
|
|
2603
|
-
}
|
|
2604
|
-
/**
|
|
2605
|
-
* @protected
|
|
2606
|
-
* @async
|
|
2607
|
-
* @description Checks if all required dependencies are present.
|
|
2608
|
-
* @summary Retrieves the list of dependencies and compares it against the required dependencies for the command.
|
|
2609
|
-
* @returns {Promise<void>} A promise that resolves when the check is complete.
|
|
2610
|
-
*
|
|
2611
|
-
* @mermaid
|
|
2612
|
-
* sequenceDiagram
|
|
2613
|
-
* participant Command
|
|
2614
|
-
* participant getDependencies
|
|
2615
|
-
* participant Set
|
|
2616
|
-
* Command->>getDependencies: Call
|
|
2617
|
-
* getDependencies-->>Command: Return {prod, dev, peer}
|
|
2618
|
-
* Command->>Set: Create Set from prod, dev, peer
|
|
2619
|
-
* Set-->>Command: Return unique dependencies
|
|
2620
|
-
* Command->>Command: Compare against requirements
|
|
2621
|
-
* alt Missing dependencies
|
|
2622
|
-
* Command->>Command: Add to missing list
|
|
2623
|
-
* end
|
|
2624
|
-
* Note over Command: If missing.length > 0, handle missing dependencies
|
|
2625
|
-
*/
|
|
2626
|
-
async checkRequirements() {
|
|
2627
|
-
const { prod, dev, peer } = await getDependencies();
|
|
2628
|
-
const missing = [];
|
|
2629
|
-
const fullList = Array.from(new Set([...prod, ...dev, ...peer]).values()).map((d) => d.name);
|
|
2630
|
-
for (const dep of this.requirements)
|
|
2631
|
-
if (!fullList.includes(dep))
|
|
2632
|
-
missing.push(dep);
|
|
2633
|
-
if (!missing.length)
|
|
2634
|
-
return;
|
|
2635
|
-
}
|
|
2636
|
-
/**
|
|
2637
|
-
* @protected
|
|
2638
|
-
* @description Provides help information for the command.
|
|
2639
|
-
* @summary This method should be overridden in derived classes to provide specific help information.
|
|
2640
|
-
* @param {ParseArgsResult} args - The parsed command-line arguments.
|
|
2641
|
-
* @returns {void}
|
|
2642
|
-
*/
|
|
2643
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2644
|
-
help(args) {
|
|
2645
|
-
return this.log.info(`This is help. I'm no use because I should have been overridden.`);
|
|
2646
|
-
}
|
|
2647
|
-
/**
|
|
2648
|
-
* @async
|
|
2649
|
-
* @description Executes the command.
|
|
2650
|
-
* @summary This method handles the overall execution flow of the command, including parsing arguments,
|
|
2651
|
-
* setting up logging, checking for version or help requests, and running the command.
|
|
2652
|
-
* @returns {Promise<R | string | void>} A promise that resolves with the command's result.
|
|
2653
|
-
*
|
|
2654
|
-
* @mermaid
|
|
2655
|
-
* sequenceDiagram
|
|
2656
|
-
* participant Command
|
|
2657
|
-
* participant UserInput
|
|
2658
|
-
* participant Logging
|
|
2659
|
-
* participant getPackageVersion
|
|
2660
|
-
* participant printBanner
|
|
2661
|
-
* Command->>UserInput: parseArgs(inputs)
|
|
2662
|
-
* UserInput-->>Command: Return ParseArgsResult
|
|
2663
|
-
* Command->>Command: Process options
|
|
2664
|
-
* Command->>Logging: setConfig(options)
|
|
2665
|
-
* alt version requested
|
|
2666
|
-
* Command->>getPackageVersion: Call
|
|
2667
|
-
* getPackageVersion-->>Command: Return version
|
|
2668
|
-
* else help requested
|
|
2669
|
-
* Command->>Command: help(args)
|
|
2670
|
-
* else banner requested
|
|
2671
|
-
* Command->>printBanner: Call
|
|
2672
|
-
* end
|
|
2673
|
-
* Command->>Command: run(args)
|
|
2674
|
-
* alt error occurs
|
|
2675
|
-
* Command->>Command: Log error
|
|
2676
|
-
* end
|
|
2677
|
-
* Command-->>Command: Return result
|
|
2678
|
-
*/
|
|
2679
|
-
async execute() {
|
|
2680
|
-
const args = UserInput.parseArgs(this.inputs);
|
|
2681
|
-
const env = logging.LoggedEnvironment.accumulate(DefaultCommandValues).accumulate(args.values);
|
|
2682
|
-
const { version, help, banner } = env;
|
|
2683
|
-
if (version) {
|
|
2684
|
-
return getPackageVersion();
|
|
2685
|
-
}
|
|
2686
|
-
if (help) {
|
|
2687
|
-
return this.help(args);
|
|
2688
|
-
}
|
|
2689
|
-
if (banner)
|
|
2690
|
-
printBanner(this.log.for(printBanner, {
|
|
2691
|
-
timestamp: false,
|
|
2692
|
-
style: false,
|
|
2693
|
-
context: false,
|
|
2694
|
-
logLevel: false,
|
|
2695
|
-
}));
|
|
2696
|
-
let result;
|
|
2697
|
-
try {
|
|
2698
|
-
result = await this.run(env);
|
|
2699
|
-
}
|
|
2700
|
-
catch (e) {
|
|
2701
|
-
this.log.error(`Error while running provided cli function: ${e}`);
|
|
2702
|
-
throw e;
|
|
2703
|
-
}
|
|
2704
|
-
return result;
|
|
2705
|
-
}
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
|
-
/* istanbul ignore file */
|
|
2709
|
-
/**
|
|
2710
|
-
* @description A simple HTTP client for downloading files.
|
|
2711
|
-
* @summary This class provides functionality to download files from HTTPS URLs.
|
|
2712
|
-
* It uses Node.js built-in https module to make requests.
|
|
2713
|
-
*
|
|
2714
|
-
* @class HttpClient
|
|
2715
|
-
*/
|
|
2716
|
-
class HttpClient {
|
|
2717
|
-
static { this.log = logging.Logging.for(HttpClient); }
|
|
2718
|
-
/**
|
|
2719
|
-
* @description Downloads a file from a given URL.
|
|
2720
|
-
* @summary This method sends a GET request to the specified URL and returns the response body as a string.
|
|
2721
|
-
* It handles different scenarios such as non-200 status codes and network errors.
|
|
2722
|
-
*
|
|
2723
|
-
* @param url - The URL of the file to download.
|
|
2724
|
-
* @return A promise that resolves with the file content as a string.
|
|
2725
|
-
*
|
|
2726
|
-
* @mermaid
|
|
2727
|
-
* sequenceDiagram
|
|
2728
|
-
* participant Client
|
|
2729
|
-
* participant HttpClient
|
|
2730
|
-
* participant HTTPS
|
|
2731
|
-
* participant Server
|
|
2732
|
-
* Client->>HttpClient: downloadFile(url)
|
|
2733
|
-
* HttpClient->>HTTPS: get(url)
|
|
2734
|
-
* HTTPS->>Server: GET request
|
|
2735
|
-
* Server-->>HTTPS: Response
|
|
2736
|
-
* HTTPS-->>HttpClient: Response object
|
|
2737
|
-
* alt Status code is 200
|
|
2738
|
-
* loop For each data chunk
|
|
2739
|
-
* HTTPS->>HttpClient: 'data' event
|
|
2740
|
-
* HttpClient->>HttpClient: Accumulate data
|
|
2741
|
-
* end
|
|
2742
|
-
* HTTPS->>HttpClient: 'end' event
|
|
2743
|
-
* HttpClient-->>Client: Resolve with data
|
|
2744
|
-
* else Status code is not 200
|
|
2745
|
-
* HttpClient-->>Client: Reject with error
|
|
2746
|
-
* end
|
|
2747
|
-
*/
|
|
2748
|
-
static async downloadFile(url) {
|
|
2749
|
-
return new Promise((resolve, reject) => {
|
|
2750
|
-
function request(url) {
|
|
2751
|
-
url = encodeURI(url);
|
|
2752
|
-
https__default["default"].get(url, (res) => {
|
|
2753
|
-
if (res.statusCode === 301 || res.statusCode === 307)
|
|
2754
|
-
return request(res.headers.location);
|
|
2755
|
-
if (res.statusCode !== 200) {
|
|
2756
|
-
HttpClient.log.error(`Failed to fetch ${url} (status: ${res.statusCode})`);
|
|
2757
|
-
return reject(new Error(`Failed to fetch ${url}`));
|
|
2758
|
-
}
|
|
2759
|
-
let data = "";
|
|
2760
|
-
res.on("data", (chunk) => {
|
|
2761
|
-
data += chunk;
|
|
2762
|
-
});
|
|
2763
|
-
res.on("error", (error) => {
|
|
2764
|
-
reject(error);
|
|
2765
|
-
});
|
|
2766
|
-
res.on("end", () => {
|
|
2767
|
-
resolve(data);
|
|
2768
|
-
});
|
|
2769
|
-
});
|
|
2770
|
-
}
|
|
2771
|
-
request(url);
|
|
2772
|
-
});
|
|
2773
|
-
}
|
|
2774
|
-
}
|
|
2775
|
-
|
|
2776
|
-
function parseList(input) {
|
|
2777
|
-
if (!input)
|
|
2778
|
-
return [];
|
|
2779
|
-
if (Array.isArray(input))
|
|
2780
|
-
return input.map((i) => `${i}`.trim()).filter(Boolean);
|
|
2781
|
-
return `${input}`
|
|
2782
|
-
.split(",")
|
|
2783
|
-
.map((p) => p.trim())
|
|
2784
|
-
.filter(Boolean);
|
|
2785
|
-
}
|
|
2786
|
-
function packageToGlobal(name) {
|
|
2787
|
-
// Remove scope and split by non-alphanumeric chars, then camelCase
|
|
2788
|
-
const withoutScope = name.replace(/^@/, "");
|
|
2789
|
-
const parts = withoutScope.split(/[/\-_.]+/).filter(Boolean);
|
|
2790
|
-
return parts
|
|
2791
|
-
.map((p, i) => i === 0
|
|
2792
|
-
? p.replace(/[^a-zA-Z0-9]/g, "")
|
|
2793
|
-
: `${p.charAt(0).toUpperCase()}${p.slice(1)}`)
|
|
2794
|
-
.join("");
|
|
2795
|
-
}
|
|
2796
|
-
function getPackageDependencies() {
|
|
2797
|
-
// Try the current working directory first
|
|
2798
|
-
let pkg;
|
|
2799
|
-
try {
|
|
2800
|
-
pkg = getPackage(process.cwd());
|
|
2801
|
-
}
|
|
2802
|
-
catch {
|
|
2803
|
-
pkg = undefined;
|
|
2804
|
-
}
|
|
2805
|
-
// If no dependencies found in cwd, try the package next to this source file (fallback for tests)
|
|
2806
|
-
try {
|
|
2807
|
-
const hasDeps = pkg &&
|
|
2808
|
-
(Object.keys(pkg.dependencies || {}).length > 0 ||
|
|
2809
|
-
Object.keys(pkg.devDependencies || {}).length > 0 ||
|
|
2810
|
-
Object.keys(pkg.peerDependencies || {}).length > 0);
|
|
2811
|
-
if (!hasDeps) {
|
|
2812
|
-
const fallbackDir = path__default["default"].resolve(__dirname, "../../..");
|
|
2813
|
-
try {
|
|
2814
|
-
pkg = getPackage(fallbackDir);
|
|
2815
|
-
}
|
|
2816
|
-
catch {
|
|
2817
|
-
// ignore and keep pkg as-is
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
|
-
}
|
|
2821
|
-
catch {
|
|
2822
|
-
// ignore
|
|
2823
|
-
}
|
|
2824
|
-
const deps = Object.keys((pkg && pkg.dependencies) || {});
|
|
2825
|
-
const peer = Object.keys((pkg && pkg.peerDependencies) || {});
|
|
2826
|
-
const dev = Object.keys((pkg && pkg.devDependencies) || {});
|
|
2827
|
-
return Array.from(new Set([...deps, ...peer, ...dev]));
|
|
2828
|
-
}
|
|
2829
|
-
const VERSION_STRING = "0.4.1";
|
|
2830
|
-
const PACKAGE_STRING = "@decaf-ts/utils";
|
|
2831
|
-
var Modes;
|
|
2832
|
-
(function (Modes) {
|
|
2833
|
-
Modes["CJS"] = "commonjs";
|
|
2834
|
-
Modes["ESM"] = "es2022";
|
|
2835
|
-
})(Modes || (Modes = {}));
|
|
2836
|
-
var BuildMode;
|
|
2837
|
-
(function (BuildMode) {
|
|
2838
|
-
BuildMode["BUILD"] = "build";
|
|
2839
|
-
BuildMode["BUNDLE"] = "bundle";
|
|
2840
|
-
BuildMode["ALL"] = "all";
|
|
2841
|
-
})(BuildMode || (BuildMode = {}));
|
|
2842
|
-
const options$2 = {
|
|
2843
|
-
prod: {
|
|
2844
|
-
type: "boolean",
|
|
2845
|
-
default: false,
|
|
2846
|
-
},
|
|
2847
|
-
dev: {
|
|
2848
|
-
type: "boolean",
|
|
2849
|
-
default: false,
|
|
2850
|
-
},
|
|
2851
|
-
buildMode: {
|
|
2852
|
-
type: "string",
|
|
2853
|
-
default: BuildMode.ALL,
|
|
2854
|
-
},
|
|
2855
|
-
includes: {
|
|
2856
|
-
type: "string",
|
|
2857
|
-
default: "",
|
|
2858
|
-
},
|
|
2859
|
-
externals: {
|
|
2860
|
-
type: "string",
|
|
2861
|
-
default: "",
|
|
2862
|
-
},
|
|
2863
|
-
docs: {
|
|
2864
|
-
type: "boolean",
|
|
2865
|
-
default: false,
|
|
2866
|
-
},
|
|
2867
|
-
commands: {
|
|
2868
|
-
type: "boolean",
|
|
2869
|
-
default: false,
|
|
2870
|
-
},
|
|
2871
|
-
banner: {
|
|
2872
|
-
type: "boolean",
|
|
2873
|
-
default: false,
|
|
2874
|
-
},
|
|
2875
|
-
};
|
|
2876
|
-
const cjs2Transformer = (ext = ".cjs") => {
|
|
2877
|
-
const log = BuildScripts.log.for(cjs2Transformer);
|
|
2878
|
-
const resolutionCache = new Map();
|
|
2879
|
-
return (transformationContext) => {
|
|
2880
|
-
return (sourceFile) => {
|
|
2881
|
-
const sourceDir = path__default["default"].dirname(sourceFile.fileName);
|
|
2882
|
-
function resolvePath(importPath) {
|
|
2883
|
-
const cacheKey = JSON.stringify([sourceDir, importPath]);
|
|
2884
|
-
const cachedValue = resolutionCache.get(cacheKey);
|
|
2885
|
-
if (cachedValue != null)
|
|
2886
|
-
return cachedValue;
|
|
2887
|
-
let resolvedPath = importPath;
|
|
2888
|
-
try {
|
|
2889
|
-
resolvedPath = path__default["default"].resolve(sourceDir, resolvedPath + ".ts");
|
|
2890
|
-
}
|
|
2891
|
-
catch (error) {
|
|
2892
|
-
throw new Error(`Failed to resolve path ${importPath}: ${error}`);
|
|
2893
|
-
}
|
|
2894
|
-
let stat;
|
|
2895
|
-
try {
|
|
2896
|
-
stat = fs__default["default"].statSync(resolvedPath);
|
|
2897
|
-
}
|
|
2898
|
-
catch (e) {
|
|
2899
|
-
try {
|
|
2900
|
-
log.verbose(`Testing existence of path ${resolvedPath} as a folder defaulting to index file`);
|
|
2901
|
-
stat = fs__default["default"].statSync(resolvedPath.replace(/\.ts$/gm, ""));
|
|
2902
|
-
}
|
|
2903
|
-
catch (e2) {
|
|
2904
|
-
throw new Error(`Failed to resolve path ${importPath}: ${e}, ${e2}`);
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
|
-
if (stat.isDirectory())
|
|
2908
|
-
resolvedPath = resolvedPath.replace(/\.ts$/gm, "/index.ts");
|
|
2909
|
-
if (path__default["default"].isAbsolute(resolvedPath)) {
|
|
2910
|
-
const extension = (/\.tsx?$/.exec(path__default["default"].basename(resolvedPath)) || [])[0] || void 0;
|
|
2911
|
-
resolvedPath =
|
|
2912
|
-
"./" +
|
|
2913
|
-
path__default["default"].relative(sourceDir, path__default["default"].resolve(path__default["default"].dirname(resolvedPath), path__default["default"].basename(resolvedPath, extension) + ext));
|
|
2914
|
-
}
|
|
2915
|
-
resolutionCache.set(cacheKey, resolvedPath);
|
|
2916
|
-
return resolvedPath;
|
|
2917
|
-
}
|
|
2918
|
-
function visitNode(node) {
|
|
2919
|
-
if (shouldMutateModuleSpecifier(node)) {
|
|
2920
|
-
if (ts__namespace.isImportDeclaration(node)) {
|
|
2921
|
-
const resolvedPath = resolvePath(node.moduleSpecifier.text);
|
|
2922
|
-
const newModuleSpecifier = transformationContext.factory.createStringLiteral(resolvedPath);
|
|
2923
|
-
return transformationContext.factory.updateImportDeclaration(node, node.modifiers, node.importClause, newModuleSpecifier, undefined);
|
|
2924
|
-
}
|
|
2925
|
-
else if (ts__namespace.isExportDeclaration(node)) {
|
|
2926
|
-
const resolvedPath = resolvePath(node.moduleSpecifier.text);
|
|
2927
|
-
const newModuleSpecifier = transformationContext.factory.createStringLiteral(resolvedPath);
|
|
2928
|
-
return transformationContext.factory.updateExportDeclaration(node, node.modifiers, node.isTypeOnly, node.exportClause, newModuleSpecifier, undefined);
|
|
2929
|
-
}
|
|
2930
|
-
}
|
|
2931
|
-
return ts__namespace.visitEachChild(node, visitNode, transformationContext);
|
|
2932
|
-
}
|
|
2933
|
-
function shouldMutateModuleSpecifier(node) {
|
|
2934
|
-
if (!ts__namespace.isImportDeclaration(node) && !ts__namespace.isExportDeclaration(node))
|
|
2935
|
-
return false;
|
|
2936
|
-
if (node.moduleSpecifier === undefined)
|
|
2937
|
-
return false;
|
|
2938
|
-
// only when module specifier is valid
|
|
2939
|
-
if (!ts__namespace.isStringLiteral(node.moduleSpecifier))
|
|
2940
|
-
return false;
|
|
2941
|
-
// only when path is relative
|
|
2942
|
-
if (!node.moduleSpecifier.text.startsWith("./") &&
|
|
2943
|
-
!node.moduleSpecifier.text.startsWith("../"))
|
|
2944
|
-
return false;
|
|
2945
|
-
// only when module specifier has no extension
|
|
2946
|
-
if (path__default["default"].extname(node.moduleSpecifier.text) !== "")
|
|
2947
|
-
return false;
|
|
2948
|
-
return true;
|
|
2949
|
-
}
|
|
2950
|
-
return ts__namespace.visitNode(sourceFile, visitNode);
|
|
2951
|
-
};
|
|
2952
|
-
};
|
|
2953
|
-
};
|
|
2954
|
-
class BuildScripts extends Command {
|
|
2955
|
-
constructor() {
|
|
2956
|
-
super("BuildScripts", Object.assign({}, DefaultCommandOptions, options$2));
|
|
2957
|
-
this.replacements = {};
|
|
2958
|
-
const pkg = getPackage();
|
|
2959
|
-
const { name, version } = pkg;
|
|
2960
|
-
this.pkgName = name.includes("@") ? name.split("/")[1] : name;
|
|
2961
|
-
this.pkgVersion = version;
|
|
2962
|
-
this.replacements[VERSION_STRING] = this.pkgVersion;
|
|
2963
|
-
this.replacements[PACKAGE_STRING] = name;
|
|
2964
|
-
}
|
|
2965
|
-
patchFiles(p) {
|
|
2966
|
-
const log = this.log.for(this.patchFiles);
|
|
2967
|
-
const { name, version } = getPackage();
|
|
2968
|
-
log.info(`Patching ${name} ${version} module in ${p}...`);
|
|
2969
|
-
const stat = fs__default["default"].statSync(p);
|
|
2970
|
-
if (stat.isDirectory())
|
|
2971
|
-
fs__default["default"].readdirSync(p, { withFileTypes: true, recursive: true })
|
|
2972
|
-
.filter((p) => p.isFile())
|
|
2973
|
-
.forEach((file) => patchFile(path__default["default"].join(file.parentPath, file.name), this.replacements));
|
|
2974
|
-
log.verbose(`Module ${name} ${version} patched in ${p}...`);
|
|
2975
|
-
}
|
|
2976
|
-
reportDiagnostics(diagnostics, logLevel) {
|
|
2977
|
-
const msg = this.formatDiagnostics(diagnostics);
|
|
2978
|
-
if (!Object.values(logging.LogLevel).includes(logLevel))
|
|
2979
|
-
throw new Error(`Invalid LogLevel ${logLevel}`);
|
|
2980
|
-
try {
|
|
2981
|
-
this.log[logLevel](msg);
|
|
2982
|
-
}
|
|
2983
|
-
catch (e) {
|
|
2984
|
-
console.warn(`Failed to get logger for ${logLevel}`);
|
|
2985
|
-
throw e;
|
|
2986
|
-
}
|
|
2987
|
-
return msg;
|
|
2988
|
-
}
|
|
2989
|
-
// Format diagnostics into a single string for throwing or logging
|
|
2990
|
-
formatDiagnostics(diagnostics) {
|
|
2991
|
-
return diagnostics
|
|
2992
|
-
.map((diagnostic) => {
|
|
2993
|
-
let message = "";
|
|
2994
|
-
if (diagnostic.file && diagnostic.start) {
|
|
2995
|
-
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
2996
|
-
message += `${diagnostic.file.fileName} (${line + 1},${character + 1})`;
|
|
2997
|
-
}
|
|
2998
|
-
message +=
|
|
2999
|
-
": " + ts__namespace.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
3000
|
-
return message;
|
|
3001
|
-
})
|
|
3002
|
-
.join("\n");
|
|
3003
|
-
}
|
|
3004
|
-
readConfigFile(configFileName) {
|
|
3005
|
-
// Read config file
|
|
3006
|
-
const configFileText = fs__default["default"].readFileSync(configFileName).toString();
|
|
3007
|
-
// Parse JSON, after removing comments. Just fancier JSON.parse
|
|
3008
|
-
const result = ts__namespace.parseConfigFileTextToJson(configFileName, configFileText);
|
|
3009
|
-
const configObject = result.config;
|
|
3010
|
-
if (!configObject) {
|
|
3011
|
-
this.reportDiagnostics([result.error], logging.LogLevel.error);
|
|
3012
|
-
}
|
|
3013
|
-
// Extract config infromation
|
|
3014
|
-
const configParseResult = ts__namespace.parseJsonConfigFileContent(configObject, ts__namespace.sys, path__default["default"].dirname(configFileName));
|
|
3015
|
-
if (configParseResult.errors.length > 0)
|
|
3016
|
-
this.reportDiagnostics(configParseResult.errors, logging.LogLevel.error);
|
|
3017
|
-
return configParseResult;
|
|
3018
|
-
}
|
|
3019
|
-
evalDiagnostics(diagnostics) {
|
|
3020
|
-
if (diagnostics && diagnostics.length > 0) {
|
|
3021
|
-
const errors = diagnostics.filter((d) => d.category === ts__namespace.DiagnosticCategory.Error);
|
|
3022
|
-
const warnings = diagnostics.filter((d) => d.category === ts__namespace.DiagnosticCategory.Warning);
|
|
3023
|
-
const suggestions = diagnostics.filter((d) => d.category === ts__namespace.DiagnosticCategory.Suggestion);
|
|
3024
|
-
const messages = diagnostics.filter((d) => d.category === ts__namespace.DiagnosticCategory.Message);
|
|
3025
|
-
// Log diagnostics to console
|
|
3026
|
-
if (warnings.length)
|
|
3027
|
-
this.reportDiagnostics(warnings, logging.LogLevel.info);
|
|
3028
|
-
if (errors.length) {
|
|
3029
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3030
|
-
this.reportDiagnostics(diagnostics, logging.LogLevel.error);
|
|
3031
|
-
this.log.info(`TypeScript reported ${diagnostics.length} diagnostic(s) during check; aborting.`);
|
|
3032
|
-
// throw new Error(
|
|
3033
|
-
// `TypeScript reported ${diagnostics.length} diagnostic(s) during check; aborting.`
|
|
3034
|
-
// );
|
|
3035
|
-
}
|
|
3036
|
-
if (suggestions.length)
|
|
3037
|
-
this.reportDiagnostics(suggestions, logging.LogLevel.info);
|
|
3038
|
-
if (messages.length)
|
|
3039
|
-
this.reportDiagnostics(messages, logging.LogLevel.info);
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
preCheckDiagnostics(program) {
|
|
3043
|
-
const diagnostics = ts__namespace.getPreEmitDiagnostics(program);
|
|
3044
|
-
this.evalDiagnostics(diagnostics);
|
|
3045
|
-
}
|
|
3046
|
-
// Create a TypeScript program for the current tsconfig and fail if there are any error diagnostics.
|
|
3047
|
-
async checkTsDiagnostics(isDev, mode, bundle = false) {
|
|
3048
|
-
const log = this.log.for(this.checkTsDiagnostics);
|
|
3049
|
-
let tsConfig;
|
|
3050
|
-
try {
|
|
3051
|
-
tsConfig = this.readConfigFile("./tsconfig.json");
|
|
3052
|
-
}
|
|
3053
|
-
catch (e) {
|
|
3054
|
-
throw new Error(`Failed to parse tsconfig.json: ${e}`);
|
|
3055
|
-
}
|
|
3056
|
-
if (bundle) {
|
|
3057
|
-
tsConfig.options.module = ts.ModuleKind.AMD;
|
|
3058
|
-
tsConfig.options.outDir = "dist";
|
|
3059
|
-
tsConfig.options.isolatedModules = false;
|
|
3060
|
-
tsConfig.options.outFile = this.pkgName;
|
|
3061
|
-
}
|
|
3062
|
-
else {
|
|
3063
|
-
tsConfig.options.outDir = `lib${mode === Modes.ESM ? "/esm" : ""}`;
|
|
3064
|
-
tsConfig.options.module =
|
|
3065
|
-
mode === Modes.ESM ? ts.ModuleKind.ES2022 : ts.ModuleKind.CommonJS;
|
|
3066
|
-
}
|
|
3067
|
-
if (isDev) {
|
|
3068
|
-
tsConfig.options.inlineSourceMap = true;
|
|
3069
|
-
tsConfig.options.sourceMap = false;
|
|
3070
|
-
}
|
|
3071
|
-
else {
|
|
3072
|
-
tsConfig.options.sourceMap = false;
|
|
3073
|
-
}
|
|
3074
|
-
const program = ts__namespace.createProgram(tsConfig.fileNames, tsConfig.options);
|
|
3075
|
-
this.preCheckDiagnostics(program);
|
|
3076
|
-
log.verbose(`TypeScript checks passed (${bundle ? "bundle" : "normal"} mode).`);
|
|
3077
|
-
}
|
|
3078
|
-
async buildTs(isDev, mode, bundle = false) {
|
|
3079
|
-
const log = this.log.for(this.buildTs);
|
|
3080
|
-
log.info(`Building ${this.pkgName} ${this.pkgVersion} module (${mode}) in ${isDev ? "dev" : "prod"} mode...`);
|
|
3081
|
-
let tsConfig;
|
|
3082
|
-
try {
|
|
3083
|
-
tsConfig = this.readConfigFile("./tsconfig.json");
|
|
3084
|
-
}
|
|
3085
|
-
catch (e) {
|
|
3086
|
-
throw new Error(`Failed to parse tsconfig.json: ${e}`);
|
|
3087
|
-
}
|
|
3088
|
-
if (bundle) {
|
|
3089
|
-
tsConfig.options.module = ts.ModuleKind.AMD;
|
|
3090
|
-
tsConfig.options.outDir = "dist";
|
|
3091
|
-
tsConfig.options.isolatedModules = false;
|
|
3092
|
-
tsConfig.options.outFile = this.pkgName;
|
|
3093
|
-
}
|
|
3094
|
-
else {
|
|
3095
|
-
tsConfig.options.outDir = `lib${mode === Modes.ESM ? "/esm" : ""}`;
|
|
3096
|
-
tsConfig.options.module =
|
|
3097
|
-
mode === Modes.ESM ? ts.ModuleKind.ES2022 : ts.ModuleKind.CommonJS;
|
|
3098
|
-
}
|
|
3099
|
-
if (isDev) {
|
|
3100
|
-
tsConfig.options.inlineSourceMap = true;
|
|
3101
|
-
tsConfig.options.sourceMap = false;
|
|
3102
|
-
}
|
|
3103
|
-
else {
|
|
3104
|
-
tsConfig.options.sourceMap = false;
|
|
3105
|
-
}
|
|
3106
|
-
const program = ts__namespace.createProgram(tsConfig.fileNames, tsConfig.options);
|
|
3107
|
-
const transformations = {};
|
|
3108
|
-
if (mode === Modes.CJS) {
|
|
3109
|
-
transformations.before = [cjs2Transformer(".cjs")];
|
|
3110
|
-
}
|
|
3111
|
-
else if (mode === Modes.ESM) {
|
|
3112
|
-
transformations.before = [cjs2Transformer(".js")];
|
|
3113
|
-
}
|
|
3114
|
-
const emitResult = program.emit(undefined, undefined, undefined, undefined, transformations);
|
|
3115
|
-
const allDiagnostics = ts__namespace
|
|
3116
|
-
.getPreEmitDiagnostics(program)
|
|
3117
|
-
.concat(emitResult.diagnostics);
|
|
3118
|
-
this.evalDiagnostics(allDiagnostics);
|
|
3119
|
-
}
|
|
3120
|
-
async build(isDev, mode, bundle = false) {
|
|
3121
|
-
const log = this.log.for(this.build);
|
|
3122
|
-
await this.buildTs(isDev, mode, bundle);
|
|
3123
|
-
log.verbose(`Module ${this.pkgName} ${this.pkgVersion} (${mode}) built in ${isDev ? "dev" : "prod"} mode...`);
|
|
3124
|
-
if (mode === Modes.CJS && !bundle) {
|
|
3125
|
-
const files = getAllFiles("lib", (file) => file.endsWith(".js") && !file.includes("/esm/"));
|
|
3126
|
-
for (const file of files) {
|
|
3127
|
-
log.verbose(`Patching ${file}'s cjs imports...`);
|
|
3128
|
-
const f = file.replace(".js", ".cjs");
|
|
3129
|
-
await renameFile(file, f);
|
|
3130
|
-
}
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
copyAssets(mode) {
|
|
3134
|
-
const log = this.log.for(this.copyAssets);
|
|
3135
|
-
let hasAssets = false;
|
|
3136
|
-
try {
|
|
3137
|
-
hasAssets = fs__default["default"].statSync("./src/assets").isDirectory();
|
|
3138
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3139
|
-
}
|
|
3140
|
-
catch (e) {
|
|
3141
|
-
return log.verbose(`No assets found in ./src/assets to copy`);
|
|
3142
|
-
}
|
|
3143
|
-
if (hasAssets)
|
|
3144
|
-
copyFile("./src/assets", `./${mode === Modes.CJS ? "lib" : "dist"}/assets`);
|
|
3145
|
-
}
|
|
3146
|
-
async bundle(mode, isDev, isLib, entryFile = "src/index.ts", nameOverride = this.pkgName, externalsArg, includeArg = [
|
|
3147
|
-
"prompts",
|
|
3148
|
-
"styled-string-builder",
|
|
3149
|
-
"typed-object-accumulator",
|
|
3150
|
-
"@decaf-ts/logging",
|
|
3151
|
-
]) {
|
|
3152
|
-
// Run a TypeScript-only diagnostic check for the bundling configuration and fail fast on any errors.
|
|
3153
|
-
await this.checkTsDiagnostics(isDev, mode, true);
|
|
3154
|
-
const isEsm = mode === Modes.ESM;
|
|
3155
|
-
const pkgName = this.pkgName;
|
|
3156
|
-
// normalize include and externals
|
|
3157
|
-
const include = Array.from(new Set([...parseList(includeArg)]));
|
|
3158
|
-
let externalsList = parseList(externalsArg);
|
|
3159
|
-
if (externalsList.length === 0) {
|
|
3160
|
-
// if no externals specified, include package.json dependencies to avoid rollup treating them as resolvable
|
|
3161
|
-
externalsList = getPackageDependencies();
|
|
3162
|
-
}
|
|
3163
|
-
const ext = Array.from(new Set([
|
|
3164
|
-
// builtins and always external runtime deps
|
|
3165
|
-
...(function builtinList() {
|
|
3166
|
-
try {
|
|
3167
|
-
return (Array.isArray(module.builtinModules) ? module.builtinModules : []);
|
|
3168
|
-
}
|
|
3169
|
-
catch {
|
|
3170
|
-
// fallback to a reasonable subset if `builtinModules` is unavailable
|
|
3171
|
-
return [
|
|
3172
|
-
"fs",
|
|
3173
|
-
"path",
|
|
3174
|
-
"process",
|
|
3175
|
-
"child_process",
|
|
3176
|
-
"util",
|
|
3177
|
-
"https",
|
|
3178
|
-
"http",
|
|
3179
|
-
"os",
|
|
3180
|
-
"stream",
|
|
3181
|
-
"crypto",
|
|
3182
|
-
"zlib",
|
|
3183
|
-
"net",
|
|
3184
|
-
"tls",
|
|
3185
|
-
"url",
|
|
3186
|
-
"querystring",
|
|
3187
|
-
"assert",
|
|
3188
|
-
"events",
|
|
3189
|
-
"tty",
|
|
3190
|
-
"dns",
|
|
3191
|
-
"querystring",
|
|
3192
|
-
];
|
|
3193
|
-
}
|
|
3194
|
-
})(),
|
|
3195
|
-
...externalsList,
|
|
3196
|
-
]));
|
|
3197
|
-
const plugins = [
|
|
3198
|
-
typescript__default["default"]({
|
|
3199
|
-
compilerOptions: {
|
|
3200
|
-
module: "esnext",
|
|
3201
|
-
declaration: false,
|
|
3202
|
-
outDir: isLib ? "bin" : "dist",
|
|
3203
|
-
},
|
|
3204
|
-
include: ["src/**/*.ts"],
|
|
3205
|
-
exclude: ["node_modules", "**/*.spec.ts"],
|
|
3206
|
-
tsconfig: "./tsconfig.json",
|
|
3207
|
-
}),
|
|
3208
|
-
json__default["default"](),
|
|
3209
|
-
];
|
|
3210
|
-
// production minification
|
|
3211
|
-
if (!isDev) {
|
|
3212
|
-
// terser is optional at runtime; import lazily inside bundle to avoid test-time resolution errors
|
|
3213
|
-
try {
|
|
3214
|
-
const terserMod = await import('@rollup/plugin-terser');
|
|
3215
|
-
const terserFn = (terserMod && terserMod.terser) || terserMod.default || terserMod;
|
|
3216
|
-
plugins.push(terserFn());
|
|
3217
|
-
}
|
|
3218
|
-
catch {
|
|
3219
|
-
// if terser isn't available, ignore
|
|
3220
|
-
}
|
|
3221
|
-
}
|
|
3222
|
-
if (isLib) {
|
|
3223
|
-
plugins.push(commonjs__default["default"]({
|
|
3224
|
-
include: [],
|
|
3225
|
-
exclude: externalsList,
|
|
3226
|
-
}), pluginNodeResolve.nodeResolve({
|
|
3227
|
-
resolveOnly: include,
|
|
3228
|
-
}));
|
|
3229
|
-
}
|
|
3230
|
-
const input = {
|
|
3231
|
-
input: entryFile,
|
|
3232
|
-
plugins: plugins,
|
|
3233
|
-
external: ext,
|
|
3234
|
-
};
|
|
3235
|
-
// prepare output globals mapping for externals
|
|
3236
|
-
const globals = {};
|
|
3237
|
-
// include all externals and builtins (ext) so Rollup won't guess names for builtins
|
|
3238
|
-
ext.forEach((e) => {
|
|
3239
|
-
globals[e] = packageToGlobal(e);
|
|
3240
|
-
});
|
|
3241
|
-
const outputs = [
|
|
3242
|
-
{
|
|
3243
|
-
file: `${isLib ? "bin/" : "dist/"}${nameOverride ? nameOverride : `.bundle.${!isDev ? "min" : ""}`}${isEsm ? ".esm" : ""}.cjs`,
|
|
3244
|
-
format: isLib ? "cjs" : isEsm ? "esm" : "umd",
|
|
3245
|
-
name: pkgName,
|
|
3246
|
-
esModule: isEsm,
|
|
3247
|
-
sourcemap: isDev ? "inline" : false,
|
|
3248
|
-
globals: globals,
|
|
3249
|
-
exports: "auto",
|
|
3250
|
-
},
|
|
3251
|
-
];
|
|
3252
|
-
try {
|
|
3253
|
-
const bundle = await rollup.rollup(input);
|
|
3254
|
-
console.log(bundle.watchFiles);
|
|
3255
|
-
async function generateOutputs(bundle) {
|
|
3256
|
-
for (const outputOptions of outputs) {
|
|
3257
|
-
await bundle.write(outputOptions);
|
|
3258
|
-
}
|
|
3259
|
-
}
|
|
3260
|
-
await generateOutputs(bundle);
|
|
3261
|
-
}
|
|
3262
|
-
catch (e) {
|
|
3263
|
-
throw new Error(`Failed to bundle: ${e}`);
|
|
3264
|
-
}
|
|
3265
|
-
}
|
|
3266
|
-
async buildByEnv(isDev, mode = BuildMode.ALL, includesArg, externalsArg) {
|
|
3267
|
-
// note: includes and externals will be passed through from run() into this method by callers
|
|
3268
|
-
try {
|
|
3269
|
-
deletePath("lib");
|
|
3270
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3271
|
-
}
|
|
3272
|
-
catch (e) {
|
|
3273
|
-
// do nothing
|
|
3274
|
-
}
|
|
3275
|
-
try {
|
|
3276
|
-
deletePath("dist");
|
|
3277
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3278
|
-
}
|
|
3279
|
-
catch (e) {
|
|
3280
|
-
// do nothing
|
|
3281
|
-
}
|
|
3282
|
-
fs__default["default"].mkdirSync("lib");
|
|
3283
|
-
fs__default["default"].mkdirSync("dist");
|
|
3284
|
-
if ([BuildMode.ALL, BuildMode.BUILD].includes(mode)) {
|
|
3285
|
-
await this.build(isDev, Modes.ESM);
|
|
3286
|
-
await this.build(isDev, Modes.CJS);
|
|
3287
|
-
this.patchFiles("lib");
|
|
3288
|
-
}
|
|
3289
|
-
if ([BuildMode.ALL, BuildMode.BUNDLE].includes(mode)) {
|
|
3290
|
-
await this.bundle(Modes.ESM, isDev, false, "src/index.ts", this.pkgName, externalsArg, includesArg);
|
|
3291
|
-
await this.bundle(Modes.CJS, isDev, false, "src/index.ts", this.pkgName, externalsArg, includesArg);
|
|
3292
|
-
this.patchFiles("dist");
|
|
3293
|
-
}
|
|
3294
|
-
this.copyAssets(Modes.CJS);
|
|
3295
|
-
this.copyAssets(Modes.ESM);
|
|
3296
|
-
}
|
|
3297
|
-
async buildDev(mode = BuildMode.ALL, includesArg, externalsArg) {
|
|
3298
|
-
return this.buildByEnv(true, mode, includesArg, externalsArg);
|
|
3299
|
-
}
|
|
3300
|
-
async buildProd(mode = BuildMode.ALL, includesArg, externalsArg) {
|
|
3301
|
-
return this.buildByEnv(false, mode, includesArg, externalsArg);
|
|
3302
|
-
}
|
|
3303
|
-
async buildDocs() {
|
|
3304
|
-
await runCommand(`npm install better-docs taffydb`).promise;
|
|
3305
|
-
await runCommand(`npx markdown-include ./workdocs/readme-md.json`).promise;
|
|
3306
|
-
await runCommand(`npx jsdoc -c ./workdocs/jsdocs.json -t ./node_modules/better-docs`).promise;
|
|
3307
|
-
await runCommand(`npm remove better-docs taffydb`).promise;
|
|
3308
|
-
[
|
|
3309
|
-
{
|
|
3310
|
-
src: "workdocs/assets",
|
|
3311
|
-
dest: "./docs/workdocs/assets",
|
|
3312
|
-
},
|
|
3313
|
-
{
|
|
3314
|
-
src: "workdocs/reports/coverage",
|
|
3315
|
-
dest: "./docs/workdocs/reports/coverage",
|
|
3316
|
-
},
|
|
3317
|
-
{
|
|
3318
|
-
src: "workdocs/reports/html",
|
|
3319
|
-
dest: "./docs/workdocs/reports/html",
|
|
3320
|
-
},
|
|
3321
|
-
{
|
|
3322
|
-
src: "workdocs/resources",
|
|
3323
|
-
dest: "./docs/workdocs/resources",
|
|
3324
|
-
},
|
|
3325
|
-
{
|
|
3326
|
-
src: "LICENSE.md",
|
|
3327
|
-
dest: "./docs/LICENSE.md",
|
|
3328
|
-
},
|
|
3329
|
-
].forEach((f) => {
|
|
3330
|
-
const { src, dest } = f;
|
|
3331
|
-
copyFile(src, dest);
|
|
3332
|
-
});
|
|
3333
|
-
}
|
|
3334
|
-
async run(answers) {
|
|
3335
|
-
const { dev, prod, docs, buildMode, includes, externals } = answers;
|
|
3336
|
-
if (dev) {
|
|
3337
|
-
return await this.buildDev(buildMode, includes, externals);
|
|
3338
|
-
}
|
|
3339
|
-
if (prod) {
|
|
3340
|
-
return await this.buildProd(buildMode, includes, externals);
|
|
3341
|
-
}
|
|
3342
|
-
if (docs) {
|
|
3343
|
-
return await this.buildDocs();
|
|
3344
|
-
}
|
|
3345
|
-
}
|
|
3346
|
-
}
|
|
3347
|
-
|
|
3348
|
-
/* istanbul ignore file */
|
|
3349
|
-
const options$1 = {
|
|
3350
|
-
ci: {
|
|
3351
|
-
type: "boolean",
|
|
3352
|
-
default: true,
|
|
3353
|
-
},
|
|
3354
|
-
message: {
|
|
3355
|
-
type: "string",
|
|
3356
|
-
short: "m",
|
|
3357
|
-
},
|
|
3358
|
-
tag: {
|
|
3359
|
-
type: "string",
|
|
3360
|
-
short: "t",
|
|
3361
|
-
default: undefined,
|
|
3362
|
-
},
|
|
3363
|
-
};
|
|
3364
|
-
/**
|
|
3365
|
-
* @class ReleaseScript
|
|
3366
|
-
* @extends {Command}
|
|
3367
|
-
* @cavegory scripts
|
|
3368
|
-
* @description A command-line script for managing releases and version updates.
|
|
3369
|
-
* @summary This script automates the process of creating and pushing new releases. It handles version updates,
|
|
3370
|
-
* commit messages, and optionally publishes to NPM. The script supports semantic versioning and can work in both CI and non-CI environments.
|
|
3371
|
-
*
|
|
3372
|
-
* @param {Object} options - Configuration options for the script
|
|
3373
|
-
* @param {boolean} options.ci - Whether the script is running in a CI environment (default: true)
|
|
3374
|
-
* @param {string} options.message - The release message (short: 'm')
|
|
3375
|
-
* @param {string} options.tag - The version tag to use (short: 't', default: undefined)
|
|
3376
|
-
*/
|
|
3377
|
-
class ReleaseScript extends Command {
|
|
3378
|
-
constructor() {
|
|
3379
|
-
super("ReleaseScript", options$1);
|
|
3380
|
-
}
|
|
3381
|
-
/**
|
|
3382
|
-
* @description Prepares the version for the release.
|
|
3383
|
-
* @summary This method validates the provided tag or prompts the user for a new one if not provided or invalid.
|
|
3384
|
-
* It also displays the latest git tags for reference.
|
|
3385
|
-
* @param {string} tag - The version tag to prepare
|
|
3386
|
-
* @returns {Promise<string>} The prepared version tag
|
|
3387
|
-
*
|
|
3388
|
-
* @mermaid
|
|
3389
|
-
* sequenceDiagram
|
|
3390
|
-
* participant R as ReleaseScript
|
|
3391
|
-
* participant T as TestVersion
|
|
3392
|
-
* participant U as UserInput
|
|
3393
|
-
* participant G as Git
|
|
3394
|
-
* R->>T: testVersion(tag)
|
|
3395
|
-
* alt tag is valid
|
|
3396
|
-
* T-->>R: return tag
|
|
3397
|
-
* else tag is invalid or not provided
|
|
3398
|
-
* R->>G: List latest git tags
|
|
3399
|
-
* R->>U: Prompt for new tag
|
|
3400
|
-
* U-->>R: return new tag
|
|
3401
|
-
* end
|
|
3402
|
-
*/
|
|
3403
|
-
async prepareVersion(tag) {
|
|
3404
|
-
const log = this.log.for(this.prepareVersion);
|
|
3405
|
-
tag = this.testVersion(tag || "");
|
|
3406
|
-
if (!tag) {
|
|
3407
|
-
log.verbose("No release message provided. Prompting for one:");
|
|
3408
|
-
log.info(`Listing latest git tags:`);
|
|
3409
|
-
await runCommand("git tag --sort=-taggerdate | head -n 5").promise;
|
|
3410
|
-
return await UserInput.insistForText("tag", "Enter the new tag number (accepts v*.*.*[-...])", (val) => !!val.toString().match(/^v[0-9]+\.[0-9]+.[0-9]+(-[0-9a-zA-Z-]+)?$/));
|
|
3411
|
-
}
|
|
3412
|
-
return tag;
|
|
3413
|
-
}
|
|
3414
|
-
/**
|
|
3415
|
-
* @description Tests if the provided version is valid.
|
|
3416
|
-
* @summary This method checks if the version is a valid semantic version or a predefined update type (PATCH, MINOR, MAJOR).
|
|
3417
|
-
* @param {string} version - The version to test
|
|
3418
|
-
* @returns {string | undefined} The validated version or undefined if invalid
|
|
3419
|
-
*/
|
|
3420
|
-
testVersion(version) {
|
|
3421
|
-
const log = this.log.for(this.testVersion);
|
|
3422
|
-
version = version.trim().toLowerCase();
|
|
3423
|
-
switch (version) {
|
|
3424
|
-
case exports.SemVersion.PATCH:
|
|
3425
|
-
case exports.SemVersion.MINOR:
|
|
3426
|
-
case exports.SemVersion.MAJOR:
|
|
3427
|
-
log.verbose(`Using provided SemVer update: ${version}`, 1);
|
|
3428
|
-
return version;
|
|
3429
|
-
default:
|
|
3430
|
-
log.verbose(`Testing provided version for SemVer compatibility: ${version}`, 1);
|
|
3431
|
-
if (!new RegExp(SemVersionRegex).test(version)) {
|
|
3432
|
-
log.debug(`Invalid version number: ${version}`);
|
|
3433
|
-
return undefined;
|
|
3434
|
-
}
|
|
3435
|
-
log.verbose(`version approved: ${version}`, 1);
|
|
3436
|
-
return version;
|
|
3437
|
-
}
|
|
3438
|
-
}
|
|
3439
|
-
/**
|
|
3440
|
-
* @description Prepares the release message.
|
|
3441
|
-
* @summary This method either returns the provided message or prompts the user for a new one if not provided.
|
|
3442
|
-
* @param {string} [message] - The release message
|
|
3443
|
-
* @returns {Promise<string>} The prepared release message
|
|
3444
|
-
*/
|
|
3445
|
-
async prepareMessage(message) {
|
|
3446
|
-
const log = this.log.for(this.prepareMessage);
|
|
3447
|
-
if (!message) {
|
|
3448
|
-
log.verbose("No release message provided. Prompting for one");
|
|
3449
|
-
return await UserInput.insistForText("message", "What should be the release message/ticket?", (val) => !!val && val.toString().length > 5);
|
|
3450
|
-
}
|
|
3451
|
-
return message;
|
|
3452
|
-
}
|
|
3453
|
-
/**
|
|
3454
|
-
* @description Runs the release script.
|
|
3455
|
-
* @summary This method orchestrates the entire release process, including version preparation, message creation,
|
|
3456
|
-
* git operations, and npm publishing (if not in CI environment).
|
|
3457
|
-
* @param {ParseArgsResult} args - The parsed command-line arguments
|
|
3458
|
-
* @returns {Promise<void>}
|
|
3459
|
-
*
|
|
3460
|
-
* @mermaid
|
|
3461
|
-
* sequenceDiagram
|
|
3462
|
-
* participant R as ReleaseScript
|
|
3463
|
-
* participant V as PrepareVersion
|
|
3464
|
-
* participant M as PrepareMessage
|
|
3465
|
-
* participant N as NPM
|
|
3466
|
-
* participant G as Git
|
|
3467
|
-
* participant U as UserInput
|
|
3468
|
-
* R->>V: prepareVersion(tag)
|
|
3469
|
-
* R->>M: prepareMessage(message)
|
|
3470
|
-
* R->>N: Run prepare-release script
|
|
3471
|
-
* R->>G: Check git status
|
|
3472
|
-
* alt changes exist
|
|
3473
|
-
* R->>U: Ask for confirmation
|
|
3474
|
-
* U-->>R: Confirm
|
|
3475
|
-
* R->>G: Add and commit changes
|
|
3476
|
-
* end
|
|
3477
|
-
* R->>N: Update npm version
|
|
3478
|
-
* R->>G: Push changes and tags
|
|
3479
|
-
* alt not CI environment
|
|
3480
|
-
* R->>N: Publish to npm
|
|
3481
|
-
* end
|
|
3482
|
-
*/
|
|
3483
|
-
async run(args) {
|
|
3484
|
-
let result;
|
|
3485
|
-
const { ci } = args;
|
|
3486
|
-
let { tag, message } = args;
|
|
3487
|
-
tag = await this.prepareVersion(tag);
|
|
3488
|
-
message = await this.prepareMessage(message);
|
|
3489
|
-
result = await runCommand(`npm run prepare-release -- ${tag} ${message}`, {
|
|
3490
|
-
cwd: process.cwd(),
|
|
3491
|
-
}).promise;
|
|
3492
|
-
result = await runCommand("git status --porcelain").promise;
|
|
3493
|
-
await result;
|
|
3494
|
-
if (result.logs.length &&
|
|
3495
|
-
(await UserInput.askConfirmation("git-changes", "Do you want to push the changes to the remote repository?", true))) {
|
|
3496
|
-
await runCommand("git add .").promise;
|
|
3497
|
-
await runCommand(`git commit -m "${tag} - ${message} - after release preparation${ci ? "" : NoCIFLag}"`).promise;
|
|
3498
|
-
}
|
|
3499
|
-
await runCommand(`npm version "${tag}" -m "${message}${ci ? "" : NoCIFLag}"`).promise;
|
|
3500
|
-
await runCommand("git push --follow-tags").promise;
|
|
3501
|
-
if (!ci) {
|
|
3502
|
-
await runCommand("NPM_TOKEN=$(cat .npmtoken) npm publish --access public")
|
|
3503
|
-
.promise;
|
|
3504
|
-
}
|
|
3505
|
-
}
|
|
3506
|
-
}
|
|
3507
|
-
|
|
3508
|
-
/* istanbul ignore file */
|
|
3509
|
-
const baseUrl = "https://raw.githubusercontent.com/decaf-ts/ts-workspace/master";
|
|
3510
|
-
const options = {
|
|
3511
|
-
templates: [
|
|
3512
|
-
".github/ISSUE_TEMPLATE/bug_report.md",
|
|
3513
|
-
".github/ISSUE_TEMPLATE/feature_request.md",
|
|
3514
|
-
".github/FUNDING.yml",
|
|
3515
|
-
],
|
|
3516
|
-
workflows: [
|
|
3517
|
-
".github/workflows/codeql-analysis.yml",
|
|
3518
|
-
".github/workflows/jest-coverage.yaml",
|
|
3519
|
-
".github/workflows/nodejs-build-prod.yaml",
|
|
3520
|
-
".github/workflows/pages.yaml",
|
|
3521
|
-
".github/workflows/publish-on-release.yaml",
|
|
3522
|
-
".github/workflows/release-on-tag.yaml",
|
|
3523
|
-
".github/workflows/snyk-analysis.yaml",
|
|
3524
|
-
],
|
|
3525
|
-
ide: [
|
|
3526
|
-
".idea/runConfigurations/All Tests.run.xml",
|
|
3527
|
-
".idea/runConfigurations/build.run.xml",
|
|
3528
|
-
".idea/runConfigurations/build_prod.run.xml",
|
|
3529
|
-
".idea/runConfigurations/coverage.run.xml",
|
|
3530
|
-
".idea/runConfigurations/docs.run.xml",
|
|
3531
|
-
".idea/runConfigurations/drawings.run.xml",
|
|
3532
|
-
".idea/runConfigurations/flash-forward.run.xml",
|
|
3533
|
-
".idea/runConfigurations/Integration_Tests.run.xml",
|
|
3534
|
-
".idea/runConfigurations/Bundling_Tests.run.xml",
|
|
3535
|
-
".idea/runConfigurations/lint-fix.run.xml",
|
|
3536
|
-
".idea/runConfigurations/release.run.xml",
|
|
3537
|
-
".idea/runConfigurations/test_circular.run.xml",
|
|
3538
|
-
".idea/runConfigurations/uml.run.xml",
|
|
3539
|
-
".idea/runConfigurations/Unit Tests.run.xml",
|
|
3540
|
-
".idea/runConfigurations/update-scripts.run.xml",
|
|
3541
|
-
],
|
|
3542
|
-
docs: [
|
|
3543
|
-
"workdocs/tutorials/Contributing.md",
|
|
3544
|
-
"workdocs/tutorials/Documentation.md",
|
|
3545
|
-
"workdocs/tutorials/For Developers.md",
|
|
3546
|
-
"workdocs/2-Badges.md",
|
|
3547
|
-
"workdocs/jsdocs.json",
|
|
3548
|
-
"workdocs/readme-md.json",
|
|
3549
|
-
],
|
|
3550
|
-
styles: [".prettierrc", "eslint.config.js"],
|
|
3551
|
-
scripts: [
|
|
3552
|
-
"bin/update-scripts.cjs",
|
|
3553
|
-
"bin/tag-release.cjs",
|
|
3554
|
-
"bin/build-scripts.cjs",
|
|
3555
|
-
],
|
|
3556
|
-
tests: ["jest.config.ts", "workdocs/reports/jest.coverage.config.ts"],
|
|
3557
|
-
typescript: ["tsconfig.json"],
|
|
3558
|
-
docker: ["Dockerfile"],
|
|
3559
|
-
automation: [
|
|
3560
|
-
"workdocs/confluence/Continuous Integration-Deployment/GitHub.md",
|
|
3561
|
-
"workdocs/confluence/Continuous Integration-Deployment/Jira.md",
|
|
3562
|
-
"workdocs/confluence/Continuous Integration-Deployment/Teams.md",
|
|
3563
|
-
],
|
|
3564
|
-
};
|
|
3565
|
-
const argzz = {
|
|
3566
|
-
// init attributes
|
|
3567
|
-
boot: {
|
|
3568
|
-
type: "boolean",
|
|
3569
|
-
},
|
|
3570
|
-
org: {
|
|
3571
|
-
type: "string",
|
|
3572
|
-
short: "o",
|
|
3573
|
-
},
|
|
3574
|
-
name: {
|
|
3575
|
-
type: "string",
|
|
3576
|
-
short: "n",
|
|
3577
|
-
default: undefined,
|
|
3578
|
-
},
|
|
3579
|
-
author: {
|
|
3580
|
-
type: "string",
|
|
3581
|
-
short: "a",
|
|
3582
|
-
default: undefined,
|
|
3583
|
-
},
|
|
3584
|
-
// update attributes
|
|
3585
|
-
all: {
|
|
3586
|
-
type: "boolean",
|
|
3587
|
-
},
|
|
3588
|
-
license: {
|
|
3589
|
-
type: "string",
|
|
3590
|
-
message: "Pick the license",
|
|
3591
|
-
},
|
|
3592
|
-
scripts: {
|
|
3593
|
-
type: "boolean",
|
|
3594
|
-
},
|
|
3595
|
-
styles: {
|
|
3596
|
-
type: "boolean",
|
|
3597
|
-
},
|
|
3598
|
-
docs: {
|
|
3599
|
-
type: "boolean",
|
|
3600
|
-
},
|
|
3601
|
-
ide: {
|
|
3602
|
-
type: "boolean",
|
|
3603
|
-
},
|
|
3604
|
-
workflows: {
|
|
3605
|
-
type: "boolean",
|
|
3606
|
-
},
|
|
3607
|
-
templates: {
|
|
3608
|
-
type: "boolean",
|
|
3609
|
-
},
|
|
3610
|
-
typescript: {
|
|
3611
|
-
type: "boolean",
|
|
3612
|
-
},
|
|
3613
|
-
docker: {
|
|
3614
|
-
type: "boolean",
|
|
3615
|
-
},
|
|
3616
|
-
pkg: {
|
|
3617
|
-
type: "boolean",
|
|
3618
|
-
},
|
|
3619
|
-
dependencies: {
|
|
3620
|
-
type: "boolean",
|
|
3621
|
-
},
|
|
3622
|
-
tests: {
|
|
3623
|
-
type: "boolean",
|
|
3624
|
-
},
|
|
3625
|
-
automation: {
|
|
3626
|
-
type: "boolean",
|
|
3627
|
-
},
|
|
3628
|
-
};
|
|
3629
|
-
/**
|
|
3630
|
-
* @class TemplateSync
|
|
3631
|
-
* @extends {Command}
|
|
3632
|
-
* @category scripts
|
|
3633
|
-
* @description A command-line tool for synchronizing project templates and configurations.
|
|
3634
|
-
* @summary This class provides functionality to download and update various project files and configurations from a remote repository.
|
|
3635
|
-
* It supports updating licenses, IDE configurations, scripts, styles, documentation, workflows, and templates.
|
|
3636
|
-
*
|
|
3637
|
-
* @param {CommandOptions} args - The command options for TemplateSync
|
|
3638
|
-
*/
|
|
3639
|
-
class TemplateSync extends Command {
|
|
3640
|
-
constructor() {
|
|
3641
|
-
super("TemplateSync", argzz);
|
|
3642
|
-
this.replacements = {};
|
|
3643
|
-
/**
|
|
3644
|
-
* @description Downloads style configuration files.
|
|
3645
|
-
* @returns {Promise<void>}
|
|
3646
|
-
*/
|
|
3647
|
-
this.getStyles = () => this.downloadOption("styles");
|
|
3648
|
-
/**
|
|
3649
|
-
* @description Downloads template files.
|
|
3650
|
-
* @returns {Promise<void>}
|
|
3651
|
-
*/
|
|
3652
|
-
this.getTemplates = () => this.downloadOption("templates");
|
|
3653
|
-
/**
|
|
3654
|
-
* @description Downloads workflow configuration files.
|
|
3655
|
-
* @returns {Promise<void>}
|
|
3656
|
-
*/
|
|
3657
|
-
this.getWorkflows = () => this.downloadOption("workflows");
|
|
3658
|
-
/**
|
|
3659
|
-
* @description Downloads documentation files.
|
|
3660
|
-
* @returns {Promise<void>}
|
|
3661
|
-
*/
|
|
3662
|
-
this.getDocs = () => this.downloadOption("docs");
|
|
3663
|
-
/**
|
|
3664
|
-
* @description Downloads typescript config files.
|
|
3665
|
-
* @returns {Promise<void>}
|
|
3666
|
-
*/
|
|
3667
|
-
this.getTypescript = () => this.downloadOption("typescript");
|
|
3668
|
-
/**
|
|
3669
|
-
* @description Downloads automation documentation files.
|
|
3670
|
-
* @returns {Promise<void>}
|
|
3671
|
-
*/
|
|
3672
|
-
this.getAutomation = () => this.downloadOption("automation");
|
|
3673
|
-
/**
|
|
3674
|
-
* @description Downloads automation documentation files.
|
|
3675
|
-
* @returns {Promise<void>}
|
|
3676
|
-
*/
|
|
3677
|
-
this.getTests = () => this.downloadOption("tests");
|
|
3678
|
-
/**
|
|
3679
|
-
* @description Downloads docker image files.
|
|
3680
|
-
* @returns {Promise<void>}
|
|
3681
|
-
*/
|
|
3682
|
-
this.getDocker = () => this.downloadOption("docker");
|
|
3683
|
-
}
|
|
3684
|
-
loadValuesFromPackage() {
|
|
3685
|
-
const p = process.cwd();
|
|
3686
|
-
const author = getPackage(p, "author");
|
|
3687
|
-
const scopedName = getPackage(p, "name");
|
|
3688
|
-
let name = scopedName;
|
|
3689
|
-
let org;
|
|
3690
|
-
if (name.startsWith("@")) {
|
|
3691
|
-
const split = name.split("/");
|
|
3692
|
-
name = split[1];
|
|
3693
|
-
org = split[0].replace("@", "");
|
|
3694
|
-
}
|
|
3695
|
-
["Tiago Venceslau", "TiagoVenceslau", "${author}"].forEach((el) => (this.replacements[el] = author));
|
|
3696
|
-
["TS-Workspace", "ts-workspace", "${name}"].forEach((el) => (this.replacements[el] = name));
|
|
3697
|
-
["decaf-ts", "${org}"].forEach((el) => (this.replacements[el] = org || '""'));
|
|
3698
|
-
this.replacements["${org_or_owner}"] = org || name;
|
|
3699
|
-
}
|
|
3700
|
-
/**
|
|
3701
|
-
* @description Downloads files for a specific option category.
|
|
3702
|
-
* @summary This method downloads all files associated with a given option key from the remote repository.
|
|
3703
|
-
* @param {string} key - The key representing the option category to download
|
|
3704
|
-
* @returns {Promise<void>}
|
|
3705
|
-
* @throws {Error} If the specified option key is not found
|
|
3706
|
-
*/
|
|
3707
|
-
async downloadOption(key) {
|
|
3708
|
-
if (!(key in options)) {
|
|
3709
|
-
throw new Error(`Option "${key}" not found in options`);
|
|
3710
|
-
}
|
|
3711
|
-
const files = options[key];
|
|
3712
|
-
for (const file of files) {
|
|
3713
|
-
this.log.info(`Downloading ${file}`);
|
|
3714
|
-
let data = await HttpClient.downloadFile(`${baseUrl}/${file}`);
|
|
3715
|
-
data = logging.patchString(data, this.replacements);
|
|
3716
|
-
writeFile(path__default["default"].join(process.cwd(), file), data);
|
|
3717
|
-
}
|
|
3718
|
-
}
|
|
3719
|
-
/**
|
|
3720
|
-
* @description Downloads and sets up the specified license.
|
|
3721
|
-
* @summary This method downloads the chosen license file, saves it to the project, and updates the package.json license field.
|
|
3722
|
-
* @param {"MIT" | "GPL" | "Apache" | "LGPL" | "AGPL"} license - The license to download and set up
|
|
3723
|
-
* @returns {Promise<void>}
|
|
3724
|
-
*/
|
|
3725
|
-
async getLicense(license) {
|
|
3726
|
-
this.log.info(`Downloading ${license} license`);
|
|
3727
|
-
const url = `${baseUrl}/workdocs/licenses/${license}.md`;
|
|
3728
|
-
let data = await HttpClient.downloadFile(url);
|
|
3729
|
-
data = logging.patchString(data, this.replacements);
|
|
3730
|
-
writeFile(path__default["default"].join(process.cwd(), "LICENSE.md"), data);
|
|
3731
|
-
setPackageAttribute("license", license);
|
|
3732
|
-
}
|
|
3733
|
-
/**
|
|
3734
|
-
* @description Downloads IDE configuration files.
|
|
3735
|
-
* @returns {Promise<void>}
|
|
3736
|
-
*/
|
|
3737
|
-
async getIde() {
|
|
3738
|
-
fs__default["default"].mkdirSync(path__default["default"].join(process.cwd(), ".idea", "runConfigurations"), {
|
|
3739
|
-
recursive: true,
|
|
3740
|
-
});
|
|
3741
|
-
await this.downloadOption("ide");
|
|
3742
|
-
}
|
|
3743
|
-
/**
|
|
3744
|
-
* @description Update npm scripts
|
|
3745
|
-
* @returns {Promise<void>}
|
|
3746
|
-
*/
|
|
3747
|
-
async getScripts() {
|
|
3748
|
-
await this.downloadOption("scripts");
|
|
3749
|
-
this.log.info("please re-run the command");
|
|
3750
|
-
process.exit(0);
|
|
3751
|
-
}
|
|
3752
|
-
async initPackage(pkgName, author, license) {
|
|
3753
|
-
try {
|
|
3754
|
-
const pkg = getPackage();
|
|
3755
|
-
delete pkg[SetupScriptKey];
|
|
3756
|
-
pkg.name = pkgName;
|
|
3757
|
-
pkg.version = "0.0.1";
|
|
3758
|
-
pkg.author = author;
|
|
3759
|
-
pkg.license = license;
|
|
3760
|
-
fs__default["default"].writeFileSync("package.json", JSON.stringify(pkg, null, 2));
|
|
3761
|
-
}
|
|
3762
|
-
catch (e) {
|
|
3763
|
-
throw new Error(`Error fixing package.json: ${e}`);
|
|
3764
|
-
}
|
|
3765
|
-
}
|
|
3766
|
-
async updatePackageScrips() {
|
|
3767
|
-
try {
|
|
3768
|
-
const originalPkg = JSON.parse(await HttpClient.downloadFile(`${baseUrl}/package.json`));
|
|
3769
|
-
const { scripts } = originalPkg;
|
|
3770
|
-
const pkg = getPackage();
|
|
3771
|
-
Object.keys(pkg.scripts).forEach((key) => {
|
|
3772
|
-
if (key in scripts) {
|
|
3773
|
-
const replaced = logging.patchString(scripts[key], this.replacements);
|
|
3774
|
-
if (replaced !== scripts[key]) {
|
|
3775
|
-
pkg.scripts[key] = replaced;
|
|
3776
|
-
}
|
|
3777
|
-
}
|
|
3778
|
-
});
|
|
3779
|
-
pkg["exports"]["require"] = originalPkg["exports"]["require"];
|
|
3780
|
-
pkg["exports"]["import"] = originalPkg["exports"]["import"];
|
|
3781
|
-
pkg["types"] = originalPkg["types"];
|
|
3782
|
-
fs__default["default"].writeFileSync("package.json", JSON.stringify(pkg, null, 2));
|
|
3783
|
-
}
|
|
3784
|
-
catch (e) {
|
|
3785
|
-
throw new Error(`Error fixing package.json scripts: ${e}`);
|
|
3786
|
-
}
|
|
3787
|
-
}
|
|
3788
|
-
async createTokenFiles() {
|
|
3789
|
-
const log = this.log.for(this.createTokenFiles);
|
|
3790
|
-
const gitToken = await UserInput.insistForText("token", "please input your github token", (res) => {
|
|
3791
|
-
return !!res.match(/^ghp_[0-9a-zA-Z]{36}$/g);
|
|
3792
|
-
});
|
|
3793
|
-
Object.values(exports.Tokens).forEach((token) => {
|
|
3794
|
-
try {
|
|
3795
|
-
let status;
|
|
3796
|
-
try {
|
|
3797
|
-
status = fs__default["default"].existsSync(token);
|
|
3798
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3799
|
-
}
|
|
3800
|
-
catch (e) {
|
|
3801
|
-
log.info(`Token file ${token} not found. Creating a new one...`);
|
|
3802
|
-
fs__default["default"].writeFileSync(token, token === ".token" ? gitToken : "");
|
|
3803
|
-
return;
|
|
3804
|
-
}
|
|
3805
|
-
if (!status) {
|
|
3806
|
-
fs__default["default"].writeFileSync(token, token === ".token" ? gitToken : "");
|
|
3807
|
-
}
|
|
3808
|
-
}
|
|
3809
|
-
catch (e) {
|
|
3810
|
-
throw new Error(`Error creating token file ${token}: ${e}`);
|
|
3811
|
-
}
|
|
3812
|
-
});
|
|
3813
|
-
}
|
|
3814
|
-
async getOrg() {
|
|
3815
|
-
const org = await UserInput.askText("Organization", "Enter the organization name (will be used to scope your npm project. leave blank to create a unscoped project):");
|
|
3816
|
-
const confirmation = await UserInput.askConfirmation("Confirm organization", "Is this organization correct?", true);
|
|
3817
|
-
if (!confirmation)
|
|
3818
|
-
return this.getOrg();
|
|
3819
|
-
return org;
|
|
3820
|
-
}
|
|
3821
|
-
async auditFix() {
|
|
3822
|
-
return await runCommand("npm audit fix --force").promise;
|
|
3823
|
-
}
|
|
3824
|
-
patchFiles() {
|
|
3825
|
-
const files = [
|
|
3826
|
-
...fs__default["default"]
|
|
3827
|
-
.readdirSync(path__default["default"].join(process.cwd(), "src"), {
|
|
3828
|
-
recursive: true,
|
|
3829
|
-
withFileTypes: true,
|
|
3830
|
-
})
|
|
3831
|
-
.filter((entry) => entry.isFile())
|
|
3832
|
-
.map((entry) => path__default["default"].join(entry.parentPath, entry.name)),
|
|
3833
|
-
...fs__default["default"]
|
|
3834
|
-
.readdirSync(path__default["default"].join(process.cwd(), "workdocs"), {
|
|
3835
|
-
recursive: true,
|
|
3836
|
-
withFileTypes: true,
|
|
3837
|
-
})
|
|
3838
|
-
.filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
|
|
3839
|
-
.map((entry) => path__default["default"].join(entry.parentPath, entry.name)),
|
|
3840
|
-
path__default["default"].join(process.cwd(), ".gitlab-ci.yml"),
|
|
3841
|
-
path__default["default"].join(process.cwd(), "workdocs", "jsdocs.json"),
|
|
3842
|
-
];
|
|
3843
|
-
for (const file of files) {
|
|
3844
|
-
patchFile(file, this.replacements);
|
|
3845
|
-
}
|
|
3846
|
-
}
|
|
3847
|
-
async updateDependencies() {
|
|
3848
|
-
try {
|
|
3849
|
-
const originalPkg = JSON.parse(await HttpClient.downloadFile(`${baseUrl}/package.json`));
|
|
3850
|
-
const { devDependencies } = originalPkg;
|
|
3851
|
-
const pkg = getPackage();
|
|
3852
|
-
Object.keys(pkg.scripts).forEach((key) => {
|
|
3853
|
-
if (key in devDependencies) {
|
|
3854
|
-
const replaced = devDependencies[key];
|
|
3855
|
-
if (replaced !== devDependencies[key]) {
|
|
3856
|
-
pkg["devDependencies"] =
|
|
3857
|
-
pkg["devDependencies"] || {};
|
|
3858
|
-
pkg["devDependencies"][key] = replaced;
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
|
-
});
|
|
3862
|
-
fs__default["default"].writeFileSync("package.json", JSON.stringify(pkg, null, 2));
|
|
3863
|
-
await runCommand("npm install").promise;
|
|
3864
|
-
}
|
|
3865
|
-
catch (e) {
|
|
3866
|
-
throw new Error(`Error fixing package.json dependencies: ${e}`);
|
|
3867
|
-
}
|
|
3868
|
-
}
|
|
3869
|
-
/**
|
|
3870
|
-
* @description Runs the template synchronization process.
|
|
3871
|
-
* @summary This method orchestrates the downloading of various project components based on the provided arguments.
|
|
3872
|
-
* @param {ParseArgsResult} args - The parsed command-line arguments
|
|
3873
|
-
* @returns {Promise<void>}
|
|
3874
|
-
*
|
|
3875
|
-
* @mermaid
|
|
3876
|
-
* sequenceDiagram
|
|
3877
|
-
* participant T as TemplateSync
|
|
3878
|
-
* participant L as getLicense
|
|
3879
|
-
* participant I as getIde
|
|
3880
|
-
* participant S as getScripts
|
|
3881
|
-
* participant St as getStyles
|
|
3882
|
-
* participant D as getDocs
|
|
3883
|
-
* participant W as getWorkflows
|
|
3884
|
-
* participant Te as getTemplates
|
|
3885
|
-
* T->>T: Parse arguments
|
|
3886
|
-
* alt all flag is true
|
|
3887
|
-
* T->>T: Set all component flags to true
|
|
3888
|
-
* end
|
|
3889
|
-
* alt license is specified
|
|
3890
|
-
* T->>L: getLicense(license)
|
|
3891
|
-
* end
|
|
3892
|
-
* alt ide flag is true
|
|
3893
|
-
* T->>I: getIde()
|
|
3894
|
-
* end
|
|
3895
|
-
* alt scripts flag is true
|
|
3896
|
-
* T->>S: getScripts()
|
|
3897
|
-
* end
|
|
3898
|
-
* alt styles flag is true
|
|
3899
|
-
* T->>St: getStyles()
|
|
3900
|
-
* end
|
|
3901
|
-
* alt docs flag is true
|
|
3902
|
-
* T->>D: getDocs()
|
|
3903
|
-
* end
|
|
3904
|
-
* alt workflows flag is true
|
|
3905
|
-
* T->>W: getWorkflows()
|
|
3906
|
-
* end
|
|
3907
|
-
* alt templates flag is true
|
|
3908
|
-
* T->>Te: getTemplates()
|
|
3909
|
-
* end
|
|
3910
|
-
*/
|
|
3911
|
-
async run(args) {
|
|
3912
|
-
let { license } = args;
|
|
3913
|
-
const { boot } = args;
|
|
3914
|
-
let { all, scripts, styles, docs, ide, workflows, templates, docker, typescript, dependencies, tests, automation, pkg, } = args;
|
|
3915
|
-
if (scripts ||
|
|
3916
|
-
styles ||
|
|
3917
|
-
docs ||
|
|
3918
|
-
ide ||
|
|
3919
|
-
workflows ||
|
|
3920
|
-
templates ||
|
|
3921
|
-
docker ||
|
|
3922
|
-
typescript ||
|
|
3923
|
-
automation ||
|
|
3924
|
-
dependencies ||
|
|
3925
|
-
tests ||
|
|
3926
|
-
pkg)
|
|
3927
|
-
all = false;
|
|
3928
|
-
if (boot) {
|
|
3929
|
-
const org = await this.getOrg();
|
|
3930
|
-
const name = await UserInput.insistForText("Project name", "Enter the project name:", (res) => res.length > 1);
|
|
3931
|
-
const author = await UserInput.insistForText("Author", "Enter the author name:", (res) => res.length > 1);
|
|
3932
|
-
const pkgName = org ? `@${org}/${name}` : name;
|
|
3933
|
-
await this.initPackage(pkgName, author, license);
|
|
3934
|
-
await this.createTokenFiles();
|
|
3935
|
-
await this.auditFix();
|
|
3936
|
-
this.patchFiles();
|
|
3937
|
-
}
|
|
3938
|
-
if (all) {
|
|
3939
|
-
scripts = false;
|
|
3940
|
-
styles = true;
|
|
3941
|
-
docs = true;
|
|
3942
|
-
ide = true;
|
|
3943
|
-
workflows = true;
|
|
3944
|
-
templates = true;
|
|
3945
|
-
docker = true;
|
|
3946
|
-
typescript = true;
|
|
3947
|
-
pkg = true;
|
|
3948
|
-
dependencies = true;
|
|
3949
|
-
tests = true;
|
|
3950
|
-
automation = false;
|
|
3951
|
-
}
|
|
3952
|
-
if (typeof scripts === "undefined")
|
|
3953
|
-
scripts = await UserInput.askConfirmation("scripts", "Do you want to get scripts?", true);
|
|
3954
|
-
if (scripts)
|
|
3955
|
-
await this.getScripts();
|
|
3956
|
-
this.loadValuesFromPackage();
|
|
3957
|
-
if (!all && typeof license === "undefined") {
|
|
3958
|
-
const confirmation = await UserInput.askConfirmation("license", "Do you want to set a license?", true);
|
|
3959
|
-
if (confirmation)
|
|
3960
|
-
license = await UserInput.insistForText("license", "Enter the desired License (MIT|GPL|Apache|LGPL|AGPL):", (val) => !!val && !!val.match(/^(MIT|GPL|Apache|LGPL|AGPL)$/g));
|
|
3961
|
-
}
|
|
3962
|
-
if (typeof license !== "undefined")
|
|
3963
|
-
await this.getLicense(license);
|
|
3964
|
-
if (typeof ide === "undefined")
|
|
3965
|
-
ide = await UserInput.askConfirmation("ide", "Do you want to get ide configs?", true);
|
|
3966
|
-
if (ide)
|
|
3967
|
-
await this.getIde();
|
|
3968
|
-
if (typeof typescript === "undefined")
|
|
3969
|
-
typescript = await UserInput.askConfirmation("typescript", "Do you want to get typescript configs?", true);
|
|
3970
|
-
if (typescript)
|
|
3971
|
-
await this.getTypescript();
|
|
3972
|
-
if (typeof docker === "undefined")
|
|
3973
|
-
docker = await UserInput.askConfirmation("docker", "Do you want to get docker configs?", true);
|
|
3974
|
-
if (docker)
|
|
3975
|
-
await this.getDocker();
|
|
3976
|
-
if (typeof automation === "undefined")
|
|
3977
|
-
automation = await UserInput.askConfirmation("automation", "Do you want to get automation configs?", true);
|
|
3978
|
-
if (automation)
|
|
3979
|
-
await this.getAutomation();
|
|
3980
|
-
if (typeof styles === "undefined")
|
|
3981
|
-
styles = await UserInput.askConfirmation("styles", "Do you want to get styles?", true);
|
|
3982
|
-
if (styles)
|
|
3983
|
-
await this.getStyles();
|
|
3984
|
-
if (typeof docs === "undefined")
|
|
3985
|
-
docs = await UserInput.askConfirmation("docs", "Do you want to get docs?", true);
|
|
3986
|
-
if (docs)
|
|
3987
|
-
await this.getDocs();
|
|
3988
|
-
if (typeof workflows === "undefined")
|
|
3989
|
-
workflows = await UserInput.askConfirmation("workflows", "Do you want to get workflows?", true);
|
|
3990
|
-
if (workflows)
|
|
3991
|
-
await this.getWorkflows();
|
|
3992
|
-
if (typeof templates === "undefined")
|
|
3993
|
-
templates = await UserInput.askConfirmation("templates", "Do you want to get templates?", true);
|
|
3994
|
-
if (templates)
|
|
3995
|
-
await this.getTemplates();
|
|
3996
|
-
if (typeof pkg === "undefined")
|
|
3997
|
-
pkg = await UserInput.askConfirmation("pkg", "Do you want to update your package.json scripts?", true);
|
|
3998
|
-
if (pkg)
|
|
3999
|
-
await this.updatePackageScrips();
|
|
4000
|
-
if (typeof tests === "undefined")
|
|
4001
|
-
tests = await UserInput.askConfirmation("pkg", "Do you want to update your test configs?", true);
|
|
4002
|
-
if (tests)
|
|
4003
|
-
await this.getTests();
|
|
4004
|
-
if (typeof dependencies === "undefined")
|
|
4005
|
-
dependencies = await UserInput.askConfirmation("pkg", "Do you want to update dev dependencies?", true);
|
|
4006
|
-
if (dependencies)
|
|
4007
|
-
await this.updateDependencies();
|
|
4008
|
-
}
|
|
4009
|
-
}
|
|
4010
|
-
|
|
4011
|
-
/**
|
|
4012
|
-
* @description A specialized output writer that uses regular expressions to process output.
|
|
4013
|
-
* @summary This class extends StandardOutputWriter to provide regex-based output processing.
|
|
4014
|
-
* It allows for pattern matching in the output stream and can trigger specific actions
|
|
4015
|
-
* based on matched patterns.
|
|
4016
|
-
*
|
|
4017
|
-
* @template T - The type of the resolved value, defaulting to string.
|
|
4018
|
-
*
|
|
4019
|
-
* @param cmd - The command string to be executed.
|
|
4020
|
-
* @param lock - A PromiseExecutor to control the asynchronous flow.
|
|
4021
|
-
* @param regexp - A string or RegExp to match against the output.
|
|
4022
|
-
* @param flags - Optional flags for the RegExp constructor, defaults to "g".
|
|
4023
|
-
*
|
|
4024
|
-
* @class
|
|
4025
|
-
* @example
|
|
4026
|
-
* ```typescript
|
|
4027
|
-
* import { RegexpOutputWriter } from '@decaf-ts/utils';
|
|
4028
|
-
* import { PromiseExecutor } from '@decaf-ts/utils';
|
|
4029
|
-
*
|
|
4030
|
-
* // Create a promise executor
|
|
4031
|
-
* const executor: PromiseExecutor<string, Error> = {
|
|
4032
|
-
* resolve: (value) => console.log(`Resolved: ${value}`),
|
|
4033
|
-
* reject: (error) => console.error(`Rejected: ${error.message}`)
|
|
4034
|
-
* };
|
|
4035
|
-
*
|
|
4036
|
-
* // Create a regexp output writer that matches version numbers
|
|
4037
|
-
* const writer = new RegexpOutputWriter('node --version', executor, /v(\d+\.\d+\.\d+)/);
|
|
4038
|
-
*
|
|
4039
|
-
* // Use the writer to handle command output
|
|
4040
|
-
* writer.data('v14.17.0'); // This will automatically resolve with "v14.17.0"
|
|
4041
|
-
* ```
|
|
4042
|
-
*
|
|
4043
|
-
* @mermaid
|
|
4044
|
-
* sequenceDiagram
|
|
4045
|
-
* participant Client
|
|
4046
|
-
* participant RegexpOutputWriter
|
|
4047
|
-
* participant StandardOutputWriter
|
|
4048
|
-
* participant Logger
|
|
4049
|
-
*
|
|
4050
|
-
* Client->>RegexpOutputWriter: new RegexpOutputWriter(cmd, lock, regexp, flags)
|
|
4051
|
-
* RegexpOutputWriter->>StandardOutputWriter: super(cmd, lock)
|
|
4052
|
-
* StandardOutputWriter->>Logger: Logging.for(cmd)
|
|
4053
|
-
* RegexpOutputWriter->>RegexpOutputWriter: compile regexp
|
|
4054
|
-
*
|
|
4055
|
-
* Client->>RegexpOutputWriter: data(chunk)
|
|
4056
|
-
* RegexpOutputWriter->>StandardOutputWriter: super.data(chunk)
|
|
4057
|
-
* StandardOutputWriter->>Logger: logger.info(log)
|
|
4058
|
-
* RegexpOutputWriter->>RegexpOutputWriter: testAndResolve(chunk)
|
|
4059
|
-
* RegexpOutputWriter->>RegexpOutputWriter: test(chunk)
|
|
4060
|
-
* alt match found
|
|
4061
|
-
* RegexpOutputWriter->>RegexpOutputWriter: resolve(match[0])
|
|
4062
|
-
* RegexpOutputWriter->>StandardOutputWriter: resolve(match[0])
|
|
4063
|
-
* end
|
|
4064
|
-
*
|
|
4065
|
-
* Client->>RegexpOutputWriter: error(chunk)
|
|
4066
|
-
* RegexpOutputWriter->>StandardOutputWriter: super.error(chunk)
|
|
4067
|
-
* StandardOutputWriter->>Logger: logger.info(log)
|
|
4068
|
-
* RegexpOutputWriter->>RegexpOutputWriter: testAndReject(chunk)
|
|
4069
|
-
* RegexpOutputWriter->>RegexpOutputWriter: test(chunk)
|
|
4070
|
-
* alt match found
|
|
4071
|
-
* RegexpOutputWriter->>RegexpOutputWriter: reject(match[0])
|
|
4072
|
-
* RegexpOutputWriter->>StandardOutputWriter: reject(match[0])
|
|
4073
|
-
* end
|
|
4074
|
-
*/
|
|
4075
|
-
class RegexpOutputWriter extends StandardOutputWriter {
|
|
4076
|
-
constructor(cmd, lock, regexp, flags = "g") {
|
|
4077
|
-
super(cmd, lock);
|
|
4078
|
-
try {
|
|
4079
|
-
this.regexp =
|
|
4080
|
-
typeof regexp === "string" ? new RegExp(regexp, flags) : regexp;
|
|
4081
|
-
}
|
|
4082
|
-
catch (e) {
|
|
4083
|
-
throw new Error(`Invalid regular expression: ${e}`);
|
|
4084
|
-
}
|
|
4085
|
-
}
|
|
4086
|
-
/**
|
|
4087
|
-
* @description Tests the input data against the stored regular expression.
|
|
4088
|
-
* @summary Executes the regular expression on the input data and returns the match result.
|
|
4089
|
-
*
|
|
4090
|
-
* @param data - The string to test against the regular expression.
|
|
4091
|
-
* @return The result of the regular expression execution, or undefined if an error occurs.
|
|
4092
|
-
*/
|
|
4093
|
-
test(data) {
|
|
4094
|
-
this.regexp.lastIndex = 0;
|
|
4095
|
-
let match;
|
|
4096
|
-
try {
|
|
4097
|
-
match = this.regexp.exec(data);
|
|
4098
|
-
}
|
|
4099
|
-
catch (e) {
|
|
4100
|
-
return console.debug(`Failed to parse chunk: ${data}\nError: ${e} `);
|
|
4101
|
-
}
|
|
4102
|
-
return match;
|
|
4103
|
-
}
|
|
4104
|
-
/**
|
|
4105
|
-
* @description Tests the data and resolves the promise if a match is found.
|
|
4106
|
-
* @summary Executes the test method and resolves the promise with the first match group if successful.
|
|
4107
|
-
*
|
|
4108
|
-
* @param data - The string to test against the regular expression.
|
|
4109
|
-
*/
|
|
4110
|
-
testAndResolve(data) {
|
|
4111
|
-
const match = this.test(data);
|
|
4112
|
-
if (match)
|
|
4113
|
-
this.resolve(match[0]);
|
|
4114
|
-
}
|
|
4115
|
-
/**
|
|
4116
|
-
* @description Tests the data and rejects the promise if a match is found.
|
|
4117
|
-
* @summary Executes the test method and rejects the promise with the first match group if successful.
|
|
4118
|
-
*
|
|
4119
|
-
* @param data - The string to test against the regular expression.
|
|
4120
|
-
*/
|
|
4121
|
-
testAndReject(data) {
|
|
4122
|
-
const match = this.test(data);
|
|
4123
|
-
if (match)
|
|
4124
|
-
this.reject(match[0]);
|
|
4125
|
-
}
|
|
4126
|
-
/**
|
|
4127
|
-
* @description Processes incoming data chunks.
|
|
4128
|
-
* @summary Calls the parent class data method and then tests the data for a match to potentially resolve the promise.
|
|
4129
|
-
*
|
|
4130
|
-
* @param chunk - The data chunk to process.
|
|
4131
|
-
*/
|
|
4132
|
-
data(chunk) {
|
|
4133
|
-
super.data(chunk);
|
|
4134
|
-
this.testAndResolve(String(chunk));
|
|
4135
|
-
}
|
|
4136
|
-
/**
|
|
4137
|
-
* @description Processes incoming error chunks.
|
|
4138
|
-
* @summary Calls the parent class error method and then tests the data for a match to potentially reject the promise.
|
|
4139
|
-
*
|
|
4140
|
-
* @param chunk - The error chunk to process.
|
|
4141
|
-
*/
|
|
4142
|
-
error(chunk) {
|
|
4143
|
-
super.error(chunk);
|
|
4144
|
-
this.testAndReject(String(chunk));
|
|
4145
|
-
}
|
|
4146
|
-
}
|
|
4147
|
-
|
|
4148
|
-
/**
|
|
4149
|
-
* @module utils
|
|
4150
|
-
* @description Utilities and building blocks for Decaf-TS CLI and scripting.
|
|
4151
|
-
* @summary Aggregates CLI command infrastructure, input helpers, output writers, filesystem/network utilities,
|
|
4152
|
-
* and shared types. Consumers typically use {@link Command}, {@link UserInput}, {@link StandardOutputWriter},
|
|
4153
|
-
* {@link printBanner}, and utilities from {@link module:utils|utils}.
|
|
4154
|
-
*
|
|
4155
|
-
* This entrypoint re-exports subpackages:
|
|
4156
|
-
* - CLI framework under `./cli` (command base, options, built-in commands)
|
|
4157
|
-
* - Input helpers under `./input` (prompting and arg parsing)
|
|
4158
|
-
* - Output helpers under `./output` (banner and styling)
|
|
4159
|
-
* - General utilities under `./utils` (fs, http, exec, types)
|
|
4160
|
-
* - Writers under `./writers` (stdout/stderr processors)
|
|
4161
|
-
*
|
|
4162
|
-
* Note: Individual exports are documented in their source files.
|
|
4163
|
-
*/
|
|
4164
|
-
/**
|
|
4165
|
-
* @description Represents the current version of the module.
|
|
4166
|
-
* @summary Stores the version for the @decaf-ts/utils package. The build replaces
|
|
4167
|
-
* the placeholder with the actual version number at publish time.
|
|
4168
|
-
* @const VERSION
|
|
4169
|
-
* @memberOf module:utils
|
|
4170
|
-
*/
|
|
4171
|
-
const VERSION = "0.4.1";
|
|
4172
|
-
/**
|
|
4173
|
-
* @description Represents the current version of the module.
|
|
4174
|
-
* @summary Stores the version for the @decaf-ts/utils package. The build replaces
|
|
4175
|
-
* the placeholder with the actual version number at publish time.
|
|
4176
|
-
* @const VERSION
|
|
4177
|
-
* @memberOf module:utils
|
|
4178
|
-
*/
|
|
4179
|
-
const PACKAGE_NAME = "@decaf-ts/utils";
|
|
4180
|
-
|
|
4181
|
-
exports.AbortCode = AbortCode;
|
|
4182
|
-
exports.BuildScripts = BuildScripts;
|
|
4183
|
-
exports.Command = Command;
|
|
4184
|
-
exports.DefaultCommandOptions = DefaultCommandOptions;
|
|
4185
|
-
exports.DefaultCommandValues = DefaultCommandValues;
|
|
4186
|
-
exports.Encoding = Encoding;
|
|
4187
|
-
exports.HttpClient = HttpClient;
|
|
4188
|
-
exports.NoCIFLag = NoCIFLag;
|
|
4189
|
-
exports.PACKAGE_NAME = PACKAGE_NAME;
|
|
4190
|
-
exports.RegexpOutputWriter = RegexpOutputWriter;
|
|
4191
|
-
exports.ReleaseScript = ReleaseScript;
|
|
4192
|
-
exports.SemVersionRegex = SemVersionRegex;
|
|
4193
|
-
exports.SetupScriptKey = SetupScriptKey;
|
|
4194
|
-
exports.StandardOutputWriter = StandardOutputWriter;
|
|
4195
|
-
exports.TemplateSync = TemplateSync;
|
|
4196
|
-
exports.UserInput = UserInput;
|
|
4197
|
-
exports.VERSION = VERSION;
|
|
4198
|
-
exports.chainAbortController = chainAbortController;
|
|
4199
|
-
exports.copyFile = copyFile;
|
|
4200
|
-
exports.deletePath = deletePath;
|
|
4201
|
-
exports.getAllFiles = getAllFiles;
|
|
4202
|
-
exports.getDependencies = getDependencies;
|
|
4203
|
-
exports.getPackage = getPackage;
|
|
4204
|
-
exports.getPackageDependencies = getPackageDependencies;
|
|
4205
|
-
exports.getPackageVersion = getPackageVersion;
|
|
4206
|
-
exports.getSlogan = getSlogan;
|
|
4207
|
-
exports.installDependencies = installDependencies;
|
|
4208
|
-
exports.installIfNotAvailable = installIfNotAvailable;
|
|
4209
|
-
exports.lockify = lockify;
|
|
4210
|
-
exports.normalizeImport = normalizeImport;
|
|
4211
|
-
exports.packageToGlobal = packageToGlobal;
|
|
4212
|
-
exports.parseList = parseList;
|
|
4213
|
-
exports.patchFile = patchFile;
|
|
4214
|
-
exports.printBanner = printBanner;
|
|
4215
|
-
exports.pushToGit = pushToGit;
|
|
4216
|
-
exports.readFile = readFile;
|
|
4217
|
-
exports.renameFile = renameFile;
|
|
4218
|
-
exports.runCommand = runCommand;
|
|
4219
|
-
exports.setPackageAttribute = setPackageAttribute;
|
|
4220
|
-
exports.spawnCommand = spawnCommand;
|
|
4221
|
-
exports.updateDependencies = updateDependencies;
|
|
4222
|
-
exports.writeFile = writeFile;
|
|
4223
|
-
|
|
4224
|
-
}));
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("prompts"),require("util"),require("@decaf-ts/logging"),require("fs"),require("path"),require("child_process"),require("styled-string-builder"),require("https"),require("rollup"),require("@rollup/plugin-typescript"),require("@rollup/plugin-commonjs"),require("@rollup/plugin-node-resolve"),require("@rollup/plugin-json"),require("module"),require("typescript")):"function"==typeof define&&define.amd?define(["exports","prompts","util","@decaf-ts/logging","fs","path","child_process","styled-string-builder","https","rollup","@rollup/plugin-typescript","@rollup/plugin-commonjs","@rollup/plugin-node-resolve","@rollup/plugin-json","module","typescript"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).utils={},e.prompts,e.util,e.decafTsLogging,e.fs,e.path,e.childProcess,e.styledStringBuilder,e.https,e.rollup,e.rollupPluginTypescript,e.rollupPluginCommonjs,e.rollupPluginNodeResolve,e.rollupPluginJson,e.module,e.typescript)}(this,function(e,t,o,a,n,s,i,r,l,c,g,d,f,u,h,p){"use strict";function m(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function S(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach(function(o){if("default"!==o){var a=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,a.get?a:{enumerable:!0,get:function(){return e[o]}})}}),t.default=e,Object.freeze(t)}var y=m(t),w=m(n),T=m(s),b=m(l),C=m(g),v=m(d),D=m(u),k=S(p);class ${static{this.logger=a.Logging.for($)}constructor(e){this.type="text",this.name=e}setType(e){return $.logger.verbose(`Setting type to: ${e}`),this.type=e,this}setMessage(e){return $.logger.verbose(`Setting message to: ${e}`),this.message=e,this}setInitial(e){return $.logger.verbose(`Setting initial value to: ${e}`),this.initial=e,this}setStyle(e){return $.logger.verbose(`Setting style to: ${e}`),this.style=e,this}setFormat(e){return $.logger.verbose("Setting format function"),this.format=e,this}setValidate(e){return $.logger.verbose("Setting validate function"),this.validate=e,this}setOnState(e){return $.logger.verbose("Setting onState callback"),this.onState=e,this}setMin(e){return $.logger.verbose(`Setting min value to: ${e}`),this.min=e,this}setMax(e){return $.logger.verbose(`Setting max value to: ${e}`),this.max=e,this}setFloat(e){return $.logger.verbose(`Setting float to: ${e}`),this.float=e,this}setRound(e){return $.logger.verbose(`Setting round to: ${e}`),this.round=e,this}setInstructions(e){return $.logger.verbose(`Setting instructions to: ${e}`),this.instructions=e,this}setIncrement(e){return $.logger.verbose(`Setting increment to: ${e}`),this.increment=e,this}setSeparator(e){return $.logger.verbose(`Setting separator to: ${e}`),this.separator=e,this}setActive(e){return $.logger.verbose(`Setting active style to: ${e}`),this.active=e,this}setInactive(e){return $.logger.verbose(`Setting inactive style to: ${e}`),this.inactive=e,this}setChoices(e){return $.logger.verbose(`Setting choices: ${JSON.stringify(e)}`),this.choices=e,this}setHint(e){return $.logger.verbose(`Setting hint to: ${e}`),this.hint=e,this}setWarn(e){return $.logger.verbose(`Setting warn to: ${e}`),this.warn=e,this}setSuggest(e){return $.logger.verbose("Setting suggest function"),this.suggest=e,this}setLimit(e){return $.logger.verbose(`Setting limit to: ${e}`),this.limit=e,this}setMask(e){return $.logger.verbose(`Setting mask to: ${e}`),this.mask=e,this}setStdout(e){return $.logger.verbose("Setting stdout stream"),this.stdout=e,this}setStdin(e){return this.stdin=e,this}async ask(){return(await $.ask(this))[this.name]}static async ask(e){const t=$.logger.for(this.ask);let o;Array.isArray(e)||(e=[e]);try{t.verbose(`Asking questions: ${e.map(e=>e.name).join(", ")}`),o=await y.default(e),t.verbose(`Received answers: ${JSON.stringify(o,null,2)}`)}catch(e){throw new Error(`Error while getting input: ${e}`)}return o}static async askNumber(e,t,o,a,n){$.logger.for(this.askNumber).verbose(`Asking number input: undefined, question: ${t}, min: ${o}, max: ${a}, initial: ${n}`);const s=new $(e).setMessage(t).setType("number");return"number"==typeof o&&s.setMin(o),"number"==typeof a&&s.setMax(a),"number"==typeof n&&s.setInitial(n),(await this.ask(s))[e]}static async askText(e,t,o=void 0,a){$.logger.for(this.askText).verbose(`Asking text input: undefined, question: ${t}, mask: ${o}, initial: ${a}`);const n=new $(e).setMessage(t);return o&&n.setMask(o),"string"==typeof a&&n.setInitial(a),(await this.ask(n))[e]}static async askConfirmation(e,t,o){$.logger.for(this.askConfirmation).verbose(`Asking confirmation input: undefined, question: ${t}, initial: ${o}`);const a=new $(e).setMessage(t).setType("confirm");return void 0!==o&&a.setInitial(o),(await this.ask(a))[e]}static async insist(e,t,o,a=1){const n=$.logger.for(this.insist);let s;n.verbose(`Insisting on input: ${e.name}, test: ${t.toString()}, defaultConfirmation: ${o}, limit: ${a}`);let i,r=0;try{do{s=(await $.ask(e))[e.name],t(s)?(i=await $.askConfirmation(`${e.name}-confirm`,`Is the ${e.type} correct?`,o),i||(s=void 0)):s=void 0}while(void 0===s&&a>1&&r++<a)}catch(e){throw n.error(`Error while insisting: ${e}`),e}return void 0===s&&n.info("no selection..."),s}static async insistForText(e,t,o,a=void 0,n,s=!1,i=-1){$.logger.for(this.insistForText).verbose(`Insisting for text input: undefined, question: ${t}, test: ${o.toString()}, mask: ${a}, initial: ${n}, defaultConfirmation: ${s}, limit: ${i}`);const r=new $(e).setMessage(t);return a&&r.setMask(a),"string"==typeof n&&r.setInitial(n),await this.insist(r,o,s,i)}static async insistForNumber(e,t,o,a,n,s,i=!1,r=-1){$.logger.for(this.insistForNumber).verbose(`Insisting for number input: undefined, question: ${t}, test: ${o.toString()}, min: ${a}, max: ${n}, initial: ${s}, defaultConfirmation: ${i}, limit: ${r}`);const l=new $(e).setMessage(t).setType("number");return"number"==typeof a&&l.setMin(a),"number"==typeof n&&l.setMax(n),"number"==typeof s&&l.setInitial(s),await this.insist(l,o,i,r)}static parseArgs(e){const t=$.logger.for(this.parseArgs),a={args:process.argv.slice(2),options:e};t.debug(`Parsing arguments: ${JSON.stringify(a,null,2)}`);try{return o.parseArgs(a)}catch(o){throw t.debug(`Error while parsing arguments:\n${JSON.stringify(a,null,2)}\n | options\n${JSON.stringify(e,null,2)}\n | ${o}`),new Error(`Error while parsing arguments: ${o}`)}}}const E={verbose:{type:"boolean",short:"V",default:void 0},version:{type:"boolean",short:"v",default:void 0},help:{type:"boolean",short:"h",default:!1},logLevel:{type:"string",default:"info"},logStyle:{type:"boolean",default:!0},timestamp:{type:"boolean",default:!0},banner:{type:"boolean",default:!0}},I=Object.keys(E).reduce((e,t)=>(e[t]=E[t].default,e),{}),F="utf-8",j=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z])))/g;var x;e.SemVersion=void 0,(x=e.SemVersion||(e.SemVersion={})).PATCH="patch",x.MINOR="minor",x.MAJOR="major";const A="-no-ci",N="postinstall";var P;e.Tokens=void 0,(P=e.Tokens||(e.Tokens={})).GIT=".token",P.NPM=".npmtoken",P.DOCKER=".dockertoken",P.CONFLUENCE=".confluence-token";const O="Aborted";class L{constructor(e,t,...o){this.cmd=e,this.lock=t,this.logger=a.Logging.for(this.cmd)}log(e,t){t=Buffer.isBuffer(t)?t.toString(F):t;const o="stderr"===e?r.style(t).red.text:t;this.logger.info(o)}data(e){this.log("stdout",String(e))}error(e){this.log("stderr",String(e))}errors(e){this.log("stderr",`Error executing command exited : ${e}`)}exit(e,t){this.log("stdout",`command exited code : ${0===e?r.style(e.toString()).green.text:r.style(null===e?"null":e.toString()).red.text}`),0===e?this.resolve(t.map(e=>e.trim()).join("\n")):this.reject(new Error(t.length?t.join("\n"):e.toString()))}parseCommand(e){return e="string"==typeof e?e.split(" "):e,this.cmd=e.join(" "),[e[0],e.slice(1)]}resolve(e){this.log("stdout",`${this.cmd} executed successfully: ${r.style(e?"ran to completion":e).green}`),this.lock.resolve(e)}reject(e){e instanceof Error||(e=new Error("number"==typeof e?`Exit code ${e}`:e)),this.log("stderr",`${this.cmd} failed to execute: ${r.style(e.message).red}`),this.lock.reject(e)}}function B(e,...t){let o,a;if(e instanceof AbortSignal?(a=new AbortController,o=[e,...t]):(a=e,o=t),a.signal.aborted)return a;const n=()=>a.abort();for(const e of o){if(e.aborted){a.abort();break}e.addEventListener("abort",n,{once:!0,signal:a.signal})}return a}function M(e,t,o,a,n){function s(t,a){const[s,r]=e.parseCommand(t);n.info(`Running command: ${s}`),n.debug(`with args: ${r.join(" ")}`);const l=i.spawn(s,r,{...o,cwd:o.cwd||process.cwd(),env:Object.assign({},process.env,o.env,{PATH:process.env.PATH}),shell:o.shell||!1,signal:a.signal});return n.verbose(`pid : ${l.pid}`),l}const r=t.match(/[<>$#]/g);if(r)throw new Error(`Invalid command: ${t}. contains invalid characters: ${r}`);if(t.includes(" | ")){const e=t.split(" | "),o=[],n=new Array(e.length);n[0]=a;for(let t=0;t<e.length;t++)0!==t&&(n[t]=B(n[t-1].signal)),o.push(s(e[t],n[t])),0!==t&&o[t-1].stdout.pipe(o[t].stdin);return o[e.length-1]}return s(t,a)}function U(e,t={},o=L,...n){const s=a.Logging.for(U),i=new AbortController,r={abort:i,command:e,logs:[],errs:[]},l=new Promise((a,l)=>{let c;try{c=new o(e,{resolve:a,reject:l},...n),r.cmd=M(c,e,t,i,s)}catch(t){return l(new Error(`Error running command ${e}: ${t}`))}r.cmd.stdout.setEncoding("utf8"),r.cmd.stdout.on("data",e=>{e=e.toString(),r.logs.push(e),c.data(e)}),r.cmd.stderr.on("data",e=>{e=e.toString(),r.errs.push(e),c.error(e)}),r.cmd.once("error",e=>{c.exit(e.message,r.errs)}),r.cmd.once("exit",(e=0)=>{i.signal.aborted&&null===e&&(e=O),c.exit(e,0===e?r.logs:r.errs)})});return Object.assign(r,{promise:l,pipe:async t=>{const o=s.for("pipe");try{o.verbose(`Executing pipe function ${e}...`);const a=await l;return o.verbose(`Piping output to ${t.name}: ${a}`),t(a)}catch(e){throw o.error(`Error piping command output: ${e}`),e}}}),r}const R=a.Logging.for("fs");function J(e,t){const o=R.for(J);if(!w.default.existsSync(e))throw new Error(`File not found at path "${e}".`);let n=V(e);try{o.verbose(`Patching file "${e}"...`),o.debug(`with value: ${JSON.stringify(t)}`),n=a.patchString(n,t)}catch(e){throw new Error(`Error patching file: ${e}`)}q(e,n)}function V(e){const t=R.for(V);try{return t.verbose(`Reading file "${e}"...`),w.default.readFileSync(e,"utf8")}catch(o){throw t.verbose(`Error reading file "${e}": ${o}`),new Error(`Error reading file "${e}": ${o}`)}}function q(e,t){const o=R.for(q);try{o.verbose(`Writing file "${e} with ${t.length} bytes...`),w.default.writeFileSync(e,t,"utf8")}catch(t){throw o.verbose(`Error writing file "${e}": ${t}`),new Error(`Error writing file "${e}": ${t}`)}}function G(e,t){const o=R.for(G),a=[];try{o.verbose(`Retrieving all files from "${e}"...`);return w.default.readdirSync(e).forEach(t=>{const o=T.default.join(e,t),n=w.default.statSync(o);n.isFile()?a.push(o):n.isDirectory()&&a.push(...G(o))}),t?a.filter(t):a}catch(t){throw o.verbose(`Error retrieving files from "${e}": ${t}`),new Error(`Error retrieving files from "${e}": ${t}`)}}async function W(e,t){const o=R.for(W);let a,n;try{a=w.default.statSync(e)}catch(t){throw o.verbose(`Source path "${e}" does not exist: ${t}`),new Error(`Source path "${e}" does not exist: ${t}`)}try{n=w.default.statSync(t)}catch(e){}if(n)throw o.verbose(`Destination path "${t}" already exists`),new Error(`Destination path "${t}" already exists`);try{o.verbose(`Renaming ${a.isFile()?"file":"directory"} "${e}" to "${t}...`),w.default.renameSync(e,t),o.verbose(`Successfully renamed to "${t}"`)}catch(n){throw o.verbose(`Error renaming ${a.isFile()?"file":"directory"} "${e}" to "${t}": ${n}`),new Error(`Error renaming ${a.isFile()?"file":"directory"} "${e}" to "${t}": ${n}`)}}function X(e,t){const o=R.for(X);let a,n;try{a=w.default.statSync(e)}catch(t){throw o.verbose(`Source path "${e}" does not exist: ${t}`),new Error(`Source path "${e}" does not exist: ${t}`)}try{n=w.default.statSync(t)}catch(e){a.isDirectory()&&(o.verbose(`Dest path "${t}" does not exist. creating`),w.default.mkdirSync(t,{recursive:!0}))}try{o.verbose(`Copying ${a.isFile()?"file":"directory"} "${e}" to "${t}...`),w.default.cpSync(e,t,{recursive:!0})}catch(n){throw o.verbose(`Error copying ${a.isFile()?"file":"directory"} "${e}" to "${t}: ${n}`),new Error(`Error copying ${a.isFile()?"file":"directory"} "${e}" to "${t}: ${n}`)}}function _(e){const t=R.for(_);try{const o=w.default.statSync(e);o.isFile()?(t.verbose(`Deleting file "${e}...`),w.default.rmSync(e,{recursive:!0,force:!0})):o.isDirectory()&&w.default.rmSync(e,{recursive:!0,force:!0})}catch(o){throw t.verbose(`Error Deleting "${e}": ${o}`),new Error(`Error Deleting "${e}": ${o}`)}}function H(e=process.cwd(),t){let o;try{o=JSON.parse(V(T.default.join(e,"package.json")))}catch(e){throw new Error(`Failed to retrieve package information" ${e}`)}if(t){if(!(t in o))throw new Error(`Property "${t}" not found in package.json`);return o[t]}return o}function K(e,t,o=process.cwd()){const a=H(o);a[e]=t,q(T.default.join(o,"package.json"),JSON.stringify(a,null,2))}function z(e=process.cwd()){return H(e,"version")}async function Y(e=process.cwd()){let t;try{t=JSON.parse(await U("npm ls --json",{cwd:e}).promise)}catch(e){throw new Error(`Failed to retrieve dependencies: ${e}`)}const o=(e,t)=>({name:e[0],version:e[1].version});return{prod:Object.entries(t.dependencies||{}).map(o),dev:Object.entries(t.devDependencies||{}).map(o),peer:Object.entries(t.peerDependencies||{}).map(o)}}async function Z(e){const t=R.for(Z),o=e.prod||[],a=e.dev||[],n=e.peer||[];o.length&&(t.info(`Installing dependencies ${o.join(", ")}...`),await U(`npm install ${o.join(" ")}`,{cwd:process.cwd()}).promise),a.length&&(t.info(`Installing devDependencies ${a.join(", ")}...`),await U(`npm install --save-dev ${a.join(" ")}`,{cwd:process.cwd()}).promise),n.length&&(t.info(`Installing peerDependencies ${n.join(", ")}...`),await U(`npm install --save-peer ${n.join(" ")}`,{cwd:process.cwd()}).promise)}const Q=[{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That's Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"Decaf-TS: Where smart contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No CRUD, no problem — Decaf your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS: Your frontend already understands your smart contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye CRUD, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From smart contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No CRUD. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"},{Slogan:"Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No boilerplate, no problem — Decaf-TS your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS-TS: Your frontend already understands your blockchain contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye boilerplate, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From blockchain contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No boilerplate. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf-TS.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"},{Slogan:"Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No boilerplate, no problem — Decaf-TS your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS-TS: Your frontend already understands your blockchain contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye boilerplate, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From blockchain contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No boilerplate. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf-TS.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"},{Slogan:"Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No boilerplate, no problem — Decaf-TS your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS-TS: Your frontend already understands your blockchain contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye boilerplate, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From blockchain contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No boilerplate. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf-TS.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"},{Slogan:"Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No boilerplate, no problem — Decaf-TS your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS-TS: Your frontend already understands your blockchain contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye boilerplate, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From blockchain contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No boilerplate. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf-TS.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"}],ee=["[38;5;215m","[38;5;209m","[38;5;205m","[38;5;210m","[38;5;217m","[38;5;216m","[38;5;224m","[38;5;230m","[38;5;230m"];function te(e){const t=oe(),o="# ░▒▓███████▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓██████▓▒░ ░▒▓████████▓▒░ ░▒▓████████▓▒░ ░▒▓███████▓▒░ \n# ( ( ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ \n# ) ) ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ \n# [=======] ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓██████▓▒░ \n# `-----´ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ \n# ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ \n# ░▒▓███████▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓███████▓▒░ \n#".split("\n"),a=o.reduce((e,t)=>Math.max(e,t.length),0);o.push(`# ${t.padStart(a-3)}`),o.forEach((t,o)=>{(e?e.info.bind(e):console.log.bind(console))(r.style(t||"").raw(ee[o]).text)})}function oe(e){try{return e=void 0===e?Math.floor(Math.random()*Q.length):e,Q[e].Slogan}catch(e){throw new Error(`Failed to retrieve slogans: ${e}`)}}class ae extends a.LoggedClass{constructor(e,t={},o=[]){super(),this.name=e,this.inputs=t,this.requirements=o,ae.log||Object.defineProperty(ae,"log",{writable:!1,value:a.Logging.for(ae.name)}),this.inputs=Object.assign({},E,t)}async checkRequirements(){const{prod:e,dev:t,peer:o}=await Y(),a=[],n=Array.from(new Set([...e,...t,...o]).values()).map(e=>e.name);for(const e of this.requirements)n.includes(e)||a.push(e);a.length}help(e){return this.log.info("This is help. I'm no use because I should have been overridden.")}async execute(){const e=$.parseArgs(this.inputs),t=a.LoggedEnvironment.accumulate(I).accumulate(e.values),{version:o,help:n,banner:s}=t;if(o)return z();if(n)return this.help(e);let i;s&&te(this.log.for(te,{timestamp:!1,style:!1,context:!1,logLevel:!1}));try{i=await this.run(t)}catch(e){throw e}return i}}class ne{static{this.log=a.Logging.for(ne)}static async downloadFile(e){return new Promise((t,o)=>{!function e(a){a=encodeURI(a),b.default.get(a,n=>{if(301===n.statusCode||307===n.statusCode)return e(n.headers.location);if(200!==n.statusCode)return ne.log.error(`Failed to fetch ${a} (status: ${n.statusCode})`),o(new Error(`Failed to fetch ${a}`));let s="";n.on("data",e=>{s+=e}),n.on("error",e=>{o(e)}),n.on("end",()=>{t(s)})})}(e)})}}function se(e){return e?Array.isArray(e)?e.map(e=>`${e}`.trim()).filter(Boolean):`${e}`.split(",").map(e=>e.trim()).filter(Boolean):[]}function ie(e){return e.replace(/^@/,"").split(/[/\-_.]+/).filter(Boolean).map((e,t)=>0===t?e.replace(/[^a-zA-Z0-9]/g,""):`${e.charAt(0).toUpperCase()}${e.slice(1)}`).join("")}function re(){let e;try{e=H(process.cwd())}catch{e=void 0}try{if(!(e&&(Object.keys(e.dependencies||{}).length>0||Object.keys(e.devDependencies||{}).length>0||Object.keys(e.peerDependencies||{}).length>0))){const t=T.default.resolve(__dirname,"../../..");try{e=H(t)}catch{}}}catch{}const t=Object.keys(e&&e.dependencies||{}),o=Object.keys(e&&e.peerDependencies||{}),a=Object.keys(e&&e.devDependencies||{});return Array.from(new Set([...t,...o,...a]))}var le,ce;!function(e){e.CJS="commonjs",e.ESM="es2022"}(le||(le={})),function(e){e.BUILD="build",e.BUNDLE="bundle",e.ALL="all"}(ce||(ce={}));const ge={prod:{type:"boolean",default:!1},dev:{type:"boolean",default:!1},buildMode:{type:"string",default:ce.ALL},includes:{type:"string",default:""},externals:{type:"string",default:""},docs:{type:"boolean",default:!1},commands:{type:"boolean",default:!1},banner:{type:"boolean",default:!1}},de=(e=".cjs")=>{const t=fe.log.for(de),o=new Map;return a=>n=>{const s=T.default.dirname(n.fileName);function i(a){const n=JSON.stringify([s,a]),i=o.get(n);if(null!=i)return i;let r,l=a;try{l=T.default.resolve(s,l+".ts")}catch(e){throw new Error(`Failed to resolve path ${a}: ${e}`)}try{r=w.default.statSync(l)}catch(e){try{t.verbose(`Testing existence of path ${l} as a folder defaulting to index file`),r=w.default.statSync(l.replace(/\.ts$/gm,""))}catch(t){throw new Error(`Failed to resolve path ${a}: ${e}, ${t}`)}}if(r.isDirectory()&&(l=l.replace(/\.ts$/gm,"/index.ts")),T.default.isAbsolute(l)){const t=(/\.tsx?$/.exec(T.default.basename(l))||[])[0]||void 0;l="./"+T.default.relative(s,T.default.resolve(T.default.dirname(l),T.default.basename(l,t)+e))}return o.set(n,l),l}return k.visitNode(n,function e(t){if(function(e){return!(!k.isImportDeclaration(e)&&!k.isExportDeclaration(e))&&(void 0!==e.moduleSpecifier&&(!!k.isStringLiteral(e.moduleSpecifier)&&(!(!e.moduleSpecifier.text.startsWith("./")&&!e.moduleSpecifier.text.startsWith("../"))&&""===T.default.extname(e.moduleSpecifier.text))))}(t)){if(k.isImportDeclaration(t)){const e=i(t.moduleSpecifier.text),o=a.factory.createStringLiteral(e);return a.factory.updateImportDeclaration(t,t.modifiers,t.importClause,o,void 0)}if(k.isExportDeclaration(t)){const e=i(t.moduleSpecifier.text),o=a.factory.createStringLiteral(e);return a.factory.updateExportDeclaration(t,t.modifiers,t.isTypeOnly,t.exportClause,o,void 0)}}return k.visitEachChild(t,e,a)})}};class fe extends ae{constructor(){super("BuildScripts",Object.assign({},E,ge)),this.replacements={};const e=H(),{name:t,version:o}=e;this.pkgName=t.includes("@")?t.split("/")[1]:t,this.pkgVersion=o,this.replacements["0.4.2"]=this.pkgVersion,this.replacements["@decaf-ts/utils"]=t}patchFiles(e){const t=this.log.for(this.patchFiles),{name:o,version:a}=H();t.info(`Patching ${o} ${a} module in ${e}...`);w.default.statSync(e).isDirectory()&&w.default.readdirSync(e,{withFileTypes:!0,recursive:!0}).filter(e=>e.isFile()).forEach(e=>J(T.default.join(e.parentPath,e.name),this.replacements)),t.verbose(`Module ${o} ${a} patched in ${e}...`)}reportDiagnostics(e,t){const o=this.formatDiagnostics(e);if(!Object.values(a.LogLevel).includes(t))throw new Error(`Invalid LogLevel ${t}`);try{this.log[t](o)}catch(e){throw console.warn(`Failed to get logger for ${t}`),e}return o}formatDiagnostics(e){return e.map(e=>{let t="";if(e.file&&e.start){const{line:o,character:a}=e.file.getLineAndCharacterOfPosition(e.start);t+=`${e.file.fileName} (${o+1},${a+1})`}return t+=": "+k.flattenDiagnosticMessageText(e.messageText,"\n"),t}).join("\n")}readConfigFile(e){const t=w.default.readFileSync(e).toString(),o=k.parseConfigFileTextToJson(e,t),n=o.config;n||this.reportDiagnostics([o.error],a.LogLevel.error);const s=k.parseJsonConfigFileContent(n,k.sys,T.default.dirname(e));return s.errors.length>0&&this.reportDiagnostics(s.errors,a.LogLevel.error),s}evalDiagnostics(e){if(e&&e.length>0){const t=e.filter(e=>e.category===k.DiagnosticCategory.Error),o=e.filter(e=>e.category===k.DiagnosticCategory.Warning),n=e.filter(e=>e.category===k.DiagnosticCategory.Suggestion),s=e.filter(e=>e.category===k.DiagnosticCategory.Message);o.length&&this.reportDiagnostics(o,a.LogLevel.info),t.length&&(this.reportDiagnostics(e,a.LogLevel.error),this.log.info(`TypeScript reported ${e.length} diagnostic(s) during check; aborting.`)),n.length&&this.reportDiagnostics(n,a.LogLevel.info),s.length&&this.reportDiagnostics(s,a.LogLevel.info)}}preCheckDiagnostics(e){const t=k.getPreEmitDiagnostics(e);this.evalDiagnostics(t)}async checkTsDiagnostics(e,t,o=!1){const a=this.log.for(this.checkTsDiagnostics);let n;try{n=this.readConfigFile("./tsconfig.json")}catch(e){throw new Error(`Failed to parse tsconfig.json: ${e}`)}o?(n.options.module=p.ModuleKind.AMD,n.options.outDir="dist",n.options.isolatedModules=!1,n.options.outFile=this.pkgName):(n.options.outDir="lib"+(t===le.ESM?"/esm":""),n.options.module=t===le.ESM?p.ModuleKind.ES2022:p.ModuleKind.CommonJS),e?(n.options.inlineSourceMap=!0,n.options.sourceMap=!1):n.options.sourceMap=!1;const s=k.createProgram(n.fileNames,n.options);this.preCheckDiagnostics(s),a.verbose(`TypeScript checks passed (${o?"bundle":"normal"} mode).`)}async buildTs(e,t,o=!1){let a;this.log.for(this.buildTs).info(`Building ${this.pkgName} ${this.pkgVersion} module (${t}) in ${e?"dev":"prod"} mode...`);try{a=this.readConfigFile("./tsconfig.json")}catch(e){throw new Error(`Failed to parse tsconfig.json: ${e}`)}o?(a.options.module=p.ModuleKind.AMD,a.options.outDir="dist",a.options.isolatedModules=!1,a.options.outFile=this.pkgName):(a.options.outDir="lib"+(t===le.ESM?"/esm":""),a.options.module=t===le.ESM?p.ModuleKind.ES2022:p.ModuleKind.CommonJS),e?(a.options.inlineSourceMap=!0,a.options.sourceMap=!1):a.options.sourceMap=!1;const n=k.createProgram(a.fileNames,a.options),s={};t===le.CJS?s.before=[de(".cjs")]:t===le.ESM&&(s.before=[de(".js")]);const i=n.emit(void 0,void 0,void 0,void 0,s),r=k.getPreEmitDiagnostics(n).concat(i.diagnostics);this.evalDiagnostics(r)}async build(e,t,o=!1){const a=this.log.for(this.build);if(await this.buildTs(e,t,o),a.verbose(`Module ${this.pkgName} ${this.pkgVersion} (${t}) built in ${e?"dev":"prod"} mode...`),t===le.CJS&&!o){const e=G("lib",e=>e.endsWith(".js")&&!e.includes("/esm/"));for(const t of e){a.verbose(`Patching ${t}'s cjs imports...`);const e=t.replace(".js",".cjs");await W(t,e)}}}copyAssets(e){const t=this.log.for(this.copyAssets);let o=!1;try{o=w.default.statSync("./src/assets").isDirectory()}catch(e){return t.verbose("No assets found in ./src/assets to copy")}o&&X("./src/assets",`./${e===le.CJS?"lib":"dist"}/assets`)}async bundle(e,t,o,a="src/index.ts",n=this.pkgName,s,i=["prompts","styled-string-builder","typed-object-accumulator","@decaf-ts/logging"]){await this.checkTsDiagnostics(t,e,!0);const r=e===le.ESM,l=this.pkgName,g=Array.from(new Set([...se(i)]));let d=se(s);0===d.length&&(d=re());const u=Array.from(new Set([...function(){try{return Array.isArray(h.builtinModules)?h.builtinModules:[]}catch{return["fs","path","process","child_process","util","https","http","os","stream","crypto","zlib","net","tls","url","querystring","assert","events","tty","dns","querystring"]}}(),...d])),p=[C.default({compilerOptions:{module:"esnext",declaration:!1,outDir:o?"bin":"dist"},include:["src/**/*.ts"],exclude:["node_modules","**/*.spec.ts"],tsconfig:"./tsconfig.json"}),D.default()];if(!t)try{const w=await import("@rollup/plugin-terser"),T=w&&w.terser||w.default||w;p.push(T())}catch{}o&&p.push(v.default({include:[],exclude:d}),f.nodeResolve({resolveOnly:g}));const m={input:a,plugins:p,external:u},S={};u.forEach(e=>{S[e]=ie(e)});const y=[{file:`${o?"bin/":"dist/"}${n||".bundle."+(t?"":"min")}${r?".esm":""}.cjs`,format:o?"cjs":r?"esm":"umd",name:l,esModule:r,sourcemap:!!t&&"inline",globals:S,exports:"auto"}];try{const b=await c.rollup(m);async function k(e){for(const t of y)await e.write(t)}console.log(b.watchFiles),await k(b)}catch($){throw new Error(`Failed to bundle: ${$}`)}}async buildByEnv(e,t=ce.ALL,o,a){try{_("lib")}catch(e){}try{_("dist")}catch(e){}w.default.mkdirSync("lib"),w.default.mkdirSync("dist"),[ce.ALL,ce.BUILD].includes(t)&&(await this.build(e,le.ESM),await this.build(e,le.CJS),this.patchFiles("lib")),[ce.ALL,ce.BUNDLE].includes(t)&&(await this.bundle(le.ESM,e,!1,"src/index.ts",this.pkgName,a,o),await this.bundle(le.CJS,e,!1,"src/index.ts",this.pkgName,a,o),this.patchFiles("dist")),this.copyAssets(le.CJS),this.copyAssets(le.ESM)}async buildDev(e=ce.ALL,t,o){return this.buildByEnv(!0,e,t,o)}async buildProd(e=ce.ALL,t,o){return this.buildByEnv(!1,e,t,o)}async buildDocs(){await U("npm install better-docs taffydb").promise,await U("npx markdown-include ./workdocs/readme-md.json").promise,await U("npx jsdoc -c ./workdocs/jsdocs.json -t ./node_modules/better-docs").promise,await U("npm remove better-docs taffydb").promise,[{src:"workdocs/assets",dest:"./docs/workdocs/assets"},{src:"workdocs/reports/coverage",dest:"./docs/workdocs/reports/coverage"},{src:"workdocs/reports/html",dest:"./docs/workdocs/reports/html"},{src:"workdocs/resources",dest:"./docs/workdocs/resources"},{src:"LICENSE.md",dest:"./docs/LICENSE.md"}].forEach(e=>{const{src:t,dest:o}=e;X(t,o)})}async run(e){const{dev:t,prod:o,docs:a,buildMode:n,includes:s,externals:i}=e;return t?await this.buildDev(n,s,i):o?await this.buildProd(n,s,i):a?await this.buildDocs():void 0}}const ue={ci:{type:"boolean",default:!0},message:{type:"string",short:"m"},tag:{type:"string",short:"t",default:void 0}};const he="https://raw.githubusercontent.com/decaf-ts/ts-workspace/master",pe={templates:[".github/ISSUE_TEMPLATE/bug_report.md",".github/ISSUE_TEMPLATE/feature_request.md",".github/FUNDING.yml"],workflows:[".github/workflows/codeql-analysis.yml",".github/workflows/jest-coverage.yaml",".github/workflows/nodejs-build-prod.yaml",".github/workflows/pages.yaml",".github/workflows/publish-on-release.yaml",".github/workflows/release-on-tag.yaml",".github/workflows/snyk-analysis.yaml"],ide:[".idea/runConfigurations/All Tests.run.xml",".idea/runConfigurations/build.run.xml",".idea/runConfigurations/build_prod.run.xml",".idea/runConfigurations/coverage.run.xml",".idea/runConfigurations/docs.run.xml",".idea/runConfigurations/drawings.run.xml",".idea/runConfigurations/flash-forward.run.xml",".idea/runConfigurations/Integration_Tests.run.xml",".idea/runConfigurations/Bundling_Tests.run.xml",".idea/runConfigurations/lint-fix.run.xml",".idea/runConfigurations/release.run.xml",".idea/runConfigurations/test_circular.run.xml",".idea/runConfigurations/uml.run.xml",".idea/runConfigurations/Unit Tests.run.xml",".idea/runConfigurations/update-scripts.run.xml"],docs:["workdocs/tutorials/Contributing.md","workdocs/tutorials/Documentation.md","workdocs/tutorials/For Developers.md","workdocs/2-Badges.md","workdocs/jsdocs.json","workdocs/readme-md.json"],styles:[".prettierrc","eslint.config.js"],scripts:["bin/update-scripts.cjs","bin/tag-release.cjs","bin/build-scripts.cjs"],tests:["jest.config.ts","workdocs/reports/jest.coverage.config.ts"],typescript:["tsconfig.json"],docker:["Dockerfile"],automation:["workdocs/confluence/Continuous Integration-Deployment/GitHub.md","workdocs/confluence/Continuous Integration-Deployment/Jira.md","workdocs/confluence/Continuous Integration-Deployment/Teams.md"]},me={boot:{type:"boolean"},org:{type:"string",short:"o"},name:{type:"string",short:"n",default:void 0},author:{type:"string",short:"a",default:void 0},all:{type:"boolean"},license:{type:"string",message:"Pick the license"},scripts:{type:"boolean"},styles:{type:"boolean"},docs:{type:"boolean"},ide:{type:"boolean"},workflows:{type:"boolean"},templates:{type:"boolean"},typescript:{type:"boolean"},docker:{type:"boolean"},pkg:{type:"boolean"},dependencies:{type:"boolean"},tests:{type:"boolean"},automation:{type:"boolean"}};e.AbortCode=O,e.BuildScripts=fe,e.Command=ae,e.DefaultCommandOptions=E,e.DefaultCommandValues=I,e.Encoding=F,e.HttpClient=ne,e.NoCIFLag=A,e.PACKAGE_NAME="@decaf-ts/utils",e.RegexpOutputWriter=class extends L{constructor(e,t,o,a="g"){super(e,t);try{this.regexp="string"==typeof o?new RegExp(o,a):o}catch(e){throw new Error(`Invalid regular expression: ${e}`)}}test(e){let t;this.regexp.lastIndex=0;try{t=this.regexp.exec(e)}catch(t){return console.debug(`Failed to parse chunk: ${e}\nError: ${t} `)}return t}testAndResolve(e){const t=this.test(e);t&&this.resolve(t[0])}testAndReject(e){const t=this.test(e);t&&this.reject(t[0])}data(e){super.data(e),this.testAndResolve(String(e))}error(e){super.error(e),this.testAndReject(String(e))}},e.ReleaseScript=class extends ae{constructor(){super("ReleaseScript",ue)}async prepareVersion(e){const t=this.log.for(this.prepareVersion);return(e=this.testVersion(e||""))||(t.verbose("No release message provided. Prompting for one:"),t.info("Listing latest git tags:"),await U("git tag --sort=-taggerdate | head -n 5").promise,await $.insistForText("tag","Enter the new tag number (accepts v*.*.*[-...])",e=>!!e.toString().match(/^v[0-9]+\.[0-9]+.[0-9]+(-[0-9a-zA-Z-]+)?$/)))}testVersion(t){const o=this.log.for(this.testVersion);switch(t=t.trim().toLowerCase()){case e.SemVersion.PATCH:case e.SemVersion.MINOR:case e.SemVersion.MAJOR:return o.verbose(`Using provided SemVer update: ${t}`,1),t;default:return o.verbose(`Testing provided version for SemVer compatibility: ${t}`,1),new RegExp(j).test(t)?(o.verbose(`version approved: ${t}`,1),t):void o.debug(`Invalid version number: ${t}`)}}async prepareMessage(e){const t=this.log.for(this.prepareMessage);return e||(t.verbose("No release message provided. Prompting for one"),await $.insistForText("message","What should be the release message/ticket?",e=>!!e&&e.toString().length>5))}async run(e){let t;const{ci:o}=e;let{tag:a,message:n}=e;a=await this.prepareVersion(a),n=await this.prepareMessage(n),t=await U(`npm run prepare-release -- ${a} ${n}`,{cwd:process.cwd()}).promise,t=await U("git status --porcelain").promise,await t,t.logs.length&&await $.askConfirmation("git-changes","Do you want to push the changes to the remote repository?",!0)&&(await U("git add .").promise,await U(`git commit -m "${a} - ${n} - after release preparation${o?"":A}"`).promise),await U(`npm version "${a}" -m "${n}${o?"":A}"`).promise,await U("git push --follow-tags").promise,o||await U("NPM_TOKEN=$(cat .npmtoken) npm publish --access public").promise}},e.SemVersionRegex=j,e.SetupScriptKey=N,e.StandardOutputWriter=L,e.TemplateSync=class extends ae{constructor(){super("TemplateSync",me),this.replacements={},this.getStyles=()=>this.downloadOption("styles"),this.getTemplates=()=>this.downloadOption("templates"),this.getWorkflows=()=>this.downloadOption("workflows"),this.getDocs=()=>this.downloadOption("docs"),this.getTypescript=()=>this.downloadOption("typescript"),this.getAutomation=()=>this.downloadOption("automation"),this.getTests=()=>this.downloadOption("tests"),this.getDocker=()=>this.downloadOption("docker")}loadValuesFromPackage(){const e=process.cwd(),t=H(e,"author");let o,a=H(e,"name");if(a.startsWith("@")){const e=a.split("/");a=e[1],o=e[0].replace("@","")}["Tiago Venceslau","TiagoVenceslau","${author}"].forEach(e=>this.replacements[e]=t),["TS-Workspace","ts-workspace","${name}"].forEach(e=>this.replacements[e]=a),["decaf-ts","${org}"].forEach(e=>this.replacements[e]=o||'""'),this.replacements["${org_or_owner}"]=o||a}async downloadOption(e){if(!(e in pe))throw new Error(`Option "${e}" not found in options`);const t=pe[e];for(const e of t){this.log.info(`Downloading ${e}`);let t=await ne.downloadFile(`${he}/${e}`);t=a.patchString(t,this.replacements),q(T.default.join(process.cwd(),e),t)}}async getLicense(e){this.log.info(`Downloading ${e} license`);const t=`${he}/workdocs/licenses/${e}.md`;let o=await ne.downloadFile(t);o=a.patchString(o,this.replacements),q(T.default.join(process.cwd(),"LICENSE.md"),o),K("license",e)}async getIde(){w.default.mkdirSync(T.default.join(process.cwd(),".idea","runConfigurations"),{recursive:!0}),await this.downloadOption("ide")}async getScripts(){await this.downloadOption("scripts"),this.log.info("please re-run the command"),process.exit(0)}async initPackage(e,t,o){try{const a=H();delete a[N],a.name=e,a.version="0.0.1",a.author=t,a.license=o,w.default.writeFileSync("package.json",JSON.stringify(a,null,2))}catch(e){throw new Error(`Error fixing package.json: ${e}`)}}async updatePackageScrips(){try{const e=JSON.parse(await ne.downloadFile(`${he}/package.json`)),{scripts:t}=e,o=H();Object.keys(o.scripts).forEach(e=>{if(e in t){const n=a.patchString(t[e],this.replacements);n!==t[e]&&(o.scripts[e]=n)}}),o.exports.require=e.exports.require,o.exports.import=e.exports.import,o.types=e.types,w.default.writeFileSync("package.json",JSON.stringify(o,null,2))}catch(e){throw new Error(`Error fixing package.json scripts: ${e}`)}}async createTokenFiles(){const t=this.log.for(this.createTokenFiles),o=await $.insistForText("token","please input your github token",e=>!!e.match(/^ghp_[0-9a-zA-Z]{36}$/g));Object.values(e.Tokens).forEach(e=>{try{let a;try{a=w.default.existsSync(e)}catch(a){return t.info(`Token file ${e} not found. Creating a new one...`),void w.default.writeFileSync(e,".token"===e?o:"")}a||w.default.writeFileSync(e,".token"===e?o:"")}catch(t){throw new Error(`Error creating token file ${e}: ${t}`)}})}async getOrg(){const e=await $.askText("Organization","Enter the organization name (will be used to scope your npm project. leave blank to create a unscoped project):");return await $.askConfirmation("Confirm organization","Is this organization correct?",!0)?e:this.getOrg()}async auditFix(){return await U("npm audit fix --force").promise}patchFiles(){const e=[...w.default.readdirSync(T.default.join(process.cwd(),"src"),{recursive:!0,withFileTypes:!0}).filter(e=>e.isFile()).map(e=>T.default.join(e.parentPath,e.name)),...w.default.readdirSync(T.default.join(process.cwd(),"workdocs"),{recursive:!0,withFileTypes:!0}).filter(e=>e.isFile()&&e.name.endsWith(".md")).map(e=>T.default.join(e.parentPath,e.name)),T.default.join(process.cwd(),".gitlab-ci.yml"),T.default.join(process.cwd(),"workdocs","jsdocs.json")];for(const t of e)J(t,this.replacements)}async updateDependencies(){try{const e=JSON.parse(await ne.downloadFile(`${he}/package.json`)),{devDependencies:t}=e,o=H();Object.keys(o.scripts).forEach(e=>{if(e in t){const a=t[e];a!==t[e]&&(o.devDependencies=o.devDependencies||{},o.devDependencies[e]=a)}}),w.default.writeFileSync("package.json",JSON.stringify(o,null,2)),await U("npm install").promise}catch(e){throw new Error(`Error fixing package.json dependencies: ${e}`)}}async run(e){let{license:t}=e;const{boot:o}=e;let{all:a,scripts:n,styles:s,docs:i,ide:r,workflows:l,templates:c,docker:g,typescript:d,dependencies:f,tests:u,automation:h,pkg:p}=e;if((n||s||i||r||l||c||g||d||h||f||u||p)&&(a=!1),o){const e=await this.getOrg(),o=await $.insistForText("Project name","Enter the project name:",e=>e.length>1),a=await $.insistForText("Author","Enter the author name:",e=>e.length>1),n=e?`@${e}/${o}`:o;await this.initPackage(n,a,t),await this.createTokenFiles(),await this.auditFix(),this.patchFiles()}if(a&&(n=!1,s=!0,i=!0,r=!0,l=!0,c=!0,g=!0,d=!0,p=!0,f=!0,u=!0,h=!1),void 0===n&&(n=await $.askConfirmation("scripts","Do you want to get scripts?",!0)),n&&await this.getScripts(),this.loadValuesFromPackage(),!a&&void 0===t){await $.askConfirmation("license","Do you want to set a license?",!0)&&(t=await $.insistForText("license","Enter the desired License (MIT|GPL|Apache|LGPL|AGPL):",e=>!!e&&!!e.match(/^(MIT|GPL|Apache|LGPL|AGPL)$/g)))}void 0!==t&&await this.getLicense(t),void 0===r&&(r=await $.askConfirmation("ide","Do you want to get ide configs?",!0)),r&&await this.getIde(),void 0===d&&(d=await $.askConfirmation("typescript","Do you want to get typescript configs?",!0)),d&&await this.getTypescript(),void 0===g&&(g=await $.askConfirmation("docker","Do you want to get docker configs?",!0)),g&&await this.getDocker(),void 0===h&&(h=await $.askConfirmation("automation","Do you want to get automation configs?",!0)),h&&await this.getAutomation(),void 0===s&&(s=await $.askConfirmation("styles","Do you want to get styles?",!0)),s&&await this.getStyles(),void 0===i&&(i=await $.askConfirmation("docs","Do you want to get docs?",!0)),i&&await this.getDocs(),void 0===l&&(l=await $.askConfirmation("workflows","Do you want to get workflows?",!0)),l&&await this.getWorkflows(),void 0===c&&(c=await $.askConfirmation("templates","Do you want to get templates?",!0)),c&&await this.getTemplates(),void 0===p&&(p=await $.askConfirmation("pkg","Do you want to update your package.json scripts?",!0)),p&&await this.updatePackageScrips(),void 0===u&&(u=await $.askConfirmation("pkg","Do you want to update your test configs?",!0)),u&&await this.getTests(),void 0===f&&(f=await $.askConfirmation("pkg","Do you want to update dev dependencies?",!0)),f&&await this.updateDependencies()}},e.UserInput=$,e.VERSION="0.4.2",e.chainAbortController=B,e.copyFile=X,e.deletePath=_,e.getAllFiles=G,e.getDependencies=Y,e.getPackage=H,e.getPackageDependencies=re,e.getPackageVersion=z,e.getSlogan=oe,e.installDependencies=Z,e.installIfNotAvailable=async function(e,t){if(!t){const e=await Y();t={prod:e.prod?.map(e=>e.name)||[],dev:e.dev?.map(e=>e.name)||[],peer:e.peer?.map(e=>e.name)||[]}}const{prod:o,dev:a,peer:n}=t,s=Array.from(new Set([...o||[],...a||[],...n||[]])),i=(e="string"==typeof e?[e]:e).filter(e=>!s.includes(e));return i.length&&await Z({dev:i}),t.dev=t.dev||[],t.dev.push(...i),t},e.lockify=function(e){let t=Promise.resolve();return(...o)=>{const a=t.then(()=>e(...o));return t=a.catch(()=>{}),a}},e.normalizeImport=async function(e){return e.then(e=>e.default||e)},e.packageToGlobal=ie,e.parseList=se,e.patchFile=J,e.printBanner=te,e.pushToGit=async function e(){const t=R.for(e),o=await U("git config user.name").promise,a=await U("git config user.email").promise;t.verbose(`cached git id: ${o}/${a}. changing to automation`),await U('git config user.email "automation@decaf.ts"').promise,await U('git config user.name "decaf"').promise,t.info("Pushing changes to git..."),await U("git add .").promise,await U('git commit -m "refs #1 - after repo setup"').promise,await U("git push").promise,await U(`git config user.email "${a}"`).promise,await U(`git config user.name "${o}"`).promise,t.verbose(`reverted to git id: ${o}/${a}`)},e.readFile=V,e.renameFile=W,e.runCommand=U,e.setPackageAttribute=K,e.spawnCommand=M,e.updateDependencies=async function e(){const t=R.for(e);t.info("checking for updates..."),await U("npx npm-check-updates -u").promise,t.info("updating..."),await U("npx npm run do-install").promise},e.writeFile=q});
|