@dhayalesh/create-ktern-nestjs-clean 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 +102 -0
- package/package.json +15 -0
- package/templates/Dockerfile.template +32 -0
- package/templates/package.json.template +70 -0
- package/templates/project.json.template +23 -0
- package/templates/src/api/app.controller.ts.template +26 -0
- package/templates/src/api/app.module.ts.template +11 -0
- package/templates/src/api/health.controller.ts.template +11 -0
- package/templates/src/api/root.controller.ts.template +11 -0
- package/templates/src/application/.gitkeep +0 -0
- package/templates/src/domain/.gitkeep +0 -0
- package/templates/src/infrastructure/.gitkeep +0 -0
- package/templates/src/main.ts.template +27 -0
- package/templates/tsconfig.json.template +22 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const inquirer = require('inquirer');
|
|
6
|
+
const ejs = require('ejs');
|
|
7
|
+
const { promisify } = require('util');
|
|
8
|
+
|
|
9
|
+
const copyFile = promisify(fs.copyFile);
|
|
10
|
+
const mkdir = promisify(fs.mkdir);
|
|
11
|
+
const readdir = promisify(fs.readdir);
|
|
12
|
+
const stat = promisify(fs.stat);
|
|
13
|
+
const writeFile = promisify(fs.writeFile);
|
|
14
|
+
const readFile = promisify(fs.readFile);
|
|
15
|
+
|
|
16
|
+
async function copyTemplate(src, dest, data) {
|
|
17
|
+
const stats = await stat(src);
|
|
18
|
+
if (stats.isDirectory()) {
|
|
19
|
+
if (!fs.existsSync(dest)) {
|
|
20
|
+
await mkdir(dest, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
const files = await readdir(src);
|
|
23
|
+
for (const file of files) {
|
|
24
|
+
await copyTemplate(path.join(src, file), path.join(dest, file), data);
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
let content = await readFile(src, 'utf8');
|
|
28
|
+
// Simple template replacement if no EJS variables are found or if we want to use EJS
|
|
29
|
+
try {
|
|
30
|
+
content = ejs.render(content, data);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
// If EJS fails (e.g. invalid syntax in non-template files), just use raw content
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Remove .template or .tmpl suffix if present in destination filename
|
|
36
|
+
const finalDest = dest.replace(/\.template$/, '').replace(/\.tmpl$/, '');
|
|
37
|
+
await writeFile(finalDest, content);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function run() {
|
|
42
|
+
console.log('š Welcome to the KTERN Generator!');
|
|
43
|
+
|
|
44
|
+
const questions = [
|
|
45
|
+
{
|
|
46
|
+
type: 'input',
|
|
47
|
+
name: 'name',
|
|
48
|
+
message: 'What is the project name?',
|
|
49
|
+
validate: (input) => input.length > 0 || 'Project name is required',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'input',
|
|
53
|
+
name: 'directory',
|
|
54
|
+
message: 'In which directory should the project be created?',
|
|
55
|
+
default: (answers) => path.join(process.cwd(), answers.name),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'number',
|
|
59
|
+
name: 'port',
|
|
60
|
+
message: 'What port should the service run on?',
|
|
61
|
+
default: 3000,
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const answers = await inquirer.prompt(questions);
|
|
66
|
+
const targetDir = path.resolve(answers.directory);
|
|
67
|
+
|
|
68
|
+
if (fs.existsSync(targetDir)) {
|
|
69
|
+
const { overwrite } = await inquirer.prompt([
|
|
70
|
+
{
|
|
71
|
+
type: 'confirm',
|
|
72
|
+
name: 'overwrite',
|
|
73
|
+
message: `Directory ${targetDir} already exists. Overwrite?`,
|
|
74
|
+
default: false,
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
if (!overwrite) {
|
|
78
|
+
console.log('Exiting...');
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(`\nCreating project "${answers.name}" in ${targetDir}...`);
|
|
84
|
+
|
|
85
|
+
const templateDir = path.join(__dirname, '../templates');
|
|
86
|
+
await copyTemplate(templateDir, targetDir, {
|
|
87
|
+
name: answers.name,
|
|
88
|
+
port: answers.port,
|
|
89
|
+
// Add more default variables if needed
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
console.log('\nā
Project created successfully!');
|
|
93
|
+
console.log(`\nNext steps:`);
|
|
94
|
+
console.log(` cd ${targetDir}`);
|
|
95
|
+
console.log(` npm install`);
|
|
96
|
+
console.log(` npm start`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
run().catch((err) => {
|
|
100
|
+
console.error('ā Error:', err.message);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dhayalesh/create-ktern-nestjs-clean",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Standalone KTERN clean generator",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-ktern-nestjs-clean": "./bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"inquirer": "^8.2.4",
|
|
10
|
+
"ejs": "^3.1.8"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "restricted"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
FROM node:20-alpine AS builder
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Install pnpm
|
|
6
|
+
RUN npm install -g pnpm
|
|
7
|
+
|
|
8
|
+
# Copy files
|
|
9
|
+
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml ./
|
|
10
|
+
COPY packages/<%= name %> packages/<%= name %>
|
|
11
|
+
COPY tsconfig.base.json ./
|
|
12
|
+
COPY nx.json ./
|
|
13
|
+
|
|
14
|
+
# Install dependencies (frozen-lockfile for consistency)
|
|
15
|
+
RUN pnpm install --frozen-lockfile
|
|
16
|
+
|
|
17
|
+
# Build the project
|
|
18
|
+
RUN pnpm nx build <%= name %>
|
|
19
|
+
|
|
20
|
+
# Production stage
|
|
21
|
+
FROM node:20-alpine
|
|
22
|
+
|
|
23
|
+
WORKDIR /app
|
|
24
|
+
|
|
25
|
+
# Copy built application and node_modules
|
|
26
|
+
COPY --from=builder /app/dist/packages/<%= name %> ./dist
|
|
27
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
28
|
+
COPY --from=builder /app/packages/<%= name %>/package.json ./package.json
|
|
29
|
+
|
|
30
|
+
EXPOSE <%= port %>
|
|
31
|
+
|
|
32
|
+
CMD ["node", "dist/src/main"]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= name %>",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"author": "",
|
|
6
|
+
"private": true,
|
|
7
|
+
"license": "UNLICENSED",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "nest build",
|
|
10
|
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
11
|
+
"start": "nest start",
|
|
12
|
+
"start:dev": "nest start --watch",
|
|
13
|
+
"start:debug": "nest start --debug --watch",
|
|
14
|
+
"start:prod": "node dist/packages/<%= name %>/src/main",
|
|
15
|
+
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
|
16
|
+
"test": "jest",
|
|
17
|
+
"test:watch": "jest --watch",
|
|
18
|
+
"test:cov": "jest --coverage",
|
|
19
|
+
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
|
20
|
+
"test:e2e": "jest --config ./test/jest-e2e.json"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@nestjs/common": "^10.0.0",
|
|
24
|
+
"@nestjs/core": "^10.0.0",
|
|
25
|
+
"@nestjs/platform-express": "^10.0.0",
|
|
26
|
+
"@nestjs/swagger": "^7.1.0",
|
|
27
|
+
"reflect-metadata": "^0.1.13",
|
|
28
|
+
"rxjs": "^7.8.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@nestjs/cli": "^10.0.0",
|
|
32
|
+
"@nestjs/schematics": "^10.0.0",
|
|
33
|
+
"@nestjs/testing": "^10.0.0",
|
|
34
|
+
"@types/express": "^4.17.17",
|
|
35
|
+
"@types/jest": "^29.5.2",
|
|
36
|
+
"@types/node": "^20.3.1",
|
|
37
|
+
"@types/supertest": "^2.0.12",
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
39
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
40
|
+
"eslint": "^8.42.0",
|
|
41
|
+
"eslint-config-prettier": "^9.0.0",
|
|
42
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
43
|
+
"jest": "^29.5.0",
|
|
44
|
+
"prettier": "^3.0.0",
|
|
45
|
+
"source-map-support": "^0.5.21",
|
|
46
|
+
"supertest": "^6.3.3",
|
|
47
|
+
"ts-jest": "^29.1.0",
|
|
48
|
+
"ts-loader": "^9.4.3",
|
|
49
|
+
"ts-node": "^10.9.1",
|
|
50
|
+
"tsconfig-paths": "^4.2.0",
|
|
51
|
+
"typescript": "^5.1.3"
|
|
52
|
+
},
|
|
53
|
+
"jest": {
|
|
54
|
+
"moduleFileExtensions": [
|
|
55
|
+
"js",
|
|
56
|
+
"json",
|
|
57
|
+
"ts"
|
|
58
|
+
],
|
|
59
|
+
"rootDir": "src",
|
|
60
|
+
"testRegex": ".*\\.spec\\.ts$",
|
|
61
|
+
"transform": {
|
|
62
|
+
"^.+\\.(t|j)s$": "ts-jest"
|
|
63
|
+
},
|
|
64
|
+
"collectCoverageFrom": [
|
|
65
|
+
"**/*.(t|j)s"
|
|
66
|
+
],
|
|
67
|
+
"coverageDirectory": "../coverage",
|
|
68
|
+
"testEnvironment": "node"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= name %>",
|
|
3
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"projectType": "application",
|
|
5
|
+
"sourceRoot": "<%= projectRoot %>/src",
|
|
6
|
+
"targets": {
|
|
7
|
+
"build": {
|
|
8
|
+
"executor": "@nx/js:tsc",
|
|
9
|
+
"options": {
|
|
10
|
+
"outputPath": "dist/<%= name %>",
|
|
11
|
+
"main": "<%= projectRoot %>/src/main.ts",
|
|
12
|
+
"tsConfig": "<%= projectRoot %>/tsconfig.json",
|
|
13
|
+
"assets": ["<%= projectRoot %>/src/assets"]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"serve": {
|
|
17
|
+
"executor": "@nx/js:node",
|
|
18
|
+
"options": {
|
|
19
|
+
"buildTarget": "<%= name %>:build"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Controller, Get, Post, Put, Delete } from '@nestjs/common';
|
|
2
|
+
import { ApiTags } from '@nestjs/swagger';
|
|
3
|
+
|
|
4
|
+
@ApiTags('<%= name %>')
|
|
5
|
+
@Controller('<%= name %>')
|
|
6
|
+
export class AppController {
|
|
7
|
+
@Get()
|
|
8
|
+
getData(): string {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@Post()
|
|
13
|
+
createData(): string {
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@Put()
|
|
18
|
+
updateData(): string {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Delete()
|
|
23
|
+
deleteData(): string {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { AppController } from './app.controller';
|
|
3
|
+
import { HealthController } from './health.controller';
|
|
4
|
+
import { RootController } from './root.controller';
|
|
5
|
+
|
|
6
|
+
@Module({
|
|
7
|
+
imports: [],
|
|
8
|
+
controllers: [AppController, HealthController, RootController],
|
|
9
|
+
providers: [],
|
|
10
|
+
})
|
|
11
|
+
export class AppModule {}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Controller, Get } from '@nestjs/common';
|
|
2
|
+
import { ApiTags } from '@nestjs/swagger';
|
|
3
|
+
|
|
4
|
+
@ApiTags('root')
|
|
5
|
+
@Controller('')
|
|
6
|
+
export class RootController {
|
|
7
|
+
@Get()
|
|
8
|
+
getRoot(): string {
|
|
9
|
+
return '<%= name %> is running on port <%= port %>';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { NestFactory } from '@nestjs/core';
|
|
2
|
+
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
3
|
+
import { AppModule } from './api/app.module';
|
|
4
|
+
|
|
5
|
+
async function bootstrap() {
|
|
6
|
+
const app = await NestFactory.create(AppModule);
|
|
7
|
+
|
|
8
|
+
// Set global prefix with versioning, excluding operational endpoints
|
|
9
|
+
app.setGlobalPrefix('v1/api', {
|
|
10
|
+
exclude: ['health', '/'],
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// Swagger Configuration
|
|
14
|
+
const config = new DocumentBuilder()
|
|
15
|
+
.setTitle('<%= name %>')
|
|
16
|
+
.setDescription('The <%= name %> API description')
|
|
17
|
+
.setVersion('1.0')
|
|
18
|
+
.build();
|
|
19
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
20
|
+
|
|
21
|
+
SwaggerModule.setup('api/docs', app, document);
|
|
22
|
+
|
|
23
|
+
await app.listen(<%= port %>, '0.0.0.0');
|
|
24
|
+
console.log(`Application is running on: http://localhost:<%= port %>`);
|
|
25
|
+
console.log(`Swagger documentation: http://localhost:<%= port %>/api/docs`);
|
|
26
|
+
}
|
|
27
|
+
bootstrap();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"removeComments": true,
|
|
6
|
+
"emitDecoratorMetadata": true,
|
|
7
|
+
"experimentalDecorators": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"target": "es2021",
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"outDir": "../../dist/packages/<%= name %>",
|
|
12
|
+
"baseUrl": "./",
|
|
13
|
+
"incremental": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"strictNullChecks": false,
|
|
16
|
+
"noImplicitAny": false,
|
|
17
|
+
"strictBindCallApply": false,
|
|
18
|
+
"forceConsistentCasingInFileNames": false,
|
|
19
|
+
"noFallthroughCasesInSwitch": false
|
|
20
|
+
},
|
|
21
|
+
"include": ["src/**/*"]
|
|
22
|
+
}
|