@hazeljs/cli 0.2.0-alpha.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/@template/.eslintrc.js +36 -0
- package/@template/.lintstagedrc +6 -0
- package/@template/README.md +61 -0
- package/@template/jest.config.js +28 -0
- package/@template/jest.setup.js +81 -0
- package/@template/package.json +34 -0
- package/@template/src/app.module.ts +7 -0
- package/@template/src/hello.controller.ts +9 -0
- package/@template/src/index.ts +18 -0
- package/@template/tsconfig.json +25 -0
- package/LICENSE +192 -0
- package/README.md +683 -0
- package/dist/commands/add.d.ts +2 -0
- package/dist/commands/add.js +176 -0
- package/dist/commands/add.test.d.ts +1 -0
- package/dist/commands/add.test.js +51 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.js +52 -0
- package/dist/commands/generate-agent.d.ts +2 -0
- package/dist/commands/generate-agent.js +58 -0
- package/dist/commands/generate-agent.test.d.ts +1 -0
- package/dist/commands/generate-agent.test.js +36 -0
- package/dist/commands/generate-ai-service.d.ts +2 -0
- package/dist/commands/generate-ai-service.js +48 -0
- package/dist/commands/generate-ai-service.test.d.ts +1 -0
- package/dist/commands/generate-ai-service.test.js +41 -0
- package/dist/commands/generate-app.d.ts +2 -0
- package/dist/commands/generate-app.js +448 -0
- package/dist/commands/generate-auth.d.ts +2 -0
- package/dist/commands/generate-auth.js +194 -0
- package/dist/commands/generate-auth.test.d.ts +1 -0
- package/dist/commands/generate-auth.test.js +48 -0
- package/dist/commands/generate-cache.d.ts +2 -0
- package/dist/commands/generate-cache.js +64 -0
- package/dist/commands/generate-cache.test.d.ts +1 -0
- package/dist/commands/generate-cache.test.js +36 -0
- package/dist/commands/generate-config.d.ts +2 -0
- package/dist/commands/generate-config.js +57 -0
- package/dist/commands/generate-config.test.d.ts +1 -0
- package/dist/commands/generate-config.test.js +35 -0
- package/dist/commands/generate-controller.d.ts +2 -0
- package/dist/commands/generate-controller.js +60 -0
- package/dist/commands/generate-controller.test.d.ts +1 -0
- package/dist/commands/generate-controller.test.js +48 -0
- package/dist/commands/generate-cron.d.ts +2 -0
- package/dist/commands/generate-cron.js +59 -0
- package/dist/commands/generate-cron.test.d.ts +1 -0
- package/dist/commands/generate-cron.test.js +42 -0
- package/dist/commands/generate-crud.d.ts +2 -0
- package/dist/commands/generate-crud.js +177 -0
- package/dist/commands/generate-crud.test.d.ts +1 -0
- package/dist/commands/generate-crud.test.js +52 -0
- package/dist/commands/generate-discovery.d.ts +2 -0
- package/dist/commands/generate-discovery.js +61 -0
- package/dist/commands/generate-discovery.test.d.ts +1 -0
- package/dist/commands/generate-discovery.test.js +36 -0
- package/dist/commands/generate-dto.d.ts +2 -0
- package/dist/commands/generate-dto.js +69 -0
- package/dist/commands/generate-dto.test.d.ts +1 -0
- package/dist/commands/generate-dto.test.js +44 -0
- package/dist/commands/generate-exception-filter.d.ts +2 -0
- package/dist/commands/generate-exception-filter.js +48 -0
- package/dist/commands/generate-exception-filter.test.d.ts +1 -0
- package/dist/commands/generate-exception-filter.test.js +43 -0
- package/dist/commands/generate-guard.d.ts +2 -0
- package/dist/commands/generate-guard.js +36 -0
- package/dist/commands/generate-guard.test.d.ts +1 -0
- package/dist/commands/generate-guard.test.js +41 -0
- package/dist/commands/generate-interceptor.d.ts +2 -0
- package/dist/commands/generate-interceptor.js +37 -0
- package/dist/commands/generate-interceptor.test.d.ts +1 -0
- package/dist/commands/generate-interceptor.test.js +41 -0
- package/dist/commands/generate-middleware.d.ts +2 -0
- package/dist/commands/generate-middleware.js +46 -0
- package/dist/commands/generate-middleware.test.d.ts +1 -0
- package/dist/commands/generate-middleware.test.js +41 -0
- package/dist/commands/generate-module.d.ts +2 -0
- package/dist/commands/generate-module.js +106 -0
- package/dist/commands/generate-module.test.d.ts +1 -0
- package/dist/commands/generate-module.test.js +54 -0
- package/dist/commands/generate-pipe.d.ts +2 -0
- package/dist/commands/generate-pipe.js +33 -0
- package/dist/commands/generate-pipe.test.d.ts +1 -0
- package/dist/commands/generate-pipe.test.js +35 -0
- package/dist/commands/generate-rag.d.ts +2 -0
- package/dist/commands/generate-rag.js +66 -0
- package/dist/commands/generate-rag.test.d.ts +1 -0
- package/dist/commands/generate-rag.test.js +35 -0
- package/dist/commands/generate-repository.d.ts +2 -0
- package/dist/commands/generate-repository.js +40 -0
- package/dist/commands/generate-repository.test.d.ts +1 -0
- package/dist/commands/generate-repository.test.js +41 -0
- package/dist/commands/generate-serverless-handler.d.ts +2 -0
- package/dist/commands/generate-serverless-handler.js +46 -0
- package/dist/commands/generate-serverless-handler.test.d.ts +1 -0
- package/dist/commands/generate-serverless-handler.test.js +42 -0
- package/dist/commands/generate-service.d.ts +2 -0
- package/dist/commands/generate-service.js +52 -0
- package/dist/commands/generate-service.test.d.ts +1 -0
- package/dist/commands/generate-service.test.js +41 -0
- package/dist/commands/generate-websocket-gateway.d.ts +2 -0
- package/dist/commands/generate-websocket-gateway.js +47 -0
- package/dist/commands/generate-websocket-gateway.test.d.ts +1 -0
- package/dist/commands/generate-websocket-gateway.test.js +41 -0
- package/dist/commands/info.d.ts +2 -0
- package/dist/commands/info.js +91 -0
- package/dist/commands/pdf-to-audio.d.ts +2 -0
- package/dist/commands/pdf-to-audio.js +170 -0
- package/dist/commands/start.d.ts +2 -0
- package/dist/commands/start.js +61 -0
- package/dist/commands/test.d.ts +2 -0
- package/dist/commands/test.js +63 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +75 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +93 -0
- package/dist/utils/generator.d.ts +25 -0
- package/dist/utils/generator.js +107 -0
- package/dist/utils/generator.test.d.ts +1 -0
- package/dist/utils/generator.test.js +137 -0
- package/package.json +73 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const generate_auth_1 = require("./generate-auth");
|
|
9
|
+
jest.mock('fs');
|
|
10
|
+
describe('generateAuth', () => {
|
|
11
|
+
const mockFs = fs_1.default;
|
|
12
|
+
let program;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
mockFs.existsSync.mockReturnValue(false);
|
|
16
|
+
mockFs.mkdirSync.mockImplementation(() => undefined);
|
|
17
|
+
mockFs.writeFileSync.mockImplementation(() => { });
|
|
18
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
19
|
+
program = new commander_1.Command();
|
|
20
|
+
});
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
jest.restoreAllMocks();
|
|
23
|
+
});
|
|
24
|
+
it('should generate all auth files', async () => {
|
|
25
|
+
(0, generate_auth_1.generateAuth)(program);
|
|
26
|
+
await program.parseAsync(['node', 'test', 'auth']);
|
|
27
|
+
const writtenFiles = mockFs.writeFileSync.mock.calls.map(call => call[0]);
|
|
28
|
+
expect(writtenFiles.some(f => f.includes('auth.module.ts'))).toBe(true);
|
|
29
|
+
expect(writtenFiles.some(f => f.includes('auth.service.ts'))).toBe(true);
|
|
30
|
+
expect(writtenFiles.some(f => f.includes('auth.controller.ts'))).toBe(true);
|
|
31
|
+
expect(writtenFiles.some(f => f.includes('jwt-auth.guard.ts'))).toBe(true);
|
|
32
|
+
expect(writtenFiles.some(f => f.includes('register.dto.ts'))).toBe(true);
|
|
33
|
+
expect(writtenFiles.some(f => f.includes('login.dto.ts'))).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
it('should use @hazeljs/auth imports in guard', async () => {
|
|
36
|
+
(0, generate_auth_1.generateAuth)(program);
|
|
37
|
+
await program.parseAsync(['node', 'test', 'auth']);
|
|
38
|
+
const guardContent = mockFs.writeFileSync.mock.calls
|
|
39
|
+
.find(call => call[0].includes('jwt-auth.guard'))?.[1];
|
|
40
|
+
expect(guardContent).toContain("from '@hazeljs/auth'");
|
|
41
|
+
expect(guardContent).toContain('JwtService');
|
|
42
|
+
});
|
|
43
|
+
it('should support --dry-run flag', async () => {
|
|
44
|
+
(0, generate_auth_1.generateAuth)(program);
|
|
45
|
+
await program.parseAsync(['node', 'test', 'auth', '--dry-run']);
|
|
46
|
+
expect(mockFs.writeFileSync).not.toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateCache = generateCache;
|
|
7
|
+
const generator_1 = require("../utils/generator");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const CACHE_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
|
|
10
|
+
import { CacheService, Cacheable, CacheEvict } from '@hazeljs/cache';
|
|
11
|
+
|
|
12
|
+
@Service()
|
|
13
|
+
export class {{className}}CacheService {
|
|
14
|
+
constructor(private readonly cacheService: CacheService) {}
|
|
15
|
+
|
|
16
|
+
@Cacheable({ key: '{{fileName}}:all', ttl: 60 })
|
|
17
|
+
async findAll() {
|
|
18
|
+
// This result will be cached for 60 seconds
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Cacheable({ key: '{{fileName}}:{{=<% %>=}}#{id}<%={{ }}=%>', ttl: 300 })
|
|
23
|
+
async findOne(id: string) {
|
|
24
|
+
// This result will be cached for 5 minutes
|
|
25
|
+
return { id };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@CacheEvict({ key: '{{fileName}}:all' })
|
|
29
|
+
async create(data: any) {
|
|
30
|
+
// Creating a new item evicts the list cache
|
|
31
|
+
return data;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async clearAll() {
|
|
35
|
+
await this.cacheService.clear();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
class CacheServiceGenerator extends generator_1.Generator {
|
|
40
|
+
constructor() {
|
|
41
|
+
super(...arguments);
|
|
42
|
+
this.suffix = 'cache';
|
|
43
|
+
}
|
|
44
|
+
getDefaultTemplate() {
|
|
45
|
+
return CACHE_SERVICE_TEMPLATE;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function generateCache(command) {
|
|
49
|
+
command
|
|
50
|
+
.command('cache <name>')
|
|
51
|
+
.description('Generate a cache service with decorators')
|
|
52
|
+
.option('-p, --path <path>', 'Specify the path')
|
|
53
|
+
.option('--dry-run', 'Preview files without writing them')
|
|
54
|
+
.action(async (name, options) => {
|
|
55
|
+
const generator = new CacheServiceGenerator();
|
|
56
|
+
await generator.generate({ name, path: options.path, dryRun: options.dryRun });
|
|
57
|
+
if (!options.dryRun) {
|
|
58
|
+
console.log(chalk_1.default.gray('\nNext steps:'));
|
|
59
|
+
console.log(chalk_1.default.gray(' 1. npm install @hazeljs/cache'));
|
|
60
|
+
console.log(chalk_1.default.gray(' 2. Add CacheModule to your module imports'));
|
|
61
|
+
console.log(chalk_1.default.gray(' 3. Configure the cache strategy (memory, redis, or multi-tier)'));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const generate_cache_1 = require("./generate-cache");
|
|
9
|
+
jest.mock('fs');
|
|
10
|
+
describe('generateCache', () => {
|
|
11
|
+
const mockFs = fs_1.default;
|
|
12
|
+
let program;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
16
|
+
mockFs.writeFileSync.mockImplementation(() => { });
|
|
17
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
18
|
+
program = new commander_1.Command();
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
jest.restoreAllMocks();
|
|
22
|
+
});
|
|
23
|
+
it('should generate a cache service file', async () => {
|
|
24
|
+
(0, generate_cache_1.generateCache)(program);
|
|
25
|
+
await program.parseAsync(['node', 'test', 'cache', 'product']);
|
|
26
|
+
expect(mockFs.writeFileSync).toHaveBeenCalledWith(expect.stringContaining('product.cache.ts'), expect.stringContaining('ProductCacheService'));
|
|
27
|
+
});
|
|
28
|
+
it('should import from @hazeljs/cache', async () => {
|
|
29
|
+
(0, generate_cache_1.generateCache)(program);
|
|
30
|
+
await program.parseAsync(['node', 'test', 'cache', 'product']);
|
|
31
|
+
const writtenContent = mockFs.writeFileSync.mock.calls[0][1];
|
|
32
|
+
expect(writtenContent).toContain("from '@hazeljs/cache'");
|
|
33
|
+
expect(writtenContent).toContain('@Cacheable');
|
|
34
|
+
expect(writtenContent).toContain('@CacheEvict');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateConfig = generateConfig;
|
|
7
|
+
const generator_1 = require("../utils/generator");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const CONFIG_TEMPLATE = `import { HazelModule } from '@hazeljs/core';
|
|
10
|
+
import { ConfigModule, ConfigService } from '@hazeljs/config';
|
|
11
|
+
|
|
12
|
+
// Import ConfigModule.forRoot() in your app module:
|
|
13
|
+
//
|
|
14
|
+
// @HazelModule({
|
|
15
|
+
// imports: [
|
|
16
|
+
// ConfigModule.forRoot({
|
|
17
|
+
// envFilePath: '.env',
|
|
18
|
+
// }),
|
|
19
|
+
// ],
|
|
20
|
+
// })
|
|
21
|
+
//
|
|
22
|
+
// Then inject ConfigService wherever you need it:
|
|
23
|
+
//
|
|
24
|
+
// constructor(private readonly config: ConfigService) {}
|
|
25
|
+
//
|
|
26
|
+
// Usage:
|
|
27
|
+
// this.config.get('DATABASE_URL');
|
|
28
|
+
// this.config.get('PORT', '3000'); // with default value
|
|
29
|
+
|
|
30
|
+
export { ConfigModule, ConfigService };
|
|
31
|
+
`;
|
|
32
|
+
class ConfigGenerator extends generator_1.Generator {
|
|
33
|
+
constructor() {
|
|
34
|
+
super(...arguments);
|
|
35
|
+
this.suffix = 'config';
|
|
36
|
+
}
|
|
37
|
+
getDefaultTemplate() {
|
|
38
|
+
return CONFIG_TEMPLATE;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function generateConfig(command) {
|
|
42
|
+
command
|
|
43
|
+
.command('config')
|
|
44
|
+
.description('Generate a config module setup')
|
|
45
|
+
.option('-p, --path <path>', 'Specify the path', 'src')
|
|
46
|
+
.option('--dry-run', 'Preview files without writing them')
|
|
47
|
+
.action(async (options) => {
|
|
48
|
+
const generator = new ConfigGenerator();
|
|
49
|
+
await generator.generate({ name: 'app', path: options.path, dryRun: options.dryRun });
|
|
50
|
+
if (!options.dryRun) {
|
|
51
|
+
console.log(chalk_1.default.gray('\nNext steps:'));
|
|
52
|
+
console.log(chalk_1.default.gray(' 1. npm install @hazeljs/config'));
|
|
53
|
+
console.log(chalk_1.default.gray(' 2. Add ConfigModule.forRoot({ envFilePath: ".env" }) to your app module imports'));
|
|
54
|
+
console.log(chalk_1.default.gray(' 3. Create a .env file in your project root'));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const generate_config_1 = require("./generate-config");
|
|
9
|
+
jest.mock('fs');
|
|
10
|
+
describe('generateConfig', () => {
|
|
11
|
+
const mockFs = fs_1.default;
|
|
12
|
+
let program;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
16
|
+
mockFs.writeFileSync.mockImplementation(() => { });
|
|
17
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
18
|
+
program = new commander_1.Command();
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
jest.restoreAllMocks();
|
|
22
|
+
});
|
|
23
|
+
it('should generate a config module file', async () => {
|
|
24
|
+
(0, generate_config_1.generateConfig)(program);
|
|
25
|
+
await program.parseAsync(['node', 'test', 'config']);
|
|
26
|
+
expect(mockFs.writeFileSync).toHaveBeenCalledWith(expect.stringContaining('app.config.ts'), expect.stringContaining('ConfigModule'));
|
|
27
|
+
});
|
|
28
|
+
it('should import from @hazeljs/config', async () => {
|
|
29
|
+
(0, generate_config_1.generateConfig)(program);
|
|
30
|
+
await program.parseAsync(['node', 'test', 'config']);
|
|
31
|
+
const writtenContent = mockFs.writeFileSync.mock.calls[0][1];
|
|
32
|
+
expect(writtenContent).toContain("from '@hazeljs/config'");
|
|
33
|
+
expect(writtenContent).toContain('ConfigService');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateController = generateController;
|
|
4
|
+
const generator_1 = require("../utils/generator");
|
|
5
|
+
const CONTROLLER_TEMPLATE = `import { Controller, Get, Post, Body, Param, Delete, Put } from '@hazeljs/core';
|
|
6
|
+
import { {{className}}Service } from './{{fileName}}.service';
|
|
7
|
+
import { Create{{className}}Dto } from './dto/create-{{fileName}}.dto';
|
|
8
|
+
import { Update{{className}}Dto } from './dto/update-{{fileName}}.dto';
|
|
9
|
+
|
|
10
|
+
@Controller('{{fileName}}')
|
|
11
|
+
export class {{className}}Controller {
|
|
12
|
+
constructor(private readonly {{camelName}}Service: {{className}}Service) {}
|
|
13
|
+
|
|
14
|
+
@Get()
|
|
15
|
+
findAll() {
|
|
16
|
+
return this.{{camelName}}Service.findAll();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Get(':id')
|
|
20
|
+
findOne(@Param('id') id: string) {
|
|
21
|
+
return this.{{camelName}}Service.findOne(id);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Post()
|
|
25
|
+
create(@Body(Create{{className}}Dto) createDto: Create{{className}}Dto) {
|
|
26
|
+
return this.{{camelName}}Service.create(createDto);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Put(':id')
|
|
30
|
+
update(@Param('id') id: string, @Body(Update{{className}}Dto) updateDto: Update{{className}}Dto) {
|
|
31
|
+
return this.{{camelName}}Service.update(id, updateDto);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Delete(':id')
|
|
35
|
+
remove(@Param('id') id: string) {
|
|
36
|
+
return this.{{camelName}}Service.remove(id);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
class ControllerGenerator extends generator_1.Generator {
|
|
41
|
+
constructor() {
|
|
42
|
+
super(...arguments);
|
|
43
|
+
this.suffix = 'controller';
|
|
44
|
+
}
|
|
45
|
+
getDefaultTemplate() {
|
|
46
|
+
return CONTROLLER_TEMPLATE;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function generateController(program) {
|
|
50
|
+
program
|
|
51
|
+
.command('controller <name>')
|
|
52
|
+
.description('Generate a new controller')
|
|
53
|
+
.alias('c')
|
|
54
|
+
.option('-p, --path <path>', 'Path where the controller should be generated')
|
|
55
|
+
.option('--dry-run', 'Preview files without writing them')
|
|
56
|
+
.action(async (name, options) => {
|
|
57
|
+
const generator = new ControllerGenerator();
|
|
58
|
+
await generator.generate({ name, path: options.path, dryRun: options.dryRun });
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const generate_controller_1 = require("./generate-controller");
|
|
9
|
+
jest.mock('fs');
|
|
10
|
+
describe('generateController', () => {
|
|
11
|
+
const mockFs = fs_1.default;
|
|
12
|
+
let program;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
16
|
+
mockFs.writeFileSync.mockImplementation(() => { });
|
|
17
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
18
|
+
program = new commander_1.Command();
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
jest.restoreAllMocks();
|
|
22
|
+
});
|
|
23
|
+
it('should register the controller command with alias', () => {
|
|
24
|
+
(0, generate_controller_1.generateController)(program);
|
|
25
|
+
const cmd = program.commands.find(c => c.name() === 'controller');
|
|
26
|
+
expect(cmd).toBeDefined();
|
|
27
|
+
expect(cmd?.alias()).toBe('c');
|
|
28
|
+
});
|
|
29
|
+
it('should generate a controller file with correct suffix', async () => {
|
|
30
|
+
(0, generate_controller_1.generateController)(program);
|
|
31
|
+
await program.parseAsync(['node', 'test', 'controller', 'user']);
|
|
32
|
+
expect(mockFs.writeFileSync).toHaveBeenCalledWith(expect.stringContaining('user.controller.ts'), expect.stringContaining('UserController'));
|
|
33
|
+
});
|
|
34
|
+
it('should include proper imports from @hazeljs/core', async () => {
|
|
35
|
+
(0, generate_controller_1.generateController)(program);
|
|
36
|
+
await program.parseAsync(['node', 'test', 'controller', 'user']);
|
|
37
|
+
const writtenContent = mockFs.writeFileSync.mock.calls[0][1];
|
|
38
|
+
expect(writtenContent).toContain("from '@hazeljs/core'");
|
|
39
|
+
expect(writtenContent).toContain('Controller');
|
|
40
|
+
expect(writtenContent).toContain('Get');
|
|
41
|
+
expect(writtenContent).toContain('Post');
|
|
42
|
+
});
|
|
43
|
+
it('should support --dry-run flag', async () => {
|
|
44
|
+
(0, generate_controller_1.generateController)(program);
|
|
45
|
+
await program.parseAsync(['node', 'test', 'controller', 'user', '--dry-run']);
|
|
46
|
+
expect(mockFs.writeFileSync).not.toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateCron = generateCron;
|
|
7
|
+
const generator_1 = require("../utils/generator");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const CRON_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
|
|
10
|
+
import { Cron, CronExpression } from '@hazeljs/cron';
|
|
11
|
+
|
|
12
|
+
@Service()
|
|
13
|
+
export class {{className}}CronService {
|
|
14
|
+
@Cron(CronExpression.EVERY_MINUTE)
|
|
15
|
+
handleEveryMinute() {
|
|
16
|
+
console.log('[{{className}}Cron] Running every minute...');
|
|
17
|
+
// Add your cron job logic here
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Cron('0 0 * * *') // Every day at midnight
|
|
21
|
+
handleDaily() {
|
|
22
|
+
console.log('[{{className}}Cron] Running daily task...');
|
|
23
|
+
// Add your daily task logic here
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Cron(CronExpression.EVERY_HOUR)
|
|
27
|
+
handleHourly() {
|
|
28
|
+
console.log('[{{className}}Cron] Running hourly cleanup...');
|
|
29
|
+
// Add your hourly task logic here
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
class CronServiceGenerator extends generator_1.Generator {
|
|
34
|
+
constructor() {
|
|
35
|
+
super(...arguments);
|
|
36
|
+
this.suffix = 'cron';
|
|
37
|
+
}
|
|
38
|
+
getDefaultTemplate() {
|
|
39
|
+
return CRON_SERVICE_TEMPLATE;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function generateCron(command) {
|
|
43
|
+
command
|
|
44
|
+
.command('cron <name>')
|
|
45
|
+
.description('Generate a cron/scheduled job service')
|
|
46
|
+
.alias('job')
|
|
47
|
+
.option('-p, --path <path>', 'Specify the path')
|
|
48
|
+
.option('--dry-run', 'Preview files without writing them')
|
|
49
|
+
.action(async (name, options) => {
|
|
50
|
+
const generator = new CronServiceGenerator();
|
|
51
|
+
await generator.generate({ name, path: options.path, dryRun: options.dryRun });
|
|
52
|
+
if (!options.dryRun) {
|
|
53
|
+
console.log(chalk_1.default.gray('\nNext steps:'));
|
|
54
|
+
console.log(chalk_1.default.gray(' 1. npm install @hazeljs/cron'));
|
|
55
|
+
console.log(chalk_1.default.gray(' 2. Add CronModule to your module imports'));
|
|
56
|
+
console.log(chalk_1.default.gray(' 3. Register this service as a provider'));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const generate_cron_1 = require("./generate-cron");
|
|
9
|
+
jest.mock('fs');
|
|
10
|
+
describe('generateCron', () => {
|
|
11
|
+
const mockFs = fs_1.default;
|
|
12
|
+
let program;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
16
|
+
mockFs.writeFileSync.mockImplementation(() => { });
|
|
17
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
18
|
+
program = new commander_1.Command();
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
jest.restoreAllMocks();
|
|
22
|
+
});
|
|
23
|
+
it('should register the cron command with alias', () => {
|
|
24
|
+
(0, generate_cron_1.generateCron)(program);
|
|
25
|
+
const cmd = program.commands.find(c => c.name() === 'cron');
|
|
26
|
+
expect(cmd).toBeDefined();
|
|
27
|
+
expect(cmd?.alias()).toBe('job');
|
|
28
|
+
});
|
|
29
|
+
it('should generate a cron service file', async () => {
|
|
30
|
+
(0, generate_cron_1.generateCron)(program);
|
|
31
|
+
await program.parseAsync(['node', 'test', 'cron', 'cleanup']);
|
|
32
|
+
expect(mockFs.writeFileSync).toHaveBeenCalledWith(expect.stringContaining('cleanup.cron.ts'), expect.stringContaining('CleanupCronService'));
|
|
33
|
+
});
|
|
34
|
+
it('should import from @hazeljs/cron', async () => {
|
|
35
|
+
(0, generate_cron_1.generateCron)(program);
|
|
36
|
+
await program.parseAsync(['node', 'test', 'cron', 'cleanup']);
|
|
37
|
+
const writtenContent = mockFs.writeFileSync.mock.calls[0][1];
|
|
38
|
+
expect(writtenContent).toContain("from '@hazeljs/cron'");
|
|
39
|
+
expect(writtenContent).toContain('@Cron');
|
|
40
|
+
expect(writtenContent).toContain('CronExpression');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateCrud = generateCrud;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const generator_1 = require("../utils/generator");
|
|
11
|
+
const controllerTemplate = `import { Controller, Get, Post, Put, Delete, Body, Param } from '@hazeljs/core';
|
|
12
|
+
import { {{className}}Service } from './{{fileName}}.service';
|
|
13
|
+
import { Create{{className}}Dto, Update{{className}}Dto } from './dto/{{fileName}}.dto';
|
|
14
|
+
|
|
15
|
+
@Controller('/{{{routePath}}}')
|
|
16
|
+
export class {{className}}Controller {
|
|
17
|
+
constructor(private {{camelName}}Service: {{className}}Service) {}
|
|
18
|
+
|
|
19
|
+
@Get()
|
|
20
|
+
findAll() {
|
|
21
|
+
return this.{{camelName}}Service.findAll();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Get('/:id')
|
|
25
|
+
findOne(@Param('id') id: string) {
|
|
26
|
+
return this.{{camelName}}Service.findOne(id);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Post()
|
|
30
|
+
create(@Body() createDto: Create{{className}}Dto) {
|
|
31
|
+
return this.{{camelName}}Service.create(createDto);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Put('/:id')
|
|
35
|
+
update(@Param('id') id: string, @Body() updateDto: Update{{className}}Dto) {
|
|
36
|
+
return this.{{camelName}}Service.update(id, updateDto);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Delete('/:id')
|
|
40
|
+
delete(@Param('id') id: string) {
|
|
41
|
+
return this.{{camelName}}Service.delete(id);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
const serviceTemplate = `import { Service } from '@hazeljs/core';
|
|
46
|
+
import { Create{{className}}Dto, Update{{className}}Dto } from './dto/{{fileName}}.dto';
|
|
47
|
+
|
|
48
|
+
@Service()
|
|
49
|
+
export class {{className}}Service {
|
|
50
|
+
private {{camelName}}s: any[] = [];
|
|
51
|
+
|
|
52
|
+
findAll() {
|
|
53
|
+
return this.{{camelName}}s;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
findOne(id: string) {
|
|
57
|
+
const {{camelName}} = this.{{camelName}}s.find(item => item.id === id);
|
|
58
|
+
if (!{{camelName}}) {
|
|
59
|
+
throw new Error('{{className}} not found');
|
|
60
|
+
}
|
|
61
|
+
return {{camelName}};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
create(createDto: Create{{className}}Dto) {
|
|
65
|
+
const {{camelName}} = {
|
|
66
|
+
id: Date.now().toString(),
|
|
67
|
+
...createDto,
|
|
68
|
+
createdAt: new Date(),
|
|
69
|
+
updatedAt: new Date(),
|
|
70
|
+
};
|
|
71
|
+
this.{{camelName}}s.push({{camelName}});
|
|
72
|
+
return {{camelName}};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
update(id: string, updateDto: Update{{className}}Dto) {
|
|
76
|
+
const index = this.{{camelName}}s.findIndex(item => item.id === id);
|
|
77
|
+
if (index === -1) {
|
|
78
|
+
throw new Error('{{className}} not found');
|
|
79
|
+
}
|
|
80
|
+
this.{{camelName}}s[index] = {
|
|
81
|
+
...this.{{camelName}}s[index],
|
|
82
|
+
...updateDto,
|
|
83
|
+
updatedAt: new Date(),
|
|
84
|
+
};
|
|
85
|
+
return this.{{camelName}}s[index];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
delete(id: string) {
|
|
89
|
+
const index = this.{{camelName}}s.findIndex(item => item.id === id);
|
|
90
|
+
if (index === -1) {
|
|
91
|
+
throw new Error('{{className}} not found');
|
|
92
|
+
}
|
|
93
|
+
const deleted = this.{{camelName}}s.splice(index, 1);
|
|
94
|
+
return deleted[0];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
const dtoTemplate = `import { IsString, IsOptional, IsNotEmpty } from 'class-validator';
|
|
99
|
+
|
|
100
|
+
export class Create{{className}}Dto {
|
|
101
|
+
@IsString()
|
|
102
|
+
@IsNotEmpty()
|
|
103
|
+
name: string;
|
|
104
|
+
|
|
105
|
+
@IsString()
|
|
106
|
+
@IsOptional()
|
|
107
|
+
description?: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export class Update{{className}}Dto {
|
|
111
|
+
@IsString()
|
|
112
|
+
@IsOptional()
|
|
113
|
+
name?: string;
|
|
114
|
+
|
|
115
|
+
@IsString()
|
|
116
|
+
@IsOptional()
|
|
117
|
+
description?: string;
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
const moduleTemplate = `import { HazelModule } from '@hazeljs/core';
|
|
121
|
+
import { {{className}}Controller } from './{{fileName}}.controller';
|
|
122
|
+
import { {{className}}Service } from './{{fileName}}.service';
|
|
123
|
+
|
|
124
|
+
@HazelModule({
|
|
125
|
+
controllers: [{{className}}Controller],
|
|
126
|
+
providers: [{{className}}Service],
|
|
127
|
+
})
|
|
128
|
+
export class {{className}}Module {}
|
|
129
|
+
`;
|
|
130
|
+
function generateCrud(command) {
|
|
131
|
+
command
|
|
132
|
+
.command('crud <name>')
|
|
133
|
+
.description('Generate a complete CRUD resource (controller, service, module, DTOs)')
|
|
134
|
+
.option('-p, --path <path>', 'Specify the path', 'src')
|
|
135
|
+
.option('-r, --route <route>', 'Specify the route path')
|
|
136
|
+
.option('--dry-run', 'Preview files without writing them')
|
|
137
|
+
.action((name, options) => {
|
|
138
|
+
const className = (0, generator_1.toPascalCase)(name);
|
|
139
|
+
const fileName = (0, generator_1.toKebabCase)(name);
|
|
140
|
+
const camelName = (0, generator_1.toCamelCase)(name);
|
|
141
|
+
const routePath = options.route || fileName;
|
|
142
|
+
const basePath = path_1.default.join(process.cwd(), options.path || 'src', fileName);
|
|
143
|
+
const data = { className, fileName, camelName, routePath };
|
|
144
|
+
const files = [
|
|
145
|
+
{ file: `${fileName}.controller.ts`, template: controllerTemplate },
|
|
146
|
+
{ file: `${fileName}.service.ts`, template: serviceTemplate },
|
|
147
|
+
{ file: `dto/${fileName}.dto.ts`, template: dtoTemplate },
|
|
148
|
+
{ file: `${fileName}.module.ts`, template: moduleTemplate },
|
|
149
|
+
];
|
|
150
|
+
try {
|
|
151
|
+
for (const { file, template } of files) {
|
|
152
|
+
const filePath = path_1.default.join(basePath, file);
|
|
153
|
+
if (options.dryRun) {
|
|
154
|
+
console.log(chalk_1.default.blue(`[dry-run] Would create ${filePath}`));
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const dir = path_1.default.dirname(filePath);
|
|
158
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
159
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
160
|
+
}
|
|
161
|
+
fs_1.default.writeFileSync(filePath, (0, generator_1.renderTemplate)(template, data));
|
|
162
|
+
console.log(chalk_1.default.green(`\u2713 Generated ${filePath}`));
|
|
163
|
+
}
|
|
164
|
+
if (!options.dryRun) {
|
|
165
|
+
console.log(chalk_1.default.blue('\n\uD83D\uDCE6 CRUD resource generated successfully!'));
|
|
166
|
+
console.log(chalk_1.default.gray(`\nNext steps:`));
|
|
167
|
+
console.log(chalk_1.default.gray(`1. Import ${className}Module in your app module`));
|
|
168
|
+
console.log(chalk_1.default.gray(`2. Customize the DTOs`));
|
|
169
|
+
console.log(chalk_1.default.gray(`3. Implement your business logic in the service`));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
console.error(chalk_1.default.red('Error generating CRUD resource:'), error);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|