devstarter-tool 0.2.0 → 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.
package/README.md CHANGED
@@ -30,6 +30,8 @@ devstarter init [project-name]
30
30
  | `-t, --type <type>` | Project type: `frontend` or `backend` |
31
31
  | `--template <name>` | Template to use |
32
32
  | `--dry-run` | Preview changes without creating files |
33
+ | `--no-git` | Skip git repository initialization |
34
+ | `--no-vitest` | Skip Vitest testing framework setup |
33
35
 
34
36
  ### Examples
35
37
 
@@ -45,6 +47,12 @@ devstarter init my-app --type frontend -y
45
47
 
46
48
  # Preview what files would be created
47
49
  devstarter init my-app --type frontend --dry-run
50
+
51
+ # Create project without git initialization
52
+ devstarter init my-app --no-git
53
+
54
+ # Create project without Vitest setup
55
+ devstarter init my-app --no-vitest
48
56
  ```
49
57
 
50
58
  ## Project Structures
@@ -54,7 +62,10 @@ devstarter init my-app --type frontend --dry-run
54
62
  ```
55
63
  my-app/
56
64
  ├── src/
57
- └── main.ts (or main.tsx for React)
65
+ ├── main.ts (or main.tsx for React)
66
+ │ └── __tests__/
67
+ │ └── example.test.ts
68
+ ├── vitest.config.ts
58
69
  ├── package.json
59
70
  ├── README.md
60
71
  └── .git/ (if git is initialized)
@@ -96,10 +107,28 @@ my-app/
96
107
  - Automatic package manager detection (npm, pnpm, yarn)
97
108
  - Interactive template selection
98
109
  - Optional Git repository initialization
110
+ - Optional Vitest testing framework setup
99
111
  - Dry-run mode to preview changes
100
112
  - Automatic project name normalization (kebab-case)
101
113
  - Colored output for better readability
102
114
 
115
+ ## Testing Setup (Vitest)
116
+
117
+ By default, projects are created with Vitest configured. This includes:
118
+
119
+ - `vitest` as a dev dependency
120
+ - `vitest.config.ts` configuration file
121
+ - Example test file in `src/__tests__/example.test.ts`
122
+ - `test` script in package.json
123
+
124
+ For React projects, it also adds `jsdom` for DOM testing.
125
+
126
+ To skip Vitest setup, use the `--no-vitest` flag:
127
+
128
+ ```bash
129
+ devstarter init my-app --no-vitest
130
+ ```
131
+
103
132
  ## Development
104
133
 
105
134
  ### Requirements
package/dist/cli.js CHANGED
@@ -14,6 +14,5 @@ program
14
14
  .option('--template <name>', 'Template variant (e.g. basic, react)')
15
15
  .option('--dry-run', 'Show what would be generated without creating files')
16
16
  .option('--no-git', 'Skip git repository initialization')
17
- .option('--no-vitest', 'Skip Vitest testing framework')
18
17
  .action(initCommand);
19
18
  program.parse(process.argv);
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_INIT_OPTIONS } from '../../types/project.js';
2
- import { askProjectName, askProjectStructure, askInitQuestions, askTemplate, askInitGit, askIncludeVitest, } from '../../prompts/initPrompts.js';
2
+ import { askProjectName, askProjectStructure, askInitQuestions, askTemplate, askInitGit, } from '../../prompts/initPrompts.js';
3
3
  import { normalizeProjectName } from '../../utils/normalize.js';
4
4
  import { detectPackageManager } from '../../utils/detectPackageManager.js';
5
5
  import { listTemplates } from '../../utils/listTemplate.js';
