nodejs-quickstart-structure 1.3.5 → 1.3.9

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/lib/generator.js CHANGED
@@ -131,15 +131,15 @@ export const generateProject = async (config) => {
131
131
  const kafkaServiceTemplate = path.join(targetDir, 'src', 'services', `${kafkaServiceFileName}.ejs`);
132
132
 
133
133
  if (await fs.pathExists(kafkaServiceTemplate)) {
134
- const loggerPath = architecture === 'Clean Architecture' ? '../log/logger' : '../utils/logger';
135
- // Note: For MVC, it's relative to src/services (../utils/logger). Wait, ../utils/logger means src/utils/logger.
136
- // src/services/kafka.ts -> ../utils/logger -> src/utils/logger. Correct.
137
- // But wait, generated code for MVC usually puts it in src/services.
138
- // Let's re-verify MVC structure for logger.
139
- // In MVC, logger is usually in src/utils/logger.ts?
140
- // Let's check where logger is copied for MVC.
134
+ let loggerPath = architecture === 'Clean Architecture' ? '../log/logger' : '../utils/logger';
135
+ let configPath = '../config/kafka';
136
+
137
+ if (language === 'TypeScript') {
138
+ loggerPath = architecture === 'Clean Architecture' ? '@/infrastructure/log/logger' : '@/utils/logger';
139
+ configPath = architecture === 'Clean Architecture' ? '@/infrastructure/config/kafka' : '@/config/kafka';
140
+ }
141
141
 
142
- const content = ejs.render(await fs.readFile(kafkaServiceTemplate, 'utf-8'), { loggerPath });
142
+ const content = ejs.render(await fs.readFile(kafkaServiceTemplate, 'utf-8'), { loggerPath, configPath });
143
143
  await fs.writeFile(path.join(targetDir, 'src', 'services', kafkaServiceFileName), content);
144
144
  await fs.remove(kafkaServiceTemplate);
145
145
  }
@@ -252,6 +252,10 @@ export const generateProject = async (config) => {
252
252
 
253
253
  // 8. View Engine (MVC)
254
254
  if (architecture === 'MVC' && viewEngine && viewEngine !== 'None') {
255
+ const publicDir = path.join(templatesDir, 'common', 'public');
256
+ if (await fs.pathExists(publicDir)) {
257
+ await fs.copy(publicDir, path.join(targetDir, 'public'));
258
+ }
255
259
  }
256
260
 
257
261
  // 9. Render Swagger Config (if .ejs exists)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-quickstart-structure",
3
- "version": "1.3.5",
3
+ "version": "1.3.9",
4
4
  "type": "module",
5
5
  "description": "A CLI to scaffold Node.js microservices with MVC or Clean Architecture",
6
6
  "main": "bin/index.js",
@@ -4,12 +4,12 @@ import helmet from 'helmet';
4
4
  import hpp from 'hpp';
5
5
  import rateLimit from 'express-rate-limit';
6
6
  import dotenv from 'dotenv';
7
- import logger from './infrastructure/log/logger';
8
- <% if (communication === 'REST APIs') { %>import userRoutes from './interfaces/routes/userRoutes';<% } -%>
7
+ import logger from '@/infrastructure/log/logger';
8
+ <% if (communication === 'REST APIs') { %>import userRoutes from '@/interfaces/routes/userRoutes';<% } -%>
9
9
  <% if (communication === 'REST APIs') { -%>
10
10
  import swaggerUi from 'swagger-ui-express';
11
- import swaggerSpecs from './config/swagger';<% } -%>
12
- <% if (communication === 'Kafka') { %>import { KafkaService } from './infrastructure/messaging/kafkaClient';<% } -%>
11
+ import swaggerSpecs from '@/config/swagger';<% } -%>
12
+ <% if (communication === 'Kafka') { %>import { KafkaService } from '@/infrastructure/messaging/kafkaClient';<% } -%>
13
13
 
14
14
  dotenv.config();
15
15
 
@@ -42,7 +42,7 @@ const syncDatabase = async () => {
42
42
  let retries = 30;
43
43
  while (retries) {
44
44
  try {
45
- const sequelize = (await import('./infrastructure/database/database')).default;
45
+ const sequelize = (await import('@/infrastructure/database/database')).default;
46
46
  await sequelize.sync();
47
47
  logger.info('Database synced');
48
48
 
@@ -1,5 +1,5 @@
1
- import { User as UserEntity } from '../../domain/user';
2
- import UserModel from '../database/models/User';
1
+ import { User as UserEntity } from '@/domain/user';
2
+ import UserModel from '@/infrastructure/database/models/User';
3
3
 
4
4
  export class UserRepository {
5
5
  async save(user: UserEntity): Promise<UserEntity> {
@@ -1,9 +1,9 @@
1
1
  import { Request, Response } from 'express';
2
- import { UserRepository } from '../../infrastructure/repositories/UserRepository';
3
- import CreateUser from '../../usecases/createUser';
4
- import GetAllUsers from '../../usecases/getAllUsers';
5
- import { HTTP_STATUS } from '../../utils/httpCodes';
6
- import logger from '../../infrastructure/log/logger';
2
+ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
3
+ import CreateUser from '@/usecases/createUser';
4
+ import GetAllUsers from '@/usecases/getAllUsers';
5
+ import { HTTP_STATUS } from '@/utils/httpCodes';
6
+ import logger from '@/infrastructure/log/logger';
7
7
 
8
8
  export class UserController {
9
9
  private createUserUseCase: CreateUser;
@@ -1,5 +1,5 @@
1
1
  import { Router, Request, Response } from 'express';
2
- import { UserController } from '../controllers/userController';
2
+ import { UserController } from '@/interfaces/controllers/userController';
3
3
 
4
4
  const router = Router();
5
5
  const userController = new UserController();
@@ -1,6 +1,6 @@
1
- import { User } from '../domain/user';
1
+ import { User } from '@/domain/user';
2
2
 
3
- import { UserRepository } from '../infrastructure/repositories/UserRepository';
3
+ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
4
4
 
5
5
  export default class CreateUser {
6
6
  constructor(private userRepository: UserRepository) {}
@@ -1,4 +1,4 @@
1
- import { UserRepository } from '../infrastructure/repositories/UserRepository';
1
+ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
2
2
 
3
3
  export default class GetAllUsers {
4
4
  constructor(private userRepository: UserRepository) {}
@@ -40,6 +40,7 @@ COPY --from=builder /app/src ./src
40
40
  # Copy other necessary files (like views if MVC)
41
41
  <% if (viewEngine && viewEngine !== 'None') { %>
42
42
  COPY --from=builder /app/src/views ./dist/views
43
+ <% if (viewEngine && viewEngine !== 'None') { %>COPY --from=builder /app/public ./public<% } %>
43
44
  <% } %>
44
45
 
45
46
  EXPOSE 3000
@@ -1,5 +1,5 @@
1
1
  import { DataTypes, Model } from 'sequelize';
2
- <% if (architecture === 'MVC') { %>import sequelize from '../config/database';<% } else { %>import sequelize from '../database';<% } %>
2
+ <% if (architecture === 'MVC') { %>import sequelize from '@/config/database';<% } else { %>import sequelize from '@/infrastructure/database/database';<% } %>
3
3
 
4
4
  class User extends Model {
5
5
  public id!: number;
@@ -3,7 +3,10 @@ module.exports = {
3
3
  coverageDirectory: 'coverage',
4
4
  collectCoverageFrom: ['src/**/*.{js,ts}'],
5
5
  testMatch: ['**/*.test.ts', '**/*.test.js'],
6
- <% if (language === 'TypeScript') { %>preset: 'ts-jest',<% } %>
6
+ <% if (language === 'TypeScript') { %>preset: 'ts-jest',
7
+ moduleNameMapper: {
8
+ '^@/(.*)$': '<rootDir>/src/$1',
9
+ },<% } %>
7
10
  coveragePathIgnorePatterns: [
8
11
  "/node_modules/",
9
12
  "/dist/"
@@ -1,4 +1,4 @@
1
- import { kafka } from '../config/kafka';
1
+ import { kafka } from '<%= configPath %>';
2
2
  import { EachMessagePayload, Producer, Consumer } from 'kafkajs';
3
3
  import logger from '<%= loggerPath %>';
4
4
 
@@ -5,8 +5,8 @@
5
5
  "main": "<% if (language === 'TypeScript') { %>dist/index.js<% } else { %>src/index.js<% } %>",
6
6
  "scripts": {
7
7
  "start": "<% if (language === 'TypeScript') { %>node dist/index.js<% } else { %>node src/index.js<% } %>",
8
- "dev": "<% if (language === 'TypeScript') { %>nodemon --exec ts-node src/index.ts<% } else { %>nodemon src/index.js<% } %>"<% if (language === 'TypeScript') { %>,
9
- "build": "rimraf dist && tsc<% if (viewEngine && viewEngine !== 'None') { %> && copyfiles -u 1 \"src/views/**/*\" dist/<% } %>"<% } %>,
8
+ "dev": "<% if (language === 'TypeScript') { %>nodemon --exec ts-node -r tsconfig-paths/register src/index.ts<% } else { %>nodemon src/index.js<% } %>"<% if (language === 'TypeScript') { %>,
9
+ "build": "rimraf dist && tsc && tsc-alias<% if (viewEngine && viewEngine !== 'None') { %> && copyfiles -u 1 \"src/views/**/*\" dist/<% } %>"<% } %>,
10
10
  "lint": "eslint . --ext .ts,.js",
11
11
  "lint:fix": "eslint . --ext .ts,.js --fix",
12
12
  "format": "prettier --write .",
@@ -65,6 +65,8 @@
65
65
  "ts-jest": "^29.1.1",
66
66
  "@types/jest": "^29.5.11",
67
67
  "supertest": "^6.3.3",
68
+ "tsconfig-paths": "^4.2.0",
69
+ "tsc-alias": "^1.8.8",
68
70
  "@types/supertest": "^6.0.2"<% } else { %>,
69
71
  "jest": "^29.7.0",
70
72
  "supertest": "^6.3.3"<% } %>
@@ -0,0 +1,147 @@
1
+ :root {
2
+ --primary-color: #4f46e5;
3
+ --primary-hover: #4338ca;
4
+ --bg-color: #f9fafb;
5
+ --card-bg: #ffffff;
6
+ --text-main: #111827;
7
+ --text-muted: #6b7280;
8
+ --success-color: #10b981;
9
+ --border-color: #e5e7eb;
10
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
11
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
12
+ }
13
+
14
+ body {
15
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
16
+ background-color: var(--bg-color);
17
+ color: var(--text-main);
18
+ margin: 0;
19
+ padding: 0;
20
+ line-height: 1.5;
21
+ display: flex;
22
+ justify-content: center;
23
+ min-height: 100vh;
24
+ }
25
+
26
+ .container {
27
+ width: 100%;
28
+ max-width: 800px;
29
+ padding: 40px 20px;
30
+ }
31
+
32
+ .header {
33
+ text-align: center;
34
+ margin-bottom: 40px;
35
+ }
36
+
37
+ .logo {
38
+ font-size: 3rem;
39
+ margin-bottom: 10px;
40
+ }
41
+
42
+ h1 {
43
+ font-size: 2.5rem;
44
+ font-weight: 800;
45
+ color: var(--text-main);
46
+ margin: 0 0 10px 0;
47
+ letter-spacing: -0.025em;
48
+ }
49
+
50
+ .subtitle {
51
+ color: var(--text-muted);
52
+ font-size: 1.125rem;
53
+ }
54
+
55
+ .card-grid {
56
+ display: grid;
57
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
58
+ gap: 20px;
59
+ margin-bottom: 40px;
60
+ }
61
+
62
+ .card {
63
+ background: var(--card-bg);
64
+ padding: 24px;
65
+ border-radius: 12px;
66
+ box-shadow: var(--shadow-sm);
67
+ border: 1px solid var(--border-color);
68
+ transition: transform 0.2s, box-shadow 0.2s;
69
+ }
70
+
71
+ .card:hover {
72
+ transform: translateY(-2px);
73
+ box-shadow: var(--shadow-md);
74
+ }
75
+
76
+ .card h3 {
77
+ margin-top: 0;
78
+ font-size: 0.875rem;
79
+ text-transform: uppercase;
80
+ letter-spacing: 0.05em;
81
+ color: var(--text-muted);
82
+ font-weight: 600;
83
+ }
84
+
85
+ .card p {
86
+ font-size: 1.25rem;
87
+ font-weight: 600;
88
+ color: var(--text-main);
89
+ margin: 5px 0 0 0;
90
+ }
91
+
92
+ .status-card {
93
+ background: var(--card-bg);
94
+ border-radius: 12px;
95
+ padding: 24px;
96
+ box-shadow: var(--shadow-md);
97
+ border-left: 6px solid var(--success-color);
98
+ display: flex;
99
+ align-items: center;
100
+ gap: 20px;
101
+ }
102
+
103
+ .status-icon {
104
+ background: #d1fae5;
105
+ color: var(--success-color);
106
+ width: 48px;
107
+ height: 48px;
108
+ border-radius: 50%;
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ font-size: 1.5rem;
113
+ }
114
+
115
+ .status-content h3 {
116
+ margin: 0;
117
+ font-size: 1.25rem;
118
+ font-weight: 700;
119
+ }
120
+
121
+ .status-content p {
122
+ margin: 5px 0 0 0;
123
+ color: var(--text-muted);
124
+ }
125
+
126
+ .action-btn {
127
+ display: inline-block;
128
+ margin-top: 40px;
129
+ background-color: var(--primary-color);
130
+ color: white;
131
+ padding: 12px 24px;
132
+ border-radius: 8px;
133
+ text-decoration: none;
134
+ font-weight: 600;
135
+ transition: background-color 0.2s;
136
+ }
137
+
138
+ .action-btn:hover {
139
+ background-color: var(--primary-hover);
140
+ }
141
+
142
+ footer {
143
+ margin-top: 60px;
144
+ text-align: center;
145
+ color: var(--text-muted);
146
+ font-size: 0.875rem;
147
+ }
@@ -7,7 +7,11 @@
7
7
  "strict": true,
8
8
  "esModuleInterop": true,
9
9
  "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true
10
+ "forceConsistentCasingInFileNames": true,
11
+ "baseUrl": ".",
12
+ "paths": {
13
+ "@/*": ["src/*"]
14
+ }
11
15
  },
12
16
  "include": [
13
17
  "src/**/*"
@@ -4,28 +4,52 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title><%= projectName %></title>
7
- <style>
8
- body { font-family: sans-serif; padding: 20px; }
9
- h1 { color: #333; }
10
- .status { margin-top: 20px; padding: 10px; background: #e0f7fa; border-left: 5px solid #00acc1; }
11
- </style>
7
+ <link rel="stylesheet" href="/css/style.css">
12
8
  </head>
13
9
  <body>
14
- <h1>Welcome to <%= projectName %></h1>
15
- <p>Architecture: <strong><%= architecture %></strong></p>
16
- <p>Database: <strong><%= database %></strong></p>
17
-
18
- <% if (communication === 'Kafka') { %>
19
- <div class="status">
20
- <h3>Kafka Status</h3>
21
- <p>Connected to Kafka Broker.</p>
22
- <p>Check console for messages.</p>
10
+ <div class="container">
11
+ <header class="header">
12
+ <div class="logo">🚀</div>
13
+ <h1>Welcome to <%= projectName %></h1>
14
+ <p class="subtitle">A production-ready Node.js microservice starter.</p>
15
+ </header>
16
+
17
+ <div class="card-grid">
18
+ <div class="card">
19
+ <h3>Architecture</h3>
20
+ <p><%= architecture %></p>
21
+ </div>
22
+ <div class="card">
23
+ <h3>Database</h3>
24
+ <p><%= database %></p>
25
+ </div>
26
+ <div class="card">
27
+ <h3>Communication</h3>
28
+ <p><%= communication %></p>
29
+ </div>
30
+ </div>
31
+
32
+ <% if (communication === 'Kafka') { %>
33
+ <div class="status-card">
34
+ <div class="status-icon">🔄</div>
35
+ <div class="status-content">
36
+ <h3>Kafka Connected</h3>
37
+ <p>Connection to broker established successfully.</p>
38
+ </div>
39
+ </div>
40
+ <% } else { %>
41
+ <div class="status-card">
42
+ <div class="status-icon">✅</div>
43
+ <div class="status-content">
44
+ <h3>API Active</h3>
45
+ <p>REST API is running and ready to accept requests.</p>
46
+ </div>
47
+ </div>
48
+ <% } %>
49
+
50
+ <footer>
51
+ <p>Generated with ❤️ by Node.js Quickstart Generator</p>
52
+ </footer>
23
53
  </div>
24
- <% } else { %>
25
- <div class="status">
26
- <h3>API Status</h3>
27
- <p>REST API is active.</p>
28
- </div>
29
- <% } %>
30
54
  </body>
31
55
  </html>
@@ -4,23 +4,37 @@ html(lang="en")
4
4
  meta(charset="UTF-8")
5
5
  meta(name="viewport", content="width=device-width, initial-scale=1.0")
6
6
  title= projectName
7
- style.
8
- body { font-family: sans-serif; padding: 20px; }
9
- h1 { color: #333; }
10
- .status { margin-top: 20px; padding: 10px; background: #e0f7fa; border-left: 5px solid #00acc1; }
7
+ link(rel="stylesheet", href="/css/style.css")
11
8
  body
12
- h1 Welcome to #{projectName}
13
- p Architecture:
14
- strong #{architecture}
15
- p Database:
16
- strong #{database}
9
+ .container
10
+ header.header
11
+ .logo 🚀
12
+ h1 Welcome to #{projectName}
13
+ p.subtitle A production-ready Node.js microservice starter.
17
14
 
18
- if communication === 'Kafka'
19
- .status
20
- h3 Kafka Status
21
- p Connected to Kafka Broker.
22
- p Check console for messages.
23
- else
24
- .status
25
- h3 API Status
26
- p REST API is active.
15
+ .card-grid
16
+ .card
17
+ h3 Architecture
18
+ p #{architecture}
19
+ .card
20
+ h3 Database
21
+ p #{database}
22
+ .card
23
+ h3 Communication
24
+ p #{communication}
25
+
26
+ if communication === 'Kafka'
27
+ .status-card
28
+ .status-icon 🔄
29
+ .status-content
30
+ h3 Kafka Connected
31
+ p Connection to broker established successfully.
32
+ else
33
+ .status-card
34
+ .status-icon ✅
35
+ .status-content
36
+ h3 API Active
37
+ p REST API is running and ready to accept requests.
38
+
39
+ footer
40
+ p Generated with ❤️ by Node.js Quickstart Generator
@@ -24,7 +24,7 @@ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
24
24
  const path = require('path');
25
25
  app.set('views', path.join(__dirname, 'views'));
26
26
  app.set('view engine', '<%= viewEngine.toLowerCase() %>');
27
- <% } -%>
27
+ app.use(express.static(path.join(__dirname, '../public')));<% } -%>
28
28
 
29
29
  // Routes
30
30
  <% if (communication === 'REST APIs' || (viewEngine && viewEngine !== 'None')) { -%>
@@ -1,7 +1,7 @@
1
1
  import { Request, Response } from 'express';
2
- import User from '../models/User';
3
- import { HTTP_STATUS } from '../utils/httpCodes';
4
- import logger from '../utils/logger';
2
+ import User from '@/models/User';
3
+ import { HTTP_STATUS } from '@/utils/httpCodes';
4
+ import logger from '@/utils/logger';
5
5
 
6
6
  export class UserController {
7
7
  async getUsers(req: Request, res: Response) {
@@ -4,12 +4,12 @@ import helmet from 'helmet';
4
4
  import hpp from 'hpp';
5
5
  import rateLimit from 'express-rate-limit';
6
6
  import dotenv from 'dotenv';
7
- import logger from './utils/logger';
8
- <% if (communication === 'REST APIs' || (viewEngine && viewEngine !== 'None')) { %>import apiRoutes from './routes/api';<% } -%>
7
+ import logger from '@/utils/logger';
8
+ <% if (communication === 'REST APIs' || (viewEngine && viewEngine !== 'None')) { %>import apiRoutes from '@/routes/api';<% } -%>
9
9
  <% if (communication === 'REST APIs') { %>
10
10
  import swaggerUi from 'swagger-ui-express';
11
- import swaggerSpecs from './config/swagger';<% } -%>
12
- <% if (communication === 'Kafka') { %>import { KafkaService } from './services/kafkaService';<% } -%>
11
+ import swaggerSpecs from '@/config/swagger';<% } -%>
12
+ <% if (communication === 'Kafka') { %>import { KafkaService } from '@/services/kafkaService';<% } -%>
13
13
 
14
14
  dotenv.config();
15
15
 
@@ -34,7 +34,7 @@ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
34
34
  import path from 'path';
35
35
  app.set('views', path.join(__dirname, 'views'));
36
36
  app.set('view engine', '<%= viewEngine.toLowerCase() %>');
37
- <% } -%>
37
+ app.use(express.static(path.join(__dirname, '../public')));<% } -%>
38
38
 
39
39
  // Routes
40
40
  <% if (communication === 'REST APIs' || (viewEngine && viewEngine !== 'None')) { -%>
@@ -60,7 +60,7 @@ const syncDatabase = async () => {
60
60
  let retries = 30;
61
61
  while (retries) {
62
62
  try {
63
- const sequelize = (await import('./config/database')).default;
63
+ const sequelize = (await import('@/config/database')).default;
64
64
  await sequelize.sync();
65
65
  logger.info('Database synced');
66
66
 
@@ -1,5 +1,5 @@
1
1
  import { Router, Request, Response } from 'express';
2
- import { UserController } from '../controllers/userController';
2
+ import { UserController } from '@/controllers/userController';
3
3
 
4
4
  const router = Router();
5
5
  const userController = new UserController();