shadcn-nextjs-page-generator 1.0.5 → 1.0.6

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/README.md CHANGED
@@ -12,11 +12,13 @@
12
12
  ✨ **Interactive CLI** - User-friendly prompts guide you through the generation process
13
13
  🏗️ **Flexible Architecture** - Choose between DDD (Domain-Driven Design) or Simplified structure
14
14
  🎨 **shadcn/ui Components** - Beautiful, accessible components built with Radix UI
15
+ � **Auto-Install** - Automatically detects and installs missing shadcn components
15
16
  💨 **Tailwind CSS v4** - Latest Tailwind with modern CSS-first syntax
16
17
  🎭 **Framer Motion** - Smooth animations with configurable intensity
17
18
  📊 **Complete CRUD** - Tables with search, filter, sort, pagination out of the box
18
19
  🔄 **Multiple Data Fetching** - Support for Mock data, TanStack Query, or standard fetch
19
20
  ⚡ **TypeScript First** - Full type safety and IntelliSense support
21
+ ♻️ **Smart Overwrite** - Regenerate files safely with automatic overwrite detection
20
22
 
21
23
  ## Quick Start
22
24
 
@@ -98,6 +100,20 @@ npx shadcn-nextjs-page-generator
98
100
 
99
101
  ### 3. Install Dependencies (if needed)
100
102
 
103
+ The CLI automatically checks and installs missing shadcn/ui components! You'll see:
104
+
105
+ ```
106
+ 🔍 Checking shadcn components...
107
+ ✓ button already installed
108
+ ✓ table already installed
109
+ - card missing, installing...
110
+ - pagination missing, installing...
111
+
112
+ ✓ Installed 2 component(s): card, pagination
113
+ ```
114
+
115
+ If you prefer to install manually or need additional dependencies:
116
+
101
117
  ```bash
102
118
  # If you selected TanStack Query
103
119
  npm install @tanstack/react-query
@@ -106,6 +122,8 @@ npm install @tanstack/react-query
106
122
  npm install framer-motion
107
123
  ```
108
124
 
125
+ > **Note:** Auto-install requires shadcn/ui to be initialized in your project (`npx shadcn@latest init`)
126
+
109
127
  ### 4. Navigate to Your Page
110
128
 
111
129
  ```bash
@@ -301,8 +319,9 @@ const containerVariants = {
301
319
 
302
320
  ## shadcn/ui Components Used
303
321
 
304
- The generated code uses these shadcn/ui components:
322
+ The generator automatically detects and installs the required shadcn/ui components based on your configuration:
305
323
 
324
+ **Always installed:**
306
325
  - `button`
307
326
  - `table`
308
327
  - `card`
@@ -311,14 +330,27 @@ The generated code uses these shadcn/ui components:
311
330
  - `badge`
312
331
  - `pagination`
313
332
  - `dropdown-menu`
314
- - `popover`
315
- - `calendar` (if date filters)
316
- - `checkbox` (if row selection)
317
333
 
318
- Make sure to install them:
334
+ **Conditionally installed:**
335
+ - `popover` + `calendar` (if date filters enabled)
336
+ - `checkbox` (if row selection enabled)
337
+
338
+ ### Auto-Install Feature
339
+
340
+ The CLI automatically checks for missing components and installs them using:
341
+
342
+ ```bash
343
+ npx shadcn@latest add <component> --yes --overwrite
344
+ ```
345
+
346
+ This happens before generating your files, ensuring all required components are available.
347
+
348
+ ### Manual Installation
349
+
350
+ If you prefer to install components manually:
319
351
 
320
352
  ```bash
