@theholocron/cli 1.0.0 → 1.1.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.
Files changed (104) hide show
  1. package/LICENSE +21 -674
  2. package/README.md +19 -8
  3. package/dist/capabilities/index.d.mts +2 -0
  4. package/dist/capabilities/index.mjs +2 -0
  5. package/dist/capabilities-QjjhVlDd.mjs +43 -0
  6. package/dist/cli.d.mts +1 -0
  7. package/dist/cli.mjs +852 -0
  8. package/dist/config-DWlIfFZm.mjs +113 -0
  9. package/dist/index-CSxf0Yc7.d.mts +485 -0
  10. package/dist/index.d.mts +58 -0
  11. package/dist/index.mjs +3 -0
  12. package/package.json +46 -53
  13. package/.alexignore +0 -3
  14. package/.editorconfig +0 -24
  15. package/.editorconfig-checker.json +0 -20
  16. package/.env +0 -3
  17. package/.gitattributes +0 -12
  18. package/.github/CODEOWNERS +0 -1
  19. package/.github/dependabot.yml +0 -6
  20. package/.github/labeler.yml +0 -21
  21. package/.github/workflows/bookkeeping-pr.yml +0 -32
  22. package/.github/workflows/greetings.yml +0 -32
  23. package/.github/workflows/lint.yml +0 -96
  24. package/.github/workflows/publish.yml +0 -71
  25. package/.github/workflows/review.yml +0 -72
  26. package/.github/workflows/stale.yml +0 -25
  27. package/.husky/commit-msg +0 -1
  28. package/.husky/pre-commit +0 -13
  29. package/.husky/prepare-commit-msg +0 -18
  30. package/commitlint.config.js +0 -9
  31. package/eslint.config.js +0 -9
  32. package/media/README.md +0 -11
  33. package/media/error.mp3 +0 -0
  34. package/media/success.mp3 +0 -0
  35. package/media/warning.mp3 +0 -0
  36. package/prettier.config.js +0 -11
  37. package/src/cli.ts +0 -65
  38. package/src/commands/README.md +0 -13
  39. package/src/commands/bootstrap.ts +0 -102
  40. package/src/commands/conf/README.md +0 -45
  41. package/src/commands/conf/add.ts +0 -49
  42. package/src/commands/conf/edit.ts +0 -12
  43. package/src/commands/conf/view.ts +0 -40
  44. package/src/commands/conf.ts +0 -10
  45. package/src/commands/log.ts +0 -91
  46. package/src/const.ts +0 -19
  47. package/src/tasks/README.md +0 -33
  48. package/src/tasks/find/README.md +0 -49
  49. package/src/tasks/find/find-project.example.ts +0 -23
  50. package/src/tasks/find/find-project.ts +0 -76
  51. package/src/tasks/find/index.ts +0 -1
  52. package/src/tasks/index.ts +0 -2
  53. package/src/tasks/replace/README.md +0 -61
  54. package/src/tasks/replace/index.ts +0 -1
  55. package/src/tasks/replace/replace.example.ts +0 -31
  56. package/src/tasks/replace/replace.ts +0 -95
  57. package/src/ui/README.md +0 -8
  58. package/src/ui/index.ts +0 -2
  59. package/src/ui/open/README.md +0 -58
  60. package/src/ui/open/index.ts +0 -7
  61. package/src/ui/open/open-browser.ts +0 -25
  62. package/src/ui/open/open-editor.example.ts +0 -14
  63. package/src/ui/open/open-editor.ts +0 -30
  64. package/src/ui/prompts/README.md +0 -36
  65. package/src/ui/prompts/autocomplete.prompt.ts +0 -18
  66. package/src/ui/prompts/confirm.prompt.ts +0 -21
  67. package/src/ui/prompts/index.ts +0 -17
  68. package/src/ui/prompts/input.prompt.ts +0 -15
  69. package/src/ui/prompts/search.prompt.ts +0 -33
  70. package/src/ui/prompts/select.prompt.ts +0 -17
  71. package/src/ui/prompts/types.ts +0 -8
  72. package/src/ui/prompts/utils.ts +0 -11
  73. package/src/utils/$/README.md +0 -87
  74. package/src/utils/$/command.test.ts +0 -48
  75. package/src/utils/$/command.ts +0 -21
  76. package/src/utils/$/directory.ts +0 -108
  77. package/src/utils/$/file.ts +0 -98
  78. package/src/utils/$/index.example.ts +0 -37
  79. package/src/utils/$/index.ts +0 -22
  80. package/src/utils/$/path.ts +0 -13
  81. package/src/utils/$/remove.ts +0 -47
  82. package/src/utils/$/spawn.ts +0 -26
  83. package/src/utils/$/utils.ts +0 -21
  84. package/src/utils/README.md +0 -15
  85. package/src/utils/config/README.md +0 -41
  86. package/src/utils/config/config.ts +0 -26
  87. package/src/utils/config/index.ts +0 -1
  88. package/src/utils/config/preferences.conf.ts +0 -45
  89. package/src/utils/env/README.md +0 -52
  90. package/src/utils/env/env.example.ts +0 -21
  91. package/src/utils/env/env.ts +0 -57
  92. package/src/utils/env/index.ts +0 -1
  93. package/src/utils/index.ts +0 -5
  94. package/src/utils/log/README.md +0 -117
  95. package/src/utils/log/index.ts +0 -2
  96. package/src/utils/log/log.example.ts +0 -13
  97. package/src/utils/log/log.ts +0 -58
  98. package/src/utils/log/logger.ts +0 -28
  99. package/src/utils/log/sound.ts +0 -35
  100. package/src/utils/node/README.md +0 -23
  101. package/src/utils/node/index.ts +0 -5
  102. package/src/utils/node/package.ts +0 -12
  103. package/tsconfig.json +0 -15
  104. package/yamllint.config.yml +0 -20
