ui-thing 0.1.56 → 0.2.1

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 +76 -0
  3. package/README.md +4 -3
  4. package/dist/index.js +1175 -15776
  5. package/dist/index.js.map +1 -1
  6. package/package.json +34 -22
  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 -3237
  39. package/src/templates/tailwind.ts +0 -142
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "ui-thing",
3
- "version": "0.1.56",
4
- "description": "CLI used to add Nuxt 3 components to a project",
3
+ "version": "0.2.1",
4
+ "description": "CLI used to add Nuxt components to a project",
5
5
  "keywords": [
6
6
  "cli",
7
7
  "ui",
8
8
  "thing",
9
- "nuxt3",
10
- "radix-vue",
9
+ "nuxt",
10
+ "reka-ui",
11
11
  "tailwindcss",
12
12
  "nuxtui",
13
13
  "shadcn-ui"
@@ -33,39 +33,51 @@
33
33
  "coverage": "vitest run --coverage",
34
34
  "dev": "tsup --watch",
35
35
  "format": "npx prettier --write .",
36
+ "knip": "knip",
37
+ "knip:fix": "knip --fix",
38
+ "lint-staged": "lint-staged",
39
+ "prepare": "husky",
36
40
  "release": "npm run build && npx changelogen@latest --release && npm publish && git push --follow-tags",
37
41
  "start": "node dist/index.js",
38
42
  "test": "vitest"
39
43
  },
44
+ "lint-staged": {
45
+ "*": [
46
+ "npm run format"
47
+ ]
48
+ },
40
49
  "dependencies": {
41
- "axios": "^1.8.1",
50
+ "axios": "^1.11.0",
42
51
  "boxen": "^8.0.1",
43
- "build": "^0.1.4",
44
- "c12": "^2.0.4",
45
- "commander": "^13.1.0",
46
- "defu": "^6.1.4",
47
- "execa": "^9.5.2",
48
- "figlet": "^1.8.0",
49
- "fs-extra": "^11.3.0",
52
+ "c12": "^3.2.0",
53
+ "commander": "^14.0.0",
54
+ "consola": "^3.4.2",
55
+ "dotenv": "^17.2.1",
56
+ "es-toolkit": "^1.39.9",
57
+ "execa": "^9.6.0",
58
+ "figlet": "^1.8.2",
59
+ "fs-extra": "^11.3.1",
50
60
  "kleur": "^4.1.5",
51
61
  "lodash": "^4.17.21",
52
- "nypm": "^0.5.4",
62
+ "magicast": "^0.3.5",
53
63
  "ora": "^8.2.0",
54
64
  "prompts": "^2.4.2"
55
65
  },
56
66
  "devDependencies": {
57
- "@gmrchk/cli-testing-library": "^0.1.2",
58
- "@ianvs/prettier-plugin-sort-imports": "^4.4.1",
67
+ "@ianvs/prettier-plugin-sort-imports": "^4.6.2",
59
68
  "@types/figlet": "^1.7.0",
60
69
  "@types/fs-extra": "^11.0.4",
61
- "@types/lodash": "^4.17.15",
62
- "@types/node": "^22.13.5",
70
+ "@types/lodash": "^4.17.20",
71
+ "@types/node": "^24.2.1",
63
72
  "@types/prompts": "^2.4.9",
64
- "@vitest/coverage-v8": "^3.0.7",
65
- "magicast": "^0.3.5",
66
- "tsup": "^8.4.0",
67
- "typescript": "^5.7.3",
68
- "vitest": "^3.0.7"
73
+ "@vitest/coverage-v8": "^3.2.4",
74
+ "husky": "^9.1.7",
75
+ "knip": "^5.62.0",
76
+ "lint-staged": "^16.1.5",
77
+ "prettier": "^3.6.2",
78
+ "tsup": "^8.5.0",
79
+ "typescript": "^5.9.2",
80
+ "vitest": "^3.2.4"
69
81
  },