321
- npx shadcn-ui@latest add button table card input select badge pagination dropdown-menu popover calendar checkbox
353
+ npx shadcn@latest add button table card input select badge pagination dropdown-menu popover calendar checkbox
322
354
  ```
323
355
 
324
356
  ## FAQ
package/dist/index.cjs CHANGED
@@ -377,7 +377,7 @@ ${message}
377
377
  };
378
378
 
379
379
  // src/generators/ddd-generator.ts
380
- var import_path2 = __toESM(require("path"), 1);
380
+ var import_path3 = __toESM(require("path"), 1);
381
381
 
382
382
  // src/utils/file-system.ts
383
383
  var import_fs_extra = __toESM(require("fs-extra"), 1);
@@ -425,6 +425,95 @@ async function checkExistingFiles(filePaths) {
425
425
  return existingFiles;
426
426
  }
427
427
 
428
+ // src/utils/shadcn-installer.ts
429
+ var import_child_process = require("child_process");
430
+ var import_fs = __toESM(require("fs"), 1);
431
+ var import_path2 = __toESM(require("path"), 1);
432
+ var COMPONENT_MAP = {
433
+ button: "button",
434
+ table: "table",
435
+ select: "select",
436
+ input: "input",
437
+ badge: "badge",
438
+ card: "card",
439
+ checkbox: "checkbox",
440
+ calendar: "calendar",
441
+ popover: "popover",
442
+ pagination: "pagination",
443
+ dropdown: "dropdown-menu"
444
+ };
445
+ function isComponentInstalled(componentName, cwd) {
446
+ const componentPath = import_path2.default.join(cwd, "components", "ui", `${componentName}.tsx`);
447
+ return import_fs.default.existsSync(componentPath);
448
+ }
449
+ function installComponent(componentName, cwd) {
450
+ try {
451
+ logger.info(`Installing ${componentName} component...`);
452
+ (0, import_child_process.execSync)(`npx shadcn@latest add ${componentName} --yes --overwrite`, {
453
+ cwd,
454
+ stdio: "inherit"
455
+ });
456
+ return true;
457
+ } catch (error) {
458
+ logger.error(`Failed to install ${componentName}: ${error}`);
459
+ return false;
460
+ }
461
+ }
462
+ function detectRequiredComponents(config) {
463
+ const required = /* @__PURE__ */ new Set();
464
+ required.add(COMPONENT_MAP.button);
465
+ required.add(COMPONENT_MAP.table);
466
+ required.add(COMPONENT_MAP.select);
467
+ required.add(COMPONENT_MAP.input);
468
+ required.add(COMPONENT_MAP.badge);
469
+ required.add(COMPONENT_MAP.card);
470
+ required.add(COMPONENT_MAP.pagination);
471
+ required.add(COMPONENT_MAP.dropdown);
472
+ if (config.includeRowSelection) {
473
+ required.add(COMPONENT_MAP.checkbox);
474
+ }
475
+ if (config.filters.some((f) => f.type === "date")) {
476
+ required.add(COMPONENT_MAP.calendar);
477
+ required.add(COMPONENT_MAP.popover);
478
+ }
479
+ return Array.from(required);
480
+ }
481
+ async function autoInstallComponents(config, cwd = process.cwd()) {
482
+ const result = {
483
+ installed: [],
484
+ skipped: [],
485
+ failed: []
486
+ };
487
+ const required = detectRequiredComponents(config);
488
+ logger.info(`Checking ${required.length} shadcn components...`);
489
+ for (const component of required) {
490
+ if (isComponentInstalled(component, cwd)) {
491
+ result.skipped.push(component);
492
+ logger.dim(` \u2713 ${component} already installed`);
493
+ } else {
494
+ logger.dim(` - ${component} missing, installing...`);
495
+ const success = installComponent(component, cwd);
496
+ if (success) {
497
+ result.installed.push(component);
498
+ } else {
499
+ result.failed.push(component);
500
+ }
501
+ }
502
+ }
503
+ return result;
504
+ }
505
+ function checkShadcnCli() {
506
+ try {
507
+ (0, import_child_process.execSync)("npx shadcn@latest --version", { stdio: "ignore" });
508
+ return true;
509
+ } catch {
510
+ return false;
511
+ }
512
+ }
513
+ function isShadcnInitialized(cwd = process.cwd()) {
514
+ return import_fs.default.existsSync(import_path2.default.join(cwd, "components.json"));
515
+ }
516
+
428
517
  // src/templates/ddd/entity.ts
429
518
  function generateEntity(config) {
430
519
  const { entityName, moduleName, columns } = config;
@@ -656,7 +745,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
656
745
  import { Calendar as CalendarIcon } from 'lucide-react';
657
746
  import { format } from 'date-fns';` : ""}
658
747
  import { Badge } from '@/components/ui/badge';
