@apexkrrish/create-node-app 1.0.0 → 1.1.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/bin/cli.js +12 -204
- package/bin/commands/createService.js +147 -0
- package/bin/commands/init.js +33 -0
- package/bin/create-service-cli.js +12 -0
- package/bin/utils/database.js +81 -0
- package/bin/utils/helpers.js +96 -0
- package/package.json +16 -5
- package/readme.md +63 -31
package/bin/cli.js
CHANGED
|
@@ -1,189 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { program } from 'commander';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
8
|
-
import chalk from 'chalk';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
|
-
import ora from 'ora';
|
|
11
|
-
|
|
12
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
-
const __dirname = path.dirname(__filename);
|
|
14
|
-
|
|
15
|
-
// ==========================================
|
|
16
|
-
// Helper Functions
|
|
17
|
-
// ==========================================
|
|
18
|
-
|
|
19
|
-
async function promptUser(projectDirectory) {
|
|
20
|
-
let targetDir = projectDirectory;
|
|
21
|
-
|
|
22
|
-
if (!targetDir) {
|
|
23
|
-
const answers = await inquirer.prompt([
|
|
24
|
-
{
|
|
25
|
-
type: 'input',
|
|
26
|
-
name: 'projectDirectory',
|
|
27
|
-
message: 'What is your project named?',
|
|
28
|
-
default: 'my-app'
|
|
29
|
-
}
|
|
30
|
-
]);
|
|
31
|
-
targetDir = answers.projectDirectory;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const { packageManager, database } = await inquirer.prompt([
|
|
35
|
-
{
|
|
36
|
-
type: 'list',
|
|
37
|
-
name: 'packageManager',
|
|
38
|
-
message: 'Which package manager would you like to use?',
|
|
39
|
-
choices: ['npm', 'yarn', 'pnpm'],
|
|
40
|
-
default: 'npm'
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
type: 'list',
|
|
44
|
-
name: 'database',
|
|
45
|
-
message: 'Which database would you like to use?',
|
|
46
|
-
choices: ['MongoDB (Mongoose)', 'PostgreSQL (Sequelize)', 'None'],
|
|
47
|
-
default: 'MongoDB (Mongoose)'
|
|
48
|
-
}
|
|
49
|
-
]);
|
|
50
|
-
|
|
51
|
-
return { targetDir, packageManager, database };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function copyTemplateFiles(templateDir, targetPath) {
|
|
55
|
-
const spinner = ora('Copying template files...').start();
|
|
56
|
-
|
|
57
|
-
if (fs.existsSync(targetPath)) {
|
|
58
|
-
spinner.fail(`Directory ${path.basename(targetPath)} already exists.`);
|
|
59
|
-
process.exit(1);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
fs.mkdirSync(targetPath, { recursive: true });
|
|
63
|
-
|
|
64
|
-
const copyRecursiveSync = (src, dest) => {
|
|
65
|
-
const exists = fs.existsSync(src);
|
|
66
|
-
const stats = exists && fs.statSync(src);
|
|
67
|
-
if (exists && stats.isDirectory()) {
|
|
68
|
-
if (!fs.existsSync(dest)) fs.mkdirSync(dest);
|
|
69
|
-
fs.readdirSync(src).forEach((child) => {
|
|
70
|
-
if (child === 'node_modules') return;
|
|
71
|
-
copyRecursiveSync(path.join(src, child), path.join(dest, child));
|
|
72
|
-
});
|
|
73
|
-
} else {
|
|
74
|
-
fs.copyFileSync(src, dest);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
copyRecursiveSync(templateDir, targetPath);
|
|
80
|
-
spinner.succeed('Template copied successfully.');
|
|
81
|
-
} catch (err) {
|
|
82
|
-
spinner.fail('Failed to copy template.');
|
|
83
|
-
console.error(err);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function configureDatabase(targetPath, database) {
|
|
89
|
-
const packageJsonPath = path.join(targetPath, 'package.json');
|
|
90
|
-
if (!fs.existsSync(packageJsonPath)) return;
|
|
91
|
-
|
|
92
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
93
|
-
pkg.name = path.basename(targetPath);
|
|
94
|
-
|
|
95
|
-
const dbConfigPath = path.join(targetPath, 'config', 'db.config.js');
|
|
96
|
-
const targetConfigDir = path.dirname(dbConfigPath);
|
|
97
|
-
if (!fs.existsSync(targetConfigDir)) {
|
|
98
|
-
fs.mkdirSync(targetConfigDir, { recursive: true });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (database === 'MongoDB (Mongoose)') {
|
|
102
|
-
pkg.dependencies = pkg.dependencies || {};
|
|
103
|
-
pkg.dependencies.mongoose = '^8.0.0';
|
|
104
|
-
|
|
105
|
-
const mongooseContent = `import mongoose from "mongoose"
|
|
106
|
-
|
|
107
|
-
const options = {
|
|
108
|
-
serverSelectionTimeoutMS: 10000,
|
|
109
|
-
dbName: '',
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const connectDB = async () => {
|
|
113
|
-
try {
|
|
114
|
-
await mongoose.connect('', options)
|
|
115
|
-
console.log(\`✅ MongoDB connected!\`)
|
|
116
|
-
} catch (error) {
|
|
117
|
-
console.error(\`❌ MongoDB connection error: \${error.message}\`)
|
|
118
|
-
process.exit(1)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export default connectDB`;
|
|
123
|
-
fs.writeFileSync(dbConfigPath, mongooseContent);
|
|
124
|
-
|
|
125
|
-
} else if (database === 'PostgreSQL (Sequelize)') {
|
|
126
|
-
pkg.dependencies = pkg.dependencies || {};
|
|
127
|
-
pkg.dependencies.sequelize = '^6.35.0';
|
|
128
|
-
pkg.dependencies.pg = '^8.11.3';
|
|
129
|
-
pkg.dependencies.pg_hstore = '^2.3.4';
|
|
130
|
-
|
|
131
|
-
const sequelizeContent = `import { Sequelize } from "sequelize"
|
|
132
|
-
|
|
133
|
-
export const sequelize = new Sequelize(
|
|
134
|
-
'', // DB name
|
|
135
|
-
'', // User
|
|
136
|
-
'', // Password
|
|
137
|
-
{
|
|
138
|
-
host: '',
|
|
139
|
-
dialect: 'postgres',
|
|
140
|
-
logging: false,
|
|
141
|
-
port: ''
|
|
142
|
-
}
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
// Test connection
|
|
146
|
-
// (async () => {
|
|
147
|
-
// try {
|
|
148
|
-
// await sequelize.authenticate();
|
|
149
|
-
// console.log("PostgreSQL connected with Sequelize");
|
|
150
|
-
// } catch (err) {
|
|
151
|
-
// console.error("Error:", err.message);
|
|
152
|
-
// }
|
|
153
|
-
// })()
|
|
154
|
-
|
|
155
|
-
export default sequelize
|
|
156
|
-
`;
|
|
157
|
-
fs.writeFileSync(dbConfigPath, sequelizeContent);
|
|
158
|
-
|
|
159
|
-
} else {
|
|
160
|
-
fs.writeFileSync(dbConfigPath, '// No database configured\n');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Save updated package.json
|
|
164
|
-
fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function installDependencies(targetPath, packageManager) {
|
|
168
|
-
const installSpinner = ora(`Installing dependencies using ${packageManager}...`).start();
|
|
169
|
-
try {
|
|
170
|
-
execSync(`${packageManager} install`, { cwd: targetPath, stdio: 'pipe' });
|
|
171
|
-
installSpinner.succeed('Dependencies installed successfully.');
|
|
172
|
-
} catch (err) {
|
|
173
|
-
installSpinner.fail('Failed to install dependencies.');
|
|
174
|
-
console.error(err);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function showSuccessMessage(targetDir, targetPath, packageManager) {
|
|
179
|
-
console.log(`\n${chalk.green('Success!')} Created ${path.basename(targetPath)} at ${targetPath}`);
|
|
180
|
-
console.log('\nInside that directory, you can run several commands:\n');
|
|
181
|
-
console.log(` ${chalk.cyan(`${packageManager} run dev`)}`);
|
|
182
|
-
console.log(' Starts the development server with nodemon.\n');
|
|
183
|
-
console.log('We suggest that you begin by typing:\n');
|
|
184
|
-
console.log(` ${chalk.cyan('cd')} ${targetDir}`);
|
|
185
|
-
console.log(` ${chalk.cyan(`${packageManager} run dev`)}\n`);
|
|
186
|
-
}
|
|
4
|
+
import { initCommand } from './commands/init.js';
|
|
5
|
+
import { createServiceCommand } from './commands/createService.js';
|
|
187
6
|
|
|
188
7
|
// ==========================================
|
|
189
8
|
// CLI Application
|
|
@@ -191,28 +10,17 @@ function showSuccessMessage(targetDir, targetPath, packageManager) {
|
|
|
191
10
|
|
|
192
11
|
program
|
|
193
12
|
.name('create-node-app')
|
|
194
|
-
.description('Scaffold a new Node.js application')
|
|
195
|
-
.argument('[project-directory]', 'Directory to create the project in')
|
|
196
|
-
.action(async (projectDirectory) => {
|
|
197
|
-
|
|
198
|
-
// 1. Prompt User
|
|
199
|
-
const { targetDir, packageManager, database } = await promptUser(projectDirectory);
|
|
200
|
-
const targetPath = path.resolve(process.cwd(), targetDir);
|
|
201
|
-
const templateDir = path.join(__dirname, '..', 'template');
|
|
202
|
-
|
|
203
|
-
console.log(`\nCreating a new Node.js app in ${chalk.green(targetPath)}\n`);
|
|
13
|
+
.description('Scaffold a new Node.js application');
|
|
204
14
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
configureDatabase(targetPath, database);
|
|
210
|
-
|
|
211
|
-
// 4. Install Dependencies
|
|
212
|
-
installDependencies(targetPath, packageManager);
|
|
15
|
+
program
|
|
16
|
+
.command('init [project-directory]', { isDefault: true })
|
|
17
|
+
.description('Initialize a new Node.js project (Default command)')
|
|
18
|
+
.action(initCommand);
|
|
213
19
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
20
|
+
program
|
|
21
|
+
.command('create-service <serviceName>')
|
|
22
|
+
.alias('create')
|
|
23
|
+
.description('Scaffold a new service (model, controller, routes)')
|
|
24
|
+
.action(createServiceCommand);
|
|
217
25
|
|
|
218
26
|
program.parse(process.argv);
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
|
|
6
|
+
export async function createServiceCommand(serviceName, targetDir = process.cwd()) {
|
|
7
|
+
if (!serviceName) {
|
|
8
|
+
console.error(chalk.red('Please provide a service name. Example: create-node-app create-service auth'));
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const currentDir = targetDir;
|
|
13
|
+
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
14
|
+
|
|
15
|
+
// Default to no database if not found
|
|
16
|
+
let dbType = 'none';
|
|
17
|
+
|
|
18
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
19
|
+
try {
|
|
20
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
21
|
+
const deps = pkg.dependencies || {};
|
|
22
|
+
if (deps.mongoose) {
|
|
23
|
+
dbType = 'mongoose';
|
|
24
|
+
} else if (deps.sequelize) {
|
|
25
|
+
dbType = 'sequelize';
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.warn(chalk.yellow('Could not parse package.json. Defaulting to standard model generation.'));
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
console.warn(chalk.yellow('No package.json found in current directory. Generating service without database dependencies.'));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const serviceDir = path.join(currentDir, 'services', serviceName);
|
|
35
|
+
const controllerDir = path.join(serviceDir, 'controller');
|
|
36
|
+
const modelDir = path.join(serviceDir, 'model');
|
|
37
|
+
const routesDir = path.join(serviceDir, 'routes');
|
|
38
|
+
|
|
39
|
+
const spinner = ora(`Generating ${serviceName} service...`).start();
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Create directories
|
|
43
|
+
fs.mkdirSync(controllerDir, { recursive: true });
|
|
44
|
+
fs.mkdirSync(modelDir, { recursive: true });
|
|
45
|
+
fs.mkdirSync(routesDir, { recursive: true });
|
|
46
|
+
|
|
47
|
+
// Generate Controller
|
|
48
|
+
const controllerContent = `export default {
|
|
49
|
+
// Add your controller methods here
|
|
50
|
+
// exampleMethod: async (req, res, next) => { ... }
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
fs.writeFileSync(path.join(controllerDir, `${serviceName}.controller.js`), controllerContent);
|
|
54
|
+
|
|
55
|
+
// Generate Routes
|
|
56
|
+
const routesContent = `import express from 'express'
|
|
57
|
+
import ${serviceName}Controller from '../controller/${serviceName}.controller.js'
|
|
58
|
+
|
|
59
|
+
const router = express.Router({ caseSensitive: true })
|
|
60
|
+
|
|
61
|
+
// Add your routes here
|
|
62
|
+
// router.get('/', ${serviceName}Controller.exampleMethod)
|
|
63
|
+
|
|
64
|
+
export default router
|
|
65
|
+
`;
|
|
66
|
+
fs.writeFileSync(path.join(routesDir, `${serviceName}.routes.js`), routesContent);
|
|
67
|
+
|
|
68
|
+
// Generate Model
|
|
69
|
+
let modelContent = '';
|
|
70
|
+
if (dbType === 'sequelize') {
|
|
71
|
+
modelContent = `import { DataTypes } from 'sequelize'
|
|
72
|
+
import sequelize from '../../../config/db.config.js'
|
|
73
|
+
|
|
74
|
+
const ${serviceName.charAt(0).toUpperCase() + serviceName.slice(1)} = sequelize.define('${serviceName}', {
|
|
75
|
+
name: {
|
|
76
|
+
type: DataTypes.STRING,
|
|
77
|
+
allowNull: false
|
|
78
|
+
}
|
|
79
|
+
}, {
|
|
80
|
+
timestamps: true
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export default ${serviceName.charAt(0).toUpperCase() + serviceName.slice(1)}
|
|
84
|
+
`;
|
|
85
|
+
} else {
|
|
86
|
+
// Default to mongoose structure
|
|
87
|
+
modelContent = `import mongoose from "mongoose";
|
|
88
|
+
|
|
89
|
+
const ${serviceName}Schema = new mongoose.Schema({
|
|
90
|
+
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
export default mongoose.model('${serviceName}', ${serviceName}Schema);
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
fs.writeFileSync(path.join(modelDir, `${serviceName}.model.js`), modelContent);
|
|
98
|
+
|
|
99
|
+
// Try to update app.js automatically
|
|
100
|
+
const appJsPath = path.join(currentDir, 'app.js');
|
|
101
|
+
if (fs.existsSync(appJsPath)) {
|
|
102
|
+
let appJsContent = fs.readFileSync(appJsPath, 'utf8');
|
|
103
|
+
|
|
104
|
+
const importStatement = `import ${serviceName}Routes from './services/${serviceName}/routes/${serviceName}.routes.js'`;
|
|
105
|
+
const useStatement = `app.use('/api/${serviceName}', ${serviceName}Routes)`;
|
|
106
|
+
|
|
107
|
+
// Inject import statement after the last import
|
|
108
|
+
if (!appJsContent.includes(importStatement)) {
|
|
109
|
+
const lastImportIndex = appJsContent.lastIndexOf('import ');
|
|
110
|
+
if (lastImportIndex !== -1) {
|
|
111
|
+
const endOfLastImport = appJsContent.indexOf('\n', lastImportIndex);
|
|
112
|
+
appJsContent = appJsContent.slice(0, endOfLastImport + 1) + importStatement + '\n' + appJsContent.slice(endOfLastImport + 1);
|
|
113
|
+
} else {
|
|
114
|
+
appJsContent = importStatement + '\n' + appJsContent;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Inject app.use statement before app.get('/', or before export default
|
|
119
|
+
if (!appJsContent.includes(useStatement)) {
|
|
120
|
+
const appGetIndex = appJsContent.indexOf("app.get('/'");
|
|
121
|
+
if (appGetIndex !== -1) {
|
|
122
|
+
appJsContent = appJsContent.slice(0, appGetIndex) + useStatement + '\n\n' + appJsContent.slice(appGetIndex);
|
|
123
|
+
} else {
|
|
124
|
+
const exportIndex = appJsContent.lastIndexOf('export default app');
|
|
125
|
+
if (exportIndex !== -1) {
|
|
126
|
+
appJsContent = appJsContent.slice(0, exportIndex) + useStatement + '\n\n' + appJsContent.slice(exportIndex);
|
|
127
|
+
} else {
|
|
128
|
+
appJsContent += '\n' + useStatement + '\n';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fs.writeFileSync(appJsPath, appJsContent);
|
|
134
|
+
spinner.succeed(`Successfully created ${chalk.green(serviceName)} service and updated app.js`);
|
|
135
|
+
} else {
|
|
136
|
+
spinner.succeed(`Successfully created ${chalk.green(serviceName)} service at ./services/${serviceName}`);
|
|
137
|
+
console.log(`\nDon't forget to register your routes in ${chalk.cyan('app.js')}!`);
|
|
138
|
+
console.log(` import ${serviceName}Routes from './services/${serviceName}/routes/${serviceName}.routes.js'`);
|
|
139
|
+
console.log(` app.use('/api/${serviceName}', ${serviceName}Routes)\n`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
} catch (err) {
|
|
143
|
+
spinner.fail(`Failed to create ${serviceName} service.`);
|
|
144
|
+
console.error(err);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { promptUser, copyTemplateFiles, installDependencies, showSuccessMessage } from '../utils/helpers.js';
|
|
5
|
+
import { configureDatabase } from '../utils/database.js';
|
|
6
|
+
import { createServiceCommand } from './createService.js';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
export async function initCommand(projectDirectory) {
|
|
12
|
+
// 1. Prompt User
|
|
13
|
+
const { targetDir, packageManager, database } = await promptUser(projectDirectory);
|
|
14
|
+
const targetPath = path.resolve(process.cwd(), targetDir);
|
|
15
|
+
const templateDir = path.join(__dirname, '..', '..', 'template');
|
|
16
|
+
|
|
17
|
+
console.log(`\nCreating a new Node.js app in ${chalk.green(targetPath)}\n`);
|
|
18
|
+
|
|
19
|
+
// 2. Copy Template Files
|
|
20
|
+
copyTemplateFiles(templateDir, targetPath);
|
|
21
|
+
|
|
22
|
+
// 3. Configure Database
|
|
23
|
+
configureDatabase(targetPath, database);
|
|
24
|
+
|
|
25
|
+
// 4. Dynamically generate the 'auth' service so it uses the correct DB model
|
|
26
|
+
await createServiceCommand('auth', targetPath);
|
|
27
|
+
|
|
28
|
+
// 5. Install Dependencies
|
|
29
|
+
installDependencies(targetPath, packageManager);
|
|
30
|
+
|
|
31
|
+
// 6. Success Message
|
|
32
|
+
showSuccessMessage(targetDir, targetPath, packageManager);
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { program } from 'commander';
|
|
4
|
+
import { createServiceCommand } from './commands/createService.js';
|
|
5
|
+
|
|
6
|
+
program
|
|
7
|
+
.name('create-service')
|
|
8
|
+
.description('Scaffold a new service (model, controller, routes)')
|
|
9
|
+
.argument('<serviceName>', 'Name of the service to create')
|
|
10
|
+
.action(createServiceCommand);
|
|
11
|
+
|
|
12
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export function configureDatabase(targetPath, database) {
|
|
5
|
+
const packageJsonPath = path.join(targetPath, 'package.json');
|
|
6
|
+
if (!fs.existsSync(packageJsonPath)) return;
|
|
7
|
+
|
|
8
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
9
|
+
pkg.name = path.basename(targetPath);
|
|
10
|
+
|
|
11
|
+
const dbConfigPath = path.join(targetPath, 'config', 'db.config.js');
|
|
12
|
+
const targetConfigDir = path.dirname(dbConfigPath);
|
|
13
|
+
if (!fs.existsSync(targetConfigDir)) {
|
|
14
|
+
fs.mkdirSync(targetConfigDir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (database === 'MongoDB (Mongoose)') {
|
|
18
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
19
|
+
pkg.dependencies.mongoose = '^8.0.0';
|
|
20
|
+
|
|
21
|
+
const mongooseContent = `import mongoose from "mongoose"
|
|
22
|
+
|
|
23
|
+
const options = {
|
|
24
|
+
serverSelectionTimeoutMS: 10000,
|
|
25
|
+
dbName: '',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const connectDB = async () => {
|
|
29
|
+
try {
|
|
30
|
+
await mongoose.connect('', options)
|
|
31
|
+
console.log(\`✅ MongoDB connected!\`)
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(\`❌ MongoDB connection error: \${error.message}\`)
|
|
34
|
+
process.exit(1)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default connectDB`;
|
|
39
|
+
fs.writeFileSync(dbConfigPath, mongooseContent);
|
|
40
|
+
|
|
41
|
+
} else if (database === 'PostgreSQL (Sequelize)') {
|
|
42
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
43
|
+
pkg.dependencies.sequelize = '^6.35.0';
|
|
44
|
+
pkg.dependencies.pg = '^8.11.3';
|
|
45
|
+
pkg.dependencies['pg-hstore'] = '^2.3.4';
|
|
46
|
+
|
|
47
|
+
const sequelizeContent = `import { Sequelize } from "sequelize"
|
|
48
|
+
|
|
49
|
+
export const sequelize = new Sequelize(
|
|
50
|
+
'', // DB name
|
|
51
|
+
'', // User
|
|
52
|
+
'', // Password
|
|
53
|
+
{
|
|
54
|
+
host: '',
|
|
55
|
+
dialect: 'postgres',
|
|
56
|
+
logging: false,
|
|
57
|
+
port: ''
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
// Test connection
|
|
62
|
+
// (async () => {
|
|
63
|
+
// try {
|
|
64
|
+
// await sequelize.authenticate();
|
|
65
|
+
// console.log("PostgreSQL connected with Sequelize");
|
|
66
|
+
// } catch (err) {
|
|
67
|
+
// console.error("Error:", err.message);
|
|
68
|
+
// }
|
|
69
|
+
// })()
|
|
70
|
+
|
|
71
|
+
export default sequelize
|
|
72
|
+
`;
|
|
73
|
+
fs.writeFileSync(dbConfigPath, sequelizeContent);
|
|
74
|
+
|
|
75
|
+
} else {
|
|
76
|
+
fs.writeFileSync(dbConfigPath, '// No database configured\n');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Save updated package.json
|
|
80
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
|
|
81
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
|
|
8
|
+
export async function promptUser(projectDirectory) {
|
|
9
|
+
let targetDir = projectDirectory;
|
|
10
|
+
|
|
11
|
+
if (!targetDir) {
|
|
12
|
+
const answers = await inquirer.prompt([
|
|
13
|
+
{
|
|
14
|
+
type: 'input',
|
|
15
|
+
name: 'projectDirectory',
|
|
16
|
+
message: 'What is your project named?',
|
|
17
|
+
default: 'my-app'
|
|
18
|
+
}
|
|
19
|
+
]);
|
|
20
|
+
targetDir = answers.projectDirectory;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { packageManager, database } = await inquirer.prompt([
|
|
24
|
+
{
|
|
25
|
+
type: 'list',
|
|
26
|
+
name: 'packageManager',
|
|
27
|
+
message: 'Which package manager would you like to use?',
|
|
28
|
+
choices: ['npm', 'yarn', 'pnpm'],
|
|
29
|
+
default: 'npm'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'list',
|
|
33
|
+
name: 'database',
|
|
34
|
+
message: 'Which database would you like to use?',
|
|
35
|
+
choices: ['MongoDB (Mongoose)', 'PostgreSQL (Sequelize)', 'None'],
|
|
36
|
+
default: 'MongoDB (Mongoose)'
|
|
37
|
+
}
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
return { targetDir, packageManager, database };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function copyTemplateFiles(templateDir, targetPath) {
|
|
44
|
+
const spinner = ora('Copying template files...').start();
|
|
45
|
+
|
|
46
|
+
if (fs.existsSync(targetPath)) {
|
|
47
|
+
spinner.fail(`Directory ${path.basename(targetPath)} already exists.`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
52
|
+
|
|
53
|
+
const copyRecursiveSync = (src, dest) => {
|
|
54
|
+
const exists = fs.existsSync(src);
|
|
55
|
+
const stats = exists && fs.statSync(src);
|
|
56
|
+
if (exists && stats.isDirectory()) {
|
|
57
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest);
|
|
58
|
+
fs.readdirSync(src).forEach((child) => {
|
|
59
|
+
if (child === 'node_modules') return;
|
|
60
|
+
copyRecursiveSync(path.join(src, child), path.join(dest, child));
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
fs.copyFileSync(src, dest);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
copyRecursiveSync(templateDir, targetPath);
|
|
69
|
+
spinner.succeed('Template copied successfully.');
|
|
70
|
+
} catch (err) {
|
|
71
|
+
spinner.fail('Failed to copy template.');
|
|
72
|
+
console.error(err);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function installDependencies(targetPath, packageManager) {
|
|
78
|
+
const installSpinner = ora(`Installing dependencies using ${packageManager}...`).start();
|
|
79
|
+
try {
|
|
80
|
+
execSync(`${packageManager} install`, { cwd: targetPath, stdio: 'pipe' });
|
|
81
|
+
installSpinner.succeed('Dependencies installed successfully.');
|
|
82
|
+
} catch (err) {
|
|
83
|
+
installSpinner.fail('Failed to install dependencies.');
|
|
84
|
+
console.error(err);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function showSuccessMessage(targetDir, targetPath, packageManager) {
|
|
89
|
+
console.log(`\n${chalk.green('Success!')} Created ${path.basename(targetPath)} at ${targetPath}`);
|
|
90
|
+
console.log('\nInside that directory, you can run several commands:\n');
|
|
91
|
+
console.log(` ${chalk.cyan(`${packageManager} run dev`)}`);
|
|
92
|
+
console.log(' Starts the development server with nodemon.\n');
|
|
93
|
+
console.log('We suggest that you begin by typing:\n');
|
|
94
|
+
console.log(` ${chalk.cyan('cd')} ${targetDir}`);
|
|
95
|
+
console.log(` ${chalk.cyan(`${packageManager} run dev`)}\n`);
|
|
96
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apexkrrish/create-node-app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A CLI tool to scaffold Node.js applications.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"private": false,
|
|
8
8
|
"bin": {
|
|
9
|
-
"create-node-app": "bin/cli.js"
|
|
9
|
+
"create-node-app": "bin/cli.js",
|
|
10
|
+
"create-service": "bin/create-service-cli.js"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
|
-
"start": "node ./bin/cli.js"
|
|
13
|
+
"start": "node ./bin/cli.js",
|
|
14
|
+
"create-service": "node ./bin/cli.js create-service"
|
|
13
15
|
},
|
|
14
16
|
"dependencies": {
|
|
15
17
|
"chalk": "^5.3.0",
|
|
@@ -23,9 +25,18 @@
|
|
|
23
25
|
},
|
|
24
26
|
"keywords": [
|
|
25
27
|
"node",
|
|
26
|
-
"
|
|
28
|
+
"nodejs",
|
|
29
|
+
"cli",
|
|
30
|
+
"scaffold",
|
|
31
|
+
"generator",
|
|
32
|
+
"boilerplate",
|
|
33
|
+
"express",
|
|
34
|
+
"mongoose",
|
|
35
|
+
"sequelize",
|
|
36
|
+
"mvc",
|
|
37
|
+
"create-node-app"
|
|
27
38
|
],
|
|
28
|
-
"author": "",
|
|
39
|
+
"author": "Krish Dhiman",
|
|
29
40
|
"license": "ISC",
|
|
30
41
|
"bugs": {
|
|
31
42
|
"url": "https://github.com/KrishDhimanOfficial/create_node_app/issues"
|
package/readme.md
CHANGED
|
@@ -1,51 +1,83 @@
|
|
|
1
|
-
# create-node-app
|
|
1
|
+
# @apexkrrish/create-node-app
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A powerful and fast CLI tool to scaffold production-ready Node.js applications with built-in database configurations and modular service generation.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
- PostgreSQL (using Sequelize)
|
|
12
|
-
- None (no database setup out-of-the-box)
|
|
13
|
-
- **Ready to Go**: Automatically installs dependencies and sets up the basic folder structure based on the provided template.
|
|
7
|
+
- Scaffolds a complete Express.js server structure.
|
|
8
|
+
- Automatic database setup (MongoDB/Mongoose or PostgreSQL/Sequelize).
|
|
9
|
+
- Integrated auto-generator for new service modules (Controllers, Models, Routes).
|
|
10
|
+
- Pre-configured `app.js` with best practices (CORS, compression, error handlers).
|
|
14
11
|
|
|
15
|
-
## Usage
|
|
12
|
+
## Installation & Usage
|
|
16
13
|
|
|
17
|
-
You can use
|
|
14
|
+
You don't need to install this globally (though you can if you want). The easiest way to use it is with `npx`.
|
|
15
|
+
|
|
16
|
+
### 1. Create a New Application
|
|
17
|
+
|
|
18
|
+
To create a brand new Node.js app, simply run:
|
|
18
19
|
|
|
19
20
|
```bash
|
|
20
|
-
npx create-node-app
|
|
21
|
+
npx @apexkrrish/create-node-app my-app
|
|
21
22
|
```
|
|
23
|
+
*(If you leave out the `my-app` name, the CLI will ask you for it!)*
|
|
24
|
+
|
|
25
|
+
The CLI will prompt you to choose:
|
|
26
|
+
- Your preferred package manager (`npm`, `yarn`, `pnpm`).
|
|
27
|
+
- Your preferred database (`MongoDB`, `PostgreSQL`, or `None`).
|
|
28
|
+
|
|
29
|
+
It will then automatically scaffold your project, configure the database connection, generate an initial `auth` service tailored to your selected database, and install dependencies!
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
### 2. Run Your Application
|
|
24
32
|
|
|
25
33
|
```bash
|
|
26
|
-
|
|
34
|
+
cd my-app
|
|
35
|
+
npm run dev
|
|
27
36
|
```
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
If you do not provide a project directory, the CLI will ask you for one. It will then prompt you to answer the following:
|
|
38
|
+
---
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
2. **Which package manager would you like to use?**
|
|
35
|
-
- npm
|
|
36
|
-
- yarn
|
|
37
|
-
- pnpm
|
|
38
|
-
3. **Which database would you like to use?**
|
|
39
|
-
- MongoDB (Mongoose)
|
|
40
|
-
- PostgreSQL (Sequelize)
|
|
41
|
-
- None
|
|
40
|
+
## Code Generator: Create Services
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
Once your application is created, this tool comes with an amazing built-in code generator that saves you from writing boilerplate.
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
To generate a new service module (for example, a `user` service), navigate into your project folder and run:
|
|
46
45
|
|
|
47
46
|
```bash
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
npx create-service user
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
*(If you installed the CLI globally via `npm install -g @apexkrrish/create-node-app`, you can just type `create-service user`)*
|
|
51
|
+
|
|
52
|
+
### What it does:
|
|
53
|
+
1. It creates `services/user/controller/user.controller.js`
|
|
54
|
+
2. It creates `services/user/routes/user.routes.js`
|
|
55
|
+
3. It detects the database you chose during setup (Mongoose or Sequelize) and generates the correct `services/user/model/user.model.js` structure automatically!
|
|
56
|
+
4. **It automatically updates your `app.js` file** to import and register your new routes!
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Folder Structure Generated
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
my-app/
|
|
64
|
+
├── bin/
|
|
65
|
+
│ └── www
|
|
66
|
+
├── config/
|
|
67
|
+
│ └── db.config.js # Database connection
|
|
68
|
+
├── public/
|
|
69
|
+
├── services/
|
|
70
|
+
│ ├── auth/ # Automatically generated on init
|
|
71
|
+
│ │ ├── controller/
|
|
72
|
+
│ │ ├── model/
|
|
73
|
+
│ │ └── routes/
|
|
74
|
+
│ └── <your-service>/ # Generated via 'create-service'
|
|
75
|
+
├── utils/
|
|
76
|
+
│ └── helper.utils.js
|
|
77
|
+
├── app.js # Express configuration (auto-updated)
|
|
78
|
+
└── package.json
|
|
50
79
|
```
|
|
51
|
-
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
ISC License.
|