@apexkrrish/create-node-app 1.0.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/bin/cli.js ADDED
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { program } from 'commander';
4
+ import inquirer from 'inquirer';
5
+ import fs from 'fs';
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
+ }
187
+
188
+ // ==========================================
189
+ // CLI Application
190
+ // ==========================================
191
+
192
+ program
193
+ .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`);
204
+
205
+ // 2. Copy Template Files
206
+ copyTemplateFiles(templateDir, targetPath);
207
+
208
+ // 3. Configure Database
209
+ configureDatabase(targetPath, database);
210
+
211
+ // 4. Install Dependencies
212
+ installDependencies(targetPath, packageManager);
213
+
214
+ // 5. Success Message
215
+ showSuccessMessage(targetDir, targetPath, packageManager);
216
+ });
217
+
218
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@apexkrrish/create-node-app",
3
+ "version": "1.0.0",
4
+ "description": "A CLI tool to scaffold Node.js applications.",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "private": false,
8
+ "bin": {
9
+ "create-node-app": "bin/cli.js"
10
+ },
11
+ "scripts": {
12
+ "start": "node ./bin/cli.js"
13
+ },
14
+ "dependencies": {
15
+ "chalk": "^5.3.0",
16
+ "commander": "^12.0.0",
17
+ "inquirer": "^9.2.14",
18
+ "ora": "^8.0.1"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/KrishDhimanOfficial/create_node_app.git"
23
+ },
24
+ "keywords": [
25
+ "node",
26
+ "utility"
27
+ ],
28
+ "author": "",
29
+ "license": "ISC",
30
+ "bugs": {
31
+ "url": "https://github.com/KrishDhimanOfficial/create_node_app/issues"
32
+ },
33
+ "homepage": "https://github.com/KrishDhimanOfficial/create_node_app#readme"
34
+ }
package/readme.md ADDED
@@ -0,0 +1,51 @@
1
+ # create-node-app
2
+
3
+ A fast and interactive CLI tool to scaffold Node.js applications.
4
+
5
+ ## Features
6
+
7
+ - **Interactive Prompts**: Choose your project name, package manager, and database just by answering simple questions.
8
+ - **Multiple Package Managers**: Supports `npm`, `yarn`, and `pnpm`.
9
+ - **Database Configuration**: Automatically configures database connections with pre-written boilerplate for:
10
+ - MongoDB (using Mongoose)
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.
14
+
15
+ ## Usage
16
+
17
+ You can use the CLI directly:
18
+
19
+ ```bash
20
+ npx create-node-app [project-directory]
21
+ ```
22
+
23
+ Or, if you clone locally and link:
24
+
25
+ ```bash
26
+ npm start -- my-new-project
27
+ ```
28
+
29
+ ### Interactive Prompts
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:
32
+
33
+ 1. **What is your project named?** (if no folder name was supplied)
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
42
+
43
+ ### After Setup
44
+
45
+ Once the tool has finished scaffolding the project and installing dependencies, you can navigate into your new app and start the development server:
46
+
47
+ ```bash
48
+ cd <your-project-directory>
49
+ npm run dev
50
+ ```
51
+ *(Replace `npm` with your chosen package manager!)*
package/template/.env ADDED
File without changes
@@ -0,0 +1,36 @@
1
+ import express from 'express'
2
+ import cookieParser from 'cookie-parser'
3
+ import logger from 'morgan'
4
+ import { globalErrorHandler } from './utils/helper.utils.js'
5
+ import compression from 'compression'
6
+ import cors from 'cors'
7
+ import { fileURLToPath } from 'node:url'
8
+ import path from 'node:path'
9
+
10
+ const __filename = fileURLToPath(import.meta.url)
11
+ const __dirname = path.dirname(__filename)
12
+ const app = express()
13
+
14
+ app.use(cors())
15
+ app.use(logger('dev'))
16
+ app.use(express.json({ limit: '10kb' }))
17
+ app.use(express.urlencoded({ extended: true }))
18
+ app.use(compression(
19
+ {
20
+ level: 4, // compression level
21
+ threshold: 0, // Compress all
22
+ memLevel: 9, // memory usuage
23
+ filter: (req, res) => compression.filter(req, res)
24
+ }
25
+ ))
26
+ app.use(cookieParser())
27
+ app.use(express.static(path.join(__dirname, 'public')))
28
+
29
+ app.get('/', (req, res) => {
30
+ return res.send('Hello World!')
31
+ })
32
+
33
+ // error handler
34
+ app.use(globalErrorHandler)
35
+
36
+ export default app
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Module dependencies.
5
+ */
6
+
7
+ import app from '../app.js';
8
+ import Debug from 'debug';
9
+ const debug = Debug('create-node:server');
10
+ import http from 'http';
11
+
12
+ /**
13
+ * Get port from environment and store in Express.
14
+ */
15
+
16
+ const port = normalizePort(process.env.PORT || '3000');
17
+ app.set('port', port);
18
+
19
+ /**
20
+ * Create HTTP server.
21
+ */
22
+
23
+ const server = http.createServer(app);
24
+
25
+ /**
26
+ * Listen on provided port, on all network interfaces.
27
+ */
28
+
29
+ server.listen(port, () => {
30
+ console.log(`✅ Server is running on port ${port}`)
31
+ })
32
+ server.on('error', onError);
33
+ server.on('listening', onListening);
34
+
35
+ /**
36
+ * Normalize a port into a number, string, or false.
37
+ */
38
+
39
+ function normalizePort(val) {
40
+ const port = parseInt(val, 10);
41
+
42
+ if (isNaN(port)) {
43
+ // named pipe
44
+ return val;
45
+ }
46
+
47
+ if (port >= 0) {
48
+ // port number
49
+ return port;
50
+ }
51
+
52
+ return false;
53
+ }
54
+
55
+ /**
56
+ * Event listener for HTTP server "error" event.
57
+ */
58
+
59
+ function onError(error) {
60
+ if (error.syscall !== 'listen') {
61
+ throw error;
62
+ }
63
+
64
+ const bind = typeof port === 'string'
65
+ ? 'Pipe ' + port
66
+ : 'Port ' + port;
67
+
68
+ // handle specific listen errors with friendly messages
69
+ switch (error.code) {
70
+ case 'EACCES':
71
+ console.error(bind + ' requires elevated privileges');
72
+ process.exit(1);
73
+ break;
74
+ case 'EADDRINUSE':
75
+ console.error(bind + ' is already in use');
76
+ process.exit(1);
77
+ break;
78
+ default:
79
+ throw error;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Event listener for HTTP server "listening" event.
85
+ */
86
+
87
+ function onListening() {
88
+ const addr = server.address();
89
+ const bind = typeof addr === 'string'
90
+ ? 'pipe ' + addr
91
+ : 'port ' + addr.port;
92
+ debug('Listening on ' + bind);
93
+ }
@@ -0,0 +1,6 @@
1
+ import dotenv from 'dotenv'
2
+ dotenv.config({ quiet: true })
3
+
4
+ export default {
5
+
6
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "backend",
3
+ "version": "0.0.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "scripts": {
7
+ "start": "node ./bin/www",
8
+ "dev": "nodemon ./bin/www"
9
+ },
10
+ "dependencies": {
11
+ "compression": "^1.8.1",
12
+ "cookie-parser": "~1.4.4",
13
+ "cors": "^2.8.6",
14
+ "debug": "~2.6.9",
15
+ "express": "^4.22.1",
16
+ "morgan": "^1.10.1"
17
+ },
18
+ "devDependencies": {
19
+ "nodemon": "^3.1.14"
20
+ }
21
+ }
@@ -0,0 +1,8 @@
1
+ body {
2
+ padding: 50px;
3
+ font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4
+ }
5
+
6
+ a {
7
+ color: #00B7FF;
8
+ }
@@ -0,0 +1,33 @@
1
+ import { deleteFile } from './removeFile.utils.js'
2
+
3
+ export const ApiError = (message, statusCode) => {
4
+ const error = new Error(message)
5
+ error.statusCode = statusCode
6
+ error.success = false
7
+ return error
8
+ }
9
+
10
+ export const asyncHandler = (fn, name = 'UnknownController') => {
11
+
12
+ return (req, res, next) => {
13
+ Promise.resolve(fn(req, res, next)).catch((err) => {
14
+ console.error(`🔥 Error in ${name} : ${err.message}`)
15
+ if (req.file?.filename) deleteFile(req.file?.path)
16
+ if (req.files && req.files?.length > 0) req.files?.forEach(file => deleteFile(file.path))
17
+ next(err)
18
+ })
19
+ }
20
+ }
21
+
22
+ export const globalErrorHandler = (err, req, res, next) => {
23
+ const statusCode = err.statusCode || 500;
24
+
25
+ // For API requests (JSON response)
26
+ return res.status(statusCode).json(
27
+ {
28
+ success: false,
29
+ message: err.message || 'Something went wrong',
30
+ }
31
+ )
32
+ }
33
+
@@ -0,0 +1,21 @@
1
+ import { fileURLToPath } from 'node:url'
2
+ import path from 'node:path'
3
+ import fs from 'node:fs'
4
+ import chalk from 'chalk'
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = path.dirname(__filename)
8
+
9
+
10
+ const deleteFile = async (folderPath) => {
11
+ try {
12
+ const imagePath = path.join(__dirname, '..', folderPath)
13
+ if (!fs.existsSync(imagePath)) return
14
+
15
+ await fs.promises.rm(imagePath, { force: true })
16
+ } catch (error) {
17
+ console.error(chalk.red('deleteFile error:', error.message))
18
+ }
19
+ }
20
+
21
+ export { deleteFile }