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 +127 -0
- package/dist/generator.js +59 -0
- package/dist/index.js +32 -0
- package/dist/prompts.js +156 -0
- package/dist/templates/backend/cdk.js +211 -0
- package/dist/templates/backend/express.js +427 -0
- package/dist/templates/backend/fastapi.js +259 -0
- package/dist/templates/backend/nestjs.js +478 -0
- package/dist/templates/backend/sam.js +258 -0
- package/dist/templates/frontend/html.js +141 -0
- package/dist/templates/frontend/index.js +9 -0
- package/dist/templates/frontend/nextjs.js +240 -0
- package/dist/templates/frontend/react.js +249 -0
- package/dist/templates/index.js +341 -0
- package/dist/types.js +2 -0
- package/package.json +52 -0
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();
|
package/dist/prompts.js
ADDED
|
@@ -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
|
+
}
|