@forinda/kickjs-cli 0.5.0 → 0.5.2
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/dist/cli.js +300 -94
- package/dist/cli.js.map +1 -1
- package/dist/index.js +300 -94
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generators/module.ts","../src/utils/fs.ts","../src/utils/naming.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/config.ts"],"sourcesContent":["import { join } from 'node:path'\nimport { writeFileSafe, fileExists } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase, pluralize, pluralizePascal } from '../utils/naming'\nimport { readFile, writeFile } from 'node:fs/promises'\n\ninterface GenerateModuleOptions {\n name: string\n modulesDir: string\n noEntity?: boolean\n noTests?: boolean\n repo?: 'drizzle' | 'inmemory'\n minimal?: boolean\n}\n\n/**\n * Generate a full DDD module with all layers:\n * presentation/ — controller\n * application/ — use-cases, DTOs\n * domain/ — entity, value objects, repository interface, domain service\n * infrastructure/ — repository implementation\n */\nexport async function generateModule(options: GenerateModuleOptions): Promise<string[]> {\n const { name, modulesDir, noEntity, noTests, repo = 'inmemory', minimal } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const camel = toCamelCase(name)\n const plural = pluralize(kebab)\n const pluralPascal = pluralizePascal(pascal)\n const moduleDir = join(modulesDir, plural)\n\n const files: string[] = []\n\n const write = async (relativePath: string, content: string) => {\n const fullPath = join(moduleDir, relativePath)\n await writeFileSafe(fullPath, content)\n files.push(fullPath)\n }\n\n // ── Module Index ────────────────────────────────────────────────────\n await write(\n 'index.ts',\n `/**\n * ${pascal} Module\n *\n * Self-contained feature module following Domain-Driven Design (DDD).\n * Registers dependencies in the DI container and declares HTTP routes.\n *\n * Structure:\n * presentation/ — HTTP controllers (entry points)\n * application/ — Use cases (orchestration) and DTOs (validation)\n * domain/ — Entities, value objects, repository interfaces, domain services\n * infrastructure/ — Repository implementations (in-memory, Drizzle, Prisma, etc.)\n */\nimport { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'\nimport { buildRoutes } from '@forinda/kickjs-http'\nimport { ${pascal.toUpperCase()}_REPOSITORY } from './domain/repositories/${kebab}.repository'\nimport { ${repo === 'inmemory' ? `InMemory${pascal}Repository` : `Drizzle${pascal}Repository`} } from './infrastructure/repositories/${repo === 'inmemory' ? `in-memory-${kebab}` : `drizzle-${kebab}`}.repository'\nimport { ${pascal}Controller } from './presentation/${kebab}.controller'\n\n// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container\nimport.meta.glob(\n ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],\n { eager: true },\n)\n\nexport class ${pascal}Module implements AppModule {\n /**\n * Register module dependencies in the DI container.\n * Bind repository interface tokens to their implementations here.\n * To swap implementations (e.g. in-memory -> Drizzle), change the factory target.\n */\n register(container: Container): void {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repo === 'inmemory' ? `InMemory${pascal}Repository` : `Drizzle${pascal}Repository`}),\n )\n }\n\n /**\n * Declare HTTP routes for this module.\n * The path is prefixed with the global apiPrefix and version (e.g. /api/v1/${plural}).\n * Passing 'controller' enables automatic OpenAPI spec generation via SwaggerAdapter.\n */\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n router: buildRoutes(${pascal}Controller),\n controller: ${pascal}Controller,\n }\n }\n}\n`,\n )\n\n // ── Controller ──────────────────────────────────────────────────────\n await write(\n `presentation/${kebab}.controller.ts`,\n `/**\n * ${pascal} Controller\n *\n * Presentation layer — handles HTTP requests and delegates to use cases.\n * Each method receives a RequestContext with typed body, params, and query.\n *\n * Decorators:\n * @Controller(path?) — registers this class as an HTTP controller\n * @Get/@Post/@Put/@Delete(path?, validation?) — defines routes with optional Zod validation\n * @Autowired() — injects dependencies lazily from the DI container\n * @Middleware(...handlers) — attach middleware at class or method level\n *\n * Add Swagger decorators (@ApiTags, @ApiOperation, @ApiResponse) from @forinda/kickjs-swagger\n * for automatic OpenAPI documentation.\n */\nimport { Controller, Get, Post, Put, Delete, Autowired } from '@forinda/kickjs-core'\nimport { RequestContext } from '@forinda/kickjs-http'\nimport { Create${pascal}UseCase } from '../application/use-cases/create-${kebab}.use-case'\nimport { Get${pascal}UseCase } from '../application/use-cases/get-${kebab}.use-case'\nimport { List${pluralPascal}UseCase } from '../application/use-cases/list-${plural}.use-case'\nimport { Update${pascal}UseCase } from '../application/use-cases/update-${kebab}.use-case'\nimport { Delete${pascal}UseCase } from '../application/use-cases/delete-${kebab}.use-case'\nimport { create${pascal}Schema } from '../application/dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from '../application/dtos/update-${kebab}.dto'\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private create${pascal}UseCase!: Create${pascal}UseCase\n @Autowired() private get${pascal}UseCase!: Get${pascal}UseCase\n @Autowired() private list${pluralPascal}UseCase!: List${pluralPascal}UseCase\n @Autowired() private update${pascal}UseCase!: Update${pascal}UseCase\n @Autowired() private delete${pascal}UseCase!: Delete${pascal}UseCase\n\n @Post('/', { body: create${pascal}Schema })\n async create(ctx: RequestContext) {\n const result = await this.create${pascal}UseCase.execute(ctx.body)\n ctx.created(result)\n }\n\n @Get('/')\n async list(ctx: RequestContext) {\n const result = await this.list${pluralPascal}UseCase.execute()\n ctx.json(result)\n }\n\n @Get('/:id')\n async getById(ctx: RequestContext) {\n const result = await this.get${pascal}UseCase.execute(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema })\n async update(ctx: RequestContext) {\n const result = await this.update${pascal}UseCase.execute(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n async remove(ctx: RequestContext) {\n await this.delete${pascal}UseCase.execute(ctx.params.id)\n ctx.noContent()\n }\n}\n`,\n )\n\n // ── DTOs ────────────────────────────────────────────────────────────\n await write(\n `application/dtos/create-${kebab}.dto.ts`,\n `import { z } from 'zod'\n\n/**\n * Create ${pascal} DTO — Zod schema for validating POST request bodies.\n * This schema is passed to @Post('/', { body: create${pascal}Schema }) for automatic validation.\n * It also generates OpenAPI request body docs when SwaggerAdapter is used.\n *\n * Add more fields as needed. Supported Zod types:\n * z.string(), z.number(), z.boolean(), z.enum([...]),\n * z.array(), z.object(), .optional(), .default(), .transform()\n */\nexport const create${pascal}Schema = z.object({\n name: z.string().min(1, 'Name is required').max(200),\n})\n\nexport type Create${pascal}DTO = z.infer<typeof create${pascal}Schema>\n`,\n )\n\n await write(\n `application/dtos/update-${kebab}.dto.ts`,\n `import { z } from 'zod'\n\nexport const update${pascal}Schema = z.object({\n name: z.string().min(1).max(200).optional(),\n})\n\nexport type Update${pascal}DTO = z.infer<typeof update${pascal}Schema>\n`,\n )\n\n await write(\n `application/dtos/${kebab}-response.dto.ts`,\n `export interface ${pascal}ResponseDTO {\n id: string\n name: string\n createdAt: string\n updatedAt: string\n}\n`,\n )\n\n // ── Use Cases ───────────────────────────────────────────────────────\n const useCases = [\n {\n file: `create-${kebab}.use-case.ts`,\n content: `/**\n * Create ${pascal} Use Case\n *\n * Application layer — orchestrates a single business operation.\n * Use cases are thin: validate input (via DTO), call domain/repo, return response.\n * Keep business rules in the domain service, not here.\n */\nimport { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Create${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.create(dto)\n }\n}\n`,\n },\n {\n file: `get-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Get${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.repo.findById(id)\n }\n}\n`,\n },\n {\n file: `list-${plural}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class List${pluralPascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(): Promise<${pascal}ResponseDTO[]> {\n return this.repo.findAll()\n }\n}\n`,\n },\n {\n file: `update-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Update${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.update(id, dto)\n }\n}\n`,\n },\n {\n file: `delete-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\n\n@Service()\nexport class Delete${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<void> {\n await this.repo.delete(id)\n }\n}\n`,\n },\n ]\n\n for (const uc of useCases) {\n await write(`application/use-cases/${uc.file}`, uc.content)\n }\n\n // ── Domain: Repository Interface ────────────────────────────────────\n await write(\n `domain/repositories/${kebab}.repository.ts`,\n `/**\n * ${pascal} Repository Interface\n *\n * Domain layer — defines the contract for data access.\n * The interface lives in the domain layer; implementations live in infrastructure.\n * This inversion of dependencies keeps the domain pure and testable.\n *\n * To swap implementations (e.g. in-memory -> Drizzle -> Prisma),\n * change the factory in the module's register() method.\n */\nimport type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'\n\nexport interface I${pascal}Repository {\n findById(id: string): Promise<${pascal}ResponseDTO | null>\n findAll(): Promise<${pascal}ResponseDTO[]>\n create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO>\n update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO>\n delete(id: string): Promise<void>\n}\n\nexport const ${pascal.toUpperCase()}_REPOSITORY = Symbol('I${pascal}Repository')\n`,\n )\n\n // ── Domain: Service ─────────────────────────────────────────────────\n await write(\n `domain/services/${kebab}-domain.service.ts`,\n `/**\n * ${pascal} Domain Service\n *\n * Domain layer — contains business rules that don't belong to a single entity.\n * Use this for cross-entity logic, validation rules, and domain invariants.\n * Keep it free of HTTP/framework concerns.\n */\nimport { Service, Inject, HttpException } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'\n\n@Service()\nexport class ${pascal}DomainService {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async ensureExists(id: string): Promise<void> {\n const entity = await this.repo.findById(id)\n if (!entity) {\n throw HttpException.notFound('${pascal} not found')\n }\n }\n}\n`,\n )\n\n // ── Infrastructure: Repository Implementation ──────────────────────\n if (repo === 'inmemory') {\n await write(\n `infrastructure/repositories/in-memory-${kebab}.repository.ts`,\n `/**\n * In-Memory ${pascal} Repository\n *\n * Infrastructure layer — implements the repository interface using a Map.\n * Useful for prototyping and testing. Replace with a database implementation\n * (Drizzle, Prisma, etc.) for production use.\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { randomUUID } from 'node:crypto'\nimport { Repository, HttpException } from '@forinda/kickjs-core'\nimport type { I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'\n\n@Repository()\nexport class InMemory${pascal}Repository implements I${pascal}Repository {\n private store = new Map<string, ${pascal}ResponseDTO>()\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.store.get(id) ?? null\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n return Array.from(this.store.values())\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const now = new Date().toISOString()\n const entity: ${pascal}ResponseDTO = {\n id: randomUUID(),\n name: dto.name,\n createdAt: now,\n updatedAt: now,\n }\n this.store.set(entity.id, entity)\n return entity\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const existing = this.store.get(id)\n if (!existing) throw HttpException.notFound('${pascal} not found')\n const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }\n this.store.set(id, updated)\n return updated\n }\n\n async delete(id: string): Promise<void> {\n if (!this.store.has(id)) throw HttpException.notFound('${pascal} not found')\n this.store.delete(id)\n }\n}\n`,\n )\n }\n\n // ── Entity & Value Objects ──────────────────────────────────────────\n if (!noEntity && !minimal) {\n await write(\n `domain/entities/${kebab}.entity.ts`,\n `/**\n * ${pascal} Entity\n *\n * Domain layer — the core business object.\n * Uses a private constructor with static factory methods (create, reconstitute)\n * to enforce invariants. Properties are accessed via getters to maintain encapsulation.\n *\n * Patterns used:\n * - Private constructor: prevents direct instantiation\n * - create(): factory for new entities (generates ID, sets timestamps)\n * - reconstitute(): factory for rebuilding from persistence (no side effects)\n * - changeName(): mutation method that enforces business rules\n */\nimport { ${pascal}Id } from '../value-objects/${kebab}-id.vo'\n\ninterface ${pascal}Props {\n id: ${pascal}Id\n name: string\n createdAt: Date\n updatedAt: Date\n}\n\nexport class ${pascal} {\n private constructor(private props: ${pascal}Props) {}\n\n static create(params: { name: string }): ${pascal} {\n const now = new Date()\n return new ${pascal}({\n id: ${pascal}Id.create(),\n name: params.name,\n createdAt: now,\n updatedAt: now,\n })\n }\n\n static reconstitute(props: ${pascal}Props): ${pascal} {\n return new ${pascal}(props)\n }\n\n get id(): ${pascal}Id {\n return this.props.id\n }\n get name(): string {\n return this.props.name\n }\n get createdAt(): Date {\n return this.props.createdAt\n }\n get updatedAt(): Date {\n return this.props.updatedAt\n }\n\n changeName(name: string): void {\n if (!name || name.trim().length === 0) {\n throw new Error('Name cannot be empty')\n }\n this.props.name = name.trim()\n this.props.updatedAt = new Date()\n }\n\n toJSON() {\n return {\n id: this.props.id.toString(),\n name: this.props.name,\n createdAt: this.props.createdAt.toISOString(),\n updatedAt: this.props.updatedAt.toISOString(),\n }\n }\n}\n`,\n )\n\n await write(\n `domain/value-objects/${kebab}-id.vo.ts`,\n `/**\n * ${pascal} ID Value Object\n *\n * Domain layer — wraps a primitive ID with type safety and validation.\n * Value objects are immutable and compared by value, not reference.\n *\n * ${pascal}Id.create() — generate a new UUID\n * ${pascal}Id.from(id) — wrap an existing ID string (validates non-empty)\n * id.equals(other) — compare two IDs by value\n */\nimport { randomUUID } from 'node:crypto'\n\nexport class ${pascal}Id {\n private constructor(private readonly value: string) {}\n\n static create(): ${pascal}Id {\n return new ${pascal}Id(randomUUID())\n }\n\n static from(id: string): ${pascal}Id {\n if (!id || id.trim().length === 0) {\n throw new Error('${pascal}Id cannot be empty')\n }\n return new ${pascal}Id(id)\n }\n\n toString(): string {\n return this.value\n }\n\n equals(other: ${pascal}Id): boolean {\n return this.value === other.value\n }\n}\n`,\n )\n }\n\n // ── Auto-register in modules index ──────────────────────────────────\n await autoRegisterModule(modulesDir, pascal, plural)\n\n return files\n}\n\n/** Add the new module to src/modules/index.ts */\nasync function autoRegisterModule(\n modulesDir: string,\n pascal: string,\n plural: string,\n): Promise<void> {\n const indexPath = join(modulesDir, 'index.ts')\n const exists = await fileExists(indexPath)\n\n if (!exists) {\n await writeFileSafe(\n indexPath,\n `import type { AppModuleClass } from '@forinda/kickjs-core'\nimport { ${pascal}Module } from './${plural}'\n\nexport const modules: AppModuleClass[] = [${pascal}Module]\n`,\n )\n return\n }\n\n let content = await readFile(indexPath, 'utf-8')\n\n // Add import if not present\n const importLine = `import { ${pascal}Module } from './${plural}'`\n if (!content.includes(`${pascal}Module`)) {\n // Insert import after last existing import\n const lastImportIdx = content.lastIndexOf('import ')\n if (lastImportIdx !== -1) {\n const lineEnd = content.indexOf('\\n', lastImportIdx)\n content = content.slice(0, lineEnd + 1) + importLine + '\\n' + content.slice(lineEnd + 1)\n } else {\n content = importLine + '\\n' + content\n }\n\n // Add to modules array — handle both empty and existing entries\n // Match the array assignment: `= [...]` or `= [\\n...\\n]`\n content = content.replace(/(=\\s*\\[)([\\s\\S]*?)(])/, (_match, open, existing, close) => {\n const trimmed = existing.trim()\n if (!trimmed) {\n // Empty array: `= []`\n return `${open}${pascal}Module${close}`\n }\n // Existing entries: append with comma\n const needsComma = trimmed.endsWith(',') ? '' : ','\n return `${open}${existing.trimEnd()}${needsComma} ${pascal}Module${close}`\n })\n }\n\n await writeFile(indexPath, content, 'utf-8')\n}\n","import { writeFile, mkdir, access, readFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\n/** Write a file, creating parent directories if needed */\nexport async function writeFileSafe(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true })\n await writeFile(filePath, content, 'utf-8')\n}\n\n/** Ensure a directory exists */\nexport async function ensureDirectory(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true })\n}\n\n/** Check if a file exists */\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath)\n return true\n } catch {\n return false\n }\n}\n\n/** Read a JSON file */\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\n const content = await readFile(filePath, 'utf-8')\n return JSON.parse(content)\n}\n","/** Convert a name to PascalCase */\nexport function toPascalCase(name: string): string {\n return name\n .replace(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^(.)/, (c) => c.toUpperCase())\n}\n\n/** Convert a name to camelCase */\nexport function toCamelCase(name: string): string {\n const pascal = toPascalCase(name)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\n/** Convert a name to kebab-case */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase()\n}\n\n/**\n * Pluralize a kebab-case name for directory/file names.\n * If already plural (ends in 's'), returns as-is.\n */\nexport function pluralize(name: string): string {\n if (name.endsWith('s')) return name\n if (name.endsWith('x') || name.endsWith('z')) return name + 'es'\n if (name.endsWith('sh') || name.endsWith('ch')) return name + 'es'\n if (name.endsWith('y') && !/[aeiou]y$/.test(name)) return name.slice(0, -1) + 'ies'\n return name + 's'\n}\n\n/**\n * Pluralize a PascalCase name for class identifiers.\n * If already plural (ends in 's'), returns as-is.\n * Used for `List${pluralPascal}UseCase` to avoid `ListUserssUseCase`.\n */\nexport function pluralizePascal(name: string): string {\n if (name.endsWith('s')) return name\n if (name.endsWith('x') || name.endsWith('z')) return name + 'es'\n if (name.endsWith('sh') || name.endsWith('ch')) return name + 'es'\n if (name.endsWith('y') && !/[aeiou]y$/i.test(name)) return name.slice(0, -1) + 'ies'\n return name + 's'\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateAdapterOptions {\n name: string\n outDir: string\n}\n\nexport async function generateAdapter(options: GenerateAdapterOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.adapter.ts`)\n await writeFileSafe(\n filePath,\n `import type { Express } from 'express'\nimport type { AppAdapter, AdapterMiddleware, Container } from '@forinda/kickjs-core'\n\nexport interface ${pascal}AdapterOptions {\n // Add your adapter configuration here\n}\n\n/**\n * ${pascal} adapter.\n *\n * Hooks into the Application lifecycle to add middleware, routes,\n * or external service connections.\n *\n * Usage:\n * bootstrap({\n * adapters: [new ${pascal}Adapter({ ... })],\n * })\n */\nexport class ${pascal}Adapter implements AppAdapter {\n name = '${pascal}Adapter'\n\n constructor(private options: ${pascal}AdapterOptions = {}) {}\n\n /**\n * Return middleware entries that the Application will mount.\n * Use \\`phase\\` to control where in the pipeline they run:\n * 'beforeGlobal' | 'afterGlobal' | 'beforeRoutes' | 'afterRoutes'\n */\n middleware(): AdapterMiddleware[] {\n return [\n // Example: add a custom header to all responses\n // {\n // phase: 'beforeGlobal',\n // handler: (_req: any, res: any, next: any) => {\n // res.setHeader('X-${pascal}', 'true')\n // next()\n // },\n // },\n // Example: scope middleware to a specific path\n // {\n // phase: 'beforeRoutes',\n // path: '/api/v1/admin',\n // handler: myAdminMiddleware(),\n // },\n ]\n }\n\n /**\n * Called before global middleware.\n * Use this to mount routes that bypass the middleware stack\n * (health checks, docs UI, static assets).\n */\n beforeMount(app: Express, container: Container): void {\n // Example: mount a status route\n // app.get('/${kebab}/status', (_req, res) => {\n // res.json({ status: 'ok' })\n // })\n }\n\n /**\n * Called after modules and routes are registered, before the server starts.\n * Use this for late-stage DI registrations or config validation.\n */\n beforeStart(app: Express, container: Container): void {\n // Example: register a service in the DI container\n // container.registerInstance(MY_TOKEN, new MyService(this.options))\n }\n\n /**\n * Called after the HTTP server is listening.\n * Use this to attach to the raw http.Server (Socket.IO, gRPC, etc).\n */\n afterStart(server: any, container: Container): void {\n // Example: attach Socket.IO\n // const io = new Server(server)\n // container.registerInstance(SOCKET_IO, io)\n }\n\n /**\n * Called on graceful shutdown. Clean up connections.\n */\n async shutdown(): Promise<void> {\n // Example: close a connection pool\n // await this.pool.end()\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateMiddlewareOptions {\n name: string\n outDir: string\n}\n\nexport async function generateMiddleware(options: GenerateMiddlewareOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.middleware.ts`)\n await writeFileSafe(\n filePath,\n `import type { Request, Response, NextFunction } from 'express'\n\nexport interface ${toPascalCase(name)}Options {\n // Add configuration options here\n}\n\n/**\n * ${toPascalCase(name)} middleware.\n *\n * Usage in bootstrap:\n * middleware: [${camel}()]\n *\n * Usage with adapter:\n * middleware() { return [{ handler: ${camel}(), phase: 'afterGlobal' }] }\n *\n * Usage with @Middleware decorator:\n * @Middleware(${camel}())\n */\nexport function ${camel}(options: ${toPascalCase(name)}Options = {}) {\n return (req: Request, res: Response, next: NextFunction) => {\n // Implement your middleware logic here\n next()\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateGuardOptions {\n name: string\n outDir: string\n}\n\nexport async function generateGuard(options: GenerateGuardOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.guard.ts`)\n await writeFileSafe(\n filePath,\n `import { Container, HttpException } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\n\n/**\n * ${pascal} guard.\n *\n * Guards protect routes by checking conditions before the handler runs.\n * Return early with an error response to block access.\n *\n * Usage:\n * @Middleware(${camel}Guard)\n * @Get('/protected')\n * async handler(ctx: RequestContext) { ... }\n */\nexport async function ${camel}Guard(ctx: RequestContext, next: () => void): Promise<void> {\n // Example: check for an authorization header\n const header = ctx.headers.authorization\n if (!header?.startsWith('Bearer ')) {\n ctx.res.status(401).json({ message: 'Missing or invalid authorization header' })\n return\n }\n\n const token = header.slice(7)\n\n try {\n // Verify the token using a service from the DI container\n // const container = Container.getInstance()\n // const authService = container.resolve(AuthService)\n // const payload = authService.verifyToken(token)\n // ctx.set('auth', payload)\n\n next()\n } catch {\n ctx.res.status(401).json({ message: 'Invalid or expired token' })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateServiceOptions {\n name: string\n outDir: string\n}\n\nexport async function generateService(options: GenerateServiceOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.service.ts`)\n await writeFileSafe(\n filePath,\n `import { Service } from '@forinda/kickjs-core'\n\n@Service()\nexport class ${pascal}Service {\n // Inject dependencies via constructor\n // constructor(\n // @Inject(MY_REPO) private readonly repo: IMyRepository,\n // ) {}\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateControllerOptions {\n name: string\n outDir: string\n}\n\nexport async function generateController(options: GenerateControllerOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.controller.ts`)\n await writeFileSafe(\n filePath,\n `import { Controller, Get, Post, Autowired } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\n\n@Controller()\nexport class ${pascal}Controller {\n // @Autowired() private myService!: MyService\n\n @Get('/')\n async list(ctx: RequestContext) {\n ctx.json({ message: '${pascal} list' })\n }\n\n @Post('/')\n async create(ctx: RequestContext) {\n ctx.created({ message: '${pascal} created', data: ctx.body })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateDtoOptions {\n name: string\n outDir: string\n}\n\nexport async function generateDto(options: GenerateDtoOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.dto.ts`)\n await writeFileSafe(\n filePath,\n `import { z } from 'zod'\n\nexport const ${camel}Schema = z.object({\n // Define your schema fields here\n name: z.string().min(1).max(200),\n})\n\nexport type ${pascal}DTO = z.infer<typeof ${camel}Schema>\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { execSync } from 'node:child_process'\nimport { writeFileSafe } from '../utils/fs'\n\ninterface InitProjectOptions {\n name: string\n directory: string\n packageManager?: 'pnpm' | 'npm' | 'yarn'\n initGit?: boolean\n installDeps?: boolean\n}\n\n/** Scaffold a new KickJS project */\nexport async function initProject(options: InitProjectOptions): Promise<void> {\n const { name, directory, packageManager = 'pnpm' } = options\n const dir = directory\n\n console.log(`\\n Creating KickJS project: ${name}\\n`)\n\n // ── package.json ────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'package.json'),\n JSON.stringify(\n {\n name,\n version: '0.1.0',\n type: 'module',\n scripts: {\n dev: 'kick dev',\n 'dev:debug': 'kick dev:debug',\n build: 'kick build',\n start: 'kick start',\n test: 'vitest run',\n 'test:watch': 'vitest',\n typecheck: 'tsc --noEmit',\n lint: 'eslint src/',\n format: 'prettier --write src/',\n },\n dependencies: {\n '@forinda/kickjs-core': '^0.1.0',\n '@forinda/kickjs-http': '^0.1.0',\n '@forinda/kickjs-config': '^0.1.0',\n '@forinda/kickjs-swagger': '^0.1.0',\n express: '^5.1.0',\n 'reflect-metadata': '^0.2.2',\n zod: '^4.3.6',\n pino: '^10.3.1',\n 'pino-pretty': '^13.1.3',\n },\n devDependencies: {\n '@forinda/kickjs-cli': '^0.1.0',\n '@swc/core': '^1.7.28',\n '@types/express': '^5.0.6',\n '@types/node': '^24.5.2',\n 'unplugin-swc': '^1.5.9',\n vite: '^7.3.1',\n 'vite-node': '^5.3.0',\n vitest: '^3.2.4',\n typescript: '^5.9.2',\n prettier: '^3.8.1',\n },\n },\n null,\n 2,\n ),\n )\n\n // ── vite.config.ts — enables HMR + SWC for decorators ──────────────\n await writeFileSafe(\n join(dir, 'vite.config.ts'),\n `import { defineConfig } from 'vite'\nimport { resolve } from 'path'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n resolve: {\n alias: {\n '@': resolve(__dirname, 'src'),\n },\n },\n server: {\n watch: { usePolling: false },\n hmr: true,\n },\n build: {\n target: 'node20',\n ssr: true,\n outDir: 'dist',\n sourcemap: true,\n rollupOptions: {\n input: resolve(__dirname, 'src/index.ts'),\n output: { format: 'esm' },\n },\n },\n})\n`,\n )\n\n // ── tsconfig.json ───────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'tsconfig.json'),\n JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['node'],\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n sourceMap: true,\n declaration: true,\n experimentalDecorators: true,\n emitDecoratorMetadata: true,\n outDir: 'dist',\n rootDir: 'src',\n paths: { '@/*': ['./src/*'] },\n },\n include: ['src'],\n },\n null,\n 2,\n ),\n )\n\n // ── .prettierrc ─────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.prettierrc'),\n JSON.stringify(\n {\n semi: false,\n singleQuote: true,\n trailingComma: 'all',\n printWidth: 100,\n tabWidth: 2,\n },\n null,\n 2,\n ),\n )\n\n // ── .gitignore ──────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.gitignore'),\n `node_modules/\ndist/\n.env\ncoverage/\n.DS_Store\n*.tsbuildinfo\n`,\n )\n\n // ── .env ────────────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.env'),\n `PORT=3000\nNODE_ENV=development\n`,\n )\n\n await writeFileSafe(\n join(dir, '.env.example'),\n `PORT=3000\nNODE_ENV=development\n`,\n )\n\n // ── src/index.ts — clean entry point with Swagger baked in ────────\n await writeFileSafe(\n join(dir, 'src/index.ts'),\n `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { SwaggerAdapter } from '@forinda/kickjs-swagger'\nimport { modules } from './modules'\n\nbootstrap({\n modules,\n adapters: [\n new SwaggerAdapter({\n info: { title: '${name}', version: '0.1.0' },\n }),\n ],\n})\n`,\n )\n\n // ── src/modules/index.ts ────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'src/modules/index.ts'),\n `import type { AppModuleClass } from '@forinda/kickjs-core'\n\nexport const modules: AppModuleClass[] = []\n`,\n )\n\n // ── kick.config.ts — CLI configuration ─────────────────────────────\n await writeFileSafe(\n join(dir, 'kick.config.ts'),\n `import { defineConfig } from '@forinda/kickjs-cli'\n\nexport default defineConfig({\n modulesDir: 'src/modules',\n defaultRepo: 'inmemory',\n\n commands: [\n {\n name: 'test',\n description: 'Run tests with Vitest',\n steps: 'npx vitest run',\n },\n {\n name: 'format',\n description: 'Format code with Prettier',\n steps: 'npx prettier --write src/',\n },\n {\n name: 'format:check',\n description: 'Check formatting without writing',\n steps: 'npx prettier --check src/',\n },\n {\n name: 'check',\n description: 'Run typecheck + format check',\n steps: ['npx tsc --noEmit', 'npx prettier --check src/'],\n aliases: ['verify', 'ci'],\n },\n ],\n})\n`,\n )\n\n // ── vitest.config.ts ────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'vitest.config.ts'),\n `import { defineConfig } from 'vitest/config'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n test: {\n globals: true,\n environment: 'node',\n include: ['src/**/*.test.ts'],\n },\n})\n`,\n )\n\n // ── Git Init ─────────────────────────────────────────────────────────\n if (options.initGit) {\n try {\n execSync('git init', { cwd: dir, stdio: 'pipe' })\n execSync('git add -A', { cwd: dir, stdio: 'pipe' })\n execSync('git commit -m \"chore: initial commit from kick new\"', {\n cwd: dir,\n stdio: 'pipe',\n })\n console.log(' Git repository initialized')\n } catch {\n console.log(' Warning: git init failed (git may not be installed)')\n }\n }\n\n // ── Install Dependencies ────────────────────────────────────────────\n if (options.installDeps) {\n console.log(`\\n Installing dependencies with ${packageManager}...\\n`)\n try {\n execSync(`${packageManager} install`, { cwd: dir, stdio: 'inherit' })\n console.log('\\n Dependencies installed successfully!')\n } catch {\n console.log(`\\n Warning: ${packageManager} install failed. Run it manually.`)\n }\n }\n\n console.log('\\n Project scaffolded successfully!')\n console.log()\n\n const needsCd = dir !== process.cwd()\n console.log(' Next steps:')\n if (needsCd) console.log(` cd ${name}`)\n if (!options.installDeps) console.log(` ${packageManager} install`)\n console.log(' kick g module user')\n console.log(' kick dev')\n console.log()\n console.log(' Commands:')\n console.log(' kick dev Start dev server with Vite HMR')\n console.log(' kick build Production build via Vite')\n console.log(' kick start Run production build')\n console.log(' kick g module X Generate a DDD module')\n console.log()\n}\n","import { readFile, access } from 'node:fs/promises'\nimport { join } from 'node:path'\n\n/** A custom command that developers can register via kick.config.ts */\nexport interface KickCommandDefinition {\n /** The command name (e.g. 'db:migrate', 'seed', 'proto:gen') */\n name: string\n /** Description shown in --help */\n description: string\n /**\n * Shell command(s) to run. Can be a single string or an array of\n * sequential steps. Use {args} as a placeholder for CLI arguments.\n *\n * @example\n * 'npx drizzle-kit migrate'\n * ['npx drizzle-kit generate', 'npx drizzle-kit migrate']\n */\n steps: string | string[]\n /** Optional aliases (e.g. ['migrate'] for 'db:migrate') */\n aliases?: string[]\n}\n\n/** Configuration for the kick.config.ts file */\nexport interface KickConfig {\n /** Where modules live (default: 'src/modules') */\n modulesDir?: string\n /** Default repository implementation for generators */\n defaultRepo?: 'drizzle' | 'inmemory' | 'prisma'\n /** Drizzle schema output directory */\n schemaDir?: string\n /** Custom commands that extend the CLI */\n commands?: KickCommandDefinition[]\n /** Code style overrides (auto-detected from prettier when possible) */\n style?: {\n semicolons?: boolean\n quotes?: 'single' | 'double'\n trailingComma?: 'all' | 'es5' | 'none'\n indent?: number\n }\n}\n\n/** Helper to define a type-safe kick.config.ts */\nexport function defineConfig(config: KickConfig): KickConfig {\n return config\n}\n\nconst CONFIG_FILES = ['kick.config.ts', 'kick.config.js', 'kick.config.mjs', 'kick.config.json']\n\n/** Load kick.config.* from the project root */\nexport async function loadKickConfig(cwd: string): Promise<KickConfig | null> {\n for (const filename of CONFIG_FILES) {\n const filepath = join(cwd, filename)\n try {\n await access(filepath)\n } catch {\n continue\n }\n\n if (filename.endsWith('.json')) {\n const content = await readFile(filepath, 'utf-8')\n return JSON.parse(content)\n }\n\n // For .ts/.js/.mjs — dynamic import (use file URL for cross-platform compat)\n try {\n const { pathToFileURL } = await import('node:url')\n const mod = await import(pathToFileURL(filepath).href)\n return mod.default ?? mod\n } catch (err) {\n if (filename.endsWith('.ts')) {\n console.warn(\n `Warning: Failed to load ${filename}. TypeScript config files require ` +\n 'a runtime loader (e.g. tsx, ts-node) or use kick.config.js/.mjs instead.',\n )\n }\n continue\n }\n }\n return null\n}\n"],"mappings":"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","names":["join","writeFile","mkdir","access","readFile","dirname","writeFileSafe","filePath","content","mkdir","dirname","recursive","writeFile","fileExists","filePath","access","toPascalCase","name","replace","_","c","toUpperCase","toCamelCase","pascal","charAt","toLowerCase","slice","toKebabCase","pluralize","endsWith","test","pluralizePascal","readFile","writeFile","generateModule","options","name","modulesDir","noEntity","noTests","repo","minimal","kebab","toKebabCase","pascal","toPascalCase","camel","toCamelCase","plural","pluralize","pluralPascal","pluralizePascal","moduleDir","join","files","write","relativePath","content","fullPath","writeFileSafe","push","toUpperCase","useCases","file","uc","autoRegisterModule","indexPath","exists","fileExists","readFile","importLine","includes","lastImportIdx","lastIndexOf","lineEnd","indexOf","slice","replace","_match","open","existing","close","trimmed","trim","needsComma","endsWith","trimEnd","writeFile","join","generateAdapter","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateMiddleware","options","name","outDir","kebab","toKebabCase","camel","toCamelCase","files","filePath","join","writeFileSafe","toPascalCase","push","join","generateGuard","options","name","outDir","kebab","toKebabCase","camel","toCamelCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateService","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateController","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateDto","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","camel","toCamelCase","files","filePath","join","writeFileSafe","push","join","execSync","initProject","options","name","directory","packageManager","dir","console","log","writeFileSafe","join","JSON","stringify","version","type","scripts","dev","build","start","test","typecheck","lint","format","dependencies","express","zod","pino","devDependencies","vite","vitest","typescript","prettier","compilerOptions","target","module","moduleResolution","lib","types","strict","esModuleInterop","skipLibCheck","sourceMap","declaration","experimentalDecorators","emitDecoratorMetadata","outDir","rootDir","paths","include","semi","singleQuote","trailingComma","printWidth","tabWidth","initGit","execSync","cwd","stdio","installDeps","needsCd","process","readFile","access","join","defineConfig","config","CONFIG_FILES","loadKickConfig","cwd","filename","filepath","join","access","endsWith","content","readFile","JSON","parse","pathToFileURL","mod","href","default","err","console","warn"]}
|
|
1
|
+
{"version":3,"sources":["../src/generators/module.ts","../src/utils/fs.ts","../src/utils/naming.ts","../src/generators/templates/module-index.ts","../src/generators/templates/controller.ts","../src/generators/templates/constants.ts","../src/generators/templates/dtos.ts","../src/generators/templates/use-cases.ts","../src/generators/templates/repository.ts","../src/generators/templates/domain.ts","../src/generators/templates/tests.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/config.ts"],"sourcesContent":["import { join } from 'node:path'\nimport { writeFileSafe, fileExists } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase, pluralize, pluralizePascal } from '../utils/naming'\nimport { readFile, writeFile } from 'node:fs/promises'\nimport {\n generateModuleIndex,\n generateController,\n generateConstants,\n generateCreateDTO,\n generateUpdateDTO,\n generateResponseDTO,\n generateUseCases,\n generateRepositoryInterface,\n generateInMemoryRepository,\n generateDomainService,\n generateEntity,\n generateValueObject,\n generateControllerTest,\n generateRepositoryTest,\n} from './templates'\n\ninterface GenerateModuleOptions {\n name: string\n modulesDir: string\n noEntity?: boolean\n noTests?: boolean\n repo?: 'drizzle' | 'inmemory'\n minimal?: boolean\n}\n\n/**\n * Generate a full DDD module with all layers:\n * presentation/ — controller\n * application/ — use-cases, DTOs\n * domain/ — entity, value objects, repository interface, domain service\n * infrastructure/ — repository implementation\n */\nexport async function generateModule(options: GenerateModuleOptions): Promise<string[]> {\n const { name, modulesDir, noEntity, noTests, repo = 'inmemory', minimal } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const camel = toCamelCase(name)\n const plural = pluralize(kebab)\n const pluralPascal = pluralizePascal(pascal)\n const moduleDir = join(modulesDir, plural)\n\n const files: string[] = []\n\n const write = async (relativePath: string, content: string) => {\n const fullPath = join(moduleDir, relativePath)\n await writeFileSafe(fullPath, content)\n files.push(fullPath)\n }\n\n // ── Module Index ────────────────────────────────────────────────────\n await write('index.ts', generateModuleIndex(pascal, kebab, plural, repo))\n\n // ── Constants ──────────────────────────────────────────────────────\n await write('constants.ts', generateConstants(pascal))\n\n // ── Controller ──────────────────────────────────────────────────────\n await write(\n `presentation/${kebab}.controller.ts`,\n generateController(pascal, kebab, plural, pluralPascal),\n )\n\n // ── DTOs ────────────────────────────────────────────────────────────\n await write(`application/dtos/create-${kebab}.dto.ts`, generateCreateDTO(pascal, kebab))\n await write(`application/dtos/update-${kebab}.dto.ts`, generateUpdateDTO(pascal, kebab))\n await write(`application/dtos/${kebab}-response.dto.ts`, generateResponseDTO(pascal, kebab))\n\n // ── Use Cases ───────────────────────────────────────────────────────\n const useCases = generateUseCases(pascal, kebab, plural, pluralPascal)\n for (const uc of useCases) {\n await write(`application/use-cases/${uc.file}`, uc.content)\n }\n\n // ── Domain: Repository Interface ────────────────────────────────────\n await write(\n `domain/repositories/${kebab}.repository.ts`,\n generateRepositoryInterface(pascal, kebab),\n )\n\n // ── Domain: Service ─────────────────────────────────────────────────\n await write(`domain/services/${kebab}-domain.service.ts`, generateDomainService(pascal, kebab))\n\n // ── Infrastructure: Repository Implementation ──────────────────────\n if (repo === 'inmemory') {\n await write(\n `infrastructure/repositories/in-memory-${kebab}.repository.ts`,\n generateInMemoryRepository(pascal, kebab),\n )\n }\n\n // ── Entity & Value Objects ──────────────────────────────────────────\n if (!noEntity && !minimal) {\n await write(`domain/entities/${kebab}.entity.ts`, generateEntity(pascal, kebab))\n await write(`domain/value-objects/${kebab}-id.vo.ts`, generateValueObject(pascal, kebab))\n }\n\n // ── Tests ──────────────────────────────────────────────────────────\n if (!noTests) {\n await write(\n `__tests__/${kebab}.controller.test.ts`,\n generateControllerTest(pascal, kebab, plural),\n )\n await write(\n `__tests__/${kebab}.repository.test.ts`,\n generateRepositoryTest(pascal, kebab, plural),\n )\n }\n\n // ── Auto-register in modules index ──────────────────────────────────\n await autoRegisterModule(modulesDir, pascal, plural)\n\n return files\n}\n\n/** Add the new module to src/modules/index.ts */\nasync function autoRegisterModule(\n modulesDir: string,\n pascal: string,\n plural: string,\n): Promise<void> {\n const indexPath = join(modulesDir, 'index.ts')\n const exists = await fileExists(indexPath)\n\n if (!exists) {\n await writeFileSafe(\n indexPath,\n `import type { AppModuleClass } from '@forinda/kickjs-core'\nimport { ${pascal}Module } from './${plural}'\n\nexport const modules: AppModuleClass[] = [${pascal}Module]\n`,\n )\n return\n }\n\n let content = await readFile(indexPath, 'utf-8')\n\n // Add import if not present\n const importLine = `import { ${pascal}Module } from './${plural}'`\n if (!content.includes(`${pascal}Module`)) {\n // Insert import after last existing import\n const lastImportIdx = content.lastIndexOf('import ')\n if (lastImportIdx !== -1) {\n const lineEnd = content.indexOf('\\n', lastImportIdx)\n content = content.slice(0, lineEnd + 1) + importLine + '\\n' + content.slice(lineEnd + 1)\n } else {\n content = importLine + '\\n' + content\n }\n\n // Add to modules array — handle both empty and existing entries\n // Match the array assignment: `= [...]` or `= [\\n...\\n]`\n content = content.replace(/(=\\s*\\[)([\\s\\S]*?)(])/, (_match, open, existing, close) => {\n const trimmed = existing.trim()\n if (!trimmed) {\n // Empty array: `= []`\n return `${open}${pascal}Module${close}`\n }\n // Existing entries: append with comma\n const needsComma = trimmed.endsWith(',') ? '' : ','\n return `${open}${existing.trimEnd()}${needsComma} ${pascal}Module${close}`\n })\n }\n\n await writeFile(indexPath, content, 'utf-8')\n}\n","import { writeFile, mkdir, access, readFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\n/** Write a file, creating parent directories if needed */\nexport async function writeFileSafe(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true })\n await writeFile(filePath, content, 'utf-8')\n}\n\n/** Ensure a directory exists */\nexport async function ensureDirectory(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true })\n}\n\n/** Check if a file exists */\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath)\n return true\n } catch {\n return false\n }\n}\n\n/** Read a JSON file */\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\n const content = await readFile(filePath, 'utf-8')\n return JSON.parse(content)\n}\n","/** Convert a name to PascalCase */\nexport function toPascalCase(name: string): string {\n return name\n .replace(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^(.)/, (c) => c.toUpperCase())\n}\n\n/** Convert a name to camelCase */\nexport function toCamelCase(name: string): string {\n const pascal = toPascalCase(name)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\n/** Convert a name to kebab-case */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase()\n}\n\n/**\n * Pluralize a kebab-case name for directory/file names.\n * If already plural (ends in 's'), returns as-is.\n */\nexport function pluralize(name: string): string {\n if (name.endsWith('s')) return name\n if (name.endsWith('x') || name.endsWith('z')) return name + 'es'\n if (name.endsWith('sh') || name.endsWith('ch')) return name + 'es'\n if (name.endsWith('y') && !/[aeiou]y$/.test(name)) return name.slice(0, -1) + 'ies'\n return name + 's'\n}\n\n/**\n * Pluralize a PascalCase name for class identifiers.\n * If already plural (ends in 's'), returns as-is.\n * Used for `List${pluralPascal}UseCase` to avoid `ListUserssUseCase`.\n */\nexport function pluralizePascal(name: string): string {\n if (name.endsWith('s')) return name\n if (name.endsWith('x') || name.endsWith('z')) return name + 'es'\n if (name.endsWith('sh') || name.endsWith('ch')) return name + 'es'\n if (name.endsWith('y') && !/[aeiou]y$/i.test(name)) return name.slice(0, -1) + 'ies'\n return name + 's'\n}\n","export function generateModuleIndex(\n pascal: string,\n kebab: string,\n plural: string,\n repo: 'drizzle' | 'inmemory',\n): string {\n const repoClass =\n repo === 'inmemory' ? `InMemory${pascal}Repository` : `Drizzle${pascal}Repository`\n const repoFile = repo === 'inmemory' ? `in-memory-${kebab}` : `drizzle-${kebab}`\n\n return `/**\n * ${pascal} Module\n *\n * Self-contained feature module following Domain-Driven Design (DDD).\n * Registers dependencies in the DI container and declares HTTP routes.\n *\n * Structure:\n * presentation/ — HTTP controllers (entry points)\n * application/ — Use cases (orchestration) and DTOs (validation)\n * domain/ — Entities, value objects, repository interfaces, domain services\n * infrastructure/ — Repository implementations (in-memory, Drizzle, Prisma, etc.)\n */\nimport { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'\nimport { buildRoutes } from '@forinda/kickjs-http'\nimport { ${pascal.toUpperCase()}_REPOSITORY } from './domain/repositories/${kebab}.repository'\nimport { ${repoClass} } from './infrastructure/repositories/${repoFile}.repository'\nimport { ${pascal}Controller } from './presentation/${kebab}.controller'\n\n// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container\nimport.meta.glob(\n ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],\n { eager: true },\n)\n\nexport class ${pascal}Module implements AppModule {\n /**\n * Register module dependencies in the DI container.\n * Bind repository interface tokens to their implementations here.\n * To swap implementations (e.g. in-memory -> Drizzle), change the factory target.\n */\n register(container: Container): void {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n }\n\n /**\n * Declare HTTP routes for this module.\n * The path is prefixed with the global apiPrefix and version (e.g. /api/v1/${plural}).\n * Passing 'controller' enables automatic OpenAPI spec generation via SwaggerAdapter.\n */\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n router: buildRoutes(${pascal}Controller),\n controller: ${pascal}Controller,\n }\n }\n}\n`\n}\n","export function generateController(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): string {\n return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\nimport { ApiTags } from '@forinda/kickjs-swagger'\nimport { Create${pascal}UseCase } from '../application/use-cases/create-${kebab}.use-case'\nimport { Get${pascal}UseCase } from '../application/use-cases/get-${kebab}.use-case'\nimport { List${pluralPascal}UseCase } from '../application/use-cases/list-${plural}.use-case'\nimport { Update${pascal}UseCase } from '../application/use-cases/update-${kebab}.use-case'\nimport { Delete${pascal}UseCase } from '../application/use-cases/delete-${kebab}.use-case'\nimport { create${pascal}Schema } from '../application/dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from '../application/dtos/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../constants'\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private create${pascal}UseCase!: Create${pascal}UseCase\n @Autowired() private get${pascal}UseCase!: Get${pascal}UseCase\n @Autowired() private list${pluralPascal}UseCase!: List${pluralPascal}UseCase\n @Autowired() private update${pascal}UseCase!: Update${pascal}UseCase\n @Autowired() private delete${pascal}UseCase!: Delete${pascal}UseCase\n\n @Get('/')\n @ApiTags('${pascal}')\n @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)\n async list(ctx: RequestContext) {\n return ctx.paginate(\n (parsed) => this.list${pluralPascal}UseCase.execute(parsed),\n ${pascal.toUpperCase()}_QUERY_CONFIG,\n )\n }\n\n @Get('/:id')\n @ApiTags('${pascal}')\n async getById(ctx: RequestContext) {\n const result = await this.get${pascal}UseCase.execute(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })\n @ApiTags('${pascal}')\n async create(ctx: RequestContext) {\n const result = await this.create${pascal}UseCase.execute(ctx.body)\n ctx.created(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })\n @ApiTags('${pascal}')\n async update(ctx: RequestContext) {\n const result = await this.update${pascal}UseCase.execute(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n @ApiTags('${pascal}')\n async remove(ctx: RequestContext) {\n await this.delete${pascal}UseCase.execute(ctx.params.id)\n ctx.noContent()\n }\n}\n`\n}\n","export function generateConstants(pascal: string): string {\n return `import type { QueryParamsConfig } from '@forinda/kickjs-core'\n\nexport const ${pascal.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {\n filterable: ['name'],\n sortable: ['name', 'createdAt'],\n searchable: ['name'],\n}\n`\n}\n","export function generateCreateDTO(pascal: string, kebab: string): string {\n return `import { z } from 'zod'\n\n/**\n * Create ${pascal} DTO — Zod schema for validating POST request bodies.\n * This schema is passed to @Post('/', { body: create${pascal}Schema }) for automatic validation.\n * It also generates OpenAPI request body docs when SwaggerAdapter is used.\n *\n * Add more fields as needed. Supported Zod types:\n * z.string(), z.number(), z.boolean(), z.enum([...]),\n * z.array(), z.object(), .optional(), .default(), .transform()\n */\nexport const create${pascal}Schema = z.object({\n name: z.string().min(1, 'Name is required').max(200),\n})\n\nexport type Create${pascal}DTO = z.infer<typeof create${pascal}Schema>\n`\n}\n\nexport function generateUpdateDTO(pascal: string, kebab: string): string {\n return `import { z } from 'zod'\n\nexport const update${pascal}Schema = z.object({\n name: z.string().min(1).max(200).optional(),\n})\n\nexport type Update${pascal}DTO = z.infer<typeof update${pascal}Schema>\n`\n}\n\nexport function generateResponseDTO(pascal: string, kebab: string): string {\n return `export interface ${pascal}ResponseDTO {\n id: string\n name: string\n createdAt: string\n updatedAt: string\n}\n`\n}\n","export function generateUseCases(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): { file: string; content: string }[] {\n return [\n {\n file: `create-${kebab}.use-case.ts`,\n content: `/**\n * Create ${pascal} Use Case\n *\n * Application layer — orchestrates a single business operation.\n * Use cases are thin: validate input (via DTO), call domain/repo, return response.\n * Keep business rules in the domain service, not here.\n */\nimport { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Create${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.create(dto)\n }\n}\n`,\n },\n {\n file: `get-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Get${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.repo.findById(id)\n }\n}\n`,\n },\n {\n file: `list-${plural}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\n\n@Service()\nexport class List${pluralPascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(parsed: ParsedQuery) {\n return this.repo.findPaginated(parsed)\n }\n}\n`,\n },\n {\n file: `update-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Update${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.update(id, dto)\n }\n}\n`,\n },\n {\n file: `delete-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\n\n@Service()\nexport class Delete${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<void> {\n await this.repo.delete(id)\n }\n}\n`,\n },\n ]\n}\n","export function generateRepositoryInterface(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} Repository Interface\n *\n * Domain layer — defines the contract for data access.\n * The interface lives in the domain layer; implementations live in infrastructure.\n * This inversion of dependencies keeps the domain pure and testable.\n *\n * To swap implementations (e.g. in-memory -> Drizzle -> Prisma),\n * change the factory in the module's register() method.\n */\nimport type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\n\nexport interface I${pascal}Repository {\n findById(id: string): Promise<${pascal}ResponseDTO | null>\n findAll(): Promise<${pascal}ResponseDTO[]>\n findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }>\n create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO>\n update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO>\n delete(id: string): Promise<void>\n}\n\nexport const ${pascal.toUpperCase()}_REPOSITORY = Symbol('I${pascal}Repository')\n`\n}\n\nexport function generateInMemoryRepository(pascal: string, kebab: string): string {\n return `/**\n * In-Memory ${pascal} Repository\n *\n * Infrastructure layer — implements the repository interface using a Map.\n * Useful for prototyping and testing. Replace with a database implementation\n * (Drizzle, Prisma, etc.) for production use.\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { randomUUID } from 'node:crypto'\nimport { Repository, HttpException } from '@forinda/kickjs-core'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\nimport type { I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'\n\n@Repository()\nexport class InMemory${pascal}Repository implements I${pascal}Repository {\n private store = new Map<string, ${pascal}ResponseDTO>()\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.store.get(id) ?? null\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n return Array.from(this.store.values())\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n const all = Array.from(this.store.values())\n const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)\n return { data, total: all.length }\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const now = new Date().toISOString()\n const entity: ${pascal}ResponseDTO = {\n id: randomUUID(),\n name: dto.name,\n createdAt: now,\n updatedAt: now,\n }\n this.store.set(entity.id, entity)\n return entity\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const existing = this.store.get(id)\n if (!existing) throw HttpException.notFound('${pascal} not found')\n const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }\n this.store.set(id, updated)\n return updated\n }\n\n async delete(id: string): Promise<void> {\n if (!this.store.has(id)) throw HttpException.notFound('${pascal} not found')\n this.store.delete(id)\n }\n}\n`\n}\n","export function generateDomainService(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} Domain Service\n *\n * Domain layer — contains business rules that don't belong to a single entity.\n * Use this for cross-entity logic, validation rules, and domain invariants.\n * Keep it free of HTTP/framework concerns.\n */\nimport { Service, Inject, HttpException } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'\n\n@Service()\nexport class ${pascal}DomainService {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async ensureExists(id: string): Promise<void> {\n const entity = await this.repo.findById(id)\n if (!entity) {\n throw HttpException.notFound('${pascal} not found')\n }\n }\n}\n`\n}\n\nexport function generateEntity(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} Entity\n *\n * Domain layer — the core business object.\n * Uses a private constructor with static factory methods (create, reconstitute)\n * to enforce invariants. Properties are accessed via getters to maintain encapsulation.\n *\n * Patterns used:\n * - Private constructor: prevents direct instantiation\n * - create(): factory for new entities (generates ID, sets timestamps)\n * - reconstitute(): factory for rebuilding from persistence (no side effects)\n * - changeName(): mutation method that enforces business rules\n */\nimport { ${pascal}Id } from '../value-objects/${kebab}-id.vo'\n\ninterface ${pascal}Props {\n id: ${pascal}Id\n name: string\n createdAt: Date\n updatedAt: Date\n}\n\nexport class ${pascal} {\n private constructor(private props: ${pascal}Props) {}\n\n static create(params: { name: string }): ${pascal} {\n const now = new Date()\n return new ${pascal}({\n id: ${pascal}Id.create(),\n name: params.name,\n createdAt: now,\n updatedAt: now,\n })\n }\n\n static reconstitute(props: ${pascal}Props): ${pascal} {\n return new ${pascal}(props)\n }\n\n get id(): ${pascal}Id {\n return this.props.id\n }\n get name(): string {\n return this.props.name\n }\n get createdAt(): Date {\n return this.props.createdAt\n }\n get updatedAt(): Date {\n return this.props.updatedAt\n }\n\n changeName(name: string): void {\n if (!name || name.trim().length === 0) {\n throw new Error('Name cannot be empty')\n }\n this.props.name = name.trim()\n this.props.updatedAt = new Date()\n }\n\n toJSON() {\n return {\n id: this.props.id.toString(),\n name: this.props.name,\n createdAt: this.props.createdAt.toISOString(),\n updatedAt: this.props.updatedAt.toISOString(),\n }\n }\n}\n`\n}\n\nexport function generateValueObject(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} ID Value Object\n *\n * Domain layer — wraps a primitive ID with type safety and validation.\n * Value objects are immutable and compared by value, not reference.\n *\n * ${pascal}Id.create() — generate a new UUID\n * ${pascal}Id.from(id) — wrap an existing ID string (validates non-empty)\n * id.equals(other) — compare two IDs by value\n */\nimport { randomUUID } from 'node:crypto'\n\nexport class ${pascal}Id {\n private constructor(private readonly value: string) {}\n\n static create(): ${pascal}Id {\n return new ${pascal}Id(randomUUID())\n }\n\n static from(id: string): ${pascal}Id {\n if (!id || id.trim().length === 0) {\n throw new Error('${pascal}Id cannot be empty')\n }\n return new ${pascal}Id(id)\n }\n\n toString(): string {\n return this.value\n }\n\n equals(other: ${pascal}Id): boolean {\n return this.value === other.value\n }\n}\n`\n}\n","export function generateControllerTest(pascal: string, kebab: string, plural: string): string {\n return `import { describe, it, expect, beforeEach } from 'vitest'\nimport { Container } from '@forinda/kickjs-core'\n\ndescribe('${pascal}Controller', () => {\n beforeEach(() => {\n Container.reset()\n })\n\n it('should be defined', () => {\n expect(true).toBe(true)\n })\n\n describe('POST /${plural}', () => {\n it('should create a new ${kebab}', async () => {\n // TODO: Set up test module, call create endpoint, assert 201\n expect(true).toBe(true)\n })\n })\n\n describe('GET /${plural}', () => {\n it('should return paginated ${plural}', async () => {\n // TODO: Set up test module, call list endpoint, assert { data, meta }\n expect(true).toBe(true)\n })\n })\n\n describe('GET /${plural}/:id', () => {\n it('should return a ${kebab} by id', async () => {\n // TODO: Create a ${kebab}, then fetch by id, assert match\n expect(true).toBe(true)\n })\n\n it('should return 404 for non-existent ${kebab}', async () => {\n // TODO: Fetch non-existent id, assert 404\n expect(true).toBe(true)\n })\n })\n\n describe('PUT /${plural}/:id', () => {\n it('should update an existing ${kebab}', async () => {\n // TODO: Create, update, assert changes\n expect(true).toBe(true)\n })\n })\n\n describe('DELETE /${plural}/:id', () => {\n it('should delete a ${kebab}', async () => {\n // TODO: Create, delete, assert gone\n expect(true).toBe(true)\n })\n })\n})\n`\n}\n\nexport function generateRepositoryTest(pascal: string, kebab: string, plural: string): string {\n return `import { describe, it, expect, beforeEach } from 'vitest'\nimport { InMemory${pascal}Repository } from '../infrastructure/repositories/in-memory-${kebab}.repository'\n\ndescribe('InMemory${pascal}Repository', () => {\n let repo: InMemory${pascal}Repository\n\n beforeEach(() => {\n repo = new InMemory${pascal}Repository()\n })\n\n it('should create and retrieve a ${kebab}', async () => {\n const created = await repo.create({ name: 'Test ${pascal}' })\n expect(created).toBeDefined()\n expect(created.name).toBe('Test ${pascal}')\n expect(created.id).toBeDefined()\n\n const found = await repo.findById(created.id)\n expect(found).toEqual(created)\n })\n\n it('should return null for non-existent id', async () => {\n const found = await repo.findById('non-existent')\n expect(found).toBeNull()\n })\n\n it('should list all ${plural}', async () => {\n await repo.create({ name: '${pascal} 1' })\n await repo.create({ name: '${pascal} 2' })\n\n const all = await repo.findAll()\n expect(all).toHaveLength(2)\n })\n\n it('should return paginated results', async () => {\n await repo.create({ name: '${pascal} 1' })\n await repo.create({ name: '${pascal} 2' })\n await repo.create({ name: '${pascal} 3' })\n\n const result = await repo.findPaginated({\n filters: [],\n sort: [],\n search: '',\n pagination: { page: 1, limit: 2, offset: 0 },\n })\n\n expect(result.data).toHaveLength(2)\n expect(result.total).toBe(3)\n })\n\n it('should update a ${kebab}', async () => {\n const created = await repo.create({ name: 'Original' })\n const updated = await repo.update(created.id, { name: 'Updated' })\n expect(updated.name).toBe('Updated')\n })\n\n it('should delete a ${kebab}', async () => {\n const created = await repo.create({ name: 'To Delete' })\n await repo.delete(created.id)\n const found = await repo.findById(created.id)\n expect(found).toBeNull()\n })\n})\n`\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateAdapterOptions {\n name: string\n outDir: string\n}\n\nexport async function generateAdapter(options: GenerateAdapterOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.adapter.ts`)\n await writeFileSafe(\n filePath,\n `import type { Express } from 'express'\nimport type { AppAdapter, AdapterMiddleware, Container } from '@forinda/kickjs-core'\n\nexport interface ${pascal}AdapterOptions {\n // Add your adapter configuration here\n}\n\n/**\n * ${pascal} adapter.\n *\n * Hooks into the Application lifecycle to add middleware, routes,\n * or external service connections.\n *\n * Usage:\n * bootstrap({\n * adapters: [new ${pascal}Adapter({ ... })],\n * })\n */\nexport class ${pascal}Adapter implements AppAdapter {\n name = '${pascal}Adapter'\n\n constructor(private options: ${pascal}AdapterOptions = {}) {}\n\n /**\n * Return middleware entries that the Application will mount.\n * Use \\`phase\\` to control where in the pipeline they run:\n * 'beforeGlobal' | 'afterGlobal' | 'beforeRoutes' | 'afterRoutes'\n */\n middleware(): AdapterMiddleware[] {\n return [\n // Example: add a custom header to all responses\n // {\n // phase: 'beforeGlobal',\n // handler: (_req: any, res: any, next: any) => {\n // res.setHeader('X-${pascal}', 'true')\n // next()\n // },\n // },\n // Example: scope middleware to a specific path\n // {\n // phase: 'beforeRoutes',\n // path: '/api/v1/admin',\n // handler: myAdminMiddleware(),\n // },\n ]\n }\n\n /**\n * Called before global middleware.\n * Use this to mount routes that bypass the middleware stack\n * (health checks, docs UI, static assets).\n */\n beforeMount(app: Express, container: Container): void {\n // Example: mount a status route\n // app.get('/${kebab}/status', (_req, res) => {\n // res.json({ status: 'ok' })\n // })\n }\n\n /**\n * Called after modules and routes are registered, before the server starts.\n * Use this for late-stage DI registrations or config validation.\n */\n beforeStart(app: Express, container: Container): void {\n // Example: register a service in the DI container\n // container.registerInstance(MY_TOKEN, new MyService(this.options))\n }\n\n /**\n * Called after the HTTP server is listening.\n * Use this to attach to the raw http.Server (Socket.IO, gRPC, etc).\n */\n afterStart(server: any, container: Container): void {\n // Example: attach Socket.IO\n // const io = new Server(server)\n // container.registerInstance(SOCKET_IO, io)\n }\n\n /**\n * Called on graceful shutdown. Clean up connections.\n */\n async shutdown(): Promise<void> {\n // Example: close a connection pool\n // await this.pool.end()\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateMiddlewareOptions {\n name: string\n outDir: string\n}\n\nexport async function generateMiddleware(options: GenerateMiddlewareOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.middleware.ts`)\n await writeFileSafe(\n filePath,\n `import type { Request, Response, NextFunction } from 'express'\n\nexport interface ${toPascalCase(name)}Options {\n // Add configuration options here\n}\n\n/**\n * ${toPascalCase(name)} middleware.\n *\n * Usage in bootstrap:\n * middleware: [${camel}()]\n *\n * Usage with adapter:\n * middleware() { return [{ handler: ${camel}(), phase: 'afterGlobal' }] }\n *\n * Usage with @Middleware decorator:\n * @Middleware(${camel}())\n */\nexport function ${camel}(options: ${toPascalCase(name)}Options = {}) {\n return (req: Request, res: Response, next: NextFunction) => {\n // Implement your middleware logic here\n next()\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateGuardOptions {\n name: string\n outDir: string\n}\n\nexport async function generateGuard(options: GenerateGuardOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.guard.ts`)\n await writeFileSafe(\n filePath,\n `import { Container, HttpException } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\n\n/**\n * ${pascal} guard.\n *\n * Guards protect routes by checking conditions before the handler runs.\n * Return early with an error response to block access.\n *\n * Usage:\n * @Middleware(${camel}Guard)\n * @Get('/protected')\n * async handler(ctx: RequestContext) { ... }\n */\nexport async function ${camel}Guard(ctx: RequestContext, next: () => void): Promise<void> {\n // Example: check for an authorization header\n const header = ctx.headers.authorization\n if (!header?.startsWith('Bearer ')) {\n ctx.res.status(401).json({ message: 'Missing or invalid authorization header' })\n return\n }\n\n const token = header.slice(7)\n\n try {\n // Verify the token using a service from the DI container\n // const container = Container.getInstance()\n // const authService = container.resolve(AuthService)\n // const payload = authService.verifyToken(token)\n // ctx.set('auth', payload)\n\n next()\n } catch {\n ctx.res.status(401).json({ message: 'Invalid or expired token' })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateServiceOptions {\n name: string\n outDir: string\n}\n\nexport async function generateService(options: GenerateServiceOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.service.ts`)\n await writeFileSafe(\n filePath,\n `import { Service } from '@forinda/kickjs-core'\n\n@Service()\nexport class ${pascal}Service {\n // Inject dependencies via constructor\n // constructor(\n // @Inject(MY_REPO) private readonly repo: IMyRepository,\n // ) {}\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateControllerOptions {\n name: string\n outDir: string\n}\n\nexport async function generateController(options: GenerateControllerOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.controller.ts`)\n await writeFileSafe(\n filePath,\n `import { Controller, Get, Post, Autowired } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\n\n@Controller()\nexport class ${pascal}Controller {\n // @Autowired() private myService!: MyService\n\n @Get('/')\n async list(ctx: RequestContext) {\n ctx.json({ message: '${pascal} list' })\n }\n\n @Post('/')\n async create(ctx: RequestContext) {\n ctx.created({ message: '${pascal} created', data: ctx.body })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateDtoOptions {\n name: string\n outDir: string\n}\n\nexport async function generateDto(options: GenerateDtoOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.dto.ts`)\n await writeFileSafe(\n filePath,\n `import { z } from 'zod'\n\nexport const ${camel}Schema = z.object({\n // Define your schema fields here\n name: z.string().min(1).max(200),\n})\n\nexport type ${pascal}DTO = z.infer<typeof ${camel}Schema>\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { execSync } from 'node:child_process'\nimport { writeFileSafe } from '../utils/fs'\n\ninterface InitProjectOptions {\n name: string\n directory: string\n packageManager?: 'pnpm' | 'npm' | 'yarn'\n initGit?: boolean\n installDeps?: boolean\n}\n\n/** Scaffold a new KickJS project */\nexport async function initProject(options: InitProjectOptions): Promise<void> {\n const { name, directory, packageManager = 'pnpm' } = options\n const dir = directory\n\n console.log(`\\n Creating KickJS project: ${name}\\n`)\n\n // ── package.json ────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'package.json'),\n JSON.stringify(\n {\n name,\n version: '0.1.0',\n type: 'module',\n scripts: {\n dev: 'kick dev',\n 'dev:debug': 'kick dev:debug',\n build: 'kick build',\n start: 'kick start',\n test: 'vitest run',\n 'test:watch': 'vitest',\n typecheck: 'tsc --noEmit',\n lint: 'eslint src/',\n format: 'prettier --write src/',\n },\n dependencies: {\n '@forinda/kickjs-core': '^0.1.0',\n '@forinda/kickjs-http': '^0.1.0',\n '@forinda/kickjs-config': '^0.1.0',\n '@forinda/kickjs-swagger': '^0.1.0',\n express: '^5.1.0',\n 'reflect-metadata': '^0.2.2',\n zod: '^4.3.6',\n pino: '^10.3.1',\n 'pino-pretty': '^13.1.3',\n },\n devDependencies: {\n '@forinda/kickjs-cli': '^0.1.0',\n '@swc/core': '^1.7.28',\n '@types/express': '^5.0.6',\n '@types/node': '^24.5.2',\n 'unplugin-swc': '^1.5.9',\n vite: '^7.3.1',\n 'vite-node': '^5.3.0',\n vitest: '^3.2.4',\n typescript: '^5.9.2',\n prettier: '^3.8.1',\n },\n },\n null,\n 2,\n ),\n )\n\n // ── vite.config.ts — enables HMR + SWC for decorators ──────────────\n await writeFileSafe(\n join(dir, 'vite.config.ts'),\n `import { defineConfig } from 'vite'\nimport { resolve } from 'path'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n resolve: {\n alias: {\n '@': resolve(__dirname, 'src'),\n },\n },\n server: {\n watch: { usePolling: false },\n hmr: true,\n },\n build: {\n target: 'node20',\n ssr: true,\n outDir: 'dist',\n sourcemap: true,\n rollupOptions: {\n input: resolve(__dirname, 'src/index.ts'),\n output: { format: 'esm' },\n },\n },\n})\n`,\n )\n\n // ── tsconfig.json ───────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'tsconfig.json'),\n JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['node'],\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n sourceMap: true,\n declaration: true,\n experimentalDecorators: true,\n emitDecoratorMetadata: true,\n outDir: 'dist',\n rootDir: 'src',\n paths: { '@/*': ['./src/*'] },\n },\n include: ['src'],\n },\n null,\n 2,\n ),\n )\n\n // ── .prettierrc ─────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.prettierrc'),\n JSON.stringify(\n {\n semi: false,\n singleQuote: true,\n trailingComma: 'all',\n printWidth: 100,\n tabWidth: 2,\n },\n null,\n 2,\n ),\n )\n\n // ── .gitignore ──────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.gitignore'),\n `node_modules/\ndist/\n.env\ncoverage/\n.DS_Store\n*.tsbuildinfo\n`,\n )\n\n // ── .env ────────────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.env'),\n `PORT=3000\nNODE_ENV=development\n`,\n )\n\n await writeFileSafe(\n join(dir, '.env.example'),\n `PORT=3000\nNODE_ENV=development\n`,\n )\n\n // ── src/index.ts — clean entry point with Swagger baked in ────────\n await writeFileSafe(\n join(dir, 'src/index.ts'),\n `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { SwaggerAdapter } from '@forinda/kickjs-swagger'\nimport { modules } from './modules'\n\nbootstrap({\n modules,\n adapters: [\n new SwaggerAdapter({\n info: { title: '${name}', version: '0.1.0' },\n }),\n ],\n})\n`,\n )\n\n // ── src/modules/index.ts ────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'src/modules/index.ts'),\n `import type { AppModuleClass } from '@forinda/kickjs-core'\n\nexport const modules: AppModuleClass[] = []\n`,\n )\n\n // ── kick.config.ts — CLI configuration ─────────────────────────────\n await writeFileSafe(\n join(dir, 'kick.config.ts'),\n `import { defineConfig } from '@forinda/kickjs-cli'\n\nexport default defineConfig({\n modulesDir: 'src/modules',\n defaultRepo: 'inmemory',\n\n commands: [\n {\n name: 'test',\n description: 'Run tests with Vitest',\n steps: 'npx vitest run',\n },\n {\n name: 'format',\n description: 'Format code with Prettier',\n steps: 'npx prettier --write src/',\n },\n {\n name: 'format:check',\n description: 'Check formatting without writing',\n steps: 'npx prettier --check src/',\n },\n {\n name: 'check',\n description: 'Run typecheck + format check',\n steps: ['npx tsc --noEmit', 'npx prettier --check src/'],\n aliases: ['verify', 'ci'],\n },\n ],\n})\n`,\n )\n\n // ── vitest.config.ts ────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'vitest.config.ts'),\n `import { defineConfig } from 'vitest/config'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n test: {\n globals: true,\n environment: 'node',\n include: ['src/**/*.test.ts'],\n },\n})\n`,\n )\n\n // ── Git Init ─────────────────────────────────────────────────────────\n if (options.initGit) {\n try {\n execSync('git init', { cwd: dir, stdio: 'pipe' })\n execSync('git add -A', { cwd: dir, stdio: 'pipe' })\n execSync('git commit -m \"chore: initial commit from kick new\"', {\n cwd: dir,\n stdio: 'pipe',\n })\n console.log(' Git repository initialized')\n } catch {\n console.log(' Warning: git init failed (git may not be installed)')\n }\n }\n\n // ── Install Dependencies ────────────────────────────────────────────\n if (options.installDeps) {\n console.log(`\\n Installing dependencies with ${packageManager}...\\n`)\n try {\n execSync(`${packageManager} install`, { cwd: dir, stdio: 'inherit' })\n console.log('\\n Dependencies installed successfully!')\n } catch {\n console.log(`\\n Warning: ${packageManager} install failed. Run it manually.`)\n }\n }\n\n console.log('\\n Project scaffolded successfully!')\n console.log()\n\n const needsCd = dir !== process.cwd()\n console.log(' Next steps:')\n if (needsCd) console.log(` cd ${name}`)\n if (!options.installDeps) console.log(` ${packageManager} install`)\n console.log(' kick g module user')\n console.log(' kick dev')\n console.log()\n console.log(' Commands:')\n console.log(' kick dev Start dev server with Vite HMR')\n console.log(' kick build Production build via Vite')\n console.log(' kick start Run production build')\n console.log(' kick g module X Generate a DDD module')\n console.log()\n}\n","import { readFile, access } from 'node:fs/promises'\nimport { join } from 'node:path'\n\n/** A custom command that developers can register via kick.config.ts */\nexport interface KickCommandDefinition {\n /** The command name (e.g. 'db:migrate', 'seed', 'proto:gen') */\n name: string\n /** Description shown in --help */\n description: string\n /**\n * Shell command(s) to run. Can be a single string or an array of\n * sequential steps. Use {args} as a placeholder for CLI arguments.\n *\n * @example\n * 'npx drizzle-kit migrate'\n * ['npx drizzle-kit generate', 'npx drizzle-kit migrate']\n */\n steps: string | string[]\n /** Optional aliases (e.g. ['migrate'] for 'db:migrate') */\n aliases?: string[]\n}\n\n/** Configuration for the kick.config.ts file */\nexport interface KickConfig {\n /** Where modules live (default: 'src/modules') */\n modulesDir?: string\n /** Default repository implementation for generators */\n defaultRepo?: 'drizzle' | 'inmemory' | 'prisma'\n /** Drizzle schema output directory */\n schemaDir?: string\n /** Custom commands that extend the CLI */\n commands?: KickCommandDefinition[]\n /** Code style overrides (auto-detected from prettier when possible) */\n style?: {\n semicolons?: boolean\n quotes?: 'single' | 'double'\n trailingComma?: 'all' | 'es5' | 'none'\n indent?: number\n }\n}\n\n/** Helper to define a type-safe kick.config.ts */\nexport function defineConfig(config: KickConfig): KickConfig {\n return config\n}\n\nconst CONFIG_FILES = ['kick.config.ts', 'kick.config.js', 'kick.config.mjs', 'kick.config.json']\n\n/** Load kick.config.* from the project root */\nexport async function loadKickConfig(cwd: string): Promise<KickConfig | null> {\n for (const filename of CONFIG_FILES) {\n const filepath = join(cwd, filename)\n try {\n await access(filepath)\n } catch {\n continue\n }\n\n if (filename.endsWith('.json')) {\n const content = await readFile(filepath, 'utf-8')\n return JSON.parse(content)\n }\n\n // For .ts/.js/.mjs — dynamic import (use file URL for cross-platform compat)\n try {\n const { pathToFileURL } = await import('node:url')\n const mod = await import(pathToFileURL(filepath).href)\n return mod.default ?? mod\n } catch (err) {\n if (filename.endsWith('.ts')) {\n console.warn(\n `Warning: Failed to load ${filename}. TypeScript config files require ` +\n 'a runtime loader (e.g. tsx, ts-node) or use kick.config.js/.mjs instead.',\n )\n }\n continue\n }\n }\n return null\n}\n"],"mappings":"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","names":["join","writeFile","mkdir","access","readFile","dirname","writeFileSafe","filePath","content","mkdir","dirname","recursive","writeFile","fileExists","filePath","access","toPascalCase","name","replace","_","c","toUpperCase","toCamelCase","pascal","charAt","toLowerCase","slice","toKebabCase","pluralize","endsWith","test","pluralizePascal","readFile","writeFile","generateModuleIndex","pascal","kebab","plural","repo","repoClass","repoFile","toUpperCase","generateController","pascal","kebab","plural","pluralPascal","toUpperCase","generateConstants","pascal","toUpperCase","generateCreateDTO","pascal","kebab","generateUpdateDTO","generateResponseDTO","generateUseCases","pascal","kebab","plural","pluralPascal","file","content","toUpperCase","generateRepositoryInterface","pascal","kebab","toUpperCase","generateInMemoryRepository","generateDomainService","pascal","kebab","toUpperCase","generateEntity","generateValueObject","generateControllerTest","pascal","kebab","plural","generateRepositoryTest","generateModule","options","name","modulesDir","noEntity","noTests","repo","minimal","kebab","toKebabCase","pascal","toPascalCase","camel","toCamelCase","plural","pluralize","pluralPascal","pluralizePascal","moduleDir","join","files","write","relativePath","content","fullPath","writeFileSafe","push","generateModuleIndex","generateConstants","generateController","generateCreateDTO","generateUpdateDTO","generateResponseDTO","useCases","generateUseCases","uc","file","generateRepositoryInterface","generateDomainService","generateInMemoryRepository","generateEntity","generateValueObject","generateControllerTest","generateRepositoryTest","autoRegisterModule","indexPath","exists","fileExists","readFile","importLine","includes","lastImportIdx","lastIndexOf","lineEnd","indexOf","slice","replace","_match","open","existing","close","trimmed","trim","needsComma","endsWith","trimEnd","writeFile","join","generateAdapter","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateMiddleware","options","name","outDir","kebab","toKebabCase","camel","toCamelCase","files","filePath","join","writeFileSafe","toPascalCase","push","join","generateGuard","options","name","outDir","kebab","toKebabCase","camel","toCamelCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateService","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateController","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateDto","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","camel","toCamelCase","files","filePath","join","writeFileSafe","push","join","execSync","initProject","options","name","directory","packageManager","dir","console","log","writeFileSafe","join","JSON","stringify","version","type","scripts","dev","build","start","test","typecheck","lint","format","dependencies","express","zod","pino","devDependencies","vite","vitest","typescript","prettier","compilerOptions","target","module","moduleResolution","lib","types","strict","esModuleInterop","skipLibCheck","sourceMap","declaration","experimentalDecorators","emitDecoratorMetadata","outDir","rootDir","paths","include","semi","singleQuote","trailingComma","printWidth","tabWidth","initGit","execSync","cwd","stdio","installDeps","needsCd","process","readFile","access","join","defineConfig","config","CONFIG_FILES","loadKickConfig","cwd","filename","filepath","join","access","endsWith","content","readFile","JSON","parse","pathToFileURL","mod","href","default","err","console","warn"]}
|