ui-thing 0.2.9 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-thing",
3
- "version": "0.2.9",
3
+ "version": "0.3.0",
4
4
  "description": "CLI used to add Nuxt components to a project",
5
5
  "keywords": [
6
6
  "cli",
@@ -51,20 +51,20 @@
51
51
  ]
52
52
  },
53
53
  "dependencies": {
54
- "axios": "^1.17.0",
54
+ "axios": "^1.18.1",
55
55
  "boxen": "^8.0.1",
56
56
  "c12": "^3.3.4",
57
57
  "commander": "^15.0.0",
58
58
  "consola": "^3.4.2",
59
59
  "dotenv": "^17.4.2",
60
- "es-toolkit": "^1.47.0",
60
+ "es-toolkit": "^1.48.1",
61
61
  "execa": "^9.6.1",
62
62
  "figlet": "^1.11.0",
63
63
  "fs-extra": "^11.3.5",
64
64
  "kleur": "^4.1.5",
65
65
  "lodash": "^4.18.1",
66
66
  "magicast": "^0.5.3",
67
- "ora": "^9.4.0",
67
+ "ora": "^9.4.1",
68
68
  "prompts": "^2.4.2"
69
69
  },
70
70
  "devDependencies": {
@@ -72,24 +72,25 @@
72
72
  "@ianvs/prettier-plugin-sort-imports": "^4.7.1",
73
73
  "@types/fs-extra": "^11.0.4",
74
74
  "@types/lodash": "^4.17.24",
75
- "@types/node": "^25.9.2",
75
+ "@types/node": "^26.0.0",
76
76
  "@types/prompts": "^2.4.9",
77
- "@vitest/coverage-v8": "^4.1.8",
78
- "@vitest/ui": "^4.1.8",
77
+ "@vitest/coverage-v8": "^4.1.9",
78
+ "@vitest/ui": "^4.1.9",
79
79
  "axios-mock-adapter": "^2.1.0",
80
- "eslint": "^10.4.1",
81
- "globals": "^17.6.0",
80
+ "eslint": "^10.5.0",
81
+ "globals": "^17.7.0",
82
82
  "husky": "^9.1.7",
83
83
  "jiti": "^2.7.0",
84
- "knip": "^5.88.1",
85
- "lint-staged": "^17.0.7",
84
+ "knip": "^6.18.0",
85
+ "lint-staged": "^17.0.8",
86
86
  "prettier": "^3.8.4",
87
+ "prettier-plugin-packagejson": "^3.0.2",
87
88
  "tsup": "^8.5.1",
88
89
  "typescript": "^6.0.3",
89
- "typescript-eslint": "^8.61.0",
90
- "vitest": "^4.1.8"
90
+ "typescript-eslint": "^8.62.0",
91
+ "vitest": "^4.1.9"
91
92
  },
92
- "packageManager": "bun@1.3.9",
93
+ "packageManager": "bun@1.3.14",
93
94
  "publishConfig": {
94
95
  "access": "public"
95
96
  }
@@ -13,6 +13,7 @@ import { fetchComponents } from "../utils/fetchComponents";
13
13
  import { fileExists } from "../utils/fileExists";
14
14
  import { installPackages } from "../utils/installPackages";
15
15
  import { installValidator } from "../utils/installValidator";
16
+ import { logger } from "../utils/logger";
16
17
  import { printFancyBoxMessage } from "../utils/printFancyBoxMessage";
17
18
  import { promptUserForComponents } from "../utils/promptForComponents";
18
19
  import { writeFile } from "../utils/writeFile";
@@ -84,8 +85,8 @@ const runAddCommand = async (components: string[], options: AddCommand) => {
84
85
  uiConfig = await getUIConfig({ force: true });
85
86
  }
86
87
  if (_.isEmpty(uiConfig)) {
87
- consola.info("Config file not set. Exiting...");
88
- process.exit(0);
88
+ consola.error("Config file not set. Exiting...");
89
+ process.exit(1);
89
90
  }
90
91
 
91
92
  // Step 2 — Fetch all available components
@@ -206,11 +207,14 @@ const runAddCommand = async (components: string[], options: AddCommand) => {
206
207
  { box: { title: "Components Added" } }
207
208
  );
208
209
 
210
+ found
211
+ .filter((c) => c.docsPath)
212
+ .forEach((c) => consola.info(`Docs: https://uithing.com${c.docsPath}`));
213
+
209
214
  const instructions = _.compact(found.flatMap((c) => c.instructions));
