ui-thing 0.1.55 → 0.2.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 (39) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/CHANGELOG.md +80 -0
  3. package/README.md +4 -3
  4. package/dist/index.js +1273 -15547
  5. package/dist/index.js.map +1 -1
  6. package/package.json +33 -21
  7. package/src/commands/add.ts +218 -274
  8. package/src/commands/init.ts +107 -58
  9. package/src/commands/prettier.ts +6 -8
  10. package/src/commands/shortcuts.ts +13 -13
  11. package/src/commands/theme.ts +9 -6
  12. package/src/index.ts +2 -2
  13. package/src/templates/css.ts +958 -773
  14. package/src/templates/prettier.ts +14 -16
  15. package/src/templates/shortcuts.ts +225 -126
  16. package/src/templates/tw-helper.ts +8 -0
  17. package/src/templates/vs-code.ts +24 -0
  18. package/src/types.ts +74 -3
  19. package/src/utils/addPrettierConfig.ts +49 -6
  20. package/src/utils/addShortcutFiles.ts +5 -4
  21. package/src/utils/addTailwindVitePlugin.ts +35 -0
  22. package/src/utils/addVSCodeFiles.ts +13 -0
  23. package/src/utils/compareUIConfig.ts +1 -2
  24. package/src/utils/config.ts +59 -86
  25. package/src/utils/constants.ts +67 -13
  26. package/src/utils/detectNuxtVersion.ts +20 -0
  27. package/src/utils/fetchComponents.ts +14 -1
  28. package/src/utils/installPackages.ts +3 -27
  29. package/src/utils/mergeJsonFile.ts +28 -0
  30. package/src/utils/printFancyBoxMessage.ts +62 -16
  31. package/src/utils/promptForComponents.ts +8 -6
  32. package/src/utils/uiConfigPrompt.ts +12 -37
  33. package/tsconfig.json +2 -1
  34. package/dist/chunk-FW4363Y4.js +0 -2
  35. package/dist/chunk-FW4363Y4.js.map +0 -1
  36. package/dist/prompt-4NXDAQWE.js +0 -46
  37. package/dist/prompt-4NXDAQWE.js.map +0 -1
  38. package/src/comps.ts +0 -3181
  39. package/src/templates/tailwind.ts +0 -142
@@ -1,10 +1,13 @@
1
1
  import { join } from "node:path";
2
2
  import fse from "fs-extra";
3
3
 
4
- import { DEFINE_SHORTCUT, USE_SHORTCUTS } from "../templates/shortcuts";
4
+ import { DEFINE_SHORTCUT } from "../templates/shortcuts";
5
5
  import { UIConfig } from "../types";
6
6
  import { getUIConfig } from "./config";
7
7
 