@@ -40,26 +40,21 @@ async function collectStructure(useDefaults) {
40
40
  async function collectBasicContext(projectName, options, useDefaults) {
41
41
  const typeFromFlag = resolveProjectType(options.type);
42
42
  const gitFlagProvided = options.git !== undefined;
43
- const vitestFlagProvided = options.vitest !== undefined;
44
- // Obtener tipo de proyecto, initGit e includeVitest
43
+ // Obtener tipo de proyecto e initGit
45
44
  let projectType;
46
45
  let initGit;
47
- let includeVitest;
48
46
  if (useDefaults) {
49
47
  projectType = typeFromFlag ?? DEFAULT_INIT_OPTIONS.projectType;
50
48
  initGit = gitFlagProvided ? options.git : DEFAULT_INIT_OPTIONS.initGit;
51
- includeVitest = vitestFlagProvided ? options.vitest : DEFAULT_INIT_OPTIONS.includeVitest;
52
49
  }
53
50
  else {
54
51
  const answers = await askInitQuestions({
55
52
  skipProjectName: true,
56
53
  skipProjectType: Boolean(typeFromFlag),
57
54
  skipInitGit: gitFlagProvided,
58
- skipVitest: vitestFlagProvided,
59
55
  });
60
56
  projectType = typeFromFlag ?? answers.projectType;
61
57
  initGit = gitFlagProvided ? options.git : answers.initGit;
62
- includeVitest = vitestFlagProvided ? options.vitest : answers.includeVitest;
63
58
  }
64
59
  // Obtener template
65
60
  const templates = listTemplates(projectType);
@@ -71,7 +66,6 @@ async function collectBasicContext(projectName, options, useDefaults) {
71
66
  projectType,
72
67
  template,
73
68
  initGit,
74
- includeVitest,
75
69
  packageManager: detectPackageManager(),
76
70
  isDryRun: Boolean(options.dryRun),
77
71
  };
@@ -81,16 +75,13 @@ async function collectMonorepoContext(projectName, useDefaults, options) {
81
75
  const frontendTemplates = listTemplates('frontend');
82
76
  const backendTemplates = listTemplates('backend');
83
77
  const gitFlagProvided = options.git !== undefined;
84
- const vitestFlagProvided = options.vitest !== undefined;
85
78
  let webTemplate;
86
79
  let apiTemplate;
87
80
  let initGit;
88
- let includeVitest;
89
81
  if (useDefaults) {
90
82
  webTemplate = frontendTemplates[0] ?? 'basic';
91
83
  apiTemplate = backendTemplates[0] ?? 'basic';
92
84
  initGit = gitFlagProvided ? options.git : DEFAULT_INIT_OPTIONS.initGit;
93
- includeVitest = vitestFlagProvided ? options.vitest : DEFAULT_INIT_OPTIONS.includeVitest;
94
85
  }
95
86
  else {
96
87
  const webAnswer = await askTemplate({
@@ -110,13 +101,6 @@ async function collectMonorepoContext(projectName, useDefaults, options) {
110
101
  const gitAnswer = await askInitGit();
111
102
  initGit = gitAnswer.initGit;
112
103
  }
113
- if (vitestFlagProvided) {
114
- includeVitest = options.vitest;
115
- }
116
- else {
117
- const vitestAnswer = await askIncludeVitest();
118
- includeVitest = vitestAnswer.includeVitest;
119
- }
120
104
  }
121
105
  return {
122
106
  structure: 'monorepo',
@@ -124,7 +108,6 @@ async function collectMonorepoContext(projectName, useDefaults, options) {
124
108
  webTemplate,
125
109
  apiTemplate,
126
110
  initGit,
127
- includeVitest,
128
111
  packageManager: 'pnpm', // Monorepo usa pnpm por defecto
129
112
  isDryRun: Boolean(options.dryRun),
130
113
  };
@@ -3,8 +3,7 @@ import path from 'node:path';
3
3
  import { getTemplatePath } from '../utils/getTemplatePath.js';
4
4
  import { copyTemplate } from '../utils/copyTemplate.js';
5
5
  import { initGitRepo } from '../utils/git.js';
6
- import { injectVitest } from '../utils/injectVitest.js';
7
- export async function createMonorepo({ projectName, webTemplate, apiTemplate, initGit, includeVitest, }) {
6
+ export async function createMonorepo({ projectName, webTemplate, apiTemplate, initGit, }) {
8
7
  // 1. Resolver ruta absoluta del proyecto
9
8
  const projectRoot = path.resolve(process.cwd(), projectName);
10
9
  // 2. Evitar sobrescribir carpetas existentes
@@ -18,26 +17,18 @@ export async function createMonorepo({ projectName, webTemplate, apiTemplate, in
18
17
  // 4. Crear archivos de configuración del monorepo
19
18
  await createMonorepoConfig(projectRoot, projectName);
20
19
  // 5. Copiar template de frontend a apps/web
21
- const webPath = path.join(projectRoot, 'apps', 'web');
22
20
  const webTemplatePath = getTemplatePath('frontend', webTemplate);
23
21
  if (await fs.pathExists(webTemplatePath)) {
24
- await copyTemplate(webTemplatePath, webPath, {
22
+ await copyTemplate(webTemplatePath, path.join(projectRoot, 'apps', 'web'), {
25
23
  projectName: `${projectName}-web`,
26
24
  });
27
- if (includeVitest) {
28
- await injectVitest(webPath, 'frontend', webTemplate);
29
- }
30
25
  }
31
26
  // 6. Copiar template de backend a apps/api
32
- const apiPath = path.join(projectRoot, 'apps', 'api');
33
27
  const apiTemplatePath = getTemplatePath('backend', apiTemplate);
34
28
  if (await fs.pathExists(apiTemplatePath)) {
35
- await copyTemplate(apiTemplatePath, apiPath, {
29
+ await copyTemplate(apiTemplatePath, path.join(projectRoot, 'apps', 'api'), {
36
30
  projectName: `${projectName}-api`,
37
31
  });
38
- if (includeVitest) {
39
- await injectVitest(apiPath, 'backend', apiTemplate);
40
- }
41
32
  }
42
33
  // 7. Crear package shared básico
43
34
  await createSharedPackage(projectRoot, projectName);
@@ -3,8 +3,7 @@ import path from 'node:path';
3
3
  import { getTemplatePath } from '../utils/getTemplatePath.js';
4
4
  import { copyTemplate } from '../utils/copyTemplate.js';
5
5
  import { initGitRepo } from '../utils/git.js';
6
- import { injectVitest } from '../utils/injectVitest.js';
7
- export async function createProject({ projectName, projectType, template, initGit, includeVitest, }) {
6
+ export async function createProject({ projectName, projectType, template, initGit, }) {
8
7
  // 1. Resolver ruta absoluta del proyecto
9
8
  const projectRoot = path.resolve(process.cwd(), projectName);
10
9
  // 2. Evitar sobrescribir carpetas existentes
@@ -22,11 +21,7 @@ export async function createProject({ projectName, projectType, template, initGi
22
21
  await copyTemplate(templatePath, projectRoot, {
23
22
  projectName,
24
23
  });
25
- // 6. Inyectar Vitest (si aplica)
26
- if (includeVitest) {
27
- await injectVitest(projectRoot, projectType, template);
28
- }
29
- // 7. Inicializar Git (si aplica)
24
+ // 6. Inicializar Git (si aplica)
30
25
  if (initGit) {
31
26
  initGitRepo(projectRoot);
32
27
  }
@@ -56,14 +56,6 @@ export async function askInitQuestions(options = {}) {
56
56
  initial: true,
57
57
  });
58
58
  }
59
- if (!options.skipVitest) {
60
- questions.push({
61
- type: 'confirm',
62
- name: 'includeVitest',
63
- message: 'Include Vitest for testing?',
64
- initial: true,
65
- });
66
- }
67
59
  return prompts(questions, { onCancel });
68
60
  }
69
61
  export async function askTemplate(options) {
@@ -88,11 +80,3 @@ export async function askInitGit() {
88
80
  initial: true,
89
81
  }, { onCancel });
90
82
  }
91
- export async function askIncludeVitest() {
92
- return prompts({
93
- type: 'confirm',
94
- name: 'includeVitest',
95
- message: 'Include Vitest for testing?',
96
- initial: true,
97
- }, { onCancel });
98
- }
@@ -2,5 +2,4 @@ export const DEFAULT_INIT_OPTIONS = {
2
2
  projectStructure: 'basic',
3
3
  projectType: 'frontend',
4
4
  initGit: true,
5
- includeVitest: true,
6
5
  };
@@ -12,8 +12,7 @@ export function printDryRun(context) {
12
12
  else {
13
13
  printBasicPlan(context);
14
14
  }
15
- console.log(`${styles.info('- Git:')} ${context.initGit ? 'would initialize' : 'skipped'}`);
16
- console.log(`${styles.info('- Vitest:')} ${context.includeVitest ? 'would add' : 'skipped'}\n`);
15
+ console.log(`${styles.info('- Git:')} ${context.initGit ? 'would initialize' : 'skipped'}\n`);
17
16
  console.log(styles.title('Next steps'));
18
17
  console.log(` ${styles.highlight(`cd ${context.projectName}`)}`);
19
18
  console.log(` ${styles.highlight(`${context.packageManager} install`)}`);
@@ -11,8 +11,7 @@ export function printSummary(context) {
11
11
  console.log(`${styles.info('- Template:')} ${context.projectType}/${context.template}`);
12
12
  }
13
13
  console.log(`${styles.info('- Directory:')} ./${context.projectName}`);
14
- console.log(`${styles.info('- Git:')} ${context.initGit ? styles.success('initialized') : styles.muted('not initialized')}`);
15
- console.log(`${styles.info('- Vitest:')} ${context.includeVitest ? styles.success('added') : styles.muted('not included')}\n`);
14
+ console.log(`${styles.info('- Git:')} ${context.initGit ? styles.success('initialized') : styles.muted('not initialized')}\n`);
16
15
  console.log(styles.title('Next steps'));
17
16
  console.log(` ${styles.highlight(`cd ${context.projectName}`)}`);
18
17
  console.log(` ${styles.highlight(`${context.packageManager} install`)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devstarter-tool",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "CLI to generate projects with best practices (basic or monorepo)",
6
6
  "author": "abraham-diaz",
@@ -1,68 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'node:path';
3
- const VITEST_CONFIG = `import { defineConfig } from 'vitest/config';
4
-
5
- export default defineConfig({
6
- test: {
7
- globals: true,
8
- },
9
- });
10
- `;
11
- const VITEST_CONFIG_REACT = `import { defineConfig } from 'vitest/config';
12
- import react from '@vitejs/plugin-react';
13
-
14
- export default defineConfig({
15
- plugins: [react()],
16
- test: {
17
- globals: true,
18
- environment: 'jsdom',
19
- },
20
- });
21
- `;
22
- function getExampleTest(projectType, template) {
23
- if (projectType === 'frontend' && template === 'react') {
24
- return `import { describe, it, expect } from 'vitest';
25
-
26
- describe('Example test', () => {
27
- it('should pass', () => {
28
- expect(1 + 1).toBe(2);
29
- });
30
- });
31
- `;
32
- }
33
- return `import { describe, it, expect } from 'vitest';
34
-
35
- describe('Example test', () => {
36
- it('should pass', () => {
37
- expect(1 + 1).toBe(2);
38
- });
39
- });
40
- `;
41
- }
42
- export async function injectVitest(projectRoot, projectType, template) {
43
- // 1. Modificar package.json
44
- const packageJsonPath = path.join(projectRoot, 'package.json');
45
- const packageJson = await fs.readJson(packageJsonPath);
46
- // Agregar script test
47
- packageJson.scripts = packageJson.scripts || {};
48
- packageJson.scripts.test = 'vitest';
49
- // Agregar devDependencies
50
- packageJson.devDependencies = packageJson.devDependencies || {};
51
- packageJson.devDependencies.vitest = '^3.1.4';
52
- // Para React, agregar jsdom
53
- if (projectType === 'frontend' && template === 'react') {
54
- packageJson.devDependencies.jsdom = '^26.1.0';
55
- }
56
- await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
57
- // 2. Crear vitest.config.ts
58
- const vitestConfigPath = path.join(projectRoot, 'vitest.config.ts');
59
- const configContent = projectType === 'frontend' && template === 'react'
60
- ? VITEST_CONFIG_REACT
61
- : VITEST_CONFIG;
62
- await fs.writeFile(vitestConfigPath, configContent);
63
- // 3. Crear test de ejemplo
64
- const testDir = path.join(projectRoot, 'src', '__tests__');
65
- await fs.ensureDir(testDir);
66
- const testFilePath = path.join(testDir, 'example.test.ts');
67
- await fs.writeFile(testFilePath, getExampleTest(projectType, template));
68
- }