210
215
  if (instructions.length > 0) {
211
- console.log("");
212
- console.log(kleur.bgCyan(" Instructions "));
213
- instructions.forEach((i) => console.log(`${kleur.cyan("-")} ${i}`));
216
+ logger.info("Instructions:");
217
+ instructions.forEach((i) => logger.info(`- ${i}`));
214
218
  }
215
219
  };
216
220
 
@@ -59,8 +59,8 @@ const runBlockCommand = async (components: string[], options: BlockOptions) => {
59
59
  uiConfig = await getUIConfig({ force: true });
60
60
  }
61
61
  if (_.isEmpty(uiConfig)) {
62
- consola.info("Config file not set. Exiting...");
63
- process.exit(0);
62
+ consola.error("Config file not set. Exiting...");
63
+ process.exit(1);
64
64
  }
65
65
 
66
66
  // Step 1: Fetch categories
@@ -145,7 +145,9 @@ const runBlockCommand = async (components: string[], options: BlockOptions) => {
145
145
  stdio: "inherit",
146
146
  });
147
147
  if (result.error) {
148
- consola.error("Failed to add components:", result.error.message);
148
+ consola.error("Failed to spawn component installer:", result.error.message);
149
+ } else if (result.status !== 0) {
150
+ consola.error(`Component installer exited with code ${result.status}`);
149
151
  }
150
152
  }
151
153
 
@@ -0,0 +1,48 @@
1
+ import path from "node:path";
2
+ import { Command } from "commander";
3
+
4
+ import { Component } from "../types";
5
+ import { getUIConfig } from "../utils/config";
6
+ import { fetchComponents } from "../utils/fetchComponents";
7
+ import { fileExists } from "../utils/fileExists";
8
+ import { logger } from "../utils/logger";
9
+
10
+ type ListOptions = { installed: boolean };
11
+
12
+ export const runListCommand = async (options: ListOptions) => {
13
+ const uiConfig = await getUIConfig();
14
+ const components = await fetchComponents();
15
+ const currentDirectory = process.cwd();
16
+
17
+ let display: Component[] = components;
18
+
19
+ if (options.installed) {
20
+ const results = await Promise.all(
21
+ components.map(async (c) => {
22
+ if (!c.files || c.files.length === 0) return false;
23
+ const targetPath = path.join(
24
+ currentDirectory,
25
+ uiConfig.componentsLocation,
26
+ c.files[0].fileName
27
+ );
28
+ return fileExists(targetPath);
29
+ })
30
+ );
31
+ display = components.filter((_, i) => results[i]);
32
+ }
33
+
34
+ if (display.length === 0) {
35
+ logger.info(options.installed ? "No components installed." : "No components available.");
36
+ return;
37
+ }
38
+
39
+ display.forEach((c) => logger.log(`${c.name} (${c.value})`));
40
+ logger.success(`${display.length} component(s) found.`);
41
+ };
42
+
43
+ export const list = new Command()
44
+ .command("list")
45
+ .name("list")
46
+ .description("List available or installed components.")
47
+ .option("-i --installed", "Only show installed components.", false)
48
+ .action(runListCommand);
@@ -71,8 +71,8 @@ const runProseCommand = async (components: string[], options: AddCommand) => {
71
71
  uiConfig = await getUIConfig({ force: true });
72
72
  }
73
73
  if (_.isEmpty(uiConfig)) {
74
- consola.info("Config file not set. Exiting...");
75
- process.exit(0);
74
+ consola.error("Config file not set. Exiting...");
75
+ process.exit(1);
76
76
  }
77
77
 
78
78
  allProse = await fetchProseComponents();
@@ -180,7 +180,9 @@ const runProseCommand = async (components: string[], options: AddCommand) => {
180
180
  stdio: "inherit",
181
181
  });
182
182
  if (result.error) {
183
- consola.error("Failed to add components:", result.error.message);
183
+ consola.error("Failed to spawn component installer:", result.error.message);
184
+ } else if (result.status !== 0) {
185
+ consola.error(`Component installer exited with code ${result.status}`);
184
186
  }
185
187
  }
186
188
 
@@ -189,6 +191,8 @@ const runProseCommand = async (components: string[], options: AddCommand) => {
189
191
  `Run the ${kleur.cyan("ui-thing@latest --help")} command to learn more.\n`,
190
192
  { box: { title: "Prose Components Added" } }
191
193
  );
194
+
195
+ found.filter((c) => c.docsUrl).forEach((c) => consola.info(`Docs: ${c.docsUrl}`));
192
196
  };