8
+ /**
9
+ * Adds shortcut files to the specified directory.
10
+ */
8
11
  export const addShortcutFiles = async (cwd = process.cwd()) => {
9
12
  // get config
10
13
  let userConfig: UIConfig = await getUIConfig();
@@ -12,7 +15,5 @@ export const addShortcutFiles = async (cwd = process.cwd()) => {
12
15
  // ensure that the composable folder exists
13
16
  await fse.ensureDir(composablesLocation);
14
17
  // write the defineShortcuts composable
15
- await fse.writeFile(join(composablesLocation, "defineShortcuts.ts"), DEFINE_SHORTCUT, "utf-8");
16
- // write the useShortcuts composable
17
- await fse.writeFile(join(composablesLocation, "useShortcuts.ts"), USE_SHORTCUTS, "utf-8");
18
+ await fse.writeFile(join(composablesLocation, "shortcuts.ts"), DEFINE_SHORTCUT, "utf-8");
18
19
  };
@@ -0,0 +1,35 @@
1
+ import { join } from "node:path";
2
+ import { builders, loadFile, writeFile } from "magicast";
3
+ import { getDefaultExportOptions } from "magicast/helpers";
4
+
5
+ /**
6
+ * Adds the Tailwind CSS Vite plugin to the Nuxt config.
7
+ */
8
+ export const addTailwindVitePlugin = async () => {
9
+ // Get the path to nuxt config
10
+ const CONFIG_PATH = join(process.cwd(), "nuxt.config.ts");
11
+ // Load the nuxt config file
12
+ const cfg = await loadFile(CONFIG_PATH);
13
+ // check if `tailwindcss()` is already present in the code
14
+
15
+ if (!cfg.$code.includes("tailwindcss()")) {
16
+ // get the exported config object
17
+ const defaultExport = getDefaultExportOptions(cfg);
18
+ // ensure vite and plugins are defined
19
+ defaultExport.vite ||= {};
20
+ defaultExport.vite.plugins ||= [];
21
+ // push the function to the plugins array
22
+ defaultExport.vite.plugins.push(builders.functionCall("tailwindcss"));
23
+ }
24
+ // check if tailwind plugin is already imported
25
+ if (!cfg.imports.$items.find((i) => i.local === "tailwindcss")) {
26
+ // prepend the import for tailwindcss
27
+ cfg.imports.$prepend({
28
+ from: "@tailwindcss/vite",
29
+ local: "tailwindcss",
30
+ imported: "default",
31
+ });
32
+ }
33
+ // write the changes to the `nuxt.config.ts` file
34
+ await writeFile(cfg, CONFIG_PATH);
35
+ };
@@ -0,0 +1,13 @@
1
+ import fse from "fs-extra";
2
+
3
+ import { VS_CODE_RECOMMENDATIONS, VS_CODE_SETTINGS } from "../templates/vs-code";
4
+ import { mergeJsonFile } from "./mergeJsonFile";
5
+
6
+ /**
7
+ * Adds the necessary VS Code configuration files.
8
+ */
9
+ export const addVSCodeFiles = (VS_CODE_FOLDER = ".vscode") => {
10
+ fse.ensureDirSync(VS_CODE_FOLDER);
11
+ mergeJsonFile(`${VS_CODE_FOLDER}/extensions.json`, VS_CODE_RECOMMENDATIONS);
12
+ mergeJsonFile(`${VS_CODE_FOLDER}/settings.json`, VS_CODE_SETTINGS);
13
+ };
@@ -12,7 +12,6 @@ export const compareUIConfig = async () => {
12
12
  nuxtVersion: 3,
13
13
  theme: "string",
14
14
  tailwindCSSLocation: "string",
15
- tailwindConfigLocation: "string",
16
15
  componentsLocation: "string",
17
16
  composablesLocation: "string",
18
17
  utilsLocation: "string",
@@ -29,7 +28,7 @@ export const compareUIConfig = async () => {
29
28
  }
30
29
  }
31
30
 
32
- if (missingProperties.length > 0) {
31
+ if (missingProperties.length > 1) {
33
32
  return false;
34
33
  }
35
34
  return true;
@@ -1,120 +1,93 @@
1
- import { join } from "path";
1
+ import { join } from "node:path";
2
2
  import { loadConfig } from "c12";
3
3
  import fse from "fs-extra";
4
- import kleur from "kleur";
5
4
  import _ from "lodash";
6
- import { loadFile, ProxifiedModule, writeFile } from "magicast";
7
- import { addNuxtModule, getDefaultExportOptions } from "magicast/helpers";
5
+ import { loadFile, writeFile } from "magicast";
6
+ import { addNuxtModule } from "magicast/helpers";
8
7
  import prompts from "prompts";
9
8
 
10
9
  import { InitOptions, UIConfig } from "../types";
11
- import { initPrompts, promptForNuxtVersion } from "./uiConfigPrompt";
10
+ import { DEFAULT_CONFIG, DEFAULT_CONFIG_NUXT4, UI_CONFIG_FILENAME } from "./constants";
11
+ import { detectNuxtVersion } from "./detectNuxtVersion";
12
+ import { initPrompts } from "./uiConfigPrompt";
12
13
 
13
14
  const currentDir = process.cwd();
14
- const uiConfigFilename = "ui-thing.config.ts";
15
- const defaultConfig: UIConfig = {
16
- nuxtVersion: 3,
17
- theme: "zinc",
18
- tailwindCSSLocation: "assets/css/tailwind.css",
19
- tailwindConfigLocation: "tailwind.config.js",
20
- componentsLocation: "components/Ui",
21
- composablesLocation: "composables",
22
- pluginsLocation: "plugins",
23
- utilsLocation: "utils",
24
- force: true,
25
- useDefaultFilename: true,
26
- packageManager: "npm",
27
- };
28
- const defaultNuxt4Config: UIConfig = {
29
- nuxtVersion: 4,
30
- theme: "zinc",
31
- tailwindCSSLocation: "app/assets/css/tailwind.css",
32
- tailwindConfigLocation: "tailwind.config.js",
33
- componentsLocation: "app/components/Ui",
34
- composablesLocation: "app/composables",
35
- pluginsLocation: "app/plugins",
36
- utilsLocation: "app/utils",
37
- force: true,
38
- useDefaultFilename: true,
39
- packageManager: "npm",
40
- };
41
-
42
- export const getNuxtConfig = async () => {
43
- if (!fse.existsSync("nuxt.config.ts")) {
44
- console.log(kleur.red(`No ${kleur.bgWhite(`nuxt.config.ts`)} file found. Exiting...`));
45
- return process.exit(0);
46
- }
47
- const nuxtConfig = await loadFile(join(currentDir, "nuxt.config.ts"));
48
- const defaultExport = getDefaultExportOptions(nuxtConfig);
49
- return { nuxtConfig, defaultExport };
50
- };
51
15
 
52
- export const getUIConfig = async (options?: InitOptions) => {
53
- const configFileExists = fse.existsSync(uiConfigFilename);
16
+ /**
17
+ * Creates or retrieves the UI Thing config.
18
+ */
19
+ export const getUIConfig = async (options?: InitOptions): Promise<UIConfig> => {
20
+ const configExists = fse.existsSync(UI_CONFIG_FILENAME);
54
21
  let uiConfig: UIConfig = {} as UIConfig;
55
- let nuxtVersion = Number(options?.nuxtVersion);
22
+ const nuxtVersion = Number(options?.nuxtVersion) || detectNuxtVersion();
56
23
 
57
- if (!configFileExists || options?.force) {
58
- if (!nuxtVersion) {
59
- nuxtVersion = await promptForNuxtVersion();
60
- }
61
- // if option yes is passed, use default values
62
- if (options?.yes) {
63
- uiConfig = Number(nuxtVersion) === 4 ? defaultNuxt4Config : defaultConfig;
64
- } else {
65
- uiConfig = await initPrompts();
66
- }
67
- await fse.writeFile(uiConfigFilename, `export default ${JSON.stringify(uiConfig, null, 2)}`);
68
- // Check if user chose pnpm as package manager
24
+ // Force creation or first-time setup
25
+ if (!configExists || options?.force) {
26
+ uiConfig = options?.yes
27
+ ? nuxtVersion === 4
28
+ ? DEFAULT_CONFIG_NUXT4
29
+ : DEFAULT_CONFIG
30
+ : await initPrompts(nuxtVersion);
31
+
32
+ await fse.writeFile(UI_CONFIG_FILENAME, `export default ${JSON.stringify(uiConfig, null, 2)}`);
33
+
34
+ // Handle pnpm special case
69
35
  if (uiConfig.packageManager === "pnpm") {
70
- // check if a .npmrc file exists
71
36
  const npmrcExists = fse.existsSync(".npmrc");
72
- // as the user if they want to create a .npmrc file
37
+ let shouldWrite = true;
38
+
73
39
  if (npmrcExists) {
74
40
  const { confirmCreateNpmrc } = await prompts({
75
41
  type: "confirm",
76
42
  name: "confirmCreateNpmrc",
77
- message: "A .npmrc file already exists. Do you want to overwrite it?",
43
+ message: "A .npmrc file already exists. Overwrite it?",
78
44
  initial: false,
79
45
  });
80
- if (confirmCreateNpmrc) {
81
- await fse.writeFile(".npmrc", "shamefully-hoist=true\nstrict-peer-dependencies=false\n");
82
- }
83
- } else {
46
+ shouldWrite = confirmCreateNpmrc;
47
+ }
48
+
49
+ if (shouldWrite) {
84
50
  await fse.writeFile(".npmrc", "shamefully-hoist=true\nstrict-peer-dependencies=false\n");
85
51
  }
86
52
  }
87
53
  } else {
88
- const data = await loadConfig({
89
- configFile: uiConfigFilename.replace(".ts", ""),
54
+ const data = await loadConfig<UIConfig>({
55
+ configFile: UI_CONFIG_FILENAME.replace(".ts", ""),
90
56
  });
91
57
  uiConfig = data.config as UIConfig;
92
58
  }
93
- if (_.isEmpty(uiConfig)) {
94
- await getUIConfig({ force: true });
95
- }
59
+
60
+ // Ensure valid config
61
+ if (_.isEmpty(uiConfig)) return getUIConfig({ force: true });
62
+
96
63
  createConfigPaths(uiConfig);
97
64
  return uiConfig;
98
65
  };
99
66
 
100
- export const createConfigPaths = (uiConfig: UIConfig) => {
101
- // Esnure files exists
102
- if (uiConfig.tailwindCSSLocation) fse.ensureFileSync(uiConfig.tailwindConfigLocation);
103
- if (uiConfig.pluginsLocation) fse.ensureDirSync(uiConfig.pluginsLocation);
104
- if (uiConfig.tailwindConfigLocation) fse.ensureFileSync(uiConfig.tailwindCSSLocation);
105
- if (uiConfig.componentsLocation) fse.ensureDirSync(uiConfig.componentsLocation);
106
- if (uiConfig.composablesLocation) fse.ensureDirSync(uiConfig.composablesLocation);
107
- if (uiConfig.utilsLocation) fse.ensureDirSync(uiConfig.utilsLocation);
108
- };
67
+ /**
68
+ * Ensures all required paths exist for UI Thing.
69
+ */
70
+ const createConfigPaths = (uiConfig: UIConfig) => {
71
+ const ensureFileOrDir = (pathValue?: string, isDir = false) => {
72
+ if (!pathValue) return;
73
+ isDir ? fse.ensureDirSync(pathValue) : fse.ensureFileSync(pathValue);
74
+ };
109
75
 
110
- export const addModuleToConfig = (cfg: ProxifiedModule, modules: string[] | string) => {
111
- if (typeof modules === "string") {
112
- modules = [modules];
113
- }
114
- modules.forEach((m) => addNuxtModule(cfg, m));
115
- return cfg;
76
+ ensureFileOrDir(uiConfig.tailwindCSSLocation);
77
+ ensureFileOrDir(uiConfig.pluginsLocation, true);
78
+ ensureFileOrDir(uiConfig.componentsLocation, true);
79
+ ensureFileOrDir(uiConfig.composablesLocation, true);
80
+ ensureFileOrDir(uiConfig.utilsLocation, true);
116
81
  };
117
82
 
118
- export const updateConfig = async (cfg: ProxifiedModule, fileName = "nuxt.config.ts") => {
119
- await writeFile(cfg.$ast, fileName);
83
+ /**
84
+ * Adds one or multiple Nuxt modules to nuxt.config.ts safely.
85
+ */
86
+ export const addModuleToConfig = async (modules: string[] | string) => {
87
+ if (!modules) return;
88
+ const modulesArray = typeof modules === "string" ? [modules] : modules;
89
+
90
+ const proxy = await loadFile(join(currentDir, "nuxt.config.ts"));
91
+ modulesArray.forEach((m) => addNuxtModule(proxy, m));
92
+ await writeFile(proxy, join(currentDir, "nuxt.config.ts"));
120
93
  };
@@ -1,41 +1,95 @@
1
+ import { UIConfig } from "../types";
2
+
3
+ /**
4
+ * The filename of the UI Thing configuration file.
5
+ */
6
+ export const UI_CONFIG_FILENAME = "ui-thing.config.ts";
7
+ /**
8
+ * The default UI Thing configuration.
9
+ *
10
+ * Used when Nuxt 3 is detected
11
+ */
12
+ export const DEFAULT_CONFIG: UIConfig = {
13
+ theme: "zinc",
14
+ tailwindCSSLocation: "assets/css/tailwind.css",
15
+ componentsLocation: "components/Ui",
16
+ composablesLocation: "composables",
17
+ pluginsLocation: "plugins",
18
+ utilsLocation: "utils",
19
+ force: true,
20
+ useDefaultFilename: true,
21
+ packageManager: "npm",
22
+ };
23
+
24
+ /**
25
+ * The default UI Thing configuration.
26
+ *
27
+ * Used when Nuxt 4 is detected
28
+ */
29
+ export const DEFAULT_CONFIG_NUXT4: UIConfig = {
30
+ theme: "zinc",
31
+ tailwindCSSLocation: "app/assets/css/tailwind.css",
32
+ componentsLocation: "app/components/Ui",
33
+ composablesLocation: "app/composables",
34
+ pluginsLocation: "app/plugins",
35
+ utilsLocation: "app/utils",
36
+ force: true,
37
+ useDefaultFilename: true,
38
+ packageManager: "npm",
39
+ };
40
+
41
+ /**
42
+ * The initial core dependencies
43
+ */
1
44
  export const INIT_DEPS = [
2
- "radix-vue",
45
+ "tailwindcss",
46
+ "motion-v",
47
+ "@tailwindcss/vite",
48
+ "reka-ui",
3
49
  "tailwind-variants",
50
+ "tailwind-merge",
4
51
  "@nuxt/fonts",
5
- "@nuxtjs/tailwindcss",
6
52
  "@nuxtjs/color-mode",
7
53
  "@nuxt/icon",
8
54
  "@vueuse/nuxt",
9
55
  "@tailwindcss/forms",
10
- "tailwindcss-animate",
11
56
  ];
57
+
58
+ /**
59
+ * The initial development dependencies
60
+ */
12
61
  export const INIT_DEV_DEPS = [
13
62
  "typescript",
14
63
  "prettier-plugin-tailwindcss",
15
64
  "prettier",
16
65
  "@ianvs/prettier-plugin-sort-imports",
66
+ "tw-animate-css",
17
67
  ];
18
68
 
69
+ /**
70
+ * Initial modules that are needed
71
+ */
19
72
  export const INIT_MODULES = [
20
- "@nuxtjs/tailwindcss",
21
73
  "@nuxtjs/color-mode",
74
+ "motion-v/nuxt",
22
75
  "@vueuse/nuxt",
23
76
  "@nuxt/icon",
24
77
  "@nuxt/fonts",
25
78
  ];
26
79
 
27
- export const NUXT_VERSIONS = [
28
- { title: "Nuxt 3", value: 3 },
29
- { title: "Nuxt 4", value: 4 },
30
- ];
31
-
80
+ /**
81
+ * List of available package managers to chose from
82
+ */
32
83
  export const PACKAGE_MANAGER_CHOICES = [
33
- { title: "NPM", value: "npm" },
34
- { title: "YARN", value: "yarn" },
35
- { title: "PNPM", value: "pnpm" },
36
- { title: "BUN", value: "bun" },
84
+ { title: "Npm", value: "npm" },
85
+ { title: "Yarn", value: "yarn" },
86
+ { title: "Pnpm", value: "pnpm" },
87
+ { title: "Bun", value: "bun" },
37
88
  ];
38
89
 
90
+ /**
91
+ * List of available CSS themes to choose from
92
+ */
39
93
  export const CSS_THEME_OPTIONS = [
40
94
  { title: "Zinc", value: "zinc" },
41
95
  { title: "Slate", value: "slate" },
@@ -0,0 +1,20 @@
1
+ import fs from "fs";
2
+
3
+ /**
4
+ * Detect the Nuxt.js version from package.json.
5
+ */
6
+ export function detectNuxtVersion() {
7
+ try {
8
+ const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8"));
9
+ const nuxtVer = pkg.dependencies?.nuxt || pkg.devDependencies?.nuxt;
10
+ if (nuxtVer) {
11
+ // check if version is 4.x
12
+ // check for all possible formats with a regex
13
+ if (/^[~^>=<\s]*4/.test(nuxtVer)) return 4;
14
+ return 3;
15
+ }
16
+ } catch {
17
+ return 4;
18
+ }
19
+ return 4;
20
+ }
@@ -1,8 +1,21 @@
1
1
  import axios from "axios";
2
+ import dotenv from "dotenv";
3
+ import ora from "ora";
2
4
 
3
5
  import { Component } from "../types";
4
6
 
7
+ dotenv.config();
8
+
9
+ /**
10
+ * Function used to fetch components from the API.
11
+ */
5
12
  export const fetchComponents = async () => {
6
- const { data } = await axios.get<Component[]>("https://ui-thing.behonbaker.com/api/components");
13
+ 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
+
7
20
  return data;
8
21
  };
@@ -1,28 +1,7 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
1
  import { execa } from "execa";
4
2
  import _ from "lodash";
5
3
  import ora from "ora";
6
4
 
7
- function checkPackageJson() {
8
- const packageJsonPath = path.join(process.cwd(), "package.json");
9
- let scriptExists = false;
10
-
11
- // Check if package.json file exists
12
- if (fs.existsSync(packageJsonPath)) {
13
- // Read the package.json file
14
- const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
15
- const packageJson = JSON.parse(packageJsonContent);
16
-
17
- // Check if "postinstall" script is defined
18
- const postinstallScript = packageJson.scripts?.postinstall;
19
- if (postinstallScript) {
20
- scriptExists = true;
21
- }
22
- }
23
- return scriptExists;
24
- }
25
-
26
5
  export const installPackages = async (
27
6
  packageManager: string,
28
7
  deps?: string[] | string,
@@ -44,12 +23,9 @@ export const installPackages = async (
44
23
  await execa(packageManager, [packageManager === "yarn" ? "add" : "install", "-D", ...devDeps]);
45
24
  }
46
25
 
47
- // Check if package.json file exists
48
- if (checkPackageJson()) {
49
- // we should check to see if there is a postinstall script and run it
50
- depsSpinner.text = "Running postinstall script...";
51
- await execa(packageManager, ["run", "postinstall"]);
52
- }
26
+ // we should check to see if there is a postinstall script and run it
27
+ depsSpinner.text = "Running nuxt prepare...";
28
+ await execa`npx -y nuxt prepare`;
53
29
 
54
30
  depsSpinner.succeed("Installed dependencies!");
55
31
  };
@@ -0,0 +1,28 @@
1
+ import { merge } from "es-toolkit";
2
+ import fse from "fs-extra";
3
+
4
+ /**
5
+ * Merges JSON data into a file without overwriting existing keys.
6
+ * Creates the file if it does not exist.
7
+ *
8
+ * @param filePath - Path to the JSON file to update.
9
+ * @param newData - The new JSON data to merge in.
10
+ */
11
+ export function mergeJsonFile(filePath: string, newData: Record<string, any>) {
12
+ let currentData: Record<string, any> = {};
13
+
14
+ // Read current JSON if it exists
15
+ if (fse.existsSync(filePath)) {
16
+ try {
17
+ currentData = fse.readJsonSync(filePath);
18
+ } catch {
19
+ console.warn(`⚠️ Could not parse ${filePath}, starting fresh.`);
20
+ }
21
+ }
22
+
23
+ // Merge existing and new data
24
+ const merged = merge(currentData, newData);
25
+
26
+ // Write merged JSON with pretty formatting
27
+ fse.writeJsonSync(filePath, merged, { spaces: 2 });
28
+ }
@@ -1,19 +1,65 @@
1
- import boxen from "boxen";
1
+ import boxen, { Options as BoxOptions } from "boxen";
2
2
  import figlet from "figlet";
3
- import type { Options } from "boxen";
4
3
 
5
- export const printFancyBoxMessage = (title: string, boxOptions?: Options, description?: string) => {
6
- console.log("\n");
7
- console.log(
8
- boxen(figlet.textSync(title), {
9
- borderColor: "greenBright",
10
- padding: 1,
11
- borderStyle: "round",
12
- titleAlignment: "center",
13
- ...boxOptions,
14
- })
15
- );
16
- if (description) {
17
- console.log(`\n${description}`);
18
- }
4
+ /**
5
+ * Extra configuration options for the fancy box.
6
+ */
7
+ interface FancyBoxOptions {
8
+ box?: BoxOptions; // Overrides for boxen (border style, color, etc.)
9
+ figletFont?: figlet.Fonts; // Optional font name for ASCII art
10
+ }
11
+
12
+ /**
13
+ * Creates the ASCII-art box as a string (pure function — does not log).
14
+ * Useful for testing because it avoids side effects like console output.
15
+ *
16
+ * @param title - The main text to display in big ASCII font
17
+ * @param description - Optional subtitle or extra info below the box
18
+ * @param options - Styling and font configuration
19
+ * @returns A fully formatted string with ASCII title inside a box
20
+ */
21
+ const createFancyBoxMessage = (
22
+ title: string,
23
+ description?: string,
24
+ options: FancyBoxOptions = {}
25
+ ): string => {
26
+ const { box, figletFont } = options;
27
+
28
+ // Generate the ASCII art title using figlet
29
+ const asciiTitle = figlet.textSync(title, {
30
+ font: figletFont || "Standard", // Default font if none provided
31
+ });
32
+
33
+ // Default box styling so all boxes look consistent
34
+ const defaultBoxOptions: BoxOptions = {
35
+ borderColor: "greenBright",
36
+ padding: 1,
37
+ borderStyle: "round",
38
+ titleAlignment: "center",
39
+ };
40
+
41
+ // Wrap ASCII art in a decorative box
42
+ const boxMessage = boxen(asciiTitle, {
43
+ ...defaultBoxOptions, // Base styles
44
+ ...box, // User overrides
45
+ });
46
+
47
+ // Append description if provided
48
+ return description ? `${boxMessage}\n${description}` : boxMessage;
49
+ };
50
+
51
+ /**
52
+ * CLI-friendly helper that prints the fancy box directly to the terminal.
53
+ * This simply calls `createFancyBoxMessage` and logs the result.
54
+ *
55
+ * @param title - The main title to display
56
+ * @param description - Optional extra info under the box
57
+ * @param options - Font and box customization
58
+ */
59
+ export const printFancyBoxMessage = (
60
+ title: string,
61
+ description?: string,
62
+ options?: FancyBoxOptions
63
+ ) => {
64
+ console.log("\n" + createFancyBoxMessage(title, description, options));
19
65
  };
@@ -1,9 +1,15 @@
1
1
  import prompts from "prompts";
2
2
 
3
- import allComponents from "../comps";
4
3
  import { Component } from "../types";
4
+ import { fetchComponents } from "./fetchComponents";
5
5
 
6
- export const promptUserForComponents = async (all?: boolean): Promise<string[]> => {
6
+ /**
7
+ * Prompts the user to select components to add.
8
+ */
9
+ export const promptUserForComponents = async (
10
+ all?: boolean,
11
+ allComponents: Component[] = []
12
+ ): Promise<string[]> => {
7
13
  // If all is true, return all components
8
14
  if (all) return allComponents.map((c: Component) => c.value);
9
15
  const { components } = await prompts({
@@ -11,10 +17,6 @@ export const promptUserForComponents = async (all?: boolean): Promise<string[]>
11
17
  name: "components",
12
18
  message: "Select the components you want to add",
13
19
  choices: allComponents.map((c: Component) => ({ title: c.name, value: c.value })),
14
- onRender(kleur) {
15
- // @ts-ignore
16
- this.msg = kleur.bgCyan(" Choose components ") + " Select the components you want to add";
17
- },
18
20
  });
19
21
  return components;
20
22
  };