@shell-shock/plugin-console 0.2.6 → 0.2.8

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.
@@ -9,7 +9,7 @@ import { TSDoc, TSDocDefaultValue, TSDocExample, TSDocParam, TSDocRemarks, TSDoc
9
9
  import { IsNotDebug, IsNotVerbose } from "@shell-shock/core/components/helpers";
10
10
  import { useColors, useTheme } from "@shell-shock/plugin-theme/contexts/theme";
11
11
  import { colorKeys, modifierKeys } from "@shell-shock/plugin-theme/helpers/ansi-utils";
12
- import { camelCase } from "@stryke/string-format";
12
+ import { camelCase, titleCase } from "@stryke/string-format";
13
13
  import { getIndefiniteArticle } from "@stryke/string-format/vowels";
14
14
  import { isSetObject } from "@stryke/type-checks/is-set-object";
15
15
  import { defu } from "defu";
@@ -322,17 +322,18 @@ function AnsiStyleFunctionsDeclaration() {
322
322
  doubleHardline: true,
323
323
  enderPunctuation: true,
324
324
  children: (modifier) => [createComponent(TSDoc, {
325
- heading: `A function that applies ${modifier} styling to provided console text.`,
325
+ get heading() {
326
+ return `A function that applies ${getIndefiniteArticle(titleCase(modifier))} ${titleCase(modifier)} text-style to provided console text.`;
327
+ },
326
328
  get children() {
327
- return [
328
- createComponent(TSDocRemarks, { children: `This function takes a string and an optional boolean indicating whether to apply the color as a background. It returns the input string wrapped in the appropriate ANSI escape codes for the specified color, based on the terminal's color support level. If colors are not supported, it simply returns the input text as a string.` }),
329
- createIntrinsic("hbr", {}),
330
- createComponent(TSDocParam, {
331
- name: "text",
332
- children: `The console text to which the ${modifier} styling should be applied.`
333
- }),
334
- createComponent(TSDocReturns, { children: `A string with ANSI escape codes applied for ${modifier} styling, or the original text if the style is not supported in the current terminal.` })
335
- ];
329
+ return [createComponent(TSDocParam, {
330
+ name: "text",
331
+ get children() {
332
+ return `The console text to which the ${titleCase(modifier)} text-style should be applied.`;
333
+ }
334
+ }), createComponent(TSDocReturns, { get children() {
335
+ return `A string with ANSI escape codes applied for ${titleCase(modifier)} text-style, or the original text if the style is not supported in the current terminal.`;
336
+ } })];
336
337
  }
337
338
  }), createComponent(VarDeclaration, {
338
339
  "const": true,
@@ -363,16 +364,28 @@ function AnsiStyleFunctionsDeclaration() {
363
364
  doubleHardline: true,
364
365
  enderPunctuation: true,
365
366
  children: (color) => [createComponent(TSDoc, {
366
- heading: `A helper function to color the provided text as ${color}.`,
367
+ get heading() {
368
+ return `A function that applies ${getIndefiniteArticle(titleCase(color))} ${titleCase(color)} color styling to provided console text.`;
369
+ },
367
370
  get children() {
368
371
  return [
369
- createComponent(TSDocRemarks, { children: `This function takes a string and an optional boolean indicating whether to apply the color as a background. It returns the input string wrapped in the appropriate ANSI escape codes for the specified color, based on the terminal's color support level. If colors are not supported, it simply returns the input text as a string.` }),
372
+ createComponent(TSDocRemarks, { get children() {
373
+ return `This function takes a string and an optional boolean indicating whether to apply the color as a background. It returns the input string wrapped in the appropriate ANSI escape codes for ${getIndefiniteArticle(titleCase(color))} ${titleCase(color)} color styling, based on the terminal's color support level. If colors are not supported, it simply returns the input text as a string.`;
374
+ } }),
370
375
  createIntrinsic("hbr", {}),
371
376
  createComponent(TSDocParam, {
372
377
  name: "text",
373
- children: `The console text to which the ${color} color should be applied.`
378
+ get children() {
379
+ return `The console text to which the ${titleCase(color)} color styling should be applied.`;
380
+ }
374
381
  }),
375
- createComponent(TSDocReturns, { children: `A string with ANSI escape codes applied for ${color} color, or the original text if the color is not supported in the current terminal.` })
382
+ createComponent(TSDocParam, {
383
+ name: "background",
384
+ children: `A boolean indicating whether to apply the color as a background. Defaults to \`false\`.`
385
+ }),
386
+ createComponent(TSDocReturns, { get children() {
387
+ return `A string with ANSI escape codes applied for ${titleCase(color)} color styling, or the original text if the style is not supported in the current terminal.`;
388
+ } })
376
389
  ];
377
390
  }
378
391
  }), createComponent(VarDeclaration, {
@@ -533,6 +546,54 @@ function SplitTextFunctionDeclaration() {
533
546
  const openSequence = openCodes.join("");
534
547
 
535
548
  return [first.replace(/^\\s+/, "").replace(/\\s+$/, "") + closeSequence, openSequence + second.replace(/^\\s+/, "").replace(/\\s+$/, "")]; `
549
+ }),
550
+ createComponent(Spacing, {}),
551
+ createComponent(FunctionDeclaration, {
552
+ name: "innerSplitText",
553
+ parameters: [{
554
+ name: "text",
555
+ type: "string"
556
+ }, {
557
+ name: "maxLength",
558
+ type: "number | SizeToken"
559
+ }],
560
+ children: code`let line = text;
561
+ let result = [] as string[];
562
+
563
+ const calculatedMaxLength = isSizeToken(maxLength) ? calculateWidth(maxLength) : maxLength;
564
+ while (stripAnsi(line).length > calculatedMaxLength || line.indexOf("\\n") !== -1) {
565
+ if (line.indexOf("\\n") !== -1) {
566
+ result.push(...innerSplitText(line.slice(0, line.indexOf("\\n")).replace(/(\\r)?\\n/, ""), calculatedMaxLength));
567
+ line = line.indexOf("\\n") + 1 < line.length
568
+ ? line.slice(line.indexOf("\\n") + 1)
569
+ : "";
570
+ } else {
571
+ const index = [" ", "/", ".", ",", "-", ":", "|", "@", "+"].reduce((ret, split) => {
572
+ let current = ret;
573
+ while (stripAnsi(line).indexOf(split, current + 1) !== -1 && stripAnsi(line).indexOf(split, current + 1) <= calculatedMaxLength) {
574
+ current = line.indexOf(split, adjustIndex(line, current + 1));
575
+ }
576
+
577
+ return current;
578
+ }, -1);
579
+ if (index === -1) {
580
+ break;
581
+ }
582
+
583
+ const lines = breakLine(line, index);
584
+ result.push(lines[0]);
585
+ line = lines[1];
586
+ }
587
+ }
588
+
589
+ while (stripAnsi(line).length > calculatedMaxLength) {
590
+ const lines = breakLine(line, calculatedMaxLength);
591
+ result.push(lines[0]);
592
+ line = lines[1];
593
+ }
594
+
595
+ result.push(line);
596
+ return result; `
536
597
  }),
537
598
  createComponent(Spacing, {}),
538
599
  createComponent(TSDoc, {
@@ -562,45 +623,16 @@ function SplitTextFunctionDeclaration() {
562
623
  name: "maxLength",
563
624
  type: "number | SizeToken"
564
625
  }],
565
- children: code`
566
- let line = text;
567
- let result = [] as string[];
568
-
569
- const calculatedMaxLength = isSizeToken(maxLength) ? calculateWidth(maxLength) : maxLength;
570
- while (stripAnsi(line).length > calculatedMaxLength || line.indexOf("\\n") !== -1) {
571
- if (line.indexOf("\\n") !== -1) {
572
- result.push(...splitText(line.slice(0, line.indexOf("\\n")).replace(/(\\r)?\\n/, ""), calculatedMaxLength));
573
- line = line.indexOf("\\n") + 1 < line.length
574
- ? line.slice(line.indexOf("\\n") + 1)
575
- : "";
576
- } else {
577
- const index = [" ", "/", ".", ",", "-", ":", "|", "@", "+"].reduce((ret, split) => {
578
- let current = ret;
579
- while (stripAnsi(line).indexOf(split, current + 1) !== -1 && stripAnsi(line).indexOf(split, current + 1) <= calculatedMaxLength) {
580
- current = line.indexOf(split, adjustIndex(line, current + 1));
626
+ children: code`const timeout = setTimeout(() => {
627
+ throw new Error("Text splitting took too long, likely due to a very long line without spaces or a very small maxLength. Please ensure that the input text contains reasonable break points and that maxLength is set to a reasonable value.");
628
+ }, 1000);
629
+
630
+ try {
631
+ return innerSplitText(text, maxLength);
632
+ } finally {
633
+ clearTimeout(timeout);
581
634
  }
582
-
583
- return current;
584
- }, -1);
585
- if (index === -1) {
586
- break;
587
- }
588
-
589
- const lines = breakLine(line, index);
590
- result.push(lines[0]);
591
- line = lines[1];
592
- }
593
- }
594
-
595
- while (stripAnsi(line).length > calculatedMaxLength) {
596
- const lines = breakLine(line, calculatedMaxLength);
597
- result.push(lines[0]);
598
- line = lines[1];
599
- }
600
-
601
- result.push(line);
602
- return result;
603
- `
635
+ `
604
636
  })
605
637
  ];
606
638
  }
@@ -1025,65 +1057,86 @@ function DividerFunctionDeclaration() {
1025
1057
  */
1026
1058
  function LinkFunctionDeclaration() {
1027
1059
  const theme = useTheme();
1028
- return [createComponent(TSDoc, {
1029
- heading: "Render a hyperlink in the console.",
1030
- get children() {
1031
- return [
1032
- createComponent(TSDocParam, {
1033
- name: "url",
1034
- children: `The URL to render as a hyperlink.`
1035
- }),
1036
- createComponent(TSDocParam, {
1037
- name: "textOrExternal",
1038
- children: `The text to display for the link or a boolean indicating whether the link is external. If no text is provided, the URL will be used as the text. If a boolean is provided and is true, the URL will be used as the text and the link will be rendered as an external link.`
1039
- }),
1040
- createComponent(TSDocParam, {
1041
- name: "external",
1042
- children: `A boolean indicating whether the link is external. If true, the link will be rendered as an external link.`
1043
- }),
1044
- createComponent(TSDocReturns, { children: `The formatted hyperlink string.` })
1045
- ];
1046
- }
1047
- }), createComponent(FunctionDeclaration, {
1048
- "export": true,
1049
- name: "link",
1050
- parameters: [
1051
- {
1060
+ return [
1061
+ createComponent(InterfaceDeclaration, {
1062
+ "export": true,
1063
+ name: "LinkOptions",
1064
+ doc: "Options for formatting a hyperlink in the console.",
1065
+ get children() {
1066
+ return [
1067
+ createComponent(InterfaceMember, {
1068
+ name: "external",
1069
+ optional: true,
1070
+ type: "boolean",
1071
+ doc: "Whether the link is external. If true, an external link icon will be displayed next to the link text (if supported by the terminal) and the link may be styled differently based on the current theme configuration."
1072
+ }),
1073
+ createComponent(Spacing, {}),
1074
+ createComponent(InterfaceMember, {
1075
+ name: "text",
1076
+ optional: true,
1077
+ type: "string",
1078
+ doc: "The text to display for the link. If not provided, the URL will be used as the text."
1079
+ }),
1080
+ createComponent(Spacing, {}),
1081
+ createComponent(InterfaceMember, {
1082
+ name: "useTextWhenUnsupported",
1083
+ optional: true,
1084
+ type: "boolean",
1085
+ doc: "Whether to use the text when the hyperlink is not supported. If true, the text will be displayed even if the terminal does not support hyperlinks."
1086
+ })
1087
+ ];
1088
+ }
1089
+ }),
1090
+ createComponent(Spacing, {}),
1091
+ createComponent(TSDoc, {
1092
+ heading: "Render a hyperlink in the console.",
1093
+ get children() {
1094
+ return [
1095
+ createComponent(TSDocParam, {
1096
+ name: "url",
1097
+ children: `The URL to render as a hyperlink.`
1098
+ }),
1099
+ createComponent(TSDocParam, {
1100
+ name: "options",
1101
+ children: `Options for formatting the hyperlink.`
1102
+ }),
1103
+ createComponent(TSDocReturns, { children: `The formatted hyperlink string for display in the console.` })
1104
+ ];
1105
+ }
1106
+ }),
1107
+ createComponent(FunctionDeclaration, {
1108
+ "export": true,
1109
+ name: "link",
1110
+ parameters: [{
1052
1111
  name: "url",
1053
1112
  type: "string",
1054
1113
  optional: false
1055
- },
1056
- {
1057
- name: "textOrExternal",
1058
- type: "string | boolean",
1059
- optional: true
1060
- },
1061
- {
1062
- name: "external",
1063
- type: "boolean",
1064
- optional: true
1114
+ }, {
1115
+ name: "options",
1116
+ type: "LinkOptions",
1117
+ default: "{}"
1118
+ }],
1119
+ get children() {
1120
+ return [
1121
+ createComponent(IfStatement, {
1122
+ condition: code`isHyperlinkSupported()`,
1123
+ get children() {
1124
+ return code`return \`\\x1b]8;;\${url}\\u0007\${options.text ? options.text : url}\\x1b]8;;\\u0007\${options.external === true ? "${theme.icons.link.external}" : ""}\`;`;
1125
+ }
1126
+ }),
1127
+ createIntrinsic("hbr", {}),
1128
+ createComponent(IfStatement, {
1129
+ condition: code`isColorSupported`,
1130
+ get children() {
1131
+ return code`return \`\${underline(textColors.body.link(\`\${options.useTextWhenUnsupported && options.text ? options.text : url}\`))}\${options.external === true ? "${theme.icons.link.external}" : ""}\`;`;
1132
+ }
1133
+ }),
1134
+ createIntrinsic("hbr", {}),
1135
+ memo(() => code`return \`\${options.useTextWhenUnsupported && options.text ? options.text : url}\${options.external === true ? "${theme.icons.link.external}" : ""}\`;`)
1136
+ ];
1065
1137
  }
1066
- ],
1067
- get children() {
1068
- return [
1069
- createComponent(IfStatement, {
1070
- condition: code`isHyperlinkSupported()`,
1071
- get children() {
1072
- return code`return \`\\x1b]8;;\${url}\\u0007\${typeof textOrExternal === "string" && textOrExternal ? textOrExternal : url}\\x1b]8;;\\u0007\${external === true || textOrExternal === true ? "${theme.icons.link.external}" : ""}\`;`;
1073
- }
1074
- }),
1075
- createIntrinsic("hbr", {}),
1076
- createComponent(IfStatement, {
1077
- condition: code`isColorSupported`,
1078
- get children() {
1079
- return code`return \`\${underline(textColors.body.link(\`\${url}\`))}\${external === true || textOrExternal === true ? "${theme.icons.link.external}" : ""}\`;`;
1080
- }
1081
- }),
1082
- createIntrinsic("hbr", {}),
1083
- memo(() => code`return \`\${url}\${external === true || textOrExternal === true ? "${theme.icons.link.external}" : ""}\`;`)
1084
- ];
1085
- }
1086
- })];
1138
+ })
1139
+ ];
1087
1140
  }
1088
1141
  /**
1089
1142
  * A component to generate the `blockquote` function declaration
@@ -2450,25 +2503,14 @@ function ConsoleBuiltin(props) {
2450
2503
  description: "A collection of helper utilities to assist in generating content meant for display in the console.",
2451
2504
  get imports() {
2452
2505
  return defu(imports, {
2453
- "@shell-shock/plugin-theme/types/theme": [{
2454
- name: "ThemeColorsResolvedConfig",
2455
- type: true
2456
- }, {
2457
- name: "ThemeSpinnerResolvedConfig",
2458
- type: true
2459
- }],
2460
- "@shell-shock/plugin-theme/helpers/spinners": [{
2461
- name: "SpinnerPreset",
2462
- type: true
2463
- }, { name: "resolveSpinner" }],
2464
- "node:buffer": ["WithImplicitCoercion"],
2506
+ "@shell-shock/plugin-theme/types/theme": ["ThemeSpinnerResolvedConfig"],
2507
+ "@shell-shock/plugin-theme/helpers/spinners": ["SpinnerPreset", "resolveSpinner"],
2465
2508
  "node:util": ["stripVTControlCharacters"]
2466
2509
  });
2467
2510
  },
2468
2511
  get builtinImports() {
2469
2512
  return defu(builtinImports, {
2470
2513
  utils: [
2471
- "isMinimal",
2472
2514
  "isInteractive",
2473
2515
  "isColorSupported",
2474
2516
  "colorSupportLevels",
@@ -2582,7 +2624,7 @@ function ConsoleBuiltin(props) {
2582
2624
  timestamp: true,
2583
2625
  parameters: [{
2584
2626
  name: "err",
2585
- type: "string | Error",
2627
+ type: "string | { message: string; stack?: string }",
2586
2628
  optional: false
2587
2629
  }, {
2588
2630
  name: "header",
@@ -2598,19 +2640,26 @@ function ConsoleBuiltin(props) {
2598
2640
  }),
2599
2641
  createComponent(Spacing, {}),
2600
2642
  createComponent(IfStatement, {
2601
- condition: code`(err as Error)?.message`,
2602
- children: code`message = (err as Error).message;`
2643
+ condition: code`(err as { message: string; stack?: string })?.message`,
2644
+ children: code`message = (err as { message: string; stack?: string }).message;`
2603
2645
  }),
2604
2646
  createComponent(ElseClause, { children: code`message = String(err);` }),
2605
2647
  createComponent(Spacing, {}),
2606
2648
  createComponent(IfStatement, {
2607
- condition: code`env.STACKTRACE`,
2608
- get children() {
2609
- return [createComponent(IfStatement, {
2610
- condition: code`(err as Error)?.stack`,
2611
- children: code`message += " \\n\\n" + ((err as Error).stack || "");`
2612
- }), createComponent(ElseClause, { children: code`message += " \\n\\n" + ((new Error(" ")).stack || "").split("\\n").slice(2).map(line => line.trim()).join("\\n");` })];
2613
- }
2649
+ condition: code`env.STACKTRACE && typeof err === "object" && (err as { stack?: string })?.stack`,
2650
+ children: code`message += " \\n\\n" + (err as { stack?: string })?.stack
2651
+ .split("\\n")
2652
+ .slice(1)
2653
+ .map(line => {
2654
+ const match = line.match(/at (?:(.+?)\\s+\\()?(?:(.+?):(\\d+)(?::(\\d+))?|([^)]+))\\)?/);
2655
+ if (match) {
2656
+ const filePath = match[2] || match[5] || "<unknown>";
2657
+ return \`at \${match[1] || "<anonymous>"} (\${filePath === "<anonymous>" || filePath === "<unknown>" ? filePath : link(filePath, { text: \`\${filePath.replace(/^.*file:\\/\\//, "")}\${match[3] ? \`:\${match[3]}\${match[4] ? \`:\${match[4]}\` : ""}\` : ""}\`, useTextWhenUnsupported: true })})\`;
2658
+ }
2659
+
2660
+ return line.trim();
2661
+ })
2662
+ .join("\\n"); `
2614
2663
  })
2615
2664
  ];
2616
2665
  }