193
197
 
194
198
  export const prose = new Command()
@@ -0,0 +1,109 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { Command } from "commander";
4
+ import prompts from "prompts";
5
+
6
+ import { Component } from "../types";
7
+ import { getUIConfig } from "../utils/config";
8
+ import { fetchComponents } from "../utils/fetchComponents";
9
+ import { fileExists } from "../utils/fileExists";
10
+ import { logger } from "../utils/logger";
11
+ import { printFancyBoxMessage } from "../utils/printFancyBoxMessage";
12
+
13
+ export const runRemoveCommand = async (componentNames: string[]) => {
14
+ const uiConfig = await getUIConfig();
15
+ const allComponents = await fetchComponents();
16
+ const currentDirectory = process.cwd();
17
+
18
+ let toRemove = componentNames;
19
+
20
+ if (toRemove.length === 0) {
21
+ const installed = (
22
+ await Promise.all(
23
+ allComponents.map(async (c) => {
24
+ if (!c.files || c.files.length === 0) return null;
25
+ const targetPath = path.join(
26
+ currentDirectory,
27
+ uiConfig.componentsLocation,
28
+ c.files[0].fileName
29
+ );
30
+ return (await fileExists(targetPath)) ? c : null;
31
+ })
32
+ )
33
+ ).filter(Boolean) as Component[];
34
+
35
+ if (installed.length === 0) {
36
+ logger.info("No installed components found.");
37
+ return;
38
+ }
39
+
40
+ const { selected } = await prompts({
41
+ type: "autocompleteMultiselect",
42
+ name: "selected",
43
+ message: "Select components to remove",
44
+ choices: installed.map((c) => ({ title: c.name, value: c.value })),
45
+ });
46
+
47
+ if (!selected || selected.length === 0) {
48
+ logger.info("No components selected. Exiting...");
49
+ process.exit(0);
50
+ }
51
+ toRemove = selected;
52
+ }
53
+
54
+ const found = toRemove
55
+ .map((name) => allComponents.find((c) => c.value.toLowerCase() === name.toLowerCase()))
56
+ .filter(Boolean) as Component[];
57
+
58
+ if (found.length === 0) {
59
+ logger.error("None of the specified components were found in the registry.");
60
+ process.exit(1);
61
+ }
62
+
63
+ const result = await prompts({
64
+ type: "confirm",
65
+ name: "confirmed",
66
+ message: `Remove ${found.map((c) => c.name).join(", ")}?`,
67
+ initial: false,
68
+ });
69
+
70
+ if (!result?.confirmed) {
71
+ logger.info("Cancelled.");
72
+ return;
73
+ }
74
+
75
+ for (const component of found) {
76
+ const allFiles = [
77
+ ...component.files.map((f) =>
78
+ path.join(currentDirectory, uiConfig.componentsLocation, f.fileName)
79
+ ),
80
+ ...component.utils.map((f) =>
81
+ path.join(currentDirectory, uiConfig.utilsLocation, f.fileName)
82
+ ),
83
+ ...(component.composables ?? []).map((f) =>
84
+ path.join(currentDirectory, uiConfig.composablesLocation, f.fileName)
85
+ ),
86
+ ];
87
+
88
+ for (const filePath of allFiles) {
89
+ const exists = await fileExists(filePath);
90
+ if (!exists) {
91
+ logger.warn(`Skipped (not found): ${path.basename(filePath)}`);
92
+ continue;
93
+ }
94
+ await fs.unlink(filePath);
95
+ logger.success(`Removed: ${path.basename(filePath)}`);
96
+ }
97
+ }
98
+
99
+ printFancyBoxMessage("Removed!", undefined, { box: { title: "Components Removed" } });
100
+ };
101
+
102
+ export const remove = new Command()
103
+ .command("remove")
104
+ .name("remove")
105
+ .description(
106
+ "Remove installed components from your project.\nNote: shared utility files used by multiple components are deleted — other components depending on them may break."
107
+ )
108
+ .argument("[componentNames...]", "Components to remove.")
109
+ .action(runRemoveCommand);
@@ -1,6 +1,5 @@
1
1
  import { Command } from "commander";
2
2
  import fse from "fs-extra";
3
- import kleur from "kleur";
4
3
  import _ from "lodash";
5
4
  import prompts from "prompts";
6
5
 
@@ -8,40 +7,33 @@ import { createCSS } from "../templates/css";
8
7
  import { compareUIConfig } from "../utils/compareUIConfig";
