create-bluecopa-react-app 1.0.4 → 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 +47 -10
- package/bin/create-bluecopa-react-app.js +257 -51
- package/package.json +6 -5
- package/templates/latest/Agent.md +254 -0
- package/templates/latest/Dockerfile +22 -0
- package/templates/latest/README.md +157 -221
- package/templates/latest/app/app.css +134 -0
- package/templates/latest/app/app.tsx +46 -0
- package/templates/latest/app/components/app-sidebar.tsx +174 -0
- package/templates/latest/app/components/chart-area-interactive.tsx +290 -0
- package/templates/latest/app/components/data-table.tsx +807 -0
- package/templates/latest/app/components/nav-documents.tsx +92 -0
- package/templates/latest/app/components/nav-main.tsx +56 -0
- package/templates/latest/app/components/nav-secondary.tsx +42 -0
- package/templates/latest/app/components/nav-user.tsx +112 -0
- package/templates/latest/app/components/section-cards.tsx +102 -0
- package/templates/latest/app/components/site-header.tsx +19 -0
- package/templates/latest/app/components/ui/avatar.tsx +53 -0
- package/templates/latest/app/components/ui/badge.tsx +46 -0
- package/templates/latest/app/components/ui/breadcrumb.tsx +109 -0
- package/templates/latest/app/components/ui/button.tsx +58 -0
- package/templates/latest/app/components/ui/card.tsx +92 -0
- package/templates/latest/app/components/ui/chart.tsx +352 -0
- package/templates/latest/app/components/ui/checkbox.tsx +30 -0
- package/templates/latest/app/components/ui/drawer.tsx +139 -0
- package/templates/latest/app/components/ui/dropdown-menu.tsx +258 -0
- package/templates/latest/app/components/ui/input.tsx +21 -0
- package/templates/latest/app/components/ui/label.tsx +24 -0
- package/templates/latest/app/components/ui/select.tsx +183 -0
- package/templates/latest/app/components/ui/separator.tsx +26 -0
- package/templates/latest/app/components/ui/sheet.tsx +139 -0
- package/templates/latest/app/components/ui/sidebar.tsx +731 -0
- package/templates/latest/app/components/ui/skeleton.tsx +13 -0
- package/templates/latest/app/components/ui/sonner.tsx +23 -0
- package/templates/latest/app/components/ui/table.tsx +117 -0
- package/templates/latest/app/components/ui/tabs.tsx +66 -0
- package/templates/latest/app/components/ui/toggle-group.tsx +73 -0
- package/templates/latest/app/components/ui/toggle.tsx +47 -0
- package/templates/latest/app/components/ui/tooltip.tsx +59 -0
- package/templates/latest/app/dashboard/data.json +614 -0
- package/templates/latest/app/hooks/use-bluecopa-user.ts +37 -0
- package/templates/latest/app/hooks/use-mobile.ts +19 -0
- package/templates/latest/{src → app}/lib/utils.ts +1 -1
- package/templates/latest/app/main.tsx +12 -0
- package/templates/latest/app/routes/home.tsx +40 -0
- package/templates/latest/app/routes.tsx +15 -0
- package/templates/latest/{src → app}/single-spa.tsx +38 -28
- package/templates/latest/components.json +22 -0
- package/templates/latest/dist/assets/__federation_expose_App-DRwKKpS2.js +91 -0
- package/templates/latest/dist/assets/__federation_fn_import-CzfA7kmP.js +438 -0
- package/templates/latest/dist/assets/__federation_shared_react-Bp6HVBS4.js +16 -0
- package/templates/latest/dist/assets/__federation_shared_react-dom-BCcRGiYp.js +17 -0
- package/templates/latest/dist/assets/client-DgSav55y.js +12658 -0
- package/templates/latest/dist/assets/home-DOL6GrYV.js +54951 -0
- package/templates/latest/dist/assets/index-BzNimew1.js +69 -0
- package/templates/latest/dist/assets/index-DMFtQdNS.js +412 -0
- package/templates/latest/dist/assets/index-DdYpcDMk.js +60 -0
- package/templates/latest/dist/assets/remoteEntry.js +88 -0
- package/templates/latest/dist/assets/style-36A39bNN.css +3683 -0
- package/templates/latest/dist/avatars/shadcn.svg +6 -0
- package/templates/latest/dist/favicon.ico +0 -0
- package/templates/latest/dist/index.html +19 -0
- package/templates/latest/index.html +1 -1
- package/templates/latest/package-lock.json +1227 -3353
- package/templates/latest/package.json +47 -43
- package/templates/latest/pnpm-lock.yaml +4767 -0
- package/templates/latest/preview/index.html +32 -2
- package/templates/latest/public/avatars/shadcn.svg +6 -0
- package/templates/latest/public/favicon.ico +0 -0
- package/templates/latest/tsconfig.json +18 -11
- package/templates/latest/vite.config.ts +41 -41
- package/templates/latest/.env.example +0 -14
- package/templates/latest/.eslintrc.cjs +0 -42
- package/templates/latest/AGENT.md +0 -284
- package/templates/latest/clean.sh +0 -39
- package/templates/latest/postcss.config.cjs +0 -6
- package/templates/latest/public/bluecopa-logo.svg +0 -30
- package/templates/latest/public/favicon-32x32.png +0 -0
- package/templates/latest/public/favicon-96x96.png +0 -0
- package/templates/latest/setup.sh +0 -55
- package/templates/latest/src/App.tsx +0 -15
- package/templates/latest/src/components/layout/dashboard-header.tsx +0 -139
- package/templates/latest/src/components/layout/dashboard-layout.tsx +0 -29
- package/templates/latest/src/components/layout/sidebar.tsx +0 -54
- package/templates/latest/src/components/page/dashboard.tsx +0 -1506
- package/templates/latest/src/components/page/navbar.tsx +0 -104
- package/templates/latest/src/components/tables/data-grid.tsx +0 -439
- package/templates/latest/src/components/ui/alert.tsx +0 -59
- package/templates/latest/src/components/ui/avatar.tsx +0 -50
- package/templates/latest/src/components/ui/badge.tsx +0 -36
- package/templates/latest/src/components/ui/bluecopa-logo.tsx +0 -54
- package/templates/latest/src/components/ui/button.tsx +0 -58
- package/templates/latest/src/components/ui/card.tsx +0 -79
- package/templates/latest/src/components/ui/dropdown-menu.tsx +0 -200
- package/templates/latest/src/components/ui/input.tsx +0 -24
- package/templates/latest/src/components/ui/label.tsx +0 -23
- package/templates/latest/src/components/ui/select.tsx +0 -29
- package/templates/latest/src/hooks/use-api.ts +0 -55
- package/templates/latest/src/index.css +0 -59
- package/templates/latest/src/main.tsx +0 -13
- package/templates/latest/src/pages/Dashboard.tsx +0 -13
- package/templates/latest/src/pages/Home.tsx +0 -622
- package/templates/latest/src/providers/query-provider.tsx +0 -48
- package/templates/latest/src/types/api.ts +0 -78
- package/templates/latest/src/vite-env.d.ts +0 -11
- package/templates/latest/tailwind.config.js +0 -88
- package/templates/latest/tsconfig.app.json +0 -26
- package/templates/latest/tsconfig.node.json +0 -10
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ A command-line interface for bootstrapping modern React applications with BlueCo
|
|
|
20
20
|
|
|
21
21
|
## Version
|
|
22
22
|
|
|
23
|
-
Current version:
|
|
23
|
+
Current version: 1.0.5
|
|
24
24
|
|
|
25
25
|
## Installation
|
|
26
26
|
|
|
@@ -46,35 +46,47 @@ export BLUECOPA_BOILERPLATE_REGISTRY=https://registry.npmjs.org
|
|
|
46
46
|
|
|
47
47
|
## Features
|
|
48
48
|
|
|
49
|
-
- 🚀 **Modern React Stack**: React 18, TypeScript, React Router
|
|
49
|
+
- 🚀 **Modern React Stack**: React 18, TypeScript, React Router v7, Vite 6
|
|
50
50
|
- 🌐 **Microfrontend Support**: Single-spa compatible with module federation
|
|
51
51
|
- 📊 **Data Visualization**: Recharts integration for charts and analytics
|
|
52
|
-
- 🎨 **UI Components**: Radix UI primitives
|
|
52
|
+
- 🎨 **UI Components**: shadcn/ui components built on Radix UI primitives
|
|
53
53
|
- 📡 **BlueCopa React Components**: Pre-configured [`@bluecopa/react`](packages/react:1) package with hooks (includes TanStack Query integration)
|
|
54
54
|
- 🎯 **Type Safety**: Full TypeScript support
|
|
55
|
-
- 📱 **Responsive Design**: Tailwind CSS with mobile-first approach and BlueCopa design system
|
|
55
|
+
- 📱 **Responsive Design**: Tailwind CSS v4 with mobile-first approach and BlueCopa design system
|
|
56
56
|
- 🛠️ **Development Tools**: ESLint, TypeScript checking, and Vite fast build system
|
|
57
57
|
- ⚡ **Fast Development**: Hot module replacement with Vite
|
|
58
|
-
- 🔄 **
|
|
58
|
+
- 🔄 **Server-side Rendering**: React Router v7 with SSR support
|
|
59
|
+
- 🌙 **Dark Mode**: Built-in theme switching with next-themes
|
|
60
|
+
- 🗂️ **Data Tables**: TanStack Table with sorting, filtering, and pagination
|
|
61
|
+
- 🎯 **Drag & Drop**: DND Kit integration for interactive interfaces
|
|
59
62
|
|
|
60
63
|
## Usage
|
|
61
64
|
|
|
62
65
|
### Creating a New Application
|
|
63
66
|
|
|
64
67
|
```bash
|
|
65
|
-
# Create new app
|
|
68
|
+
# Create new app with default template
|
|
66
69
|
create-bluecopa-react-app my-dashboard
|
|
67
70
|
|
|
71
|
+
# Create new app with shadcn/ui template
|
|
72
|
+
create-bluecopa-react-app my-dashboard --template shadcn
|
|
73
|
+
|
|
68
74
|
# Or use npx
|
|
69
75
|
npx create-bluecopa-react-app my-dashboard
|
|
76
|
+
npx create-bluecopa-react-app my-dashboard --template shadcn
|
|
70
77
|
```
|
|
71
78
|
|
|
79
|
+
### Available Templates
|
|
80
|
+
|
|
81
|
+
- **Default Template**: Basic React Router setup with essential components
|
|
82
|
+
- **shadcn Template**: Full-featured template with shadcn/ui components, data tables, charts, and dark mode support
|
|
83
|
+
|
|
72
84
|
### CLI Options
|
|
73
85
|
|
|
74
86
|
| Option | Description |
|
|
75
87
|
|--------|-------------|
|
|
76
88
|
| `--skip-install` | Skip dependency installation |
|
|
77
|
-
| `--template` | Specify template
|
|
89
|
+
| `--template` | Specify template (default, shadcn) |
|
|
78
90
|
| `--help` | Show help information |
|
|
79
91
|
|
|
80
92
|
## API Documentation
|
|
@@ -106,6 +118,12 @@ create-bluecopa-react-app my-dashboard
|
|
|
106
118
|
create-bluecopa-react-app my-dashboard --skip-install
|
|
107
119
|
```
|
|
108
120
|
|
|
121
|
+
### Using npx (Recommended)
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx create-bluecopa-react-app my-dashboard
|
|
125
|
+
```
|
|
126
|
+
|
|
109
127
|
## Development
|
|
110
128
|
|
|
111
129
|
### Available Scripts
|
|
@@ -122,11 +140,30 @@ create-bluecopa-react-app my-dashboard --skip-install
|
|
|
122
140
|
### Technologies Used
|
|
123
141
|
|
|
124
142
|
- **React 18** - Modern React with concurrent features
|
|
125
|
-
- **React Router
|
|
126
|
-
- **Vite** - Fast build tool and development server
|
|
143
|
+
- **React Router v7** - Modern routing with SSR support
|
|
144
|
+
- **Vite 6** - Fast build tool and development server
|
|
127
145
|
- **TypeScript** - Full type safety and developer experience
|
|
128
146
|
- **[`@bluecopa/react`](packages/react:1)** - BlueCopa-specific React components and hooks
|
|
147
|
+
- **shadcn/ui** - Accessible component library built on Radix UI
|
|
129
148
|
- **Radix UI** - Unstyled, accessible UI primitives
|
|
149
|
+
- **TanStack Table** - Headless table library for React
|
|
130
150
|
- **Recharts** - Composable charting library for React
|
|
131
|
-
- **
|
|
151
|
+
- **DND Kit** - Drag and drop toolkit for React
|
|
152
|
+
- **Tailwind CSS v4** - Utility-first CSS framework with BlueCopa design system
|
|
153
|
+
- **next-themes** - Theme switching with dark mode support
|
|
132
154
|
- **Lucide React** - Beautiful, customizable icons
|
|
155
|
+
|
|
156
|
+
## Monorepo Integration
|
|
157
|
+
|
|
158
|
+
This CLI tool is part of the BlueCopa UI monorepo and integrates with the following packages:
|
|
159
|
+
|
|
160
|
+
- **[`@bluecopa/react`](packages/react:1)** - BlueCopa-specific React components and hooks
|
|
161
|
+
|
|
162
|
+
### Standalone Development
|
|
163
|
+
|
|
164
|
+
The generated applications can also be developed independently:
|
|
165
|
+
|
|
166
|
+
1. **Self-contained**: All dependencies are properly installed
|
|
167
|
+
2. **Environment setup**: Includes proper environment variable configuration
|
|
168
|
+
3. **Build optimization**: Uses Vite for fast development and production builds
|
|
169
|
+
4. **Deployment ready**: Includes Docker configuration and deployment guides
|
|
@@ -1,24 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import ora from 'ora';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import validatePackageName from 'validate-npm-package-name';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
// ES module equivalent of __dirname
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
11
16
|
|
|
12
17
|
const program = new Command();
|
|
13
18
|
|
|
14
19
|
program
|
|
15
|
-
.version('1.0.
|
|
20
|
+
.version('1.0.5')
|
|
16
21
|
.name('create-bluecopa-react-app')
|
|
17
22
|
.description('Create a new Bluecopa React application')
|
|
18
23
|
.argument('[project-name]', 'Name of the project')
|
|
24
|
+
.option('-t, --template <template>', 'Template to use (latest)', 'latest')
|
|
19
25
|
.option('--typescript', 'Use TypeScript template', true)
|
|
20
26
|
.option('--no-typescript', 'Use JavaScript template')
|
|
21
27
|
.option('--skip-install', 'Skip package installation')
|
|
28
|
+
.option('--package-manager <manager>', 'Package manager to use (npm, yarn, pnpm)', 'auto')
|
|
29
|
+
.option('--git', 'Initialize git repository', true)
|
|
30
|
+
.option('--no-git', 'Skip git initialization')
|
|
31
|
+
.option('--yes', 'Skip all prompts and use defaults')
|
|
32
|
+
.addHelpText('after', `
|
|
33
|
+
Examples:
|
|
34
|
+
$ create-bluecopa-react-app my-dashboard
|
|
35
|
+
$ create-bluecopa-react-app my-app --template latest
|
|
36
|
+
$ create-bluecopa-react-app my-app --package-manager pnpm
|
|
37
|
+
$ create-bluecopa-react-app my-app --skip-install
|
|
38
|
+
$ create-bluecopa-react-app my-app --no-git
|
|
39
|
+
$ create-bluecopa-react-app my-app --yes
|
|
40
|
+
|
|
41
|
+
Templates:
|
|
42
|
+
latest Basic React Router setup with essential components
|
|
43
|
+
|
|
44
|
+
For more information, visit:
|
|
45
|
+
https://github.com/bluecopa/blui/tree/main/packages/boilerplate/react
|
|
46
|
+
`)
|
|
22
47
|
.action(async (projectName, options) => {
|
|
23
48
|
try {
|
|
24
49
|
await createApp(projectName, options);
|
|
@@ -30,14 +55,28 @@ program
|
|
|
30
55
|
|
|
31
56
|
async function createApp(projectName, options) {
|
|
32
57
|
let appName = projectName;
|
|
58
|
+
let selectedTemplate = options.template;
|
|
59
|
+
let initGit = options.git;
|
|
60
|
+
let packageManager = options.packageManager;
|
|
61
|
+
|
|
62
|
+
// If not using --yes flag, prompt for all options
|
|
63
|
+
if (!options.yes) {
|
|
64
|
+
const prompts = [];
|
|
33
65
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
66
|
+
// 1. Project Name prompt
|
|
67
|
+
if (!appName) {
|
|
68
|
+
// Generate a random default project name
|
|
69
|
+
const adjectives = ['awesome', 'stellar', 'brilliant', 'dynamic', 'epic', 'fantastic', 'incredible', 'magnificent', 'outstanding', 'spectacular'];
|
|
70
|
+
const nouns = ['app', 'dashboard', 'project', 'platform', 'tool', 'system', 'solution', 'interface', 'portal', 'workspace'];
|
|
71
|
+
const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
72
|
+
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
73
|
+
const defaultProjectName = `my-${randomAdjective}-${randomNoun}`;
|
|
74
|
+
|
|
75
|
+
prompts.push({
|
|
38
76
|
type: 'input',
|
|
39
77
|
name: 'projectName',
|
|
40
78
|
message: 'What is your project name?',
|
|
79
|
+
default: defaultProjectName,
|
|
41
80
|
validate: (input) => {
|
|
42
81
|
if (!input) return 'Project name is required';
|
|
43
82
|
const validation = validatePackageName(input);
|
|
@@ -46,15 +85,79 @@ async function createApp(projectName, options) {
|
|
|
46
85
|
}
|
|
47
86
|
return true;
|
|
48
87
|
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 2. Template selection prompt
|
|
92
|
+
prompts.push({
|
|
93
|
+
type: 'list',
|
|
94
|
+
name: 'template',
|
|
95
|
+
message: 'Select a template:',
|
|
96
|
+
choices: [
|
|
97
|
+
{ name: 'Latest - Basic React Router setup with essential components', value: 'latest' }
|
|
98
|
+
],
|
|
99
|
+
default: selectedTemplate
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// 3. Git initialization prompt
|
|
103
|
+
prompts.push({
|
|
104
|
+
type: 'confirm',
|
|
105
|
+
name: 'initGit',
|
|
106
|
+
message: 'Initialize git repository?',
|
|
107
|
+
default: initGit !== false
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// 4. Package manager prompt
|
|
111
|
+
prompts.push({
|
|
112
|
+
type: 'list',
|
|
113
|
+
name: 'packageManager',
|
|
114
|
+
message: 'Select package manager:',
|
|
115
|
+
choices: [
|
|
116
|
+
{ name: 'pnpm (recommended)', value: 'pnpm' },
|
|
117
|
+
{ name: 'npm', value: 'npm' },
|
|
118
|
+
{ name: 'Auto-detect', value: 'auto' }
|
|
119
|
+
],
|
|
120
|
+
default: packageManager === 'auto' ? 'auto' : packageManager
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// 5. Install dependencies prompt
|
|
124
|
+
prompts.push({
|
|
125
|
+
type: 'confirm',
|
|
126
|
+
name: 'runInstall',
|
|
127
|
+
message: 'Do you want to install dependencies now?',
|
|
128
|
+
default: !options.skipInstall
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Execute all prompts
|
|
132
|
+
const answers = await inquirer.prompt(prompts);
|
|
133
|
+
|
|
134
|
+
// Update values from prompts
|
|
135
|
+
if (answers.projectName) appName = answers.projectName;
|
|
136
|
+
selectedTemplate = answers.template;
|
|
137
|
+
initGit = answers.initGit;
|
|
138
|
+
packageManager = answers.packageManager;
|
|
139
|
+
options.skipInstall = !answers.runInstall;
|
|
140
|
+
} else {
|
|
141
|
+
// When using --yes flag, validate required options
|
|
142
|
+
if (!appName) {
|
|
143
|
+
console.error(chalk.red('Project name is required when using --yes flag'));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Validate template
|
|
149
|
+
const validTemplates = ['latest'];
|
|
150
|
+
if (!validTemplates.includes(selectedTemplate)) {
|
|
151
|
+
console.error(chalk.red(`Invalid template: ${selectedTemplate}`));
|
|
152
|
+
console.error(chalk.yellow(`Available templates: ${validTemplates.join(', ')}`));
|
|
153
|
+
process.exit(1);
|
|
52
154
|
}
|
|
53
155
|
|
|
54
156
|
// Validate project name
|
|
55
157
|
const validation = validatePackageName(appName);
|
|
56
158
|
if (!validation.validForNewPackages) {
|
|
57
159
|
console.error(chalk.red('Invalid project name:'), validation.errors?.[0] || validation.warnings?.[0]);
|
|
160
|
+
console.error(chalk.yellow('Project names must be lowercase, contain no spaces, and follow npm naming conventions.'));
|
|
58
161
|
process.exit(1);
|
|
59
162
|
}
|
|
60
163
|
|
|
@@ -62,37 +165,53 @@ async function createApp(projectName, options) {
|
|
|
62
165
|
|
|
63
166
|
// Check if directory already exists
|
|
64
167
|
if (fs.existsSync(targetDir)) {
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
168
|
+
if (options.yes) {
|
|
169
|
+
console.log(chalk.yellow(`Directory ${appName} already exists. Overwriting...`));
|
|
170
|
+
await fs.remove(targetDir);
|
|
171
|
+
} else {
|
|
172
|
+
const answers = await inquirer.prompt([
|
|
173
|
+
{
|
|
174
|
+
type: 'confirm',
|
|
175
|
+
name: 'overwrite',
|
|
176
|
+
message: `Directory ${appName} already exists. Overwrite?`,
|
|
177
|
+
default: false
|
|
178
|
+
}
|
|
179
|
+
]);
|
|
180
|
+
|
|
181
|
+
if (!answers.overwrite) {
|
|
182
|
+
console.log(chalk.yellow('Operation cancelled.'));
|
|
183
|
+
return;
|
|
71
184
|
}
|
|
72
|
-
]);
|
|
73
185
|
|
|
74
|
-
|
|
75
|
-
console.log(chalk.yellow('Operation cancelled.'));
|
|
76
|
-
return;
|
|
186
|
+
await fs.remove(targetDir);
|
|
77
187
|
}
|
|
78
|
-
|
|
79
|
-
await fs.remove(targetDir);
|
|
80
188
|
}
|
|
81
189
|
|
|
82
190
|
console.log(chalk.blue(`Creating a new Bluecopa React app in ${chalk.green(targetDir)}`));
|
|
191
|
+
console.log(chalk.gray(`Template: ${selectedTemplate}`));
|
|
83
192
|
console.log();
|
|
84
193
|
|
|
85
194
|
// Create app
|
|
86
195
|
const spinner = ora('Creating project structure...').start();
|
|
87
196
|
|
|
88
197
|
try {
|
|
89
|
-
await createProjectStructure(targetDir, appName, options);
|
|
198
|
+
await createProjectStructure(targetDir, appName, { ...options, template: selectedTemplate });
|
|
90
199
|
spinner.succeed('Project structure created');
|
|
91
200
|
|
|
92
201
|
if (!options.skipInstall) {
|
|
93
|
-
|
|
94
|
-
|
|
202
|
+
const detectedPackageManager = await detectPackageManager(packageManager);
|
|
203
|
+
spinner.start(`Installing dependencies with ${detectedPackageManager}...`);
|
|
204
|
+
await installDependencies(targetDir, detectedPackageManager);
|
|
95
205
|
spinner.succeed('Dependencies installed');
|
|
206
|
+
} else {
|
|
207
|
+
console.log(chalk.yellow('Skipping dependency installation. You can run it manually later.'));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Post-installation setup
|
|
211
|
+
if (initGit !== false) {
|
|
212
|
+
spinner.start('Initializing git repository...');
|
|
213
|
+
await initializeGit(targetDir, appName);
|
|
214
|
+
spinner.succeed('Git repository initialized');
|
|
96
215
|
}
|
|
97
216
|
|
|
98
217
|
spinner.succeed(chalk.green('Success! Created ' + appName + ' at ' + targetDir));
|
|
@@ -100,30 +219,69 @@ async function createApp(projectName, options) {
|
|
|
100
219
|
console.log();
|
|
101
220
|
console.log('Inside that directory, you can run several commands:');
|
|
102
221
|
console.log();
|
|
103
|
-
|
|
222
|
+
|
|
223
|
+
// Show install command if dependencies weren't installed
|
|
224
|
+
if (options.skipInstall) {
|
|
225
|
+
const detectedPackageManager = await detectPackageManager(packageManager);
|
|
226
|
+
const installCommand = detectedPackageManager === 'yarn' ? 'yarn install' :
|
|
227
|
+
detectedPackageManager === 'pnpm' ? 'pnpm install' :
|
|
228
|
+
'npm install';
|
|
229
|
+
console.log(chalk.cyan(` ${installCommand}`));
|
|
230
|
+
console.log(' Install dependencies first.');
|
|
231
|
+
console.log();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const runCommand = packageManager === 'pnpm' ? 'pnpm run' :
|
|
235
|
+
packageManager === 'yarn' ? 'yarn' :
|
|
236
|
+
'npm run';
|
|
237
|
+
|
|
238
|
+
console.log(chalk.cyan(` ${runCommand} dev`));
|
|
104
239
|
console.log(' Starts the development server.');
|
|
105
240
|
console.log();
|
|
106
|
-
console.log(chalk.cyan(
|
|
241
|
+
console.log(chalk.cyan(` ${runCommand} build`));
|
|
107
242
|
console.log(' Bundles the app into static files for production.');
|
|
108
243
|
console.log();
|
|
109
|
-
console.log(chalk.cyan(
|
|
244
|
+
console.log(chalk.cyan(` ${runCommand} start`));
|
|
245
|
+
console.log(' Start the production build locally.');
|
|
246
|
+
console.log();
|
|
247
|
+
console.log(chalk.cyan(` ${runCommand} preview`));
|
|
110
248
|
console.log(' Preview the production build locally.');
|
|
111
249
|
console.log();
|
|
112
250
|
console.log('We suggest that you begin by typing:');
|
|
113
251
|
console.log();
|
|
114
252
|
console.log(chalk.cyan(' cd'), appName);
|
|
115
|
-
|
|
253
|
+
if (options.skipInstall) {
|
|
254
|
+
const detectedPackageManager = await detectPackageManager(packageManager);
|
|
255
|
+
const installCommand = detectedPackageManager === 'yarn' ? 'yarn install' :
|
|
256
|
+
detectedPackageManager === 'pnpm' ? 'pnpm install' :
|
|
257
|
+
'npm install';
|
|
258
|
+
console.log(chalk.cyan(` ${installCommand}`));
|
|
259
|
+
}
|
|
260
|
+
console.log(chalk.cyan(` ${runCommand} dev`));
|
|
261
|
+
console.log();
|
|
262
|
+
console.log('Happy coding! 🚀');
|
|
116
263
|
console.log();
|
|
117
|
-
console.log('
|
|
264
|
+
console.log(chalk.gray('Need help? Check out the documentation:'));
|
|
265
|
+
console.log(chalk.gray(' https://github.com/bluecopa/blui/tree/main/packages/boilerplate/react'));
|
|
118
266
|
|
|
119
267
|
} catch (error) {
|
|
120
268
|
spinner.fail('Failed to create project');
|
|
269
|
+
// Cleanup on failure
|
|
270
|
+
if (fs.existsSync(targetDir)) {
|
|
271
|
+
console.log(chalk.yellow('Cleaning up...'));
|
|
272
|
+
await fs.remove(targetDir);
|
|
273
|
+
}
|
|
121
274
|
throw error;
|
|
122
275
|
}
|
|
123
276
|
}
|
|
124
277
|
|
|
125
278
|
async function createProjectStructure(targetDir, appName, options) {
|
|
126
|
-
const templateDir = path.join(__dirname, '../templates
|
|
279
|
+
const templateDir = path.join(__dirname, '../templates', options.template);
|
|
280
|
+
|
|
281
|
+
// Validate template directory exists
|
|
282
|
+
if (!await fs.pathExists(templateDir)) {
|
|
283
|
+
throw new Error(`Template '${options.template}' not found at ${templateDir}`);
|
|
284
|
+
}
|
|
127
285
|
|
|
128
286
|
// Create base directory
|
|
129
287
|
await fs.ensureDir(targetDir);
|
|
@@ -133,9 +291,11 @@ async function createProjectStructure(targetDir, appName, options) {
|
|
|
133
291
|
|
|
134
292
|
// Update package.json with project name
|
|
135
293
|
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
294
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
295
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
296
|
+
packageJson.name = appName;
|
|
297
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
298
|
+
}
|
|
139
299
|
|
|
140
300
|
// Update index.html title if it exists
|
|
141
301
|
const indexHtmlPath = path.join(targetDir, 'public/index.html');
|
|
@@ -144,27 +304,73 @@ async function createProjectStructure(targetDir, appName, options) {
|
|
|
144
304
|
indexHtml = indexHtml.replace(/{{APP_NAME}}/g, appName);
|
|
145
305
|
await fs.writeFile(indexHtmlPath, indexHtml);
|
|
146
306
|
}
|
|
307
|
+
|
|
308
|
+
// Update root index.html if it exists
|
|
309
|
+
const rootIndexHtmlPath = path.join(targetDir, 'index.html');
|
|
310
|
+
if (await fs.pathExists(rootIndexHtmlPath)) {
|
|
311
|
+
let indexHtml = await fs.readFile(rootIndexHtmlPath, 'utf8');
|
|
312
|
+
indexHtml = indexHtml.replace(/{{APP_NAME}}/g, appName);
|
|
313
|
+
await fs.writeFile(rootIndexHtmlPath, indexHtml);
|
|
314
|
+
}
|
|
147
315
|
}
|
|
148
316
|
|
|
149
|
-
async function
|
|
317
|
+
async function detectPackageManager(preferredManager) {
|
|
318
|
+
if (preferredManager !== 'auto') {
|
|
319
|
+
// Validate the preferred manager is available
|
|
320
|
+
try {
|
|
321
|
+
execSync(`${preferredManager} --version`, { stdio: 'ignore' });
|
|
322
|
+
return preferredManager;
|
|
323
|
+
} catch {
|
|
324
|
+
console.log(chalk.yellow(`Warning: ${preferredManager} not found, falling back to auto-detection`));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Auto-detect with preference order: pnpm > yarn > npm
|
|
329
|
+
const managers = ['pnpm', 'yarn', 'npm'];
|
|
330
|
+
|
|
331
|
+
for (const manager of managers) {
|
|
332
|
+
try {
|
|
333
|
+
execSync(`${manager} --version`, { stdio: 'ignore' });
|
|
334
|
+
return manager;
|
|
335
|
+
} catch {
|
|
336
|
+
// Manager not available, try next
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Fallback to npm (should always be available)
|
|
341
|
+
return 'npm';
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async function installDependencies(targetDir, packageManager) {
|
|
150
345
|
try {
|
|
151
346
|
// Change to target directory and install
|
|
152
347
|
process.chdir(targetDir);
|
|
153
348
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
execSync('yarn --version', { stdio: 'ignore' });
|
|
158
|
-
packageManager = 'yarn';
|
|
159
|
-
} catch {
|
|
160
|
-
// yarn not available, use npm
|
|
161
|
-
}
|
|
349
|
+
const installCommand = packageManager === 'yarn' ? 'yarn install' :
|
|
350
|
+
packageManager === 'pnpm' ? 'pnpm install' :
|
|
351
|
+
'npm install';
|
|
162
352
|
|
|
163
|
-
const installCommand = packageManager === 'yarn' ? 'yarn install' : 'npm install';
|
|
164
353
|
execSync(installCommand, { stdio: 'inherit' });
|
|
165
354
|
|
|
166
355
|
} catch (error) {
|
|
167
|
-
throw new Error(
|
|
356
|
+
throw new Error(`Failed to install dependencies with ${packageManager}: ${error.message}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async function initializeGit(targetDir, appName) {
|
|
361
|
+
try {
|
|
362
|
+
process.chdir(targetDir);
|
|
363
|
+
|
|
364
|
+
// Initialize git repository
|
|
365
|
+
execSync('git init', { stdio: 'ignore' });
|
|
366
|
+
|
|
367
|
+
// Create initial commit
|
|
368
|
+
execSync('git add .', { stdio: 'ignore' });
|
|
369
|
+
execSync(`git commit -m "Initial commit: ${appName}"`, { stdio: 'ignore' });
|
|
370
|
+
|
|
371
|
+
} catch (error) {
|
|
372
|
+
// Git initialization is optional, don't fail the whole process
|
|
373
|
+
console.log(chalk.yellow('Warning: Failed to initialize git repository'));
|
|
168
374
|
}
|
|
169
375
|
}
|
|
170
376
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-bluecopa-react-app",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "CLI tool to create bluecopa React applications",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "./bin/create-bluecopa-react-app.js",
|
|
6
7
|
"bin": {
|
|
7
8
|
"create-bluecopa-react-app": "./bin/create-bluecopa-react-app.js"
|
|
@@ -19,11 +20,11 @@
|
|
|
19
20
|
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
|
-
"commander": "^
|
|
23
|
+
"commander": "^12.0.0",
|
|
23
24
|
"fs-extra": "^11.2.0",
|
|
24
|
-
"chalk": "^
|
|
25
|
-
"inquirer": "^
|
|
26
|
-
"ora": "^
|
|
25
|
+
"chalk": "^5.3.0",
|
|
26
|
+
"inquirer": "^9.2.12",
|
|
27
|
+
"ora": "^8.0.1",
|
|
27
28
|
"validate-npm-package-name": "^5.0.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {},
|