projects-init-cli 1.1.0

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 ADDED
@@ -0,0 +1,127 @@
1
+ # Projects Init CLI
2
+
3
+ A powerful CLI tool to initialize monorepo projects with customizable frontend, backend, and storage options.
4
+
5
+ ## Features
6
+
7
+ ### Frontend Options
8
+
9
+ - **Next.js** - React framework with Tailwind CSS
10
+ - **React** - React with Vite and Tailwind CSS
11
+ - **HTML** - Plain HTML with Tailwind CSS and Vite
12
+
13
+ ### Backend Options
14
+
15
+ - **Express.js** - Node.js web framework
16
+ - **NestJS** - Progressive Node.js framework
17
+ - **FastAPI** - Modern Python web framework
18
+ - **AWS CDK** - Infrastructure as code
19
+ - **AWS SAM** - Serverless Application Model
20
+
21
+ ### Storage Options
22
+
23
+ - **CDK Database** - Create database using AWS CDK
24
+ - **Local SQLite** - SQLite database for local development
25
+ - **External Database URL** - Connect to existing database
26
+
27
+ ### Database Types
28
+
29
+ - **SQL**: Raw SQL or Prisma ORM
30
+ - **NoSQL**: DynamoDB or MongoDB
31
+
32
+ ### API Types (for Express/NestJS)
33
+
34
+ - **REST API**
35
+ - **GraphQL**
36
+
37
+ ## Installation
38
+
39
+ ### Global Installation
40
+
41
+ ```bash
42
+ npm install -g projects-init-cli
43
+ ```
44
+
45
+ ### Local Installation
46
+
47
+ ```bash
48
+ npm install
49
+ npm run build
50
+ npm link
51
+ ```
52
+
53
+ ### Use with npx (No Installation Required)
54
+
55
+ ```bash
56
+ npx projects-init-cli
57
+ ```
58
+
59
+ ## Usage
60
+
61
+ Run the CLI tool:
62
+
63
+ ```bash
64
+ projects-init
65
+ ```
66
+
67
+ The tool will guide you through interactive prompts to select:
68
+
69
+ 1. Project name
70
+ 2. Frontend framework
71
+ 3. Backend framework
72
+ 4. Storage option
73
+ 5. Database type (if applicable)
74
+ 6. Database options (SQL/NoSQL, ORM, etc.)
75
+ 7. API type (for Express/NestJS)
76
+
77
+ ## Example
78
+
79
+ ```bash
80
+ $ projects-init
81
+
82
+ 🚀 Project Initializer CLI
83
+
84
+ ? What is your project name? my-awesome-app
85
+ ? Select frontend framework: Next.js (with Tailwind)
86
+ ? Select backend framework: Express.js
87
+ ? Select storage option: Local SQLite (Node.js)
88
+ ? Select database type: SQL
89
+ ? Select SQL option: Prisma ORM
90
+ ? Select API type: REST API
91
+
92
+ ✅ Project created successfully!
93
+
94
+ Next steps:
95
+ cd my-awesome-app
96
+ npm install
97
+ npm run dev
98
+ ```
99
+
100
+ ## Project Structure
101
+
102
+ Generated projects follow a monorepo structure:
103
+
104
+ ```
105
+ my-project/
106
+ ├── frontend/ # Frontend application
107
+ ├── backend/ # Backend application
108
+ ├── package.json # Root package.json with workspaces
109
+ └── README.md # Project documentation
110
+ ```
111
+
112
+ ## Development
113
+
114
+ ```bash
115
+ # Install dependencies
116
+ npm install
117
+
118
+ # Build the project
119
+ npm run build
120
+
121
+ # Run in development mode
122
+ npm run dev
123
+ ```
124
+
125
+ ## License
126
+
127
+ MIT
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateProject = generateProject;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const templates_1 = require("./templates");
11
+ async function generateProject(config) {
12
+ const projectPath = path_1.default.resolve(process.cwd(), config.projectName);
13
+ // Check if directory exists
14
+ if (await fs_extra_1.default.pathExists(projectPath)) {
15
+ throw new Error(`Directory ${config.projectName} already exists`);
16
+ }
17
+ console.log(chalk_1.default.blue(`\n📁 Creating project structure...`));
18
+ await fs_extra_1.default.ensureDir(projectPath);
19
+ // Generate monorepo structure
20
+ await (0, templates_1.generateMonorepoConfig)(projectPath, config);
21
+ // Generate frontend
22
+ console.log(chalk_1.default.blue(`\n🎨 Generating frontend (${config.frontend})...`));
23
+ await (0, templates_1.generateFrontend)(projectPath, config);
24
+ // Generate backend
25
+ console.log(chalk_1.default.blue(`\n⚙️ Generating backend (${config.backend})...`));
26
+ await (0, templates_1.generateBackend)(projectPath, config);
27
+ // Generate root files
28
+ console.log(chalk_1.default.blue(`\n📄 Generating root configuration files...`));
29
+ await (0, templates_1.generateRootFiles)(projectPath, config);
30
+ // Initialize git repository
31
+ console.log(chalk_1.default.blue(`\n🔧 Initializing git repository...`));
32
+ await initializeGit(projectPath);
33
+ console.log(chalk_1.default.green(`\n✨ Project structure created at: ${projectPath}`));
34
+ }
35
+ async function initializeGit(projectPath) {
36
+ const { execSync } = require('child_process');
37
+ try {
38
+ // Initialize git repository
39
+ execSync('git init', { cwd: projectPath, stdio: 'pipe' });
40
+ // Create initial commit
41
+ execSync('git add .', { cwd: projectPath, stdio: 'pipe' });
42
+ execSync('git commit -m "Initial commit"', {
43
+ cwd: projectPath,
44
+ stdio: 'pipe',
45
+ env: { ...process.env, GIT_AUTHOR_NAME: 'Projects Init CLI', GIT_AUTHOR_EMAIL: 'cli@projects-init.dev' }
46
+ });
47
+ console.log(chalk_1.default.green('✓ Git repository initialized with initial commit'));
48
+ }
49
+ catch (error) {
50
+ // Git might not be installed or there might be an issue, but don't fail the whole process
51
+ if (error.message && error.message.includes('not a git repository')) {
52
+ // Already initialized or other git issue
53
+ console.log(chalk_1.default.yellow('⚠️ Git initialization skipped'));
54
+ }
55
+ else {
56
+ console.log(chalk_1.default.yellow('⚠️ Git initialization skipped (git may not be installed)'));
57
+ }
58
+ }
59
+ }
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const prompts_1 = require("./prompts");
10
+ const generator_1 = require("./generator");
11
+ const program = new commander_1.Command();
12
+ program
13
+ .name('projects-init')
14
+ .description('CLI tool to initialize projects with customizable tech stacks')
15
+ .version('1.0.0')
16
+ .action(async () => {
17
+ try {
18
+ console.log(chalk_1.default.blue.bold('\n🚀 Project Initializer CLI\n'));
19
+ const answers = await (0, prompts_1.promptUser)();
20
+ await (0, generator_1.generateProject)(answers);
21
+ console.log(chalk_1.default.green.bold('\n✅ Project created successfully!'));
22
+ console.log(chalk_1.default.yellow('\nNext steps:'));
23
+ console.log(chalk_1.default.white(` cd ${answers.projectName}`));
24
+ console.log(chalk_1.default.white(' npm install'));
25
+ console.log(chalk_1.default.white(' npm run dev'));
26
+ }
27
+ catch (error) {
28
+ console.error(chalk_1.default.red.bold('\n❌ Error:'), error);
29
+ process.exit(1);
30
+ }
31
+ });
32
+ program.parse();
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.promptUser = promptUser;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ async function promptUser() {
9
+ const answers = await inquirer_1.default.prompt([
10
+ {
11
+ type: 'input',
12
+ name: 'projectName',
13
+ message: 'What is your project name?',
14
+ default: 'my-project',
15
+ validate: (input) => {
16
+ if (!input.trim()) {
17
+ return 'Project name cannot be empty';
18
+ }
19
+ if (!/^[a-z0-9-]+$/.test(input)) {
20
+ return 'Project name can only contain lowercase letters, numbers, and hyphens';
21
+ }
22
+ return true;
23
+ }
24
+ },
25
+ {
26
+ type: 'list',
27
+ name: 'frontend',
28
+ message: 'Select frontend framework:',
29
+ choices: [
30
+ { name: 'Next.js (with Tailwind)', value: 'nextjs' },
31
+ { name: 'React (with Tailwind)', value: 'react' },
32
+ { name: 'HTML (with Tailwind)', value: 'html' }
33
+ ]
34
+ },
35
+ {
36
+ type: 'list',
37
+ name: 'backend',
38
+ message: 'Select backend framework:',
39
+ choices: [
40
+ { name: 'Express.js', value: 'express' },
41
+ { name: 'NestJS', value: 'nest' },
42
+ { name: 'FastAPI (Python)', value: 'fastapi' },
43
+ { name: 'AWS CDK Deployment', value: 'cdk' },
44
+ { name: 'AWS SAM Deployment', value: 'sam' }
45
+ ]
46
+ },
47
+ {
48
+ type: 'list',
49
+ name: 'storage',
50
+ message: 'Select storage option:',
51
+ choices: [
52
+ { name: 'CDK to create database', value: 'cdk' },
53
+ { name: 'Local SQLite (Node.js)', value: 'local-sqlite' },
54
+ { name: 'Local JSON file', value: 'local-json' },
55
+ { name: 'External database URL', value: 'external-url' }
56
+ ]
57
+ }
58
+ ]);
59
+ const config = {
60
+ projectName: answers.projectName,
61
+ frontend: answers.frontend,
62
+ backend: answers.backend,
63
+ storage: answers.storage
64
+ };
65
+ // If storage is external-url, ask for database URL
66
+ if (config.storage === 'external-url') {
67
+ const urlAnswer = await inquirer_1.default.prompt([
68
+ {
69
+ type: 'input',
70
+ name: 'databaseUrl',
71
+ message: 'Enter database connection URL:',
72
+ validate: (input) => {
73
+ if (!input.trim()) {
74
+ return 'Database URL cannot be empty';
75
+ }
76
+ return true;
77
+ }
78
+ }
79
+ ]);
80
+ config.databaseUrl = urlAnswer.databaseUrl;
81
+ }
82
+ // Ask for database type if backend is not CDK/SAM or storage is not CDK/local-json
83
+ if (config.backend !== 'cdk' && config.backend !== 'sam' && config.storage !== 'cdk' && config.storage !== 'local-json') {
84
+ const dbTypeAnswer = await inquirer_1.default.prompt([
85
+ {
86
+ type: 'list',
87
+ name: 'databaseType',
88
+ message: 'Select database type:',
89
+ choices: [
90
+ { name: 'SQL', value: 'sql' },
91
+ { name: 'NoSQL', value: 'nosql' }
92
+ ]
93
+ }
94
+ ]);
95
+ config.databaseType = dbTypeAnswer.databaseType;
96
+ if (config.databaseType === 'sql') {
97
+ const sqlAnswer = await inquirer_1.default.prompt([
98
+ {
99
+ type: 'list',
100
+ name: 'sqlOption',
101
+ message: 'Select SQL option:',
102
+ choices: [
103
+ { name: 'Raw SQL', value: 'raw-sql' },
104
+ { name: 'Prisma ORM', value: 'prisma' }
105
+ ]
106
+ }
107
+ ]);
108
+ config.sqlOption = sqlAnswer.sqlOption;
109
+ }
110
+ else {
111
+ const nosqlAnswer = await inquirer_1.default.prompt([
112
+ {
113
+ type: 'list',
114
+ name: 'nosqlOption',
115
+ message: 'Select NoSQL option:',
116
+ choices: [
117
+ { name: 'DynamoDB', value: 'dynamodb' },
118
+ { name: 'MongoDB', value: 'mongodb' }
119
+ ]
120
+ }
121
+ ]);
122
+ config.nosqlOption = nosqlAnswer.nosqlOption;
123
+ }
124
+ }
125
+ else if (config.storage === 'cdk') {
126
+ // For CDK storage, still ask database type
127
+ const dbTypeAnswer = await inquirer_1.default.prompt([
128
+ {
129
+ type: 'list',
130
+ name: 'databaseType',
131
+ message: 'Select database type for CDK:',
132
+ choices: [
133
+ { name: 'SQL (RDS)', value: 'sql' },
134
+ { name: 'NoSQL (DynamoDB)', value: 'nosql' }
135
+ ]
136
+ }
137
+ ]);
138
+ config.databaseType = dbTypeAnswer.databaseType;
139
+ }
140
+ // Ask for API type if backend is Express or NestJS
141
+ if (config.backend === 'express' || config.backend === 'nest') {
142
+ const apiAnswer = await inquirer_1.default.prompt([
143
+ {
144
+ type: 'list',
145
+ name: 'apiType',
146
+ message: 'Select API type:',
147
+ choices: [
148
+ { name: 'REST API', value: 'rest' },
149
+ { name: 'GraphQL', value: 'graphql' }
150
+ ]
151
+ }
152
+ ]);
153
+ config.apiType = apiAnswer.apiType;
154
+ }
155
+ return config;
156
+ }
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateCDK = generateCDK;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function generateCDK(backendPath, config) {
10
+ const appName = config.projectName.replace(/[^a-z0-9-]/gi, '-').toLowerCase();
11
+ const stackName = config.projectName.replace(/[^a-zA-Z0-9]/g, '');
12
+ const packageJson = {
13
+ name: `${appName}-backend`,
14
+ version: '0.1.0',
15
+ bin: {
16
+ [appName]: 'bin/app.js'
17
+ },
18
+ scripts: {
19
+ build: 'tsc',
20
+ watch: 'tsc -w',
21
+ test: 'vitest',
22
+ 'test:ui': 'vitest --ui',
23
+ 'test:coverage': 'vitest --coverage',
24
+ cdk: 'cdk'
25
+ },
26
+ dependencies: {
27
+ 'aws-cdk-lib': '^2.169.0',
28
+ constructs: '^10.4.2',
29
+ 'source-map-support': '^0.5.21'
30
+ },
31
+ devDependencies: {
32
+ '@types/node': '^22.7.5',
33
+ 'aws-cdk': '^2.169.0',
34
+ 'ts-node': '^10.9.2',
35
+ 'typescript': '^5.6.3',
36
+ 'vitest': '^2.1.3',
37
+ '@vitest/ui': '^2.1.3',
38
+ '@vitest/coverage-v8': '^2.1.3'
39
+ }
40
+ };
41
+ await fs_extra_1.default.writeJSON(path_1.default.join(backendPath, 'package.json'), packageJson, { spaces: 2 });
42
+ // Create cdk.json
43
+ const cdkJson = {
44
+ app: 'npx ts-node --prefer-ts-exts bin/app.ts',
45
+ watch: {
46
+ include: ['**'],
47
+ exclude: [
48
+ 'README.md',
49
+ 'cdk*.json',
50
+ '**/*.d.ts',
51
+ '**/*.js',
52
+ 'tsconfig.json',
53
+ 'package*.json',
54
+ 'yarn.lock',
55
+ 'node_modules',
56
+ 'test'
57
+ ]
58
+ },
59
+ context: {
60
+ '@aws-cdk/aws-lambda:recognizeLayerVersion': true,
61
+ }
62
+ };
63
+ await fs_extra_1.default.writeJSON(path_1.default.join(backendPath, 'cdk.json'), cdkJson, { spaces: 2 });
64
+ // Create TypeScript config
65
+ const tsconfig = {
66
+ compilerOptions: {
67
+ target: 'ES2020',
68
+ module: 'commonjs',
69
+ lib: ['es2020'],
70
+ declaration: true,
71
+ strict: true,
72
+ noImplicitAny: true,
73
+ strictNullChecks: true,
74
+ noImplicitThis: true,
75
+ alwaysStrict: true,
76
+ noUnusedLocals: false,
77
+ noUnusedParameters: false,
78
+ noImplicitReturns: true,
79
+ noFallthroughCasesInSwitch: false,
80
+ inlineSourceMap: true,
81
+ inlineSources: true,
82
+ experimentalDecorators: true,
83
+ strictPropertyInitialization: false,
84
+ typeRoots: ['./node_modules/@types'],
85
+ esModuleInterop: true,
86
+ skipLibCheck: true,
87
+ forceConsistentCasingInFileNames: true
88
+ },
89
+ exclude: ['node_modules', 'cdk.out']
90
+ };
91
+ await fs_extra_1.default.writeJSON(path_1.default.join(backendPath, 'tsconfig.json'), tsconfig, { spaces: 2 });
92
+ // Create bin directory
93
+ const binPath = path_1.default.join(backendPath, 'bin');
94
+ await fs_extra_1.default.ensureDir(binPath);
95
+ const appTs = `#!/usr/bin/env node
96
+ import 'source-map-support/register';
97
+ import * as cdk from 'aws-cdk-lib';
98
+ import { ${stackName}Stack } from '../lib/${stackName}-stack';
99
+
100
+ const app = new cdk.App();
101
+ new ${stackName}Stack(app, '${stackName}Stack', {
102
+ env: {
103
+ account: process.env.CDK_DEFAULT_ACCOUNT,
104
+ region: process.env.CDK_DEFAULT_REGION,
105
+ },
106
+ });
107
+ `;
108
+ await fs_extra_1.default.writeFile(path_1.default.join(binPath, 'app.ts'), appTs);
109
+ // Create lib directory
110
+ const libPath = path_1.default.join(backendPath, 'lib');
111
+ await fs_extra_1.default.ensureDir(libPath);
112
+ let stackContent = `import * as cdk from 'aws-cdk-lib';
113
+ import { Construct } from 'constructs';
114
+ `;
115
+ if (config.databaseType === 'sql') {
116
+ stackContent += `import * as rds from 'aws-cdk-lib/aws-rds';
117
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
118
+ `;
119
+ }
120
+ else if (config.databaseType === 'nosql') {
121
+ stackContent += `import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
122
+ `;
123
+ }
124
+ stackContent += `
125
+ export class ${stackName}Stack extends cdk.Stack {
126
+ constructor(scope: Construct, id: string, props?: cdk.StackProps) {
127
+ super(scope, id, props);
128
+
129
+ `;
130
+ if (config.databaseType === 'sql') {
131
+ stackContent += ` // Create VPC for RDS
132
+ const vpc = new ec2.Vpc(this, 'VPC', {
133
+ maxAzs: 2,
134
+ });
135
+
136
+ // Create RDS Database
137
+ const database = new rds.DatabaseInstance(this, 'Database', {
138
+ engine: rds.DatabaseInstanceEngine.postgres({
139
+ version: rds.PostgresEngineVersion.VER_15_4,
140
+ }),
141
+ instanceType: ec2.InstanceType.of(
142
+ ec2.InstanceClass.T3,
143
+ ec2.InstanceSize.MICRO
144
+ ),
145
+ vpc,
146
+ databaseName: '${config.projectName}',
147
+ deletionProtection: false,
148
+ });
149
+
150
+ // Output database endpoint
151
+ new cdk.CfnOutput(this, 'DatabaseEndpoint', {
152
+ value: database.instanceEndpoint.hostname,
153
+ });
154
+ `;
155
+ }
156
+ else if (config.databaseType === 'nosql') {
157
+ stackContent += ` // Create DynamoDB Table
158
+ const table = new dynamodb.Table(this, 'Table', {
159
+ partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
160
+ billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
161
+ });
162
+
163
+ // Output table name
164
+ new cdk.CfnOutput(this, 'TableName', {
165
+ value: table.tableName,
166
+ });
167
+ `;
168
+ }
169
+ stackContent += ` }
170
+ }
171
+ `;
172
+ await fs_extra_1.default.writeFile(path_1.default.join(libPath, `${stackName}-stack.ts`), stackContent);
173
+ // Create README
174
+ const readme = `# CDK Backend
175
+
176
+ This is an AWS CDK project for deploying infrastructure.
177
+
178
+ ## Useful commands
179
+
180
+ - \`npm run build\` compile typescript to js
181
+ - \`npm run watch\` watch for changes and compile
182
+ - \`npm run test\` run vitest tests
183
+ - \`npm run test:ui\` run tests with UI
184
+ - \`npm run test:coverage\` run tests with coverage
185
+ - \`npm run cdk deploy\` deploy this stack to your default AWS account/region
186
+ - \`npm run cdk diff\` compare deployed stack with current state
187
+ - \`npm run cdk synth\` emits the synthesized CloudFormation template
188
+ `;
189
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'README.md'), readme);
190
+ // Create test directory with example test
191
+ const testPath = path_1.default.join(backendPath, 'test');
192
+ await fs_extra_1.default.ensureDir(testPath);
193
+ const testFile = `import { describe, it, expect } from 'vitest';
194
+ import * as cdk from 'aws-cdk-lib';
195
+ import { Template } from 'aws-cdk-lib/assertions';
196
+ import { ${stackName}Stack } from '../lib/${stackName}-stack';
197
+
198
+ describe('${stackName}Stack', () => {
199
+ it('has required resources', () => {
200
+ const app = new cdk.App();
201
+ const stack = new ${stackName}Stack(app, 'MyTestStack');
202
+ const template = Template.fromStack(stack);
203
+
204
+ // Add your test assertions here
205
+ // Example: template.resourceCountIs('AWS::DynamoDB::Table', 1);
206
+ expect(template).toBeDefined();
207
+ });
208
+ });
209
+ `;
210
+ await fs_extra_1.default.writeFile(path_1.default.join(testPath, `${stackName}-stack.test.ts`), testFile);
211
+ }