create-bluecopa-react-app 1.0.16 → 1.0.18

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.
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
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';
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
12
 
13
13
  // ES module equivalent of __dirname
14
14
  const __filename = fileURLToPath(import.meta.url);
@@ -17,19 +17,25 @@ const __dirname = path.dirname(__filename);
17
17
  const program = new Command();
18
18
 
19
19
  program
20
- .version('1.0.5')
21
- .name('create-bluecopa-react-app')
22
- .description('Create a new Bluecopa React application')
23
- .argument('[project-name]', 'Name of the project')
24
- .option('-t, --template <template>', 'Template to use (latest)', 'latest')
25
- .option('--typescript', 'Use TypeScript template', true)
26
- .option('--no-typescript', 'Use JavaScript template')
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', `
20
+ .version("1.0.5")
21
+ .name("create-bluecopa-react-app")
22
+ .description("Create a new Bluecopa React application")
23
+ .argument("[project-name]", "Name of the project")
24
+ .option("-t, --template <template>", "Template to use (latest)", "latest")
25
+ .option("--typescript", "Use TypeScript template", true)
26
+ .option("--no-typescript", "Use JavaScript template")
27
+ .option("--skip-install", "Skip package installation")
28
+ .option(
29
+ "--package-manager <manager>",
30
+ "Package manager to use (npm, yarn, pnpm)",
31
+ "auto"
32
+ )
33
+ .option("--git", "Initialize git repository", true)
34
+ .option("--no-git", "Skip git initialization")
35
+ .option("--yes", "Skip all prompts and use defaults")
36
+ .addHelpText(
37
+ "after",
38
+ `
33
39
  Examples:
34
40
  $ create-bluecopa-react-app my-dashboard
35
41
  $ create-bluecopa-react-app my-app --template latest
@@ -43,12 +49,13 @@ Templates:
43
49
 
44
50
  For more information, visit:
45
51
  https://github.com/bluecopa/blui/tree/dev/packages/boilerplate/react
46
- `)
52
+ `
53
+ )
47
54
  .action(async (projectName, options) => {
48
55
  try {
49
56
  await createApp(projectName, options);
50
57
  } catch (error) {
51
- console.error(chalk.red('Error creating app:'), error.message);
58
+ console.error(chalk.red("Error creating app:"), error.message);
52
59
  process.exit(1);
53
60
  }
54
61
  });
@@ -66,71 +73,101 @@ async function createApp(projectName, options) {
66
73
  // 1. Project Name prompt
67
74
  if (!appName) {
68
75
  // 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)];
76
+ const adjectives = [
77
+ "awesome",
78
+ "stellar",
79
+ "brilliant",
80
+ "dynamic",
81
+ "epic",
82
+ "fantastic",
83
+ "incredible",
84
+ "magnificent",
85
+ "outstanding",
86
+ "spectacular",
87
+ ];
88
+ const nouns = [
89
+ "app",
90
+ "dashboard",
91
+ "project",
92
+ "platform",
93
+ "tool",
94
+ "system",
95
+ "solution",
96
+ "interface",
97
+ "portal",
98
+ "workspace",
99
+ ];
100
+ const randomAdjective =
101
+ adjectives[Math.floor(Math.random() * adjectives.length)];
72
102
  const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
73
103
  const defaultProjectName = `my-${randomAdjective}-${randomNoun}`;
74
104
 
75
105
  prompts.push({
76
- type: 'input',
77
- name: 'projectName',
78
- message: 'What is your project name?',
106
+ type: "input",
107
+ name: "projectName",
108
+ message: "What is your project name?",
79
109
  default: defaultProjectName,
80
110
  validate: (input) => {
81
- if (!input) return 'Project name is required';
111
+ if (!input) return "Project name is required";
82
112
  const validation = validatePackageName(input);
83
113
  if (!validation.validForNewPackages) {
84
- return validation.errors?.[0] || validation.warnings?.[0] || 'Invalid package name';
114
+ return (
115
+ validation.errors?.[0] ||
116
+ validation.warnings?.[0] ||
117
+ "Invalid package name"
118
+ );
85
119
  }
86
120
  return true;
87
- }
121
+ },
88
122
  });
89
123
  }
90
124
 
91
125
  // 2. Template selection prompt
92
126
  prompts.push({
93
- type: 'list',
94
- name: 'template',
95
- message: 'Select a template:',
127
+ type: "list",
128
+ name: "template",
129
+ message: "Select a template:",
96
130
  choices: [
97
- { name: 'Latest - Basic React Router setup with essential components', value: 'latest' }
131
+ {
132
+ name: "Latest - Basic React Router setup with essential components",
133
+ value: "latest",
134
+ },
98
135
  ],
99
- default: selectedTemplate
136
+ default: selectedTemplate,
100
137
  });
101
138
 
102
139
  // 3. Git initialization prompt
103
140
  prompts.push({
104
- type: 'confirm',
105
- name: 'initGit',
106
- message: 'Initialize git repository?',
107
- default: initGit !== false
141
+ type: "confirm",
142
+ name: "initGit",
143
+ message: "Initialize git repository?",
144
+ default: initGit !== false,
108
145
  });
109
146
 
110
147
  // 4. Package manager prompt
111
148
  prompts.push({
112
- type: 'list',
113
- name: 'packageManager',
114
- message: 'Select package manager:',
149
+ type: "list",
150
+ name: "packageManager",
151
+ message: "Select package manager:",
115
152
  choices: [
116
- { name: 'pnpm (recommended)', value: 'pnpm' },
117
- { name: 'npm', value: 'npm' },
118
- { name: 'Auto-detect', value: 'auto' }
153
+ { name: "pnpm (recommended)", value: "pnpm" },
154
+ { name: "npm", value: "npm" },
155
+ { name: "Auto-detect", value: "auto" },
119
156
  ],
120
- default: packageManager === 'auto' ? 'auto' : packageManager
157
+ default: packageManager === "auto" ? "auto" : packageManager,
121
158
  });
122
159
 
123
160
  // 5. Install dependencies prompt
124
161
  prompts.push({
125
- type: 'confirm',
126
- name: 'runInstall',
127
- message: 'Do you want to install dependencies now?',
128
- default: !options.skipInstall
162
+ type: "confirm",
163
+ name: "runInstall",
164
+ message: "Do you want to install dependencies now?",
165
+ default: !options.skipInstall,
129
166
  });
130
167
 
131
168
  // Execute all prompts
132
169
  const answers = await inquirer.prompt(prompts);
133
-
170
+
134
171
  // Update values from prompts
135
172
  if (answers.projectName) appName = answers.projectName;
136
173
  selectedTemplate = answers.template;
@@ -140,24 +177,35 @@ async function createApp(projectName, options) {
140
177
  } else {
141
178
  // When using --yes flag, validate required options
142
179
  if (!appName) {
143
- console.error(chalk.red('Project name is required when using --yes flag'));
180
+ console.error(
181
+ chalk.red("Project name is required when using --yes flag")
182
+ );
144
183
  process.exit(1);
145
184
  }
146
185
  }
147
186
 
148
187
  // Validate template
149
- const validTemplates = ['latest'];
188
+ const validTemplates = ["latest"];
150
189
  if (!validTemplates.includes(selectedTemplate)) {
151
190
  console.error(chalk.red(`Invalid template: ${selectedTemplate}`));
152
- console.error(chalk.yellow(`Available templates: ${validTemplates.join(', ')}`));
191
+ console.error(
192
+ chalk.yellow(`Available templates: ${validTemplates.join(", ")}`)
193
+ );
153
194
  process.exit(1);
154
195
  }
155
196
 
156
197
  // Validate project name
157
198
  const validation = validatePackageName(appName);
158
199
  if (!validation.validForNewPackages) {
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.'));
200
+ console.error(
201
+ chalk.red("Invalid project name:"),
202
+ validation.errors?.[0] || validation.warnings?.[0]
203
+ );
204
+ console.error(
205
+ chalk.yellow(
206
+ "Project names must be lowercase, contain no spaces, and follow npm naming conventions."
207
+ )
208
+ );
161
209
  process.exit(1);
162
210
  }
163
211
 
@@ -166,20 +214,22 @@ async function createApp(projectName, options) {
166
214
  // Check if directory already exists
167
215
  if (fs.existsSync(targetDir)) {
168
216
  if (options.yes) {
169
- console.log(chalk.yellow(`Directory ${appName} already exists. Overwriting...`));
217
+ console.log(
218
+ chalk.yellow(`Directory ${appName} already exists. Overwriting...`)
219
+ );
170
220
  await fs.remove(targetDir);
171
221
  } else {
172
222
  const answers = await inquirer.prompt([
173
223
  {
174
- type: 'confirm',
175
- name: 'overwrite',
224
+ type: "confirm",
225
+ name: "overwrite",
176
226
  message: `Directory ${appName} already exists. Overwrite?`,
177
- default: false
178
- }
227
+ default: false,
228
+ },
179
229
  ]);
180
230
 
181
231
  if (!answers.overwrite) {
182
- console.log(chalk.yellow('Operation cancelled.'));
232
+ console.log(chalk.yellow("Operation cancelled."));
183
233
  return;
184
234
  }
185
235
 
@@ -187,88 +237,113 @@ async function createApp(projectName, options) {
187
237
  }
188
238
  }
189
239
 
190
- console.log(chalk.blue(`Creating a new Bluecopa React app in ${chalk.green(targetDir)}`));
240
+ console.log(
241
+ chalk.blue(`Creating a new Bluecopa React app in ${chalk.green(targetDir)}`)
242
+ );
191
243
  console.log(chalk.gray(`Template: ${selectedTemplate}`));
192
244
  console.log();
193
245
 
194
246
  // Create app
195
- const spinner = ora('Creating project structure...').start();
196
-
247
+ const spinner = ora("Creating project structure...").start();
248
+
197
249
  try {
198
- await createProjectStructure(targetDir, appName, { ...options, template: selectedTemplate });
199
- spinner.succeed('Project structure created');
250
+ await createProjectStructure(targetDir, appName, {
251
+ ...options,
252
+ template: selectedTemplate,
253
+ });
254
+ spinner.succeed("Project structure created");
200
255
 
201
256
  if (!options.skipInstall) {
202
257
  const detectedPackageManager = await detectPackageManager(packageManager);
203
- spinner.start(`Installing dependencies with ${detectedPackageManager}...`);
258
+ spinner.start(
259
+ `Installing dependencies with ${detectedPackageManager}...`
260
+ );
204
261
  await installDependencies(targetDir, detectedPackageManager);
205
- spinner.succeed('Dependencies installed');
262
+ spinner.succeed("Dependencies installed");
206
263
  } else {
207
- console.log(chalk.yellow('Skipping dependency installation. You can run it manually later.'));
264
+ console.log(
265
+ chalk.yellow(
266
+ "Skipping dependency installation. You can run it manually later."
267
+ )
268
+ );
208
269
  }
209
270
 
210
271
  // Post-installation setup
211
272
  if (initGit !== false) {
212
- spinner.start('Initializing git repository...');
273
+ spinner.start("Initializing git repository...");
213
274
  await initializeGit(targetDir, appName);
214
- spinner.succeed('Git repository initialized');
275
+ spinner.succeed("Git repository initialized");
215
276
  }
216
277
 
217
- spinner.succeed(chalk.green('Success! Created ' + appName + ' at ' + targetDir));
218
-
278
+ spinner.succeed(
279
+ chalk.green("Success! Created " + appName + " at " + targetDir)
280
+ );
281
+
219
282
  console.log();
220
- console.log('Inside that directory, you can run several commands:');
283
+ console.log("Inside that directory, you can run several commands:");
221
284
  console.log();
222
-
285
+
223
286
  // Show install command if dependencies weren't installed
224
287
  if (options.skipInstall) {
225
288
  const detectedPackageManager = await detectPackageManager(packageManager);
226
- const installCommand = detectedPackageManager === 'yarn' ? 'yarn install' :
227
- detectedPackageManager === 'pnpm' ? 'pnpm install' :
228
- 'npm install';
289
+ const installCommand =
290
+ detectedPackageManager === "yarn"
291
+ ? "yarn install"
292
+ : detectedPackageManager === "pnpm"
293
+ ? "pnpm install"
294
+ : "npm install";
229
295
  console.log(chalk.cyan(` ${installCommand}`));
230
- console.log(' Install dependencies first.');
296
+ console.log(" Install dependencies first.");
231
297
  console.log();
232
298
  }
233
-
234
- const runCommand = packageManager === 'pnpm' ? 'pnpm run' :
235
- packageManager === 'yarn' ? 'yarn' :
236
- 'npm run';
237
-
299
+
300
+ const runCommand =
301
+ packageManager === "pnpm"
302
+ ? "pnpm run"
303
+ : packageManager === "yarn"
304
+ ? "yarn"
305
+ : "npm run";
306
+
238
307
  console.log(chalk.cyan(` ${runCommand} dev`));
239
- console.log(' Starts the development server.');
308
+ console.log(" Starts the development server.");
240
309
  console.log();
241
310
  console.log(chalk.cyan(` ${runCommand} build`));
242
- console.log(' Bundles the app into static files for production.');
311
+ console.log(" Bundles the app into static files for production.");
243
312
  console.log();
244
313
  console.log(chalk.cyan(` ${runCommand} start`));
245
- console.log(' Start the production build locally.');
314
+ console.log(" Start the production build locally.");
246
315
  console.log();
247
316
  console.log(chalk.cyan(` ${runCommand} preview`));
248
- console.log(' Preview the production build locally.');
317
+ console.log(" Preview the production build locally.");
249
318
  console.log();
250
- console.log('We suggest that you begin by typing:');
319
+ console.log("We suggest that you begin by typing:");
251
320
  console.log();
252
- console.log(chalk.cyan(' cd'), appName);
321
+ console.log(chalk.cyan(" cd"), appName);
253
322
  if (options.skipInstall) {
254
323
  const detectedPackageManager = await detectPackageManager(packageManager);
255
- const installCommand = detectedPackageManager === 'yarn' ? 'yarn install' :
256
- detectedPackageManager === 'pnpm' ? 'pnpm install' :
257
- 'npm install';
324
+ const installCommand =
325
+ detectedPackageManager === "yarn"
326
+ ? "yarn install"
327
+ : detectedPackageManager === "pnpm"
328
+ ? "pnpm install"
329
+ : "npm install";
258
330
  console.log(chalk.cyan(` ${installCommand}`));
259
331
  }
260
332
  console.log(chalk.cyan(` ${runCommand} dev`));
261
333
  console.log();
262
- console.log('Happy coding! 🚀');
334
+ console.log("Happy coding! 🚀");
263
335
  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'));
266
-
336
+ console.log(chalk.gray("Need help? Check out the documentation:"));
337
+ console.log(
338
+ chalk.gray(
339
+ " https://github.com/bluecopa/blui/tree/main/packages/boilerplate/react"
340
+ )
341
+ );
267
342
  } catch (error) {
268
- spinner.fail('Failed to create project');
343
+ spinner.fail("Failed to create project");
269
344
  // Cleanup on failure
270
345
  if (fs.existsSync(targetDir)) {
271
- console.log(chalk.yellow('Cleaning up...'));
346
+ console.log(chalk.yellow("Cleaning up..."));
272
347
  await fs.remove(targetDir);
273
348
  }
274
349
  throw error;
@@ -276,101 +351,131 @@ async function createApp(projectName, options) {
276
351
  }
277
352
 
278
353
  async function createProjectStructure(targetDir, appName, options) {
279
- const templateDir = path.join(__dirname, '../templates', options.template);
280
-
354
+ const templateDir = path.join(__dirname, "../templates", options.template);
355
+
281
356
  // Validate template directory exists
282
- if (!await fs.pathExists(templateDir)) {
283
- throw new Error(`Template '${options.template}' not found at ${templateDir}`);
357
+ if (!(await fs.pathExists(templateDir))) {
358
+ throw new Error(
359
+ `Template '${options.template}' not found at ${templateDir}`
360
+ );
284
361
  }
285
-
362
+
286
363
  // Create base directory
287
364
  await fs.ensureDir(targetDir);
288
-
365
+
289
366
  // Copy template files
290
367
  await fs.copy(templateDir, targetDir);
291
-
368
+
292
369
  // Update package.json with project name
293
- const packageJsonPath = path.join(targetDir, 'package.json');
370
+ const packageJsonPath = path.join(targetDir, "package.json");
294
371
  if (await fs.pathExists(packageJsonPath)) {
295
372
  const packageJson = await fs.readJson(packageJsonPath);
296
373
  packageJson.name = appName;
297
374
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
298
375
  }
299
-
376
+
300
377
  // Update index.html title if it exists
301
- const indexHtmlPath = path.join(targetDir, 'public/index.html');
378
+ const indexHtmlPath = path.join(targetDir, "public/index.html");
302
379
  if (await fs.pathExists(indexHtmlPath)) {
303
- let indexHtml = await fs.readFile(indexHtmlPath, 'utf8');
380
+ let indexHtml = await fs.readFile(indexHtmlPath, "utf8");
304
381
  indexHtml = indexHtml.replace(/{{APP_NAME}}/g, appName);
305
382
  await fs.writeFile(indexHtmlPath, indexHtml);
306
383
  }
307
384
 
308
385
  // Update root index.html if it exists
309
- const rootIndexHtmlPath = path.join(targetDir, 'index.html');
386
+ const rootIndexHtmlPath = path.join(targetDir, "index.html");
310
387
  if (await fs.pathExists(rootIndexHtmlPath)) {
311
- let indexHtml = await fs.readFile(rootIndexHtmlPath, 'utf8');
388
+ let indexHtml = await fs.readFile(rootIndexHtmlPath, "utf8");
312
389
  indexHtml = indexHtml.replace(/{{APP_NAME}}/g, appName);
313
390
  await fs.writeFile(rootIndexHtmlPath, indexHtml);
314
391
  }
315
392
  }
316
393
 
317
394
  async function detectPackageManager(preferredManager) {
318
- if (preferredManager !== 'auto') {
395
+ if (preferredManager !== "auto") {
319
396
  // Validate the preferred manager is available
320
397
  try {
321
- execSync(`${preferredManager} --version`, { stdio: 'ignore' });
398
+ execSync(`${preferredManager} --version`, { stdio: "ignore" });
322
399
  return preferredManager;
323
400
  } catch {
324
- console.log(chalk.yellow(`Warning: ${preferredManager} not found, falling back to auto-detection`));
401
+ console.log(
402
+ chalk.yellow(
403
+ `Warning: ${preferredManager} not found, falling back to auto-detection`
404
+ )
405
+ );
325
406
  }
326
407
  }
327
408
 
328
409
  // Auto-detect with preference order: pnpm > yarn > npm
329
- const managers = ['pnpm', 'yarn', 'npm'];
330
-
410
+ const managers = ["pnpm", "yarn", "npm"];
411
+
331
412
  for (const manager of managers) {
332
413
  try {
333
- execSync(`${manager} --version`, { stdio: 'ignore' });
414
+ execSync(`${manager} --version`, { stdio: "ignore" });
334
415
  return manager;
335
416
  } catch {
336
417
  // Manager not available, try next
337
418
  }
338
419
  }
339
-
420
+
340
421
  // Fallback to npm (should always be available)
341
- return 'npm';
422
+ return "npm";
342
423
  }
343
424
 
344
425
  async function installDependencies(targetDir, packageManager) {
345
426
  try {
346
427
  // Change to target directory and install
347
428
  process.chdir(targetDir);
348
-
349
- const installCommand = packageManager === 'yarn' ? 'yarn install' :
350
- packageManager === 'pnpm' ? 'pnpm install' :
351
- 'npm install';
352
-
353
- execSync(installCommand, { stdio: 'inherit' });
354
-
429
+
430
+ const installCommand =
431
+ packageManager === "yarn"
432
+ ? "yarn install"
433
+ : packageManager === "pnpm"
434
+ ? "pnpm install"
435
+ : "npm install";
436
+
437
+ execSync(installCommand, { stdio: "inherit" });
355
438
  } catch (error) {
356
- throw new Error(`Failed to install dependencies with ${packageManager}: ${error.message}`);
439
+ throw new Error(
440
+ `Failed to install dependencies with ${packageManager}: ${error.message}`
441
+ );
357
442
  }
358
443
  }
359
444
 
360
445
  async function initializeGit(targetDir, appName) {
361
446
  try {
362
447
  process.chdir(targetDir);
363
-
448
+
364
449
  // Initialize git repository
365
- execSync('git init', { stdio: 'ignore' });
366
-
450
+ execSync("git init", { stdio: "ignore" });
451
+
367
452
  // Create initial commit
368
- execSync('git add .', { stdio: 'ignore' });
369
- execSync(`git commit -m "Initial commit: ${appName}"`, { stdio: 'ignore' });
370
-
453
+ execSync("git add .", { stdio: "ignore" });
454
+ execSync(`git commit -m "Initial commit: ${appName}"`, { stdio: "ignore" });
455
+ // Create a .gitignore file if it does not exist
456
+ const gitignoreContent = `
457
+ node_modules
458
+ dist
459
+ build
460
+ .env
461
+ .next
462
+ .cache
463
+ coverage
464
+ .DS_Store
465
+ npm-debug.log*
466
+ yarn-debug.log*
467
+ yarn-error.log*
468
+ pnpm-debug.log*
469
+ `;
470
+
471
+ const gitignorePath = path.join(targetDir, ".gitignore");
472
+ if (!fs.existsSync(gitignorePath)) {
473
+ fs.writeFileSync(gitignorePath, gitignoreContent.trimStart());
474
+ execSync("git add .gitignore", { stdio: "ignore" });
475
+ }
371
476
  } catch (error) {
372
477
  // Git initialization is optional, don't fail the whole process
373
- console.log(chalk.yellow('Warning: Failed to initialize git repository'));
478
+ console.log(chalk.yellow("Warning: Failed to initialize git repository"));
374
479
  }
375
480
  }
376
481
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bluecopa-react-app",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "CLI tool to create bluecopa React applications",
5
5
  "type": "module",
6
6
  "main": "./bin/create-bluecopa-react-app.js",