70
82
  "publishConfig": {
71
83
  "access": "public"
@@ -1,14 +1,15 @@
1
1
  import path from "node:path";
2
+ import { updateConfig } from "c12/update";
2
3
  import { Command } from "commander";
3
4
  import { consola } from "consola";
4
5
  import kleur from "kleur";
5
6
  import _ from "lodash";
6
7
  import prompts from "prompts";
7
8
 
8
- import allComponents from "../comps";
9
- import { Component } from "../types";
9
+ import { AddCommand, Component } from "../types";
10
10
  import { compareUIConfig } from "../utils/compareUIConfig";
11
- import { addModuleToConfig, getNuxtConfig, getUIConfig, updateConfig } from "../utils/config";
11
+ import { addModuleToConfig, getUIConfig } from "../utils/config";
12
+ import { fetchComponents } from "../utils/fetchComponents";
12
13
  import { fileExists } from "../utils/fileExists";
13
14
  import { installPackages } from "../utils/installPackages";
14
15
  import { installValidator } from "../utils/installValidator";
@@ -16,302 +17,245 @@ import { printFancyBoxMessage } from "../utils/printFancyBoxMessage";
16
17
  import { promptUserForComponents } from "../utils/promptForComponents";
17
18
  import { writeFile } from "../utils/writeFile";
18
19
 
20
+ let allComponents: Component[] = [];
19
21
  const currentDirectory = process.cwd();
20
22
 
21
- const findComponent = (name: string) => {
22
- return allComponents.find((c) => c.value.toLowerCase() === name.toLowerCase());
23
- };
24
-
25
23
  /**
26
- * Adds a component to your project
24
+ * Finds a component definition by its name (case-insensitive).
27
25
  */
28
- export const add = new Command()
29
- .name("add")
30
- .command("add")
31
- .description("Add a list of components to your project.")
32
- .option("-a --all", "Add all components to your project.", false)
33
- .argument("[componentNames...]", "Components that you want to add.")
34
- .action(async (components: Array<string>, options: { all?: boolean }) => {
35
- // Get nuxt config
36
- const cfg = await getNuxtConfig();
37
- // Get ui config
38
- let uiConfig = await getUIConfig();
39
- let uiConfigIsCorrect = await compareUIConfig();
40
- if (!uiConfigIsCorrect) {
41
- uiConfig = await getUIConfig({ force: true });
42
- }
43
- if (_.isEmpty(uiConfig)) {
44
- consola.info("Config file not set. Exiting...");
45
- process.exit(0);
46
- }
26
+ const findComponent = (name: string) =>
27
+ allComponents.find((c) => c.value.toLowerCase() === name.toLowerCase());
47
28
 
48
- let componentNames = components;
49
- // if no components are passed, prompt the user to select components
50
- if (componentNames.length === 0) {
51
- const response = await promptUserForComponents(options.all);
52
- if ((response && response.length === 0) || !response) {
53
- consola.info("No components selected. Exiting...");
54
- process.exit(0);
55
- }
56
- componentNames = response;
57
- }
29
+ /**
30
+ * Handles writing a file with overwrite checks.
31
+ */
32
+ async function safeWriteFile(
33
+ targetPath: string,
34
+ content: string,
35
+ forceOverwrite: boolean,
36
+ promptMessage: string
37
+ ) {
38
+ const exists = await fileExists(targetPath);
58
39
 
59
- // store the components that are not found
60
- let notFound: string[] = [];
61
- componentNames.forEach((c) => {
62
- if (!findComponent(c)) {
63
- notFound.push(c);
64
- }
40
+ if (exists && !forceOverwrite) {
41
+ const { value: overwrite } = await prompts({
42
+ type: "confirm",
43
+ name: "value",
44
+ message: promptMessage,
45
+ initial: false,
65
46
  });
66
- if (notFound.length > 0) {
67
- consola.error(`The following components were not found: ${kleur.bgRed(notFound.join(", "))}`);
47
+ if (!overwrite) {
48
+ consola.info(`Skipped: ${kleur.cyan(path.basename(targetPath))}`);
49
+ return false;
68
50
  }
51
+ }
69
52
 
70
- // store the components that are found
71
- let found: Component[] = [];
72
- componentNames.forEach((c) => {
73
- if (findComponent(c)) {
74
- found.push(findComponent(c)!);
75
- }
76
- });
77
- // check if the found components depends on other components and add them to the list
78
- for (let i = 0; i < found.length; i++) {
79
- const component = found[i];
80
- if (component.components) {
81
- for (let j = 0; j < component.components.length; j++) {
82
- const comp = component.components[j];
83
- if (!found.find((c) => c.value === comp)) {
84
- found.push(findComponent(comp)!);
85
- }
86
- }
87
- }
53
+ await writeFile(targetPath, content);
54
+ return true;
55
+ }
56
+
57
+ /**
58
+ * Writes all files in a given category (utils, composables, plugins).
59
+ */
60
+ async function writeCategoryFiles(
61
+ category: string,
62
+ items: Array<{ fileName: string; fileContent: string; dirPath?: string }>,
63
+ baseDir: string,
64
+ forceOverwrite: boolean
65
+ ) {
66
+ for (const item of items) {
67
+ const targetPath = path.join(currentDirectory, baseDir, item.fileName);
68
+ await safeWriteFile(
69
+ targetPath,
70
+ item.fileContent,
71
+ forceOverwrite,
72
+ `The ${category} file ${kleur.bold(item.fileName)} already exists. Overwrite?`
73
+ );
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Main command logic for adding components.
79
+ */
80
+ const runAddCommand = async (components: string[], options: AddCommand) => {
81
+ // Step 1 — Load and verify UI config
82
+ let uiConfig = await getUIConfig();
83
+ if (!(await compareUIConfig())) {
84
+ uiConfig = await getUIConfig({ force: true });
85
+ }
86
+ if (_.isEmpty(uiConfig)) {
87
+ consola.info("Config file not set. Exiting...");
88
+ process.exit(0);
89
+ }
90
+
91
+ // Step 2 — Fetch all available components
92
+ allComponents = await fetchComponents();
93
+
94
+ // Step 3 — If no components were passed, prompt user to select them
95
+ let componentNames = components;
96
+ if (componentNames.length === 0) {
97
+ const response = await promptUserForComponents(options.all, allComponents);
98
+ if (!response || response.length === 0) {
99
+ consola.info("No components selected. Exiting...");
100
+ process.exit(0);
88
101
  }
102
+ componentNames = response;
103
+ }
89
104
 
90
- // add the components & files associated with them
91
- for (let i = 0; i < found.length; i++) {
92
- const component = found[i];
93
- loop2: for (let k = 0; k < component.files.length; k++) {
94
- const file = component.files[k];
95
- let fileName = file.fileName;
96
- let dirPath = uiConfig.componentsLocation;
97
- let filePath = path.join(currentDirectory, dirPath, fileName);
98
- if (!uiConfig.useDefaultFilename) {
99
- const res = await prompts({
100
- type: "text",
101
- name: "value",
102
- message: `Where should we add the file`,
103
- initial: dirPath,
104
- onRender(kleur) {
105
- //@ts-ignore
106
- this.msg =
107
- kleur.bgCyan(" Location ") +
108
- ` Where should we add the file ${kleur.cyan(`${fileName}`)} `;
109
- },
110
- });
111
- if (res.value) {
112
- dirPath = res.value;
113
- filePath = path.join(currentDirectory, res.value, fileName);
114
- }
115
- }
116
- // Check if the file exists
117
- const exists = await fileExists(filePath);
118
- // if it exists & the force option was not passed, ask the user to confirm overwriting the file
119
- if (exists && !uiConfig.force) {
120
- const res = await prompts({
121
- type: "confirm",
122
- name: "value",
123
- message: `The file that we are trying to add ${kleur.bold(
124
- fileName
125
- )} to is already taken. Overwrite?`,
126
- initial: false,
127
- });
128
- if (!res.value) {
129
- consola.info(`We will not overwrite the file for ${kleur.cyan(fileName)}`);
130
- continue loop2;
131
- }
132
- }
133
- await writeFile(filePath, file.fileContent);
105
+ // Step 4 Validate component names
106
+ const notFound = componentNames.filter((name) => !findComponent(name));
107
+ if (notFound.length > 0) {
108
+ consola.error(`Not found: ${kleur.bgRed(notFound.join(", "))}`);
109
+ }
134
110
 
135
- // @not-scalable
136
- if (component.value === "vue-sonner") {
137
- // Update the nuxt config
138
- cfg.defaultExport.imports ||= {};
139
- cfg.defaultExport.build ||= {};
140
- cfg.defaultExport.imports.imports ||= [];
141
- cfg.defaultExport.build.transpile ||= [];
142
- const sonnerExists = cfg.defaultExport.imports.imports.find(
143
- (i: any) => i.from === "vue-sonner" && i.name === "toast"
144
- );
145
- if (!sonnerExists) {
146
- // prettier-ignore
147
- cfg.defaultExport.imports.imports.push({ from: "vue-sonner", name: "toast", as: "useSonner" });
148
- }
149
- const transpileExists = cfg.defaultExport.build.transpile.find((i: any) => "vue-sonner");
150
- if (!transpileExists) {
151
- cfg.defaultExport.build.transpile.push("vue-sonner");
152
- }
153
- }
154
- // @not-scalable
155
- if (component.value === "datatable") {
156
- cfg.defaultExport.app ||= {};
157
- cfg.defaultExport.app.head ||= {};
158
- cfg.defaultExport.app.head.script ||= [];
159
- const scriptOneExists = cfg.defaultExport.app.head.script.find(
160
- (i: any) =>
161
- i.src === "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/pdfmake.min.js"
162
- );
163
- if (!scriptOneExists) {
164
- cfg.defaultExport.app.head.script.push({
165
- src: "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/pdfmake.min.js",
166
- defer: true,
167
- });
168
- }
169
- const scriptTwoExists = cfg.defaultExport.app.head.script.find(
170
- (i: any) =>
171
- i.src === "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/vfs_fonts.min.js"
172
- );
173
- if (!scriptTwoExists) {
174
- cfg.defaultExport.app.head.script.push({
175
- src: "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/vfs_fonts.min.js",
176
- defer: true,
177
- });
178
- }
179
- }
111
+ // Step 5 — Collect found components and their dependencies
112
+ let found: Component[] = componentNames
113
+ .map((name) => findComponent(name))
114
+ .filter(Boolean) as Component[];
180
115
 
181
- // add utils attached to the component
182
- loop3: for (let j = 0; j < component.utils.length; j++) {
183
- const util = component.utils[j];
184
- const filePath = path.join(currentDirectory, uiConfig.utilsLocation, util.fileName);
185
- // Check if the file exists
186
- const exists = await fileExists(filePath);
187
- if (exists && !uiConfig.force) {
188
- const res = await prompts({
189
- type: "confirm",
190
- name: "value",
191
- message: `The utils file that we are trying to add ${kleur.bold(
192
- util.fileName
193
- )} already exists. Overwrite?`,
194
- initial: true,
195
- });
196
- if (!res.value) {
197
- consola.info(`We will not overwrite the file for ${kleur.cyan(util.fileName)}`);
198
- continue loop3;
199
- }
200
- }
201
- await writeFile(filePath, util.fileContent);
202
- }
203
- // add composables attached to the component
204
- loop4: for (let j = 0; j < component.composables.length; j++) {
205
- const composable = component.composables[j];
206
- const filePath = path.join(
207
- currentDirectory,
208
- uiConfig.composablesLocation,
209
- composable.fileName
210
- );
211
- // Check if the file exists
212
- const exists = await fileExists(filePath);
213
- if (exists && !uiConfig.force) {
214
- const res = await prompts({
215
- type: "confirm",
216
- name: "value",
217
- message: `The composables file that we are trying to add ${kleur.bold(
218
- composable.fileName
219
- )} already exists. Overwrite?`,
220
- initial: true,
221
- });
222
- if (!res.value) {
223
- consola.info(`We will not overwrite the file for ${kleur.cyan(composable.fileName)}`);
224
- continue loop4;
225
- }
226
- }
227
- await writeFile(filePath, composable.fileContent);
228
- }
229
- // add plugins attached to the component
230
- loop5: for (let j = 0; j < component.plugins.length; j++) {
231
- const plugin = component.plugins[j];
232
- const filePath = path.join(
233
- currentDirectory,
234
- uiConfig.pluginsLocation ?? plugin.dirPath,
235
- plugin.fileName
236
- );
237
- // Check if the file exists
238
- const exists = await fileExists(filePath);
239
- if (exists && !uiConfig.force) {
240
- const res = await prompts({
241
- type: "confirm",
242
- name: "value",
243
- message: `The plugins file that we are trying to add ${kleur.bold(
244
- plugin.fileName
245
- )} already exists. Overwrite?`,
246
- initial: true,
247
- });
248
- if (!res.value) {
249
- consola.info(`We will not overwrite the file for ${kleur.cyan(plugin.fileName)}`);
250
- continue loop5;
251
- }
252
- }
253
- await writeFile(filePath, plugin.fileContent);
116
+ for (const comp of [...found]) {
117
+ if (comp.components) {
118
+ comp.components.forEach((dep) => {
119
+ if (!found.find((c) => c.value === dep)) {
120
+ found.push(findComponent(dep)!);
254
121
  }
255
- }
122
+ });
256
123
  }
257
- // Add modules to nuxt config
258
- addModuleToConfig(cfg.nuxtConfig, _.uniq(found.map((c) => c.nuxtModules || []).flat()));
259
- // Write the changes to the nuxt config
260
- await updateConfig(cfg.nuxtConfig, "nuxt.config.ts");
261
- const foundDeps = _.uniq(found.map((c) => c.deps || []).flat());
262
- const foundDevDeps = _.uniq(found.map((c) => c.devDeps || []).flat());
124
+ }
263
125
 
264
- // check if the foundDeps & foundDevDeps lists are not empty, ask the user to install them
265
- if (foundDeps.length > 0 || foundDevDeps.length > 0) {
266
- // if the all option was passed, install the packages without asking
267
- if (options.all) {
268
- await installPackages(uiConfig.packageManager, foundDeps, foundDevDeps);
269
- } else {
270
- // Ask the user to install the packages
271
- const { confirmInstall } = await prompts({
272
- type: "confirm",
273
- name: "confirmInstall",
274
- message: `Do you want to install the following packages: ${kleur.cyan(
275
- foundDeps.join(", ")
276
- )} ${kleur.cyan(foundDevDeps.join(", "))}`,
277
- initial: true,
126
+ // Step 6 Write files for each component
127
+ for (const component of found) {
128
+ for (const file of component.files) {
129
+ let dirPath = uiConfig.componentsLocation;
130
+ let filePath = path.join(currentDirectory, dirPath, file.fileName);
131
+
132
+ // Ask for custom location if not using default
133
+ if (!uiConfig.useDefaultFilename) {
134
+ const { value: newDir } = await prompts({
135
+ type: "text",
136
+ name: "value",
137
+ message: `Where should we add the file ${kleur.cyan(file.fileName)}?`,
138
+ initial: dirPath,
278
139
  });
279
- if (confirmInstall) {
280
- await installPackages(uiConfig.packageManager, foundDeps, foundDevDeps);
140
+ if (newDir) {
141
+ dirPath = newDir;
142
+ filePath = path.join(currentDirectory, dirPath, file.fileName);
281
143
  }
282
144
  }
145
+
146
+ const fileWritten = await safeWriteFile(
147
+ filePath,
148
+ file.fileContent,
149
+ uiConfig.force,
150
+ `The file ${kleur.bold(file.fileName)} already exists. Overwrite?`
151
+ );
152
+ if (!fileWritten) continue;
153
+
154
+ // Component-specific logic hooks
155
+ if (component.value === "vue-sonner" || component.value === "sonner") await addSonner();
156
+ if (component.value === "datatable") await addDataTable();
157
+
158
+ // Write related files
159
+ await writeCategoryFiles("utils", component.utils, uiConfig.utilsLocation, uiConfig.force);
160
+ await writeCategoryFiles(
161
+ "composables",
162
+ component.composables,
163
+ uiConfig.composablesLocation,
164
+ uiConfig.force
165
+ );
166
+ await writeCategoryFiles(
167
+ "plugins",
168
+ component.plugins,
169
+ uiConfig.pluginsLocation ?? "",
170
+ uiConfig.force
171
+ );
283
172
  }
173
+ }
284
174
 
285
- // check if any of the components has the `askValidator` property set to true
286
- let shouldAskValidator = false;
287
- // Check if any component has askValidator set to true
288
- for (const component of found) {
289
- if (component.askValidator) {
290
- shouldAskValidator = true;
291
- break;
175
+ // Step 7 Add Nuxt modules
176
+ await addModuleToConfig(_.uniq(found.flatMap((c) => c.nuxtModules || [])));
177
+
178
+ // Step 8 Install dependencies if necessary
179
+ const deps = _.uniq(found.flatMap((c) => c.deps || []));
180
+ const devDeps = _.uniq(found.flatMap((c) => c.devDeps || []));
181
+ if (deps.length > 0 || devDeps.length > 0) {
182
+ if (options.all) {
183
+ await installPackages(uiConfig.packageManager, deps, devDeps);
184
+ } else {
185
+ const { confirmInstall } = await prompts({
186
+ type: "confirm",
187
+ name: "confirmInstall",
188
+ message: `Install packages: ${kleur.cyan([...deps, ...devDeps].join(", "))}?`,
189
+ initial: true,
190
+ });
191
+ if (confirmInstall) {
192
+ await installPackages(uiConfig.packageManager, deps, devDeps);
292
193
  }
293
194
  }
195
+ }
294
196
 
295
- if (shouldAskValidator) {
296
- // Ask the user for their choice of validator
297
- await installValidator(uiConfig.packageManager);
298
- }
197
+ // Step 9 — Install validator if required
198
+ if (found.some((c) => c.askValidator)) {
199
+ await installValidator(uiConfig.packageManager);
200
+ }
299
201
 
300
- printFancyBoxMessage(
301
- "All Done!",
302
- { title: "Components Added" },
303
- `Run the ${kleur.cyan("ui-thing@latest --help")} command to learn more.\n`
304
- );
305
- const combinedInstructions = found.map((c) => c.instructions).flat();
306
- // remove undefined from the array
307
- _.remove(combinedInstructions, (i) => !i);
202
+ // Step 10 — Success message & instructions
203
+ printFancyBoxMessage(
204
+ "All Done!",
205
+ `Run the ${kleur.cyan("ui-thing@latest --help")} command to learn more.\n`,
206
+ { box: { title: "Components Added" } }
207
+ );
208
+
209
+ const instructions = _.compact(found.flatMap((c) => c.instructions));
210
+ if (instructions.length > 0) {
211
+ console.log("");
212
+ console.log(kleur.bgCyan(" Instructions "));
213
+ instructions.forEach((i) => console.log(`${kleur.cyan("-")} ${i}`));
214
+ }
215
+ };
308
216
 
309
- // print the instructions if there are any
310
- if (combinedInstructions.length > 0) {
311
- console.log("");
312
- console.log(kleur.bgCyan(" Instructions "));
313
- combinedInstructions.forEach((i) => {
314
- console.log(`${kleur.cyan("-")} ${i}`);
217
+ /**
218
+ * CLI Command Registration
219
+ */
220
+ export const add = new Command()
221
+ .name("add")
222
+ .command("add")
223
+ .description("Add a list of components to your project.")
224
+ .option("-a --all", "Add all components to your project.", false)
225
+ .argument("[componentNames...]", "Components that you want to add.")
226
+ .action(runAddCommand);
227
+
228
+ /**
229
+ * Component-specific setup helpers
230
+ */
231
+ async function addSonner() {
232
+ await updateConfig({
233
+ configFile: "nuxt.config",
234
+ cwd: currentDirectory,
235
+ onUpdate(config: any) {
236
+ config.imports ||= { imports: [] };
237
+ if (!config.imports.imports.find((i: any) => i.from === "vue-sonner" && i.name === "toast")) {
238
+ config.imports.imports.push({ from: "vue-sonner", name: "toast", as: "useSonner" });
239
+ }
240
+ },
241
+ });
242
+ }
243
+
244
+ async function addDataTable() {
245
+ await updateConfig({
246
+ configFile: "nuxt.config",
247
+ cwd: currentDirectory,
248
+ onUpdate(cfg: any) {
249
+ cfg.app ||= { head: { script: [] } };
250
+ const scripts = [
251
+ "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/pdfmake.min.js",
252
+ "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/vfs_fonts.min.js",
253
+ ];
254
+ scripts.forEach((src) => {
255
+ if (!cfg.app.head.script.find((i: any) => i.src === src)) {
256
+ cfg.app.head.script.push({ src, defer: true });
257
+ }
315
258
  });
316
- }
259
+ },
317
260
  });
261
+ }