create-express-kickstart 1.2.1 ā 1.2.3
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 +4 -2
- package/bin/cli.js +86 -29
- package/package.json +1 -1
- package/templates/auth/auth.routes.js +2 -2
- package/templates/tests/healthcheck.test.js +1 -1
package/README.md
CHANGED
|
@@ -14,14 +14,16 @@ You do not need to clone this repository, install dependencies manually, or writ
|
|
|
14
14
|
|
|
15
15
|
### 1. Initialize a New Project
|
|
16
16
|
|
|
17
|
+
We highly recommend using the `@latest` tag to ensure you are always downloading the most recent version of our CLI tool dynamically, bypassing any local caching issues!
|
|
18
|
+
|
|
17
19
|
Run the following command anywhere in your terminal:
|
|
18
20
|
```bash
|
|
19
|
-
npx create-express-kickstart <your-project-name>
|
|
21
|
+
npx create-express-kickstart@latest <your-project-name>
|
|
20
22
|
```
|
|
21
23
|
|
|
22
24
|
**Example:**
|
|
23
25
|
```bash
|
|
24
|
-
npx create-express-kickstart my-awesome-api
|
|
26
|
+
npx create-express-kickstart@latest my-awesome-api
|
|
25
27
|
```
|
|
26
28
|
|
|
27
29
|
### 2. What happens under the hood?
|
package/bin/cli.js
CHANGED
|
@@ -73,7 +73,6 @@ async function init() {
|
|
|
73
73
|
const initGit = (await question('\nš Initialize a git repository? [Y/n] ')).toLowerCase() !== 'n';
|
|
74
74
|
const initDocker = (await question('š Include Dockerfile & docker-compose.yml? [Y/n] ')).toLowerCase() !== 'n';
|
|
75
75
|
const initAuth = (await question('š Include basic JWT Auth boilerplate? [Y/n] ')).toLowerCase() !== 'n';
|
|
76
|
-
const useESM = (await question('š Use ECMAScript Modules (ESM) over CommonJS? [Y/n] ')).toLowerCase() !== 'n';
|
|
77
76
|
const initTests = (await question('š Include Jest setup and boilerplate tests? [Y/n] ')).toLowerCase() !== 'n';
|
|
78
77
|
|
|
79
78
|
rl.close();
|
|
@@ -162,6 +161,69 @@ async function init() {
|
|
|
162
161
|
);
|
|
163
162
|
}
|
|
164
163
|
|
|
164
|
+
// Rewrite app.js and server.js based on selections
|
|
165
|
+
let appJsPath = path.join(projectPath, 'src', 'app.js');
|
|
166
|
+
if (fs.existsSync(appJsPath)) {
|
|
167
|
+
let appJsCode = fs.readFileSync(appJsPath, 'utf8');
|
|
168
|
+
|
|
169
|
+
if (initAuth) {
|
|
170
|
+
appJsCode = appJsCode.replace(
|
|
171
|
+
'// Import routers',
|
|
172
|
+
'// Import routers\nimport authRouter from "#routes/auth.routes.js";'
|
|
173
|
+
);
|
|
174
|
+
appJsCode = appJsCode.replace(
|
|
175
|
+
'// Mount routers',
|
|
176
|
+
'// Mount routers\napp.use("/api/v1/auth", authRouter);'
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
if (!deps.cors) {
|
|
180
|
+
appJsCode = appJsCode.replace(/import cors from "cors";\r?\n/, '');
|
|
181
|
+
appJsCode = appJsCode.replace(/\/\/ CORS setup[\s\S]*?\n\);\r?\n/, '');
|
|
182
|
+
}
|
|
183
|
+
if (!deps.helmet) {
|
|
184
|
+
appJsCode = appJsCode.replace(/import helmet from "helmet";\r?\n/, '');
|
|
185
|
+
appJsCode = appJsCode.replace(/\/\/ Security HTTP headers\r?\napp\.use\(helmet\(\)\);\r?\n/, '');
|
|
186
|
+
}
|
|
187
|
+
if (!deps['cookie-parser']) {
|
|
188
|
+
appJsCode = appJsCode.replace(/import cookieParser from "cookie-parser";\r?\n/, '');
|
|
189
|
+
appJsCode = appJsCode.replace(/app\.use\(cookieParser\(\)\);\r?\n/, '');
|
|
190
|
+
}
|
|
191
|
+
if (!deps['pino-http']) {
|
|
192
|
+
appJsCode = appJsCode.replace(/import pinoHttp from "pino-http";\r?\n/, '');
|
|
193
|
+
appJsCode = appJsCode.replace(/\/\/ Logging[\s\S]*?\}\)\(\) \? : undefined\n\}\)\);\r?\n/g, ''); // Fallback block
|
|
194
|
+
appJsCode = appJsCode.replace(/\/\/ Logging[\s\S]*?\}\)\(\) : undefined\r?\n\}\)\);\r?\n/g, '');
|
|
195
|
+
}
|
|
196
|
+
if (!deps['express-rate-limit']) {
|
|
197
|
+
appJsCode = appJsCode.replace(/import rateLimit from "express-rate-limit";\r?\n/, '');
|
|
198
|
+
appJsCode = appJsCode.replace(/\/\/ Rate Limiting[\s\S]*?app\.use\("\/api", limiter\);[^\n]*\n/g, '');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
fs.writeFileSync(appJsPath, appJsCode);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let serverJsPath = path.join(projectPath, 'src', 'server.js');
|
|
205
|
+
if (fs.existsSync(serverJsPath)) {
|
|
206
|
+
let serverJsCode = fs.readFileSync(serverJsPath, 'utf8');
|
|
207
|
+
|
|
208
|
+
if (!deps.mongoose) {
|
|
209
|
+
serverJsCode = serverJsCode.replace(/import connectDB from "#db\/index\.js";\r?\n/, '');
|
|
210
|
+
serverJsCode = serverJsCode.replace(/connectDB\(\)\r?\n \.then\(\(\) => \{\r?\n/, '');
|
|
211
|
+
serverJsCode = serverJsCode.replace(/ \}\)\r?\n \.catch\(\(err\) => \{\r?\n console\.log\("MONGO db connection failed !!! ", err\);\r?\n \}\);\r?\n/, '');
|
|
212
|
+
// Fix indentation for app.listen
|
|
213
|
+
serverJsCode = serverJsCode.replace(/ app\.listen\(PORT, \(\) => \{\r?\n console\.log\(`Server is running at port : \$\{PORT\}`\);\r?\n \}\);\r?\n/, 'app.listen(PORT, () => {\n console.log(`Server is running at port : ${PORT}`);\n});\n');
|
|
214
|
+
|
|
215
|
+
const dbDir = path.join(projectPath, 'src', 'db');
|
|
216
|
+
if (fs.existsSync(dbDir)) fs.rmSync(dbDir, { recursive: true, force: true });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!deps.dotenv) {
|
|
220
|
+
serverJsCode = serverJsCode.replace(/import dotenv from "dotenv";\r?\n/, '');
|
|
221
|
+
serverJsCode = serverJsCode.replace(/\/\/ Load environment variables[\s\S]*?\}\);\r?\n/, '');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fs.writeFileSync(serverJsPath, serverJsCode);
|
|
225
|
+
}
|
|
226
|
+
|
|
165
227
|
// 3. Create package.json
|
|
166
228
|
console.log(`š¦ Setting up package.json...`);
|
|
167
229
|
const packageJsonTemplate = {
|
|
@@ -169,7 +231,7 @@ async function init() {
|
|
|
169
231
|
version: "1.0.0",
|
|
170
232
|
description: description || "A production-ready Node.js Express API",
|
|
171
233
|
main: "src/server.js",
|
|
172
|
-
type:
|
|
234
|
+
type: "module",
|
|
173
235
|
scripts: {
|
|
174
236
|
"start": "node src/server.js",
|
|
175
237
|
"dev": "nodemon src/server.js"
|
|
@@ -187,9 +249,7 @@ async function init() {
|
|
|
187
249
|
}
|
|
188
250
|
|
|
189
251
|
if (initTests) {
|
|
190
|
-
packageJsonTemplate.scripts.test =
|
|
191
|
-
? "node --experimental-vm-modules node_modules/jest/bin/jest.js"
|
|
192
|
-
: "jest";
|
|
252
|
+
packageJsonTemplate.scripts.test = "node --experimental-vm-modules node_modules/jest/bin/jest.js";
|
|
193
253
|
}
|
|
194
254
|
|
|
195
255
|
// Write package.json
|
|
@@ -206,7 +266,6 @@ async function init() {
|
|
|
206
266
|
if (initAuth) {
|
|
207
267
|
dependenciesToInstall.push('jsonwebtoken', 'bcryptjs'); // Add bcryptjs too since it's standard with JWT
|
|
208
268
|
}
|
|
209
|
-
const depString = dependenciesToInstall.join(' ');
|
|
210
269
|
|
|
211
270
|
const devDependenciesToInstall = ['nodemon'];
|
|
212
271
|
if (deps.prettier) devDependenciesToInstall.push('prettier');
|
|
@@ -214,32 +273,30 @@ async function init() {
|
|
|
214
273
|
if (initTests) {
|
|
215
274
|
devDependenciesToInstall.push('jest', 'supertest');
|
|
216
275
|
}
|
|
217
|
-
const devDepString = devDependenciesToInstall.join(' ');
|
|
218
276
|
|
|
219
|
-
console.log(`\nā³ Installing selected core dependencies (${dependenciesToInstall.join(', ')}). This might take a minute...`);
|
|
220
277
|
try {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
: packageManager === 'bun' ? 'bun add -d'
|
|
229
|
-
: 'npm install --save-dev';
|
|
230
|
-
|
|
231
|
-
if (depString) {
|
|
232
|
-
execSync(`${installCmd} ${depString}`, {
|
|
233
|
-
cwd: projectPath,
|
|
234
|
-
stdio: 'inherit'
|
|
235
|
-
});
|
|
236
|
-
}
|
|
278
|
+
const execConfig = { cwd: projectPath, stdio: 'inherit' };
|
|
279
|
+
|
|
280
|
+
// Inject dependencies directly into package.json instead of doing them via raw arguments.
|
|
281
|
+
// This perfectly bypasses PNPM / YARN / BUN specific registry caching bugs when downloading deeply nested trees.
|
|
282
|
+
console.log(`\nā³ Configuring ${packageManager} and resolving dependency trees...`);
|
|
283
|
+
const finalPackageJsonPath = path.join(projectPath, 'package.json');
|
|
284
|
+
const finalPackageJsonCode = JSON.parse(fs.readFileSync(finalPackageJsonPath, 'utf8'));
|
|
237
285
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
286
|
+
// We add them dynamically so package managers can evaluate them holistically at once
|
|
287
|
+
const latestDeps = {};
|
|
288
|
+
dependenciesToInstall.forEach(d => latestDeps[d] = 'latest');
|
|
289
|
+
finalPackageJsonCode.dependencies = latestDeps;
|
|
290
|
+
|
|
291
|
+
const latestDevDeps = {};
|
|
292
|
+
devDependenciesToInstall.forEach(d => latestDevDeps[d] = 'latest');
|
|
293
|
+
finalPackageJsonCode.devDependencies = latestDevDeps;
|
|
294
|
+
|
|
295
|
+
fs.writeFileSync(finalPackageJsonPath, JSON.stringify(finalPackageJsonCode, null, 2));
|
|
296
|
+
|
|
297
|
+
console.log(`\nā³ Running final installation via ${packageManager} (This might take a minute)...`);
|
|
298
|
+
const installTriggerCmd = packageManager === 'npm' ? 'npm install' : `${packageManager} install`;
|
|
299
|
+
execSync(installTriggerCmd, execConfig);
|
|
243
300
|
|
|
244
301
|
if (initGit) {
|
|
245
302
|
console.log(`\nš± Initializing Git repository...`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { authController } from '
|
|
3
|
-
import { authMiddleware } from '
|
|
2
|
+
import { authController } from '#controllers/auth.controller.js';
|
|
3
|
+
import { authMiddleware } from '#middlewares/auth.middleware.js';
|
|
4
4
|
|
|
5
5
|
const router = Router();
|
|
6
6
|
|