@avandar/acclimate 0.2.1 → 0.3.0

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/index.cjs CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  var changeCase = require('change-case');
4
4
  var tsPattern = require('ts-pattern');
5
+ var process$1 = require('process');
6
+ var promises = require('readline/promises');
5
7
 
6
8
  // src/CLIError.ts
7
9
  var CLIError = class _CLIError extends Error {
@@ -174,14 +176,224 @@ var defaultCLIState = {
174
176
  positionalArgs: [],
175
177
  optionArgs: {},
176
178
  globalOptionArgs: {},
177
- action: () => {
178
- }
179
+ action: void 0
179
180
  };
180
181
 
181
182
  // src/AcclimateCLI/createCLI/createCLI.ts
182
183
  function createCLI(name) {
183
184
  return AcclimateCLI({ ...defaultCLIState, name });
184
185
  }
186
+
187
+ // src/generateTerminalMessage/generateTerminalMessage.ts
188
+ var COLOR_CODES = {
189
+ reset: "\x1B[0m",
190
+ black: "\x1B[30m",
191
+ red: "\x1B[31m",
192
+ green: "\x1B[32m",
193
+ yellow: "\x1B[33m",
194
+ blue: "\x1B[34m",
195
+ magenta: "\x1B[35m",
196
+ cyan: "\x1B[36m",
197
+ white: "\x1B[37m",
198
+ bright_black: "\x1B[90m",
199
+ gray: "\x1B[90m",
200
+ grey: "\x1B[90m",
201
+ bright_red: "\x1B[91m",
202
+ bright_green: "\x1B[92m",
203
+ bright_yellow: "\x1B[93m",
204
+ bright_blue: "\x1B[94m",
205
+ bright_magenta: "\x1B[95m",
206
+ bright_cyan: "\x1B[96m",
207
+ bright_white: "\x1B[97m"
208
+ };
209
+ var PARAM_TOKEN_REGEX = /\$([a-zA-Z0-9_]+)\$/g;
210
+ var COLOR_TOKEN_REGEX = /\|([a-zA-Z_]+)\|/g;
211
+ function interpolateParams(message, params) {
212
+ return message.replace(PARAM_TOKEN_REGEX, (match2, key) => {
213
+ const value = params[key];
214
+ if (value === void 0) {
215
+ return "";
216
+ }
217
+ return value;
218
+ });
219
+ }
220
+ function applyColors(message) {
221
+ return message.replace(COLOR_TOKEN_REGEX, (match2, colorName) => {
222
+ const code = COLOR_CODES[colorName.toLowerCase()];
223
+ return code ?? match2;
224
+ });
225
+ }
226
+ function generateTerminalMessage(message, params = {}) {
227
+ const hasColorToken = Boolean(message.match(COLOR_TOKEN_REGEX));
228
+ const endsWithReset = message.trimEnd().endsWith("|reset|");
229
+ const withReset = hasColorToken && !endsWithReset ? `${message}|reset|` : message;
230
+ const interpolated = interpolateParams(withReset, params);
231
+ return applyColors(interpolated);
232
+ }
233
+
234
+ // src/AcclimateCLI/generateHelpText/generateHelpText.ts
235
+ function formatSectionTitle(title) {
236
+ return `|bright_yellow|${title}|reset|`;
237
+ }
238
+ function formatNoneLine() {
239
+ return " |gray|None|reset|";
240
+ }
241
+ function formatDefaultValue(value) {
242
+ return `|gray|[default: ${JSON.stringify(value)}]|reset|`;
243
+ }
244
+ function formatParamLine(param) {
245
+ const description = param.description ?? "No description";
246
+ const requiredLabel = param.required ? "|red|required|reset|" : "|gray|optional|reset|";
247
+ const defaultLabel = param.defaultValue !== void 0 ? formatDefaultValue(param.defaultValue) : void 0;
248
+ const aliasList = param.aliases && param.aliases.length > 0 ? param.aliases.join(", ") : "";
249
+ const displayName = aliasList ? `${param.name}, ${aliasList}` : param.name;
250
+ const segments = [
251
+ ` |bright_white|${displayName}|reset|`,
252
+ `(${param.type})|reset| ${requiredLabel}`,
253
+ `- |gray|${description}|reset|`,
254
+ defaultLabel
255
+ ].filter(Boolean);
256
+ return segments.join(" ");
257
+ }
258
+ function formatSection(title, params) {
259
+ if (params.length === 0) {
260
+ return [formatSectionTitle(title), formatNoneLine()];
261
+ }
262
+ return [formatSectionTitle(title), ...params.map(formatParamLine)];
263
+ }
264
+ function formatCommandLine(options) {
265
+ const { name, description } = options;
266
+ const label = description ?? "No description";
267
+ return ` |bright_white|${name}|reset| - |gray|${label}|reset|`;
268
+ }
269
+ function formatAvailableCommandsLine(commands) {
270
+ const label = commands.length === 0 ? "None" : commands.map((cmd) => {
271
+ return cmd;
272
+ }).join(", ");
273
+ return `|bright_yellow|Available Commands:|reset| |gray|${label}|reset|`;
274
+ }
275
+ function _generateHelpTextHelper(cli, options) {
276
+ const name = cli.getName();
277
+ const description = cli.state.description;
278
+ const level = Number(options.level);
279
+ const positionalParams = cli.state.positionalArgs;
280
+ const optionParams = Object.values(
281
+ cli.state.optionArgs
282
+ );
283
+ const globalOptionParams = Object.values(
284
+ cli.state.globalOptionArgs
285
+ );
286
+ const sortedCommands = Object.entries(cli.state.commands).sort(([a], [b]) => {
287
+ return a.localeCompare(b);
288
+ });
289
+ const commandNames = sortedCommands.map(([commandName]) => {
290
+ return commandName;
291
+ });
292
+ const headerLines = [`|bright_cyan|${name}|reset|`];
293
+ if (description !== void 0) {
294
+ headerLines.push(` |gray|${description}|reset|`);
295
+ }
296
+ let commandSection;
297
+ if (level === 2) {
298
+ commandSection = [
299
+ formatSectionTitle("Commands"),
300
+ ` ${formatAvailableCommandsLine(commandNames)}`
301
+ ];
302
+ } else if (sortedCommands.length === 0) {
303
+ commandSection = [formatSectionTitle("Commands"), formatNoneLine()];
304
+ } else {
305
+ commandSection = [
306
+ formatSectionTitle("Commands"),
307
+ ...sortedCommands.map(([commandName, commandCLI]) => {
308
+ return formatCommandLine({
309
+ name: commandName,
310
+ description: commandCLI.state.description
311
+ });
312
+ })
313
+ ];
314
+ }
315
+ const sections = [
316
+ formatSection("Positional Arguments", positionalParams),
317
+ formatSection("Options", optionParams),
318
+ formatSection("Global Options", globalOptionParams),
319
+ commandSection
320
+ ];
321
+ const lines = [...headerLines, ""];
322
+ sections.forEach((section, idx) => {
323
+ lines.push(...section);
324
+ if (idx < sections.length - 1) {
325
+ lines.push("");
326
+ }
327
+ });
328
+ if (level === 1) {
329
+ const subCommandHelpText = sortedCommands.map(([, commandCLI]) => {
330
+ return _generateHelpTextHelper(commandCLI, { level: 2 });
331
+ });
332
+ if (subCommandHelpText.length > 0) {
333
+ subCommandHelpText.forEach((text) => {
334
+ lines.push("", ...text.split("\n"));
335
+ });
336
+ }
337
+ }
338
+ const indent = " ".repeat(Math.max(0, level - 1));
339
+ return lines.map((line) => {
340
+ return line === "" ? "" : `${indent}${line}`;
341
+ }).join("\n");
342
+ }
343
+ function generateHelpText(cli) {
344
+ return generateTerminalMessage(_generateHelpTextHelper(cli, { level: 1 }));
345
+ }
346
+ var COLOR_TOKEN_REGEX2 = /\|[a-zA-Z_]+\|/g;
347
+ function messageHasPunctuation(message) {
348
+ const stripped = message.replace(COLOR_TOKEN_REGEX2, "").trimEnd();
349
+ return stripped.endsWith(":") || stripped.endsWith("?");
350
+ }
351
+ async function requestTerminalInput(options) {
352
+ const { message, params, options: promptOptions } = options;
353
+ const notice = promptOptions.required ? "" : " |gray|(press Enter to leave empty)|reset|";
354
+ const booleanNotice = promptOptions.type === "boolean" ? " |reset|(y/n)" : "";
355
+ const promptMessage = `${message}${booleanNotice}${notice}`;
356
+ const promptText = messageHasPunctuation(message) ? `${promptMessage} ` : `${promptMessage}: `;
357
+ const prompt = generateTerminalMessage(promptText, params);
358
+ const rl = promises.createInterface({ input: process$1.stdin, output: process$1.stdout });
359
+ try {
360
+ while (true) {
361
+ const answer = await rl.question(prompt);
362
+ const trimmed = answer.trim();
363
+ if (trimmed.length === 0) {
364
+ if (promptOptions.required) {
365
+ process$1.stdout.write(
366
+ generateTerminalMessage(
367
+ "|red|This value is required.|reset| Please enter a value.\n"
368
+ )
369
+ );
370
+ continue;
371
+ }
372
+ return void 0;
373
+ }
374
+ if (promptOptions.type === "boolean") {
375
+ const normalized = trimmed.toLowerCase();
376
+ if (normalized === "y" || normalized === "yes") {
377
+ return "true";
378
+ }
379
+ if (normalized === "n" || normalized === "no") {
380
+ return "false";
381
+ }
382
+ process$1.stdout.write(
383
+ generateTerminalMessage(
384
+ "|red|That was not a valid response.|reset| Please enter y or n.\n"
385
+ )
386
+ );
387
+ continue;
388
+ }
389
+ return answer;
390
+ }
391
+ } finally {
392
+ rl.close();
393
+ }
394
+ }
395
+
396
+ // src/AcclimateCLI/runCLI/runCLI.ts
185
397
  function _isValidFullOptionName(name) {
186
398
  return name.startsWith("--");
187
399
  }
@@ -250,7 +462,19 @@ function _replaceAliases({
250
462
  {}
251
463
  );
252
464
  }
253
- function _runCLIHelper(options) {
465
+ async function askForValue(options) {
466
+ const { argConfig } = options;
467
+ const askIfEmpty = argConfig.askIfEmpty;
468
+ const description = argConfig.description ? ` (${argConfig.description})` : "";
469
+ const baseMessage = typeof askIfEmpty === "object" ? askIfEmpty.message : `Please enter a value for ${argConfig.name}${description}`;
470
+ const coloredMessage = `|bright_cyan|${baseMessage}|reset|`;
471
+ return requestTerminalInput({
472
+ message: coloredMessage,
473
+ params: {},
474
+ options: { required: argConfig.required, type: argConfig.type }
475
+ });
476
+ }
477
+ async function _runCLIHelper(options) {
254
478
  const { rawGlobalOptionArgs, rawOptionArgs, rawPositionalArgs, cli } = {
255
479
  ...options,
256
480
  rawGlobalOptionArgs: _replaceAliases({
@@ -287,74 +511,79 @@ function _runCLIHelper(options) {
287
511
  count: rawPositionalArgs.length
288
512
  });
289
513
  }
290
- const parsedPositionalArgs = cli.state.positionalArgs.reduce(
291
- (acc, argConfig, idx) => {
292
- const rawVal = rawPositionalArgs[idx];
293
- if (argConfig.required && rawVal === void 0) {
294
- throw CLIError.missingRequiredPositionalArg({
295
- cliName,
296
- positionalArgName: argConfig.name
297
- });
298
- }
299
- acc[argConfig.name] = _parseAndValidateValue({
514
+ const parsedPositionalArgs = {};
515
+ for (const [idx, argConfig] of cli.state.positionalArgs.entries()) {
516
+ let rawVal = rawPositionalArgs[idx];
517
+ if (argConfig.askIfEmpty && rawVal === void 0) {
518
+ rawVal = await askForValue({ argConfig });
519
+ }
520
+ if (argConfig.required && rawVal === void 0) {
521
+ throw CLIError.missingRequiredPositionalArg({
300
522
  cliName,
301
- inputValue: rawVal,
302
- defaultValue: argConfig.defaultValue,
303
- paramConfig: argConfig
523
+ positionalArgName: argConfig.name
304
524
  });
305
- return acc;
306
- },
307
- {}
308
- );
309
- const parsedOptionArgs = Object.values(
525
+ }
526
+ parsedPositionalArgs[argConfig.name] = _parseAndValidateValue({
527
+ cliName,
528
+ inputValue: rawVal,
529
+ defaultValue: argConfig.defaultValue,
530
+ paramConfig: argConfig
531
+ });
532
+ }
533
+ const parsedOptionArgs = {};
534
+ for (const argConfig of Object.values(
310
535
  cli.state.optionArgs
311
- ).reduce(
312
- (acc, argConfig) => {
313
- const rawVal = rawOptionArgs[argConfig.name];
314
- if (argConfig.required && rawVal === void 0) {
315
- throw CLIError.missingRequiredOption({
316
- cliName,
317
- optionName: argConfig.name
318
- });
319
- }
320
- acc[changeCase.camelCase(argConfig.name)] = _parseAndValidateValue({
536
+ )) {
537
+ let rawVal = rawOptionArgs[argConfig.name];
538
+ if (argConfig.askIfEmpty && rawVal === void 0) {
539
+ rawVal = await askForValue({ argConfig });
540
+ }
541
+ if (argConfig.required && rawVal === void 0) {
542
+ throw CLIError.missingRequiredOption({
321
543
  cliName,
322
- inputValue: rawVal,
323
- defaultValue: argConfig.defaultValue,
324
- paramConfig: argConfig
544
+ optionName: argConfig.name
325
545
  });
326
- return acc;
327
- },
328
- {}
329
- );
330
- const parsedGlobalOptionArgs = Object.values(
546
+ }
547
+ parsedOptionArgs[changeCase.camelCase(argConfig.name)] = _parseAndValidateValue({
548
+ cliName,
549
+ inputValue: rawVal,
550
+ defaultValue: argConfig.defaultValue,
551
+ paramConfig: argConfig
552
+ });
553
+ }
554
+ const parsedGlobalOptionArgs = {};
555
+ for (const argConfig of Object.values(
331
556
  cli.state.globalOptionArgs
332
- ).reduce(
333
- (acc, argConfig) => {
334
- const rawVal = rawGlobalOptionArgs[argConfig.name];
335
- if (argConfig.required && rawVal === void 0) {
336
- throw CLIError.missingRequiredOption({
337
- cliName,
338
- optionName: argConfig.name
339
- });
340
- }
341
- acc[changeCase.camelCase(argConfig.name)] = _parseAndValidateValue({
557
+ )) {
558
+ let rawVal = rawGlobalOptionArgs[argConfig.name];
559
+ if (argConfig.askIfEmpty && rawVal === void 0) {
560
+ rawVal = await askForValue({ argConfig });
561
+ }
562
+ if (argConfig.required && rawVal === void 0) {
563
+ throw CLIError.missingRequiredOption({
342
564
  cliName,
343
- inputValue: rawVal,
344
- defaultValue: argConfig.defaultValue,
345
- paramConfig: argConfig
565
+ optionName: argConfig.name
346
566
  });
347
- return acc;
348
- },
349
- {}
350
- );
351
- cli.state.action({
352
- ...parsedPositionalArgs,
353
- ...parsedOptionArgs,
354
- ...parsedGlobalOptionArgs
355
- });
567
+ }
568
+ parsedGlobalOptionArgs[changeCase.camelCase(argConfig.name)] = _parseAndValidateValue({
569
+ cliName,
570
+ inputValue: rawVal,
571
+ defaultValue: argConfig.defaultValue,
572
+ paramConfig: argConfig
573
+ });
574
+ }
575
+ const action = cli.state.action;
576
+ if (action) {
577
+ action({
578
+ ...parsedPositionalArgs,
579
+ ...parsedOptionArgs,
580
+ ...parsedGlobalOptionArgs
581
+ });
582
+ return;
583
+ }
584
+ console.log(generateHelpText(cli));
356
585
  }
357
- function runCLI(options) {
586
+ async function runCLI(options) {
358
587
  const { input, cli } = options;
359
588
  const firstOptionIdx = input.findIndex((token) => {
360
589
  return token.startsWith("-");
@@ -382,57 +611,25 @@ function runCLI(options) {
382
611
  currentVals.push(argVal);
383
612
  }
384
613
  }
385
- _runCLIHelper({ rawPositionalArgs, rawOptionArgs, rawGlobalOptionArgs, cli });
614
+ await _runCLIHelper({
615
+ rawPositionalArgs,
616
+ rawOptionArgs,
617
+ rawGlobalOptionArgs,
618
+ cli
619
+ });
386
620
  }
387
621
 
388
622
  // src/Acclimate.ts
389
- var COLOR_CODES = {
390
- reset: "\x1B[0m",
391
- black: "\x1B[30m",
392
- red: "\x1B[31m",
393
- green: "\x1B[32m",
394
- yellow: "\x1B[33m",
395
- blue: "\x1B[34m",
396
- magenta: "\x1B[35m",
397
- cyan: "\x1B[36m",
398
- white: "\x1B[37m",
399
- bright_black: "\x1B[90m",
400
- gray: "\x1B[90m",
401
- grey: "\x1B[90m",
402
- bright_red: "\x1B[91m",
403
- bright_green: "\x1B[92m",
404
- bright_yellow: "\x1B[93m",
405
- bright_blue: "\x1B[94m",
406
- bright_magenta: "\x1B[95m",
407
- bright_cyan: "\x1B[96m",
408
- bright_white: "\x1B[97m"
409
- };
410
- var PARAM_TOKEN_REGEX = /\$([a-zA-Z0-9_]+)\$/g;
411
- var COLOR_TOKEN_REGEX = /\|([a-zA-Z_]+)\|/g;
412
- function interpolateParams(message, params) {
413
- return message.replace(PARAM_TOKEN_REGEX, (match2, key) => {
414
- const value = params[key];
415
- return value ?? match2;
416
- });
417
- }
418
- function applyColors(message) {
419
- return message.replace(COLOR_TOKEN_REGEX, (match2, colorName) => {
420
- const code = COLOR_CODES[colorName.toLowerCase()];
421
- return code ?? match2;
422
- });
423
- }
424
623
  var Acclimate = {
425
624
  createCLI,
426
625
  run: (cli) => {
427
- runCLI({ cli, input: process.argv.slice(2) });
626
+ void runCLI({ cli, input: process.argv.slice(2) });
428
627
  },
429
628
  log: (message, params = {}) => {
430
- const hasColorToken = Boolean(message.match(COLOR_TOKEN_REGEX));
431
- const endsWithReset = message.trimEnd().endsWith("|reset|");
432
- const withReset = hasColorToken && !endsWithReset ? `${message}|reset|` : message;
433
- const interpolated = interpolateParams(withReset, params);
434
- const colorized = applyColors(interpolated);
435
- console.log(colorized);
629
+ console.log(generateTerminalMessage(message, params));
630
+ },
631
+ requestInput: (options) => {
632
+ return requestTerminalInput(options);
436
633
  }
437
634
  };
438
635