9
8
  import { getUIConfig } from "../utils/config";
10
9
  import { CSS_THEME_OPTIONS } from "../utils/constants";
10
+ import { logger } from "../utils/logger";
11
11
  import { printFancyBoxMessage } from "../utils/printFancyBoxMessage";
12
12
 
13
- /**
14
- * Validates if a theme name exists in the predefined options.
15
- */
16
13
  const validateThemeName = (name: string) => {
17
14
  return CSS_THEME_OPTIONS.some((option) => option.value === name?.toLowerCase());
18
15
  };
19
16
 
20
- /**
21
- * Adds a new theme to the project.
22
- */
23
17
  export const theme = new Command()
24
18
  .command("theme")
25
19
  .name("theme")
26
20
  .description("Add a new theme to your project.")
27
21
  .argument("[themeName]", "The name of the theme you would like to add")
28
22
  .action(async (themeName?: string) => {
29
- // Get ui config
30
23
  let uiConfig = await getUIConfig();
31
24
  const uiConfigIsCorrect = await compareUIConfig();
32
25
  if (!uiConfigIsCorrect) {
33
26
  uiConfig = await getUIConfig({ force: true });
34
27
  }
35
28
  if (_.isEmpty(uiConfig)) {
36
- console.log(kleur.red("Config file not set. Exiting..."));
37
- process.exit(0);
29
+ logger.error("Config file not set. Exiting...");
30
+ process.exit(1);
38
31
  }
39
32
 
40
33
  let selectedTheme =
41
34
  themeName && validateThemeName(themeName) ? themeName.toLowerCase() : undefined;
42
35
 
43
36
  if (!selectedTheme) {
44
- // Prompt for theme if not provided or invalid
45
37
  const { theme } = await prompts([
46
38
  {
47
39
  name: "theme",
@@ -51,13 +43,12 @@ export const theme = new Command()
51
43
  },
52
44
  ]);
53
45
  if (!theme) {
54
- console.log(kleur.red("No theme selected. Exiting..."));
46
+ logger.warn("No theme selected. Exiting...");
55
47
  process.exit(0);
56
48
  }
57
49
  selectedTheme = theme;
58
50
  }
59
51
 
60
- // Check if the file exists
61
52
  if (fse.existsSync(uiConfig.tailwindCSSLocation)) {
62
53
  const { force } = await prompts([
63
54
  {
@@ -68,7 +59,7 @@ export const theme = new Command()
68
59
  },
69
60
  ]);
70
61
  if (!force) {
71
- console.log("Exiting...");
62
+ logger.info("Exiting...");
72
63
  return process.exit(0);
73
64
  }
74
65
  }
@@ -0,0 +1,81 @@
1
+ import path from "node:path";
2
+ import { Command } from "commander";
3
+ import prompts from "prompts";
4
+
5
+ import { Component } from "../types";
6
+ import { getUIConfig } from "../utils/config";
7
+ import { fetchComponents } from "../utils/fetchComponents";
8
+ import { fileExists } from "../utils/fileExists";
9
+ import { logger } from "../utils/logger";
10
+ import { printFancyBoxMessage } from "../utils/printFancyBoxMessage";
11
+ import { writeFile } from "../utils/writeFile";
12
+
13
+ export const runUpdateCommand = async (componentNames: string[]) => {
14
+ const uiConfig = await getUIConfig();
15
+ const allComponents = await fetchComponents();
16
+ const currentDirectory = process.cwd();
17
+
18
+ let toUpdate = componentNames;
19
+
20
+ if (toUpdate.length === 0) {
21
+ const installed = (
22
+ await Promise.all(
23
+ allComponents.map(async (c) => {
24
+ if (!c.files || c.files.length === 0) return null;
25
+ const targetPath = path.join(
26
+ currentDirectory,
27
+ uiConfig.componentsLocation,
28
+ c.files[0].fileName
29
+ );
30
+ return (await fileExists(targetPath)) ? c : null;
31
+ })
32
+ )
33
+ ).filter(Boolean) as Component[];
34
+
35
+ if (installed.length === 0) {
36
+ logger.info("No installed components found.");
37
+ return;
38
+ }
39
+
40
+ const { selected } = await prompts({
41
+ type: "autocompleteMultiselect",
42
+ name: "selected",
43
+ message: "Select components to update",
44
+ choices: installed.map((c) => ({ title: c.name, value: c.value })),
45
+ });
46
+
47
+ if (!selected || selected.length === 0) {
48
+ logger.info("No components selected. Exiting...");
49
+ process.exit(0);
50
+ }
51
+ toUpdate = selected;
52
+ }
53
+
54
+ const notFound = toUpdate.filter(
55
+ (name) => !allComponents.find((c) => c.value.toLowerCase() === name.toLowerCase())
56
+ );
57
+ if (notFound.length > 0) {
58
+ logger.warn(`Component(s) not found in registry: ${notFound.join(", ")}`);
59
+ }
60
+
61
+ const found = toUpdate
62
+ .map((name) => allComponents.find((c) => c.value.toLowerCase() === name.toLowerCase()))
63
+ .filter(Boolean) as Component[];
64
+
65
+ for (const component of found) {
66
+ for (const file of component.files) {
67
+ const targetPath = path.join(currentDirectory, uiConfig.componentsLocation, file.fileName);
68
+ await writeFile(targetPath, file.fileContent);
69
+ logger.success(`Updated: ${file.fileName}`);
70
+ }
71
+ }
72
+
73
+ printFancyBoxMessage("Updated!", undefined, { box: { title: "Components Updated" } });
74
+ };
75
+
76
+ export const update = new Command()
77
+ .command("update")
78
+ .name("update")
79
+ .description("Re-fetch and overwrite installed components with the latest version.")
80
+ .argument("[componentNames...]", "Components to update.")
81
+ .action(runUpdateCommand);
package/src/index.ts CHANGED
@@ -5,10 +5,13 @@ import { version } from "../package.json";
5
5
  import { add } from "./commands/add";
6
6
  import { block } from "./commands/block";
7
7
  import { init } from "./commands/init";
8
+ import { list } from "./commands/list";
8
9
  import { addPrettier } from "./commands/prettier";
9
10
  import { prose } from "./commands/prose";
11
+ import { remove } from "./commands/remove";
10
12
  import { addShortcuts } from "./commands/shortcuts";
11
13
  import { theme } from "./commands/theme";
14
+ import { update } from "./commands/update";
12
15
  import { printFancyBoxMessage } from "./utils/printFancyBoxMessage";
13
16
 
14
17
  process.on("SIGINT", () => process.exit(0));
@@ -31,6 +34,9 @@ program
31
34
  .addCommand(prose)
32
35
  .addCommand(block)
33
36
  .addCommand(theme)
37
+ .addCommand(list)
38
+ .addCommand(update)
39
+ .addCommand(remove)
34
40
  .addCommand(addShortcuts)
35
41
  .addCommand(addPrettier);
36
42
 
@@ -4,7 +4,6 @@ import fse from "fs-extra";
4
4
  import _ from "lodash";
5
5
  import { loadFile, writeFile } from "magicast";
6
6
  import { addNuxtModule } from "magicast/helpers";
7
- import prompts from "prompts";
8
7
 
9
8
  import { InitOptions, UIConfig } from "../types";
10
9
  import { DEFAULT_CONFIG, DEFAULT_CONFIG_NUXT4, UI_CONFIG_FILENAME } from "./constants";
@@ -32,23 +31,17 @@ export const getUIConfig = async (options?: InitOptions): Promise<UIConfig> => {
32
31
 
33
32
  await fse.writeFile(UI_CONFIG_FILENAME, `export default ${JSON.stringify(uiConfig, null, 2)}`);
34
33
 
35
- // Handle pnpm special case
34
+ // Handle pnpm special case: append required lines without destroying existing content
36
35
  if (uiConfig.packageManager === "pnpm") {
37
- const npmrcExists = fse.existsSync(".npmrc");
38
- let shouldWrite = true;
39
-
40
- if (npmrcExists) {
41
- const { confirmCreateNpmrc } = await prompts({
42
- type: "confirm",
43
- name: "confirmCreateNpmrc",
44
- message: "A .npmrc file already exists. Overwrite it?",
45
- initial: false,
46
- });
47
- shouldWrite = confirmCreateNpmrc;
36
+ const requiredLines = ["shamefully-hoist=true", "strict-peer-dependencies=false"];
37
+ let existing = "";
38
+ if (fse.existsSync(".npmrc")) {
39
+ existing = await fse.readFile(".npmrc", "utf-8");
48
40
  }
49
-
50
- if (shouldWrite) {
51
- await fse.writeFile(".npmrc", "shamefully-hoist=true\nstrict-peer-dependencies=false\n");
41
+ const linesToAdd = requiredLines.filter((line) => !existing.includes(line));
42
+ if (linesToAdd.length > 0) {
43
+ const separator = existing && !existing.endsWith("\n") ? "\n" : "";
44
+ await fse.writeFile(".npmrc", existing + separator + linesToAdd.join("\n") + "\n");
52
45
  }
53
46
  }
54
47
  } else {
@@ -87,6 +87,7 @@ export const PACKAGE_MANAGER_CHOICES = [
87
87
  { title: "Yarn", value: "yarn" },
88
88
  { title: "Pnpm", value: "pnpm" },
89
89
  { title: "Bun", value: "bun" },
90
+ { title: "Deno", value: "deno" },
90
91
  ];
91
92
 
92
93
  /**
@@ -1,19 +1,21 @@
1
1
  import axios from "axios";
2
+ import { consola } from "consola";
2
3
  import dotenv from "dotenv";
3
4
  import ora from "ora";
4
5
 
5
6
  dotenv.config();
6
7
 
7
- /**
8
- * Fetch block categories from UI Thing API.
9
- */
10
- export const fetchBlockCategories = async () => {
8
+ export const fetchBlockCategories = async (): Promise<string[]> => {
11
9
  const spinner = ora("Fetching block categories...").start();
12
-
13
- const { data } = await axios.get<string[]>(
14
- process.env.BLOCK_CATEGORIES_API || "https://uithing.com/api/blocks/categories"
15
- );
16
-
17
- spinner.succeed("Block categories fetched.");
18
- return data;
10
+ try {
11
+ const { data } = await axios.get<string[]>(
12
+ process.env.BLOCK_CATEGORIES_API || "https://uithing.com/api/blocks/categories"
13
+ );
14
+ spinner.succeed("Block categories fetched.");
15
+ return data;
16
+ } catch {
17
+ spinner.fail("Failed to fetch block categories.");
18
+ consola.error("Could not reach the UI Thing API. Check your network connection.");
19
+ process.exit(1);
20
+ }
19
21
  };
@@ -1,4 +1,5 @@
1
1
  import axios from "axios";
2
+ import { consola } from "consola";
2
3
  import dotenv from "dotenv";
3
4
  import ora from "ora";
4
5
 
@@ -6,16 +7,17 @@ import { BlockComponent } from "../types";
6
7
 
7
8
  dotenv.config();
8
9
 
9
- /**
10
- * Fetch block components from UI Thing API.
11
- */
12
- export const fetchBlocks = async () => {
10
+ export const fetchBlocks = async (): Promise<BlockComponent[]> => {
13
11
  const spinner = ora("Fetching blocks...").start();
14
-
15
- const { data } = await axios.get<BlockComponent[]>(
16
- process.env.BLOCKS_API || "https://uithing.com/api/blocks"
17
- );
18
-
19
- spinner.succeed("Blocks fetched.");
20
- return data;
12
+ try {
13
+ const { data } = await axios.get<BlockComponent[]>(
14
+ process.env.BLOCKS_API || "https://uithing.com/api/blocks"
15
+ );
16
+ spinner.succeed("Blocks fetched.");
17
+ return data;
18
+ } catch {
19
+ spinner.fail("Failed to fetch blocks.");
20
+ consola.error("Could not reach the UI Thing API. Check your network connection.");
21
+ process.exit(1);
22
+ }
21
23
  };
@@ -1,4 +1,5 @@
1
1
  import axios from "axios";
2
+ import { consola } from "consola";
2
3
  import dotenv from "dotenv";
3
4
  import ora from "ora";
4
5
 
@@ -6,16 +7,17 @@ import { Component } from "../types";
6
7
 
7
8
  dotenv.config();
8
9
 
9
- /**
10
- * Function used to fetch components from the API.
11
- */
12
- export const fetchComponents = async () => {
10
+ export const fetchComponents = async (): Promise<Component[]> => {
13
11
  const spinner = ora("Fetching components...").start();
14
-
15
- const { data } = await axios.get<Component[]>(
16
- process.env.COMPONENTS_API || "https://uithing.com/api/components"
17
- );
18
- spinner.succeed("Components fetched.");
19
-
20
- return data;
12
+ try {
13
+ const { data } = await axios.get<Component[]>(
14
+ process.env.COMPONENTS_API || "https://uithing.com/api/components"
15
+ );
16
+ spinner.succeed("Components fetched.");
17
+ return data;
18
+ } catch {
19
+ spinner.fail("Failed to fetch components.");
20
+ consola.error("Could not reach the UI Thing API. Check your network connection.");
21
+ process.exit(1);
22
+ }
21
23
  };