@@ -1,61 +0,0 @@
1
- # Find & Replace
2
-
3
- Find a string in a file and replace it with something else.
4
-
5
- ## Usage
6
-
7
- ```javascript
8
- import { findReplace } from "@/tasks";
9
-
10
- const filePaths = [
11
- `/packages/developer-experience/sdk-session/src/tests/handlers.ts`,
12
- ];
13
- const baseString: string = "const BAM_BROWSER_SDK_VERSION =";
14
-
15
- async function main() {
16
- const [, , version] = process.argv;
17
- const [major, minor] = version.split(".");
18
-
19
- try {
20
- const [err, data, duration] = await findReplace(filePaths, baseString, `${major}.${minor}`);
21
-
22
- if (err) {
23
- console.error("Error:", err);
24
- process.exit(1);
25
- }
26
-
27
- console.log("Files replaced:\n");
28
- console.table(data);
29
- console.log(`It took ${duration} milliseconds`);
30
- }
31
- catch (error) {
32
- console.error("Error:", error);
33
- }
34
- }
35
-
36
- main();
37
- ```
38
-
39
- ## API
40
-
41
- ### `findReplace(filePaths: string[], searchTerm: string, replacement: string)`
42
-
43
- Returns an array of file paths that were modified.
44
-
45
- #### filePaths
46
-
47
- The file paths with which to search.
48
-
49
- Type: `string[]`
50
-
51
- #### searchTerm
52
-
53
- The search term to look for and replace.
54
-
55
- Type: `string`
56
-
57
- #### replacement
58
-
59
- A string to replace with
60
-
61
- Type: `string`
@@ -1 +0,0 @@
1
- export * from "./replace";
@@ -1,31 +0,0 @@
1
- import { HOME } from "@/const";
2
- import { find } from "@/tasks";
3
-
4
- const filePaths = [
5
- `${HOME}/Code/disney/solo/apps/commerce/src/common/context/__browser-mocks__/handlers.ts`,
6
- `${HOME}/Code/disney/solo/packages/developer-experience/sdk-session/src/tests/handlers.ts`,
7
- `${HOME}/Code/disney/solo/packages/instrumentation/src/glimpse/hooks/use-fire-fed-purchase-event.test.tsx`,
8
- ];
9
- const baseString: string = "const BAM_BROWSER_SDK_VERSION =";
10
-
11
- async function main() {
12
- const [, , version] = process.argv;
13
- const [major, minor] = version.split(".");
14
-
15
- try {
16
- const [err, data, duration] = await find.replace(filePaths, baseString, `${major}.${minor}`);
17
-
18
- if (err) {
19
- console.error("Error:", err);
20
- process.exit(1);
21
- }
22
-
23
- console.log("Files replaced:\n");
24
- console.table(data);
25
- console.log(`It took ${duration} milliseconds`);
26
- } catch (error) {
27
- console.error("Error:", error);
28
- }
29
- }
30
-
31
- main();
@@ -1,95 +0,0 @@
1
- import { type CLIOptions } from "@/cli";
2
- import { type Return } from "@/types";
3
- import { $, log } from "@/utils";
4
-
5
- const FN = "find.replace";
6
-
7
- /**
8
- * Perform a case-sensitive replacement in a string, keeping the casing format of each found instance.
9
- *
10
- * @param {string} text - The text in which to perform the replacement.
11
- * @param {string} searchTerm - The term to search for, ignoring case.
12
- * @param {string} replacement - The replacement term.
13
- * @returns {string} - The updated text with replacements made.
14
- */
15
- function replaceWithRespectToCase(text: string, searchTerm: string, replacement: string): string {
16
- // Create a regex to match search term with flexible spacing/hyphens between words
17
- const words = searchTerm.split(/[- ]/).map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")); // Escape special chars
18
- const regex = new RegExp(words.join("[- ]?"), "gi"); // Allow for spaces or hyphens between words
19
-
20
- return text.replace(regex, (match) => {
21
- // Transform replacement to match the case of the original found match
22
- const replacementWords = replacement.split(/[- ]/);
23
- const transformed = match
24
- .split(/[- ]/)
25
- .map((word, index) => {
26
- const replacementWord = replacementWords[index] || ""; // Handle possible extra words in replacement
27
- return /[A-Z]/.test(word[0])
28
- ? replacementWord.charAt(0).toUpperCase() + replacementWord.slice(1)
29
- : replacementWord.toLowerCase();
30
- })
31
- .join(" ");
32
-
33
- return transformed.trim();
34
- });
35
- }
36
-
37
- /**
38
- * Update a specific string in multiple files.
39
- *
40
- * @param {string[]} filePaths - An array of file paths to be updated.
41
- * @param {string} searchTerm - The base string to match.
42
- * @param {string} replacement - The new string to replace with.
43
- * @param {CLIOptions} [options={}] - CLI options for logging and additional processing.
44
- * @returns {Promise<Return<string[]>>} - A promise that resolves to a tuple containing an error (if any), an object with the update status for each domain, and the time taken.
45
- */
46
- export async function findReplace(
47
- filePaths: string[],
48
- searchTerm: string,
49
- replacement: string,
50
- options: CLIOptions = {}
51
- ): Promise<Return<string[]>> {
52
- if (!filePaths || !Array.isArray(filePaths)) {
53
- return [new Error("No file paths were provided!"), null, 0];
54
- }
55
-
56
- if (!searchTerm) {
57
- return [new Error("No search term was provided!"), null, 0];
58
- }
59
-
60
- if (!replacement) {
61
- return [new Error("No replacement was provided!"), null, 0];
62
- }
63
-
64
- const start = performance.now();
65
- const output: string[] = [];
66
-
67
- for (const filePath of filePaths) {
68
- log.process(FN, `Searching ${filePath} for occurrences of "${searchTerm}"`, options);
69
-
70
- try {
71
- // Read the file content
72
- const [fileErr, file] = await $.file(filePath);
73
- if (fileErr || !file?.content) {
74
- log.info(FN, `Cannot read file ${filePath}: skipping`, options);
75
- continue;
76
- }
77
-
78
- // Perform the replacement
79
- const updatedData = replaceWithRespectToCase(file.content, searchTerm, replacement);
80
-
81
- // Write back only if the content changed
82
- if (updatedData !== file.content) {
83
- await $.file(filePath, updatedData, true); // Ensure this function overwrites the file content
84
- output.push(filePath);
85
- log.process(FN, `Updated ${filePath}`, options);
86
- } else {
87
- log.process(FN, `No changes in ${filePath}`, options);
88
- }
89
- } catch (error) {
90
- log.error(FN, `Error processing file ${filePath}: ${(error as Error).message}`, options);
91
- }
92
- }
93
-
94
- return [null, output, performance.now() - start];
95
- }
package/src/ui/README.md DELETED
@@ -1,8 +0,0 @@
1
- # UI
2
-
3
- A set of functions designed to be used as a UI for interacting with the client.
4
-
5
- ## What's Available
6
-
7
- * [open](./open/README.md) - open an app in your `$EDITOR`
8
- * [prompts](./prompts/README.md) - prompt the user with various UI tools
package/src/ui/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export { open } from "./open";
2
- export { prompt, type Choice } from "./prompts";
@@ -1,58 +0,0 @@
1
- # Open an Application
2
-
3
- Open an application after a confirmation.
4
-
5
- ## Usage
6
-
7
- ```javascript
8
- import { open } from "@/ui";
9
-
10
- async function main () {
11
- const [, , name, location] = process.argv;
12
-
13
- try {
14
- const [err, isOpened] = await open.editor(name, location);
15
-
16
- if (err) {
17
- console.error(err);
18
- process.exit(1);
19
- }
20
-
21
- console.log(`${name} ${isOpened ? "was" : "was not"} opened.`);
22
- }
23
- catch (error) {
24
- console.error('An error occurred:', error);
25
- process.exit(1);
26
- }
27
- }
28
-
29
- main();
30
- ```
31
-
32
- ## API
33
-
34
- ### `editor(name: string, location: string)`
35
-
36
- Returns a Promise that resolves to a boolean.
37
-
38
- #### name
39
-
40
- A name of the application.
41
-
42
- Type: `string`
43
-
44
- #### location
45
-
46
- A path of where the application is located.
47
-
48
- Type: `string`
49
-
50
- ### `browser(url: string)`
51
-
52
- Opens the URL in the users default browser.
53
-
54
- #### url
55
-
56
- The URL to open.
57
-
58
- Type: `string`
@@ -1,7 +0,0 @@
1
- import { openBrowser } from "./open-browser";
2
- import { openApp } from "./open-editor";
3
-
4
- export const open = {
5
- browser: openBrowser,
6
- editor: openApp,
7
- };
@@ -1,25 +0,0 @@
1
- import defaultBrowser from "default-browser";
2
- import open, { apps } from "open";
3
-
4
- /**
5
- * Opens an application if the user confirms.
6
- *
7
- * @param {string} name - The name of the application.
8
- * @returns {Promise<void>} - A promise that resolves to a tuple containing an error (if any), a boolean indicating if the app was opened, and the time taken to perform the operation.
9
- */
10
- export async function openBrowser(url: string): Promise<void> {
11
- if (!url) {
12
- throw new Error("No URL was provided!");
13
- }
14
-
15
- let browser = apps.browser;
16
- const { name } = await defaultBrowser();
17
- const supported = ["chrome", "firefox", "edge"];
18
- if (!supported.includes(name.toLowerCase())) {
19
- browser = apps.chrome;
20
- }
21
-
22
- await open(url, {
23
- app: { name: browser },
24
- });
25
- }
@@ -1,14 +0,0 @@
1
- import { open } from "@/ui";
2
-
3
- async function main() {
4
- const [, , name, location] = process.argv;
5
-
6
- try {
7
- await open.editor(name, location);
8
- } catch (error) {
9
- console.error("An error occurred:", error);
10
- process.exit(1);
11
- }
12
- }
13
-
14
- main();
@@ -1,30 +0,0 @@
1
- import openEditor from "open-editor";
2
- import { prompt } from "@/ui";
3
-
4
- /**
5
- * Opens an application if the user confirms.
6
- *
7
- * @param {string} name - The name of the application.
8
- * @param {string} location - The file location of the application.
9
- * @returns {Promise<void>}
10
- */
11
- export async function openApp(location: string, name?: string, bypass: boolean = false): Promise<void> {
12
- if (!location) {
13
- throw new Error("No app location was provided!");
14
- }
15
- const open = async () =>
16
- await openEditor(
17
- [
18
- {
19
- file: location,
20
- },
21
- ],
22
- { wait: true }
23
- );
24
-
25
- if (!bypass) {
26
- await prompt.confirm(`Do you want to open ${name}?`, open);
27
- } else {
28
- open();
29
- }
30
- }
@@ -1,36 +0,0 @@
1
- # Prompts
2
-
3
- Inquirer prompts
4
-
5
- ## Usage
6
-
7
- ```javascript
8
- import { prompt } from "@/ui";
9
-
10
- async function main() {
11
- const [, , project] = process.argv;
12
-
13
- try {
14
- const filepath = await prompt.search(project, undefined, undefined, {});
15
- console.log(filepath);
16
- }
17
- catch (error) {
18
- console.error("Error:", error);
19
- }
20
- }
21
-
22
- main();
23
- ```
24
-
25
- ## API
26
-
27
- ### `project(message?: string)`
28
-
29
- Returns a string of the directory selected.
30
-
31
- #### message
32
-
33
- The message to display to the user.
34
-
35
- Type: `string`
36
- Default: "Enter a project:"
@@ -1,18 +0,0 @@
1
- import autocomplete from "inquirer-autocomplete-standalone";
2
- import { type Choice } from "./types";
3
-
4
- export async function autocompletePrompt(source: Choice<string>[], msg?: string): Promise<string> {
5
- return await autocomplete({
6
- emptyText: "No results found. Please enter a term",
7
- message: msg || "Search a term",
8
- source: async (input?: string) =>
9
- source.filter(({ name }) => {
10
- const proj = name?.toLowerCase() ?? "";
11
- if (proj.length > 0) {
12
- return proj.includes(input?.toLowerCase() ?? "");
13
- }
14
-
15
- return "";
16
- }),
17
- });
18
- }
@@ -1,21 +0,0 @@
1
- import { confirm } from "@inquirer/prompts";
2
-
3
- type Callback = () => void | Promise<void>;
4
-
5
- export async function confirmPrompt(
6
- message: string,
7
- callback: Callback,
8
- error: Callback = () => {
9
- /* noOp */
10
- }
11
- ): Promise<void> {
12
- const should = await confirm({
13
- message,
14
- });
15
-
16
- if (should) {
17
- await callback();
18
- } else {
19
- await error();
20
- }
21
- }
@@ -1,17 +0,0 @@
1
- import { autocompletePrompt } from "./autocomplete.prompt";
2
- import { confirmPrompt } from "./confirm.prompt";
3
- import { inputPrompt } from "./input.prompt";
4
- import { searchPrompt } from "./search.prompt";
5
- import { selectPrompt } from "./select.prompt";
6
- import { validate } from "./utils";
7
-
8
- export * from "./types";
9
-
10
- export const prompt = {
11
- autocomplete: autocompletePrompt,
12
- confirm: confirmPrompt,
13
- input: inputPrompt,
14
- search: searchPrompt,
15
- select: selectPrompt,
16
- validate,
17
- };
@@ -1,15 +0,0 @@
1
- import { input } from "@inquirer/prompts";
2
- import { validate } from "./utils";
3
-
4
- export async function inputPrompt(message: string, isRequired?: boolean = false, options): Promise<string> {
5
- const opts = {
6
- message,
7
- ...options,
8
- };
9
-
10
- if (isRequired) {
11
- opts.validate = validate.isNotEmpty;
12
- }
13
-
14
- return await input(opts);
15
- }
@@ -1,33 +0,0 @@
1
- import autocomplete from "inquirer-autocomplete-standalone";
2
- import { type CLIOptions } from "@/cli";
3
- import { findProjects } from "@/tasks";
4
- import { $, getDirectoryByLevel } from "@/utils";
5
-
6
- export async function searchPrompt(location?: string, msg?: string, options: CLIOptions): Promise<string> {
7
- const message = msg || "Choose a project:";
8
-
9
- const dirname: string = await autocomplete({
10
- emptyText: "No results found. Please enter the location",
11
- message,
12
- pageSize: 10,
13
- source: async (input?: string) => {
14
- const term = input?.toLowerCase() ?? "";
15
- const [, results] = await findProjects(location, options);
16
- const filtered = results
17
- .map(({ name, value }) => ({ name: `${name} (${getDirectoryByLevel(value)})`, value }))
18
- .filter(({ name }) => {
19
- const proj = name?.toLowerCase() ?? "";
20
- if (proj.length > 0) {
21
- return proj.includes(term);
22
- }
23
-
24
- return "";
25
- });
26
-
27
- return filtered;
28
- },
29
- });
30
-
31
- const [, dir] = await $.directory(dirname);
32
- return dir?.path ?? "";
33
- }
@@ -1,17 +0,0 @@
1
- import { checkbox } from "@inquirer/prompts";
2
- import { type Choice } from "./types";
3
-
4
- export async function selectPrompt(
5
- selected: string[] | null,
6
- message: string,
7
- choices: Choice<string>[]
8
- ): Promise<string[]> {
9
- if (selected) {
10
- return selected;
11
- }
12
-
13
- return await checkbox({
14
- message,
15
- choices,
16
- });
17
- }
@@ -1,8 +0,0 @@
1
- export type Choice<Value> = {
2
- description?: string;
3
- disabled?: boolean | string;
4
- checked?: boolean;
5
- name?: string;
6
- type?: never;
7
- value: Value;
8
- };
@@ -1,11 +0,0 @@
1
- function isNotEmpty(val: string): boolean | string {
2
- if (!val || val.trim().length === 0) {
3
- return "Cannot be empty";
4
- }
5
-
6
- return true;
7
- }
8
-
9
- export const validate = {
10
- isNotEmpty,
11
- };
@@ -1,87 +0,0 @@
1
- # $
2
-
3
- Interact with the file system or run commands.
4
-
5
- ## Usage
6
-
7
- ```javascript
8
- import { $ } from "@/utils";
9
-
10
- async function main() {
11
- const hasBrew = await $.command("brew");
12
- if (hasBrew) {
13
- const [err, data] = await $("brew install test");
14
-
15
- if (err) {
16
- console.error("Error:", err);
17
- process.exit(1);
18
- }
19
-
20
- console.log(data);
21
- }
22
- }
23
-
24
- main();
25
- ```
26
-
27
- ## API
28
-
29
- ### `$(command: string)`
30
-
31
- Run a shell command. Returns a Promise that will resolve to either an error or the standard output.
32
-
33
- #### command
34
-
35
- The shell command to run.
36
-
37
- Type: `string`
38
-
39
- ### `.command(command: string)`
40
-
41
- Checks whether a bash command exists. Returns a promise resolving to true if the command is installed, false otherwise.
42
-
43
- #### command
44
-
45
- The shell command to check.
46
-
47
- Type: `string`
48
-
49
- ### `.directory(path: string, maxDepth: number)`
50
-
51
- Returns a Promise that will resolve to a tuple with either an error, or an object with `files` and the `path`.
52
-
53
- #### path
54
-
55
- The path to the file.
56
-
57
- Type: `string`
58
-
59
- #### maxDepth
60
-
61
- The maximum levels of directories to look through.
62
-
63
- Type: `number`
64
- Default: `Infinity`
65
-
66
- ### `.file(path: string, content: string | Buffer, overwrite: boolean)`
67
-
68
- Returns a Promise that will resolve to a tuple with either an error, or an object with `content` and the `path`.
69
-
70
- #### path
71
-
72
- The path to the file.
73
-
74
- Type: `string`
75
-
76
- #### content
77
-
78
- The contents of the file to write or append.
79
-
80
- Type: `string | Buffer`
81
-
82
- #### overwrite
83
-
84
- Whether to append to the file or overwrite them completely.
85
-
86
- Type: `boolean`
87
- Default: `false`
@@ -1,48 +0,0 @@
1
- import { command, isInstalled } from "./command";
2
-
3
- describe("command", () => {
4
- test("execute command and return stdout", async () => {
5
- const cmd = "echo Hello, world!";
6
-
7
- const result = await command(cmd);
8
-
9
- expect(result).toEqual([null, "Hello, world!\n", expect.any(Number)]);
10
- });
11
-
12
- describe("errors", () => {
13
- test("return error if command fails", async () => {
14
- const result = await command("{}");
15
-
16
- expect(result[0]).toBeDefined();
17
- expect(result[1]).toBeNull();
18
- expect(result[2]).toEqual(expect.any(Number));
19
- });
20
-
21
- test("return 'Unknown error' if error is not an instance of Error", async () => {
22
- const unexpectedError = "This is not an Error object";
23
-
24
- const result = await command("valid-command-that-will-throw-an-error", { unexpectedError });
25
-
26
- expect(result[1]).toBeNull();
27
- expect(result[2]).toEqual(expect.any(Number));
28
- });
29
- });
30
- });
31
-
32
- describe("isInstalled", () => {
33
- test("return true if command is installed", async () => {
34
- const cmd = "ls"; // Assuming 'ls' command is installed on your system
35
-
36
- const result = await isInstalled(cmd);
37
-
38
- expect(result).toBe(true);
39
- });
40
-
41
- test("return false if command is not installed", async () => {
42
- const cmd = "nonexistent-command";
43
-
44
- const result = await isInstalled(cmd);
45
-
46
- expect(result).toBe(false);
47
- });
48
- });
@@ -1,21 +0,0 @@
1
- import { exec } from "node:child_process";
2
- import { promisify } from "node:util";
3
- import { type Return } from "@/types";
4
-
5
- const asyncExec = promisify(exec);
6
-
7
- export async function command(cmd: string, options: object = {}): Promise<Return<string>> {
8
- const start = performance.now();
9
-
10
- try {
11
- const { stdout } = await asyncExec(cmd, options);
12
- return [null, stdout, performance.now() - start];
13
- } catch (error) {
14
- return [error as Error, null, performance.now() - start];
15
- }
16
- }
17
-
18
- export async function isInstalled(cmd: string, options: object = {}): Promise<boolean> {
19
- const [err] = await command(`command -v ${cmd}`, options);
20
- return !err;
21
- }