659
- ${includeStats ? `import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';` : ""}
748
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
660
749
  import {
661
750
  Search,
662
751
  Plus,
@@ -1188,44 +1277,45 @@ var DDDGenerator = class {
1188
1277
  const files = [];
1189
1278
  const cwd = process.cwd();
1190
1279
  const { moduleName, routePath } = this.config;
1191
- const moduleDir = import_path2.default.join(cwd, "modules", moduleName);
1192
- const appDir = import_path2.default.join(cwd, "app", "(dashboard)", routePath);
1280
+ await this.checkAndInstallComponents(cwd);
1281
+ const moduleDir = import_path3.default.join(cwd, "modules", moduleName);
1282
+ const appDir = import_path3.default.join(cwd, "app", "(dashboard)", routePath);
1193
1283
  const dirs = [
1194
- import_path2.default.join(moduleDir, "domain", "entities"),
1195
- import_path2.default.join(moduleDir, "domain", "repositories"),
1196
- import_path2.default.join(moduleDir, "application", "use-cases"),
1197
- import_path2.default.join(moduleDir, "infrastructure", "repositories"),
1198
- import_path2.default.join(moduleDir, "presentation", "components"),
1284
+ import_path3.default.join(moduleDir, "domain", "entities"),
1285
+ import_path3.default.join(moduleDir, "domain", "repositories"),
1286
+ import_path3.default.join(moduleDir, "application", "use-cases"),
1287
+ import_path3.default.join(moduleDir, "infrastructure", "repositories"),
1288
+ import_path3.default.join(moduleDir, "presentation", "components"),
1199
1289
  appDir
1200
1290
  ];
1201
1291
  await createDirectories(dirs);
1202
1292
  files.push({
1203
- path: import_path2.default.join(moduleDir, "domain", "entities", `${moduleName}.entity.ts`),
1293
+ path: import_path3.default.join(moduleDir, "domain", "entities", `${moduleName}.entity.ts`),
1204
1294
  content: generateEntity(this.config)
1205
1295
  });
1206
1296
  files.push({
1207
- path: import_path2.default.join(moduleDir, "domain", "repositories", `${moduleName}.repository.interface.ts`),
1297
+ path: import_path3.default.join(moduleDir, "domain", "repositories", `${moduleName}.repository.interface.ts`),
1208
1298
  content: generateRepositoryInterface(this.config)
1209
1299
  });
1210
1300
  files.push({
1211
- path: import_path2.default.join(moduleDir, "infrastructure", "repositories", `${moduleName}.repository.ts`),
1301
+ path: import_path3.default.join(moduleDir, "infrastructure", "repositories", `${moduleName}.repository.ts`),
1212
1302
  content: generateRepositoryImpl(this.config)
1213
1303
  });
1214
1304
  files.push({
1215
- path: import_path2.default.join(moduleDir, "application", "use-cases", `get-${moduleName}s.use-case.ts`),
1305
+ path: import_path3.default.join(moduleDir, "application", "use-cases", `get-${moduleName}s.use-case.ts`),
1216
1306
  content: generateUseCase(this.config)
1217
1307
  });
1218
1308
  files.push({
1219
- path: import_path2.default.join(moduleDir, "presentation", "components", `${moduleName}-list.tsx`),
1309
+ path: import_path3.default.join(moduleDir, "presentation", "components", `${moduleName}-list.tsx`),
1220
1310
  content: generateComponent(this.config)
1221
1311
  });
1222
1312
  files.push({
1223
- path: import_path2.default.join(appDir, "page.tsx"),
1313
+ path: import_path3.default.join(appDir, "page.tsx"),
1224
1314
  content: generatePage(this.config)
1225
1315
  });
1226
1316
  if (this.config.animations.pageTransitions) {
1227
1317
  files.push({
1228
- path: import_path2.default.join(appDir, "template.tsx"),
1318
+ path: import_path3.default.join(appDir, "template.tsx"),
1229
1319
  content: generateTemplate(this.config)
1230
1320
  });
1231
1321
  }
@@ -1235,7 +1325,7 @@ var DDDGenerator = class {
1235
1325
  console.log("");
1236
1326
  logger.warning(`Found ${existingFiles.length} existing file(s):`);
1237
1327
  existingFiles.forEach((file) => {
1238
- const relativePath = import_path2.default.relative(cwd, file);
1328
+ const relativePath = import_path3.default.relative(cwd, file);
1239
1329
  logger.dim(` - ${relativePath}`);
1240
1330
  });
1241
1331
  console.log("");
@@ -1265,10 +1355,33 @@ var DDDGenerator = class {
1265
1355
  }
1266
1356
  return instructions;
1267
1357
  }
1358
+ async checkAndInstallComponents(cwd) {
1359
+ if (!isShadcnInitialized(cwd)) {
1360
+ logger.warning("shadcn/ui is not initialized in this project.");
1361
+ logger.info("Please run: npx shadcn@latest init");
1362
+ return;
1363
+ }
1364
+ if (!checkShadcnCli()) {
1365
+ logger.warning("shadcn CLI not available. Skipping component auto-install.");
1366
+ return;
1367
+ }
1368
+ console.log("");
1369
+ logger.info("\u{1F50D} Checking shadcn components...");
1370
+ const result = await autoInstallComponents(this.config, cwd);
1371
+ if (result.installed.length > 0) {
1372
+ console.log("");
1373
+ logger.success(`\u2713 Installed ${result.installed.length} component(s): ${result.installed.join(", ")}`);
1374
+ }
1375
+ if (result.failed.length > 0) {
1376
+ console.log("");
1377
+ logger.error(`\u2717 Failed to install ${result.failed.length} component(s): ${result.failed.join(", ")}`);
1378
+ }
1379
+ console.log("");
1380
+ }
1268
1381
  };
1269
1382
 
1270
1383
  // src/generators/simplified-generator.ts
1271
- var import_path3 = __toESM(require("path"), 1);
1384
+ var import_path4 = __toESM(require("path"), 1);
1272
1385
 
1273
1386
  // src/templates/simplified/component.ts
1274
1387
  function generateSimplifiedComponent(config) {
@@ -1395,7 +1508,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
1395
1508
  import { Calendar as CalendarIcon } from 'lucide-react';
1396
1509
  import { format } from 'date-fns';` : ""}
1397
1510
  import { Badge } from '@/components/ui/badge';
1398
- ${includeStats ? `import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';` : ""}
1511
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
1399
1512
  import {
1400
1513
  Search,
1401
1514
  Plus,
@@ -1829,20 +1942,21 @@ var SimplifiedGenerator = class {
1829
1942
  const files = [];
1830
1943
  const cwd = process.cwd();
1831
1944
  const { moduleName, routePath } = this.config;
1832
- const componentDir = import_path3.default.join(cwd, "components", moduleName);
1833
- const appDir = import_path3.default.join(cwd, "app", "(dashboard)", routePath);
1945
+ await this.checkAndInstallComponents(cwd);
1946
+ const componentDir = import_path4.default.join(cwd, "components", moduleName);
1947
+ const appDir = import_path4.default.join(cwd, "app", "(dashboard)", routePath);
1834
1948
  await createDirectories([componentDir, appDir]);
1835
1949
  files.push({
1836
- path: import_path3.default.join(componentDir, `${moduleName}-list.tsx`),
1950
+ path: import_path4.default.join(componentDir, `${moduleName}-list.tsx`),
1837
1951
  content: generateSimplifiedComponent(this.config)
1838
1952
  });
1839
1953
  files.push({
1840
- path: import_path3.default.join(appDir, "page.tsx"),
1954
+ path: import_path4.default.join(appDir, "page.tsx"),
1841
1955
  content: generateSimplifiedPage(this.config)
1842
1956
  });
1843
1957
  if (this.config.animations.pageTransitions) {
1844
1958
  files.push({
1845
- path: import_path3.default.join(appDir, "template.tsx"),
1959
+ path: import_path4.default.join(appDir, "template.tsx"),
1846
1960
  content: generateTemplate(this.config)
1847
1961
  });
1848
1962
  }
@@ -1852,7 +1966,7 @@ var SimplifiedGenerator = class {
1852
1966
  console.log("");
1853
1967
  logger.warning(`Found ${existingFiles.length} existing file(s):`);
1854
1968
  existingFiles.forEach((file) => {
1855
- const relativePath = import_path3.default.relative(cwd, file);
1969
+ const relativePath = import_path4.default.relative(cwd, file);
1856
1970
  logger.dim(` - ${relativePath}`);
1857
1971
  });
1858
1972
  console.log("");
@@ -1882,6 +1996,29 @@ var SimplifiedGenerator = class {
1882
1996
  }
1883
1997
  return instructions;
1884
1998
  }
1999
+ async checkAndInstallComponents(cwd) {
2000
+ if (!isShadcnInitialized(cwd)) {
2001
+ logger.warning("shadcn/ui is not initialized in this project.");
2002
+ logger.info("Please run: npx shadcn@latest init");
2003
+ return;
2004
+ }
2005
+ if (!checkShadcnCli()) {
2006
+ logger.warning("shadcn CLI not available. Skipping component auto-install.");
2007
+ return;
2008
+ }
2009
+ console.log("");
2010
+ logger.info("\u{1F50D} Checking shadcn components...");
2011
+ const result = await autoInstallComponents(this.config, cwd);
2012
+ if (result.installed.length > 0) {
2013
+ console.log("");
2014
+ logger.success(`\u2713 Installed ${result.installed.length} component(s): ${result.installed.join(", ")}`);
2015
+ }
2016
+ if (result.failed.length > 0) {
2017
+ console.log("");
2018
+ logger.error(`\u2717 Failed to install ${result.failed.length} component(s): ${result.failed.join(", ")}`);
2019
+ }
2020
+ console.log("");
2021
+ }
1885
2022
  };
1886
2023
 
1887
2024
  // src/generators/index.ts