@hono-di/generate 0.0.6

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/index.cjs ADDED
@@ -0,0 +1,342 @@
1
+ 'use strict';
2
+
3
+ var path4 = require('path');
4
+ var fs2 = require('fs');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var path4__default = /*#__PURE__*/_interopDefault(path4);
9
+ var fs2__default = /*#__PURE__*/_interopDefault(fs2);
10
+
11
+ // src/engine.ts
12
+
13
+ // src/utils/name.utils.ts
14
+ function toKebabCase(str) {
15
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
16
+ }
17
+ function toPascalCase(str) {
18
+ return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => word.toUpperCase()).replace(/[\s-_]+/g, "");
19
+ }
20
+ function toCamelCase(str) {
21
+ const pascal = toPascalCase(str);
22
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
23
+ }
24
+ function fileExists(filePath) {
25
+ return fs2__default.default.existsSync(filePath);
26
+ }
27
+
28
+ // src/schematics/module.schematic.ts
29
+ function generateModule(name) {
30
+ const pascalName = toPascalCase(name);
31
+ return `import { Module } from '@hono-di/core';
32
+
33
+ @Module({
34
+ imports: [
35
+ // hono-di:imports
36
+ ],
37
+ controllers: [
38
+ // hono-di:controllers
39
+ ],
40
+ providers: [
41
+ // hono-di:providers
42
+ ],
43
+ })
44
+ export class ${pascalName}Module {}
45
+ `;
46
+ }
47
+
48
+ // src/schematics/service.schematic.ts
49
+ function generateService(name) {
50
+ const pascalName = toPascalCase(name);
51
+ return `import { Injectable } from '@hono-di/core';
52
+
53
+ @Injectable()
54
+ export class ${pascalName}Service {
55
+ constructor() {}
56
+ }
57
+ `;
58
+ }
59
+
60
+ // src/schematics/controller.schematic.ts
61
+ function generateController(name) {
62
+ const pascalName = toPascalCase(name);
63
+ return `import { Controller, Get } from '@hono-di/core';
64
+
65
+ @Controller('${name.toLowerCase()}')
66
+ export class ${pascalName}Controller {
67
+ constructor() {}
68
+
69
+ @Get('/')
70
+ index() {
71
+ return 'Hello ${pascalName}';
72
+ }
73
+ }
74
+ `;
75
+ }
76
+
77
+ // src/schematics/class.schematic.ts
78
+ function generateClass(name) {
79
+ const pascalName = toPascalCase(name);
80
+ return `export class ${pascalName} {}
81
+ `;
82
+ }
83
+
84
+ // src/schematics/interface.schematic.ts
85
+ function generateInterface(name) {
86
+ const pascalName = toPascalCase(name);
87
+ return `export interface ${pascalName} {}
88
+ `;
89
+ }
90
+
91
+ // src/schematics/pipe.schematic.ts
92
+ function generatePipe(name) {
93
+ return `import { PipeTransform, Injectable, ArgumentMetadata } from '@hono-di/core';
94
+
95
+ @Injectable()
96
+ export class ${toPascalCase(name)}Pipe implements PipeTransform {
97
+ transform(value: any, metadata: ArgumentMetadata) {
98
+ return value;
99
+ }
100
+ }
101
+ `;
102
+ }
103
+
104
+ // src/schematics/guard.schematic.ts
105
+ function generateGuard(name) {
106
+ return `import { CanActivate, Injectable, ExecutionContext } from '@hono-di/core';
107
+ import { Observable } from 'rxjs';
108
+
109
+ @Injectable()
110
+ export class ${toPascalCase(name)}Guard implements CanActivate {
111
+ canActivate(
112
+ context: ExecutionContext,
113
+ ): boolean | Promise<boolean> | Observable<boolean> {
114
+ return true;
115
+ }
116
+ }
117
+ `;
118
+ }
119
+
120
+ // src/schematics/filter.schematic.ts
121
+ function generateFilter(name) {
122
+ return `import { ArgumentsHost, Catch, ExceptionFilter } from '@hono-di/core';
123
+
124
+ @Catch()
125
+ export class ${toPascalCase(name)}Filter<T> implements ExceptionFilter {
126
+ catch(exception: T, host: ArgumentsHost) {}
127
+ }
128
+ `;
129
+ }
130
+
131
+ // src/schematics/interceptor.schematic.ts
132
+ function generateInterceptor(name) {
133
+ return `import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@hono-di/core';
134
+ import { Observable } from 'rxjs';
135
+ import { map } from 'rxjs/operators';
136
+
137
+ @Injectable()
138
+ export class ${toPascalCase(name)}Interceptor implements NestInterceptor {
139
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
140
+ return next.handle().pipe(map((data) => data));
141
+ }
142
+ }
143
+ `;
144
+ }
145
+
146
+ // src/schematics/decorator.schematic.ts
147
+ function generateDecorator(name) {
148
+ return `import { SetMetadata } from '@hono-di/core';
149
+
150
+ export const ${toCamelCase(name)} = (...args: string[]) => SetMetadata('${toCamelCase(name)}', args);
151
+ `;
152
+ }
153
+ function findNearestModule(dir, rootDir = process.cwd()) {
154
+ let currentDir = dir;
155
+ while (true) {
156
+ if (fs2__default.default.existsSync(currentDir)) {
157
+ const files = fs2__default.default.readdirSync(currentDir);
158
+ const moduleFile = files.find((f) => f.endsWith(".module.ts"));
159
+ if (moduleFile) {
160
+ return path4__default.default.join(currentDir, moduleFile);
161
+ }
162
+ }
163
+ if (currentDir === rootDir || currentDir === path4__default.default.parse(currentDir).root) {
164
+ break;
165
+ }
166
+ currentDir = path4__default.default.dirname(currentDir);
167
+ }
168
+ return null;
169
+ }
170
+ function patchModule(modulePath, className, importPath, type) {
171
+ const content = fs2__default.default.readFileSync(modulePath, "utf-8");
172
+ let newContent = content;
173
+ const importLine = `import { ${className} } from '${importPath}';`;
174
+ if (!content.includes(importLine)) {
175
+ const lastImportMatches = content.match(/^import .*$/gm);
176
+ if (lastImportMatches && lastImportMatches.length > 0) {
177
+ const lastImport = lastImportMatches[lastImportMatches.length - 1];
178
+ newContent = newContent.replace(lastImport, `${lastImport}
179
+ ${importLine}`);
180
+ } else {
181
+ newContent = `${importLine}
182
+ ${newContent}`;
183
+ }
184
+ }
185
+ const markerMap = {
186
+ controller: "// hono-di:controllers",
187
+ provider: "// hono-di:providers",
188
+ import: "// hono-di:imports"
189
+ };
190
+ const marker = markerMap[type];
191
+ if (content.includes(marker)) {
192
+ newContent = newContent.replace(marker, `${className}, ${marker}`);
193
+ return newContent;
194
+ }
195
+ return null;
196
+ }
197
+ function normalizePath(p) {
198
+ return p.split(path4__default.default.sep).join("/");
199
+ }
200
+
201
+ // src/engine.ts
202
+ function generate(input) {
203
+ const result = {
204
+ success: true,
205
+ operations: [],
206
+ errors: [],
207
+ warnings: []
208
+ };
209
+ try {
210
+ const { type, name, path: inputPath = "src", flat, force, dryRun, skipImport } = input;
211
+ const kebabName = toKebabCase(name);
212
+ const pascalName = toPascalCase(name);
213
+ let suffix = "";
214
+ let ext = ".ts";
215
+ switch (type) {
216
+ case "module":
217
+ suffix = ".module";
218
+ break;
219
+ case "controller":
220
+ suffix = ".controller";
221
+ break;
222
+ case "service":
223
+ case "provider":
224
+ suffix = ".service";
225
+ break;
226
+ // Provider alias maps to service typically
227
+ case "class":
228
+ suffix = "";
229
+ break;
230
+ case "interface":
231
+ suffix = ".interface";
232
+ break;
233
+ case "pipe":
234
+ suffix = ".pipe";
235
+ break;
236
+ case "guard":
237
+ suffix = ".guard";
238
+ break;
239
+ case "filter":
240
+ suffix = ".filter";
241
+ break;
242
+ case "interceptor":
243
+ suffix = ".interceptor";
244
+ break;
245
+ case "decorator":
246
+ suffix = ".decorator";
247
+ break;
248
+ }
249
+ const fileName = `${kebabName}${suffix}${ext}`;
250
+ let targetDir = path4__default.default.join(process.cwd(), inputPath);
251
+ if (!flat) {
252
+ targetDir = path4__default.default.join(targetDir, kebabName);
253
+ }
254
+ const filePath = path4__default.default.join(targetDir, fileName);
255
+ if (fileExists(filePath) && !force) {
256
+ result.success = false;
257
+ result.errors?.push(`File ${filePath} already exists.`);
258
+ result.operations.push({ action: "error", path: filePath });
259
+ return result;
260
+ }
261
+ let content = "";
262
+ switch (type) {
263
+ case "module":
264
+ content = generateModule(name);
265
+ break;
266
+ case "controller":
267
+ content = generateController(name);
268
+ break;
269
+ case "service":
270
+ case "provider":
271
+ content = generateService(name);
272
+ break;
273
+ case "class":
274
+ content = generateClass(name);
275
+ break;
276
+ case "interface":
277
+ content = generateInterface(name);
278
+ break;
279
+ case "pipe":
280
+ content = generatePipe(name);
281
+ break;
282
+ case "guard":
283
+ content = generateGuard(name);
284
+ break;
285
+ case "filter":
286
+ content = generateFilter(name);
287
+ break;
288
+ case "interceptor":
289
+ content = generateInterceptor(name);
290
+ break;
291
+ case "decorator":
292
+ content = generateDecorator(name);
293
+ break;
294
+ }
295
+ result.operations.push({
296
+ action: fileExists(filePath) ? "overwrite" : "create",
297
+ path: filePath,
298
+ content
299
+ });
300
+ if (input.spec !== false) {
301
+ const specName = `${kebabName}${suffix}.spec${ext}`;
302
+ const specPath = path4__default.default.join(targetDir, specName);
303
+ if (!fileExists(specPath) || force) {
304
+ result.operations.push({
305
+ action: fileExists(specPath) ? "overwrite" : "create",
306
+ path: specPath,
307
+ content: `// Tests for ${name}`
308
+ });
309
+ }
310
+ }
311
+ if (!skipImport && type !== "module" && type !== "class" && type !== "interface" && type !== "decorator") {
312
+ const modulePath = findNearestModule(targetDir);
313
+ if (modulePath) {
314
+ const moduleDir = path4__default.default.dirname(modulePath);
315
+ let relativePath = path4__default.default.relative(moduleDir, filePath);
316
+ relativePath = relativePath.replace(/\.ts$/, "");
317
+ if (!relativePath.startsWith(".")) {
318
+ relativePath = `./${relativePath}`;
319
+ }
320
+ relativePath = normalizePath(relativePath);
321
+ let importType = "provider";
322
+ if (type === "controller") importType = "controller";
323
+ const patchedContent = patchModule(modulePath, `${pascalName}${toPascalCase(type === "provider" ? "Service" : type)}`, relativePath, importType);
324
+ if (patchedContent) {
325
+ result.operations.push({
326
+ action: "overwrite",
327
+ path: modulePath,
328
+ content: patchedContent
329
+ });
330
+ }
331
+ }
332
+ }
333
+ } catch (error) {
334
+ result.success = false;
335
+ result.errors?.push(error.message);
336
+ }
337
+ return result;
338
+ }
339
+
340
+ exports.generate = generate;
341
+ //# sourceMappingURL=index.cjs.map
342
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/name.utils.ts","../src/utils/fs.utils.ts","../src/schematics/module.schematic.ts","../src/schematics/service.schematic.ts","../src/schematics/controller.schematic.ts","../src/schematics/class.schematic.ts","../src/schematics/interface.schematic.ts","../src/schematics/pipe.schematic.ts","../src/schematics/guard.schematic.ts","../src/schematics/filter.schematic.ts","../src/schematics/interceptor.schematic.ts","../src/schematics/decorator.schematic.ts","../src/utils/patch.utils.ts","../src/utils/path.utils.ts","../src/engine.ts"],"names":["fs","path"],"mappings":"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","file":"index.cjs","sourcesContent":["export function toKebabCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n\nexport function toPascalCase(str: string): string {\n return str\n .replace(/(?:^\\w|[A-Z]|\\b\\w)/g, (word) => word.toUpperCase())\n .replace(/[\\s-_]+/g, \"\");\n}\n\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport function ensureDirectoryExists(filePath: string) {\n const dirname = path.dirname(filePath);\n if (fs.existsSync(dirname)) {\n return true;\n }\n ensureDirectoryExists(dirname);\n fs.mkdirSync(dirname);\n}\n\nexport function writeFile(filePath: string, content: string) {\n ensureDirectoryExists(filePath);\n fs.writeFileSync(filePath, content, 'utf-8');\n}\n\nexport function fileExists(filePath: string): boolean {\n return fs.existsSync(filePath);\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateModule(name: string) {\n const pascalName = toPascalCase(name);\n return `import { Module } from '@hono-di/core';\n\n@Module({\n imports: [\n // hono-di:imports\n ],\n controllers: [\n // hono-di:controllers\n ],\n providers: [\n // hono-di:providers\n ],\n})\nexport class ${pascalName}Module {}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateService(name: string) {\n const pascalName = toPascalCase(name);\n return `import { Injectable } from '@hono-di/core';\n\n@Injectable()\nexport class ${pascalName}Service {\n constructor() {}\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateController(name: string) {\n const pascalName = toPascalCase(name);\n return `import { Controller, Get } from '@hono-di/core';\n\n@Controller('${name.toLowerCase()}')\nexport class ${pascalName}Controller {\n constructor() {}\n\n @Get('/')\n index() {\n return 'Hello ${pascalName}';\n }\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateClass(name: string) {\n const pascalName = toPascalCase(name);\n return `export class ${pascalName} {}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateInterface(name: string) {\n const pascalName = toPascalCase(name);\n return `export interface ${pascalName} {}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generatePipe(name: string) {\n return `import { PipeTransform, Injectable, ArgumentMetadata } from '@hono-di/core';\n\n@Injectable()\nexport class ${toPascalCase(name)}Pipe implements PipeTransform {\n transform(value: any, metadata: ArgumentMetadata) {\n return value;\n }\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateGuard(name: string) {\n return `import { CanActivate, Injectable, ExecutionContext } from '@hono-di/core';\nimport { Observable } from 'rxjs';\n\n@Injectable()\nexport class ${toPascalCase(name)}Guard implements CanActivate {\n canActivate(\n context: ExecutionContext,\n ): boolean | Promise<boolean> | Observable<boolean> {\n return true;\n }\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateFilter(name: string) {\n return `import { ArgumentsHost, Catch, ExceptionFilter } from '@hono-di/core';\n\n@Catch()\nexport class ${toPascalCase(name)}Filter<T> implements ExceptionFilter {\n catch(exception: T, host: ArgumentsHost) {}\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateInterceptor(name: string) {\n return `import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@hono-di/core';\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\n\n@Injectable()\nexport class ${toPascalCase(name)}Interceptor implements NestInterceptor {\n intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n return next.handle().pipe(map((data) => data));\n }\n}\n`;\n}\n","import { toCamelCase } from '../utils/name.utils';\n\nexport function generateDecorator(name: string) {\n // Camel case for decorator function\n return `import { SetMetadata } from '@hono-di/core';\n\nexport const ${toCamelCase(name)} = (...args: string[]) => SetMetadata('${toCamelCase(name)}', args);\n`;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { toPascalCase } from './name.utils';\nimport { normalizePath } from './path.utils';\n\nexport function findNearestModule(dir: string, rootDir: string = process.cwd()): string | null {\n let currentDir = dir;\n while (true) {\n if (fs.existsSync(currentDir)) {\n const files = fs.readdirSync(currentDir);\n const moduleFile = files.find((f) => f.endsWith('.module.ts'));\n if (moduleFile) {\n return path.join(currentDir, moduleFile);\n }\n }\n\n if (currentDir === rootDir || currentDir === path.parse(currentDir).root) {\n break;\n }\n currentDir = path.dirname(currentDir);\n }\n return null;\n}\n\nexport function patchModule(\n modulePath: string,\n className: string,\n importPath: string,\n type: 'controller' | 'provider' | 'import'\n): string | null {\n const content = fs.readFileSync(modulePath, 'utf-8');\n let newContent = content;\n\n // 1. Add Import\n const importLine = `import { ${className} } from '${importPath}';`;\n if (!content.includes(importLine)) {\n const lastImportMatches = content.match(/^import .*$/gm);\n if (lastImportMatches && lastImportMatches.length > 0) {\n const lastImport = lastImportMatches[lastImportMatches.length - 1];\n newContent = newContent.replace(lastImport, `${lastImport}\\n${importLine}`);\n } else {\n newContent = `${importLine}\\n${newContent}`;\n }\n }\n\n // 2. Add to Metadata\n const markerMap = {\n controller: '// hono-di:controllers',\n provider: '// hono-di:providers',\n import: '// hono-di:imports',\n };\n const marker = markerMap[type];\n\n if (content.includes(marker)) {\n newContent = newContent.replace(marker, `${className}, ${marker}`);\n return newContent;\n }\n\n return null; // Return null if no patch happened (no markers)\n}\n","import path from 'node:path';\n\nexport function normalizePath(p: string): string {\n return p.split(path.sep).join('/');\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { GenerateInput, GenerateResult } from './types';\nimport { toKebabCase, toPascalCase } from './utils/name.utils';\nimport { ensureDirectoryExists, fileExists } from './utils/fs.utils';\nimport { generateModule } from './schematics/module.schematic';\nimport { generateService } from './schematics/service.schematic';\nimport { generateController } from './schematics/controller.schematic';\nimport { generateClass } from './schematics/class.schematic';\nimport { generateInterface } from './schematics/interface.schematic';\nimport { generatePipe } from './schematics/pipe.schematic';\nimport { generateGuard } from './schematics/guard.schematic';\nimport { generateFilter } from './schematics/filter.schematic';\nimport { generateInterceptor } from './schematics/interceptor.schematic';\nimport { generateDecorator } from './schematics/decorator.schematic';\nimport { findNearestModule, patchModule } from './utils/patch.utils';\nimport { normalizePath } from './utils/path.utils';\n\nexport function generate(input: GenerateInput): GenerateResult {\n const result: GenerateResult = {\n success: true,\n operations: [],\n errors: [],\n warnings: [],\n };\n\n try {\n const { type, name, path: inputPath = 'src', flat, force, dryRun, skipImport } = input;\n const kebabName = toKebabCase(name);\n const pascalName = toPascalCase(name);\n\n // Determine suffix and extension\n let suffix = '';\n let ext = '.ts';\n switch (type) {\n case 'module': suffix = '.module'; break;\n case 'controller': suffix = '.controller'; break;\n case 'service':\n case 'provider': suffix = '.service'; break; // Provider alias maps to service typically\n case 'class': suffix = ''; break;\n case 'interface': suffix = '.interface'; break;\n case 'pipe': suffix = '.pipe'; break;\n case 'guard': suffix = '.guard'; break;\n case 'filter': suffix = '.filter'; break;\n case 'interceptor': suffix = '.interceptor'; break;\n case 'decorator': suffix = '.decorator'; break;\n }\n\n // Determine target directory and file name\n const fileName = `${kebabName}${suffix}${ext}`;\n let targetDir = path.join(process.cwd(), inputPath);\n\n if (!flat) {\n targetDir = path.join(targetDir, kebabName);\n }\n\n const filePath = path.join(targetDir, fileName);\n\n // Check existence\n if (fileExists(filePath) && !force) {\n result.success = false;\n result.errors?.push(`File ${filePath} already exists.`);\n result.operations.push({ action: 'error', path: filePath });\n return result;\n }\n\n // Generate content\n let content = '';\n switch (type) {\n case 'module': content = generateModule(name); break;\n case 'controller': content = generateController(name); break;\n case 'service':\n case 'provider': content = generateService(name); break;\n case 'class': content = generateClass(name); break;\n case 'interface': content = generateInterface(name); break;\n case 'pipe': content = generatePipe(name); break;\n case 'guard': content = generateGuard(name); break;\n case 'filter': content = generateFilter(name); break;\n case 'interceptor': content = generateInterceptor(name); break;\n case 'decorator': content = generateDecorator(name); break;\n }\n\n // Add operation\n result.operations.push({\n action: fileExists(filePath) ? 'overwrite' : 'create',\n path: filePath,\n content: content\n });\n\n if (input.spec !== false) { // Default true\n const specName = `${kebabName}${suffix}.spec${ext}`;\n const specPath = path.join(targetDir, specName);\n if (!fileExists(specPath) || force) {\n result.operations.push({\n action: fileExists(specPath) ? 'overwrite' : 'create',\n path: specPath,\n content: `// Tests for ${name}`\n });\n }\n }\n\n // Auto-import logic\n if (!skipImport && type !== 'module' && type !== 'class' && type !== 'interface' && type !== 'decorator') {\n const modulePath = findNearestModule(targetDir);\n if (modulePath) {\n // Calculate relative path for import\n const moduleDir = path.dirname(modulePath);\n let relativePath = path.relative(moduleDir, filePath);\n // Remove extension for import\n relativePath = relativePath.replace(/\\.ts$/, '');\n if (!relativePath.startsWith('.')) {\n relativePath = `./${relativePath}`;\n }\n relativePath = normalizePath(relativePath);\n\n let importType: 'controller' | 'provider' | 'import' = 'provider';\n if (type === 'controller') importType = 'controller';\n\n // TODO: Handle module imports into other modules if newly generated is a module\n\n const patchedContent = patchModule(modulePath, `${pascalName}${toPascalCase(type === 'provider' ? 'Service' : type)}`, relativePath, importType);\n\n if (patchedContent) {\n result.operations.push({\n action: 'overwrite',\n path: modulePath,\n content: patchedContent\n });\n }\n }\n }\n\n } catch (error: any) {\n result.success = false;\n result.errors?.push(error.message);\n }\n\n return result;\n}\n"]}
@@ -0,0 +1,25 @@
1
+ type GenerateType = 'module' | 'controller' | 'provider' | 'service' | 'class' | 'interface' | 'pipe' | 'guard' | 'filter' | 'interceptor' | 'decorator';
2
+ interface GenerateInput {
3
+ type: GenerateType;
4
+ name: string;
5
+ path?: string;
6
+ flat?: boolean;
7
+ dryRun?: boolean;
8
+ force?: boolean;
9
+ spec?: boolean;
10
+ skipImport?: boolean;
11
+ }
12
+ interface GenerateResult {
13
+ success: boolean;
14
+ operations: Array<{
15
+ action: 'create' | 'overwrite' | 'skip' | 'error';
16
+ path: string;
17
+ content?: string;
18
+ }>;
19
+ errors?: string[];
20
+ warnings?: string[];
21
+ }
22
+
23
+ declare function generate(input: GenerateInput): GenerateResult;
24
+
25
+ export { type GenerateInput, type GenerateResult, type GenerateType, generate };
@@ -0,0 +1,25 @@
1
+ type GenerateType = 'module' | 'controller' | 'provider' | 'service' | 'class' | 'interface' | 'pipe' | 'guard' | 'filter' | 'interceptor' | 'decorator';
2
+ interface GenerateInput {
3
+ type: GenerateType;
4
+ name: string;
5
+ path?: string;
6
+ flat?: boolean;
7
+ dryRun?: boolean;
8
+ force?: boolean;
9
+ spec?: boolean;
10
+ skipImport?: boolean;
11
+ }
12
+ interface GenerateResult {
13
+ success: boolean;
14
+ operations: Array<{
15
+ action: 'create' | 'overwrite' | 'skip' | 'error';
16
+ path: string;
17
+ content?: string;
18
+ }>;
19
+ errors?: string[];
20
+ warnings?: string[];
21
+ }
22
+
23
+ declare function generate(input: GenerateInput): GenerateResult;
24
+
25
+ export { type GenerateInput, type GenerateResult, type GenerateType, generate };
package/dist/index.js ADDED
@@ -0,0 +1,335 @@
1
+ import path4 from 'path';
2
+ import fs2 from 'fs';
3
+
4
+ // src/engine.ts
5
+
6
+ // src/utils/name.utils.ts
7
+ function toKebabCase(str) {
8
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
9
+ }
10
+ function toPascalCase(str) {
11
+ return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => word.toUpperCase()).replace(/[\s-_]+/g, "");
12
+ }
13
+ function toCamelCase(str) {
14
+ const pascal = toPascalCase(str);
15
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
16
+ }
17
+ function fileExists(filePath) {
18
+ return fs2.existsSync(filePath);
19
+ }
20
+
21
+ // src/schematics/module.schematic.ts
22
+ function generateModule(name) {
23
+ const pascalName = toPascalCase(name);
24
+ return `import { Module } from '@hono-di/core';
25
+
26
+ @Module({
27
+ imports: [
28
+ // hono-di:imports
29
+ ],
30
+ controllers: [
31
+ // hono-di:controllers
32
+ ],
33
+ providers: [
34
+ // hono-di:providers
35
+ ],
36
+ })
37
+ export class ${pascalName}Module {}
38
+ `;
39
+ }
40
+
41
+ // src/schematics/service.schematic.ts
42
+ function generateService(name) {
43
+ const pascalName = toPascalCase(name);
44
+ return `import { Injectable } from '@hono-di/core';
45
+
46
+ @Injectable()
47
+ export class ${pascalName}Service {
48
+ constructor() {}
49
+ }
50
+ `;
51
+ }
52
+
53
+ // src/schematics/controller.schematic.ts
54
+ function generateController(name) {
55
+ const pascalName = toPascalCase(name);
56
+ return `import { Controller, Get } from '@hono-di/core';
57
+
58
+ @Controller('${name.toLowerCase()}')
59
+ export class ${pascalName}Controller {
60
+ constructor() {}
61
+
62
+ @Get('/')
63
+ index() {
64
+ return 'Hello ${pascalName}';
65
+ }
66
+ }
67
+ `;
68
+ }
69
+
70
+ // src/schematics/class.schematic.ts
71
+ function generateClass(name) {
72
+ const pascalName = toPascalCase(name);
73
+ return `export class ${pascalName} {}
74
+ `;
75
+ }
76
+
77
+ // src/schematics/interface.schematic.ts
78
+ function generateInterface(name) {
79
+ const pascalName = toPascalCase(name);
80
+ return `export interface ${pascalName} {}
81
+ `;
82
+ }
83
+
84
+ // src/schematics/pipe.schematic.ts
85
+ function generatePipe(name) {
86
+ return `import { PipeTransform, Injectable, ArgumentMetadata } from '@hono-di/core';
87
+
88
+ @Injectable()
89
+ export class ${toPascalCase(name)}Pipe implements PipeTransform {
90
+ transform(value: any, metadata: ArgumentMetadata) {
91
+ return value;
92
+ }
93
+ }
94
+ `;
95
+ }
96
+
97
+ // src/schematics/guard.schematic.ts
98
+ function generateGuard(name) {
99
+ return `import { CanActivate, Injectable, ExecutionContext } from '@hono-di/core';
100
+ import { Observable } from 'rxjs';
101
+
102
+ @Injectable()
103
+ export class ${toPascalCase(name)}Guard implements CanActivate {
104
+ canActivate(
105
+ context: ExecutionContext,
106
+ ): boolean | Promise<boolean> | Observable<boolean> {
107
+ return true;
108
+ }
109
+ }
110
+ `;
111
+ }
112
+
113
+ // src/schematics/filter.schematic.ts
114
+ function generateFilter(name) {
115
+ return `import { ArgumentsHost, Catch, ExceptionFilter } from '@hono-di/core';
116
+
117
+ @Catch()
118
+ export class ${toPascalCase(name)}Filter<T> implements ExceptionFilter {
119
+ catch(exception: T, host: ArgumentsHost) {}
120
+ }
121
+ `;
122
+ }
123
+
124
+ // src/schematics/interceptor.schematic.ts
125
+ function generateInterceptor(name) {
126
+ return `import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@hono-di/core';
127
+ import { Observable } from 'rxjs';
128
+ import { map } from 'rxjs/operators';
129
+
130
+ @Injectable()
131
+ export class ${toPascalCase(name)}Interceptor implements NestInterceptor {
132
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
133
+ return next.handle().pipe(map((data) => data));
134
+ }
135
+ }
136
+ `;
137
+ }
138
+
139
+ // src/schematics/decorator.schematic.ts
140
+ function generateDecorator(name) {
141
+ return `import { SetMetadata } from '@hono-di/core';
142
+
143
+ export const ${toCamelCase(name)} = (...args: string[]) => SetMetadata('${toCamelCase(name)}', args);
144
+ `;
145
+ }
146
+ function findNearestModule(dir, rootDir = process.cwd()) {
147
+ let currentDir = dir;
148
+ while (true) {
149
+ if (fs2.existsSync(currentDir)) {
150
+ const files = fs2.readdirSync(currentDir);
151
+ const moduleFile = files.find((f) => f.endsWith(".module.ts"));
152
+ if (moduleFile) {
153
+ return path4.join(currentDir, moduleFile);
154
+ }
155
+ }
156
+ if (currentDir === rootDir || currentDir === path4.parse(currentDir).root) {
157
+ break;
158
+ }
159
+ currentDir = path4.dirname(currentDir);
160
+ }
161
+ return null;
162
+ }
163
+ function patchModule(modulePath, className, importPath, type) {
164
+ const content = fs2.readFileSync(modulePath, "utf-8");
165
+ let newContent = content;
166
+ const importLine = `import { ${className} } from '${importPath}';`;
167
+ if (!content.includes(importLine)) {
168
+ const lastImportMatches = content.match(/^import .*$/gm);
169
+ if (lastImportMatches && lastImportMatches.length > 0) {
170
+ const lastImport = lastImportMatches[lastImportMatches.length - 1];
171
+ newContent = newContent.replace(lastImport, `${lastImport}
172
+ ${importLine}`);
173
+ } else {
174
+ newContent = `${importLine}
175
+ ${newContent}`;
176
+ }
177
+ }
178
+ const markerMap = {
179
+ controller: "// hono-di:controllers",
180
+ provider: "// hono-di:providers",
181
+ import: "// hono-di:imports"
182
+ };
183
+ const marker = markerMap[type];
184
+ if (content.includes(marker)) {
185
+ newContent = newContent.replace(marker, `${className}, ${marker}`);
186
+ return newContent;
187
+ }
188
+ return null;
189
+ }
190
+ function normalizePath(p) {
191
+ return p.split(path4.sep).join("/");
192
+ }
193
+
194
+ // src/engine.ts
195
+ function generate(input) {
196
+ const result = {
197
+ success: true,
198
+ operations: [],
199
+ errors: [],
200
+ warnings: []
201
+ };
202
+ try {
203
+ const { type, name, path: inputPath = "src", flat, force, dryRun, skipImport } = input;
204
+ const kebabName = toKebabCase(name);
205
+ const pascalName = toPascalCase(name);
206
+ let suffix = "";
207
+ let ext = ".ts";
208
+ switch (type) {
209
+ case "module":
210
+ suffix = ".module";
211
+ break;
212
+ case "controller":
213
+ suffix = ".controller";
214
+ break;
215
+ case "service":
216
+ case "provider":
217
+ suffix = ".service";
218
+ break;
219
+ // Provider alias maps to service typically
220
+ case "class":
221
+ suffix = "";
222
+ break;
223
+ case "interface":
224
+ suffix = ".interface";
225
+ break;
226
+ case "pipe":
227
+ suffix = ".pipe";
228
+ break;
229
+ case "guard":
230
+ suffix = ".guard";
231
+ break;
232
+ case "filter":
233
+ suffix = ".filter";
234
+ break;
235
+ case "interceptor":
236
+ suffix = ".interceptor";
237
+ break;
238
+ case "decorator":
239
+ suffix = ".decorator";
240
+ break;
241
+ }
242
+ const fileName = `${kebabName}${suffix}${ext}`;
243
+ let targetDir = path4.join(process.cwd(), inputPath);
244
+ if (!flat) {
245
+ targetDir = path4.join(targetDir, kebabName);
246
+ }
247
+ const filePath = path4.join(targetDir, fileName);
248
+ if (fileExists(filePath) && !force) {
249
+ result.success = false;
250
+ result.errors?.push(`File ${filePath} already exists.`);
251
+ result.operations.push({ action: "error", path: filePath });
252
+ return result;
253
+ }
254
+ let content = "";
255
+ switch (type) {
256
+ case "module":
257
+ content = generateModule(name);
258
+ break;
259
+ case "controller":
260
+ content = generateController(name);
261
+ break;
262
+ case "service":
263
+ case "provider":
264
+ content = generateService(name);
265
+ break;
266
+ case "class":
267
+ content = generateClass(name);
268
+ break;
269
+ case "interface":
270
+ content = generateInterface(name);
271
+ break;
272
+ case "pipe":
273
+ content = generatePipe(name);
274
+ break;
275
+ case "guard":
276
+ content = generateGuard(name);
277
+ break;
278
+ case "filter":
279
+ content = generateFilter(name);
280
+ break;
281
+ case "interceptor":
282
+ content = generateInterceptor(name);
283
+ break;
284
+ case "decorator":
285
+ content = generateDecorator(name);
286
+ break;
287
+ }
288
+ result.operations.push({
289
+ action: fileExists(filePath) ? "overwrite" : "create",
290
+ path: filePath,
291
+ content
292
+ });
293
+ if (input.spec !== false) {
294
+ const specName = `${kebabName}${suffix}.spec${ext}`;
295
+ const specPath = path4.join(targetDir, specName);
296
+ if (!fileExists(specPath) || force) {
297
+ result.operations.push({
298
+ action: fileExists(specPath) ? "overwrite" : "create",
299
+ path: specPath,
300
+ content: `// Tests for ${name}`
301
+ });
302
+ }
303
+ }
304
+ if (!skipImport && type !== "module" && type !== "class" && type !== "interface" && type !== "decorator") {
305
+ const modulePath = findNearestModule(targetDir);
306
+ if (modulePath) {
307
+ const moduleDir = path4.dirname(modulePath);
308
+ let relativePath = path4.relative(moduleDir, filePath);
309
+ relativePath = relativePath.replace(/\.ts$/, "");
310
+ if (!relativePath.startsWith(".")) {
311
+ relativePath = `./${relativePath}`;
312
+ }
313
+ relativePath = normalizePath(relativePath);
314
+ let importType = "provider";
315
+ if (type === "controller") importType = "controller";
316
+ const patchedContent = patchModule(modulePath, `${pascalName}${toPascalCase(type === "provider" ? "Service" : type)}`, relativePath, importType);
317
+ if (patchedContent) {
318
+ result.operations.push({
319
+ action: "overwrite",
320
+ path: modulePath,
321
+ content: patchedContent
322
+ });
323
+ }
324
+ }
325
+ }
326
+ } catch (error) {
327
+ result.success = false;
328
+ result.errors?.push(error.message);
329
+ }
330
+ return result;
331
+ }
332
+
333
+ export { generate };
334
+ //# sourceMappingURL=index.js.map
335
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/name.utils.ts","../src/utils/fs.utils.ts","../src/schematics/module.schematic.ts","../src/schematics/service.schematic.ts","../src/schematics/controller.schematic.ts","../src/schematics/class.schematic.ts","../src/schematics/interface.schematic.ts","../src/schematics/pipe.schematic.ts","../src/schematics/guard.schematic.ts","../src/schematics/filter.schematic.ts","../src/schematics/interceptor.schematic.ts","../src/schematics/decorator.schematic.ts","../src/utils/patch.utils.ts","../src/utils/path.utils.ts","../src/engine.ts"],"names":["fs","path"],"mappings":"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","file":"index.js","sourcesContent":["export function toKebabCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n\nexport function toPascalCase(str: string): string {\n return str\n .replace(/(?:^\\w|[A-Z]|\\b\\w)/g, (word) => word.toUpperCase())\n .replace(/[\\s-_]+/g, \"\");\n}\n\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport function ensureDirectoryExists(filePath: string) {\n const dirname = path.dirname(filePath);\n if (fs.existsSync(dirname)) {\n return true;\n }\n ensureDirectoryExists(dirname);\n fs.mkdirSync(dirname);\n}\n\nexport function writeFile(filePath: string, content: string) {\n ensureDirectoryExists(filePath);\n fs.writeFileSync(filePath, content, 'utf-8');\n}\n\nexport function fileExists(filePath: string): boolean {\n return fs.existsSync(filePath);\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateModule(name: string) {\n const pascalName = toPascalCase(name);\n return `import { Module } from '@hono-di/core';\n\n@Module({\n imports: [\n // hono-di:imports\n ],\n controllers: [\n // hono-di:controllers\n ],\n providers: [\n // hono-di:providers\n ],\n})\nexport class ${pascalName}Module {}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateService(name: string) {\n const pascalName = toPascalCase(name);\n return `import { Injectable } from '@hono-di/core';\n\n@Injectable()\nexport class ${pascalName}Service {\n constructor() {}\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateController(name: string) {\n const pascalName = toPascalCase(name);\n return `import { Controller, Get } from '@hono-di/core';\n\n@Controller('${name.toLowerCase()}')\nexport class ${pascalName}Controller {\n constructor() {}\n\n @Get('/')\n index() {\n return 'Hello ${pascalName}';\n }\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateClass(name: string) {\n const pascalName = toPascalCase(name);\n return `export class ${pascalName} {}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateInterface(name: string) {\n const pascalName = toPascalCase(name);\n return `export interface ${pascalName} {}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generatePipe(name: string) {\n return `import { PipeTransform, Injectable, ArgumentMetadata } from '@hono-di/core';\n\n@Injectable()\nexport class ${toPascalCase(name)}Pipe implements PipeTransform {\n transform(value: any, metadata: ArgumentMetadata) {\n return value;\n }\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateGuard(name: string) {\n return `import { CanActivate, Injectable, ExecutionContext } from '@hono-di/core';\nimport { Observable } from 'rxjs';\n\n@Injectable()\nexport class ${toPascalCase(name)}Guard implements CanActivate {\n canActivate(\n context: ExecutionContext,\n ): boolean | Promise<boolean> | Observable<boolean> {\n return true;\n }\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateFilter(name: string) {\n return `import { ArgumentsHost, Catch, ExceptionFilter } from '@hono-di/core';\n\n@Catch()\nexport class ${toPascalCase(name)}Filter<T> implements ExceptionFilter {\n catch(exception: T, host: ArgumentsHost) {}\n}\n`;\n}\n","import { toPascalCase } from '../utils/name.utils';\n\nexport function generateInterceptor(name: string) {\n return `import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@hono-di/core';\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\n\n@Injectable()\nexport class ${toPascalCase(name)}Interceptor implements NestInterceptor {\n intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n return next.handle().pipe(map((data) => data));\n }\n}\n`;\n}\n","import { toCamelCase } from '../utils/name.utils';\n\nexport function generateDecorator(name: string) {\n // Camel case for decorator function\n return `import { SetMetadata } from '@hono-di/core';\n\nexport const ${toCamelCase(name)} = (...args: string[]) => SetMetadata('${toCamelCase(name)}', args);\n`;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { toPascalCase } from './name.utils';\nimport { normalizePath } from './path.utils';\n\nexport function findNearestModule(dir: string, rootDir: string = process.cwd()): string | null {\n let currentDir = dir;\n while (true) {\n if (fs.existsSync(currentDir)) {\n const files = fs.readdirSync(currentDir);\n const moduleFile = files.find((f) => f.endsWith('.module.ts'));\n if (moduleFile) {\n return path.join(currentDir, moduleFile);\n }\n }\n\n if (currentDir === rootDir || currentDir === path.parse(currentDir).root) {\n break;\n }\n currentDir = path.dirname(currentDir);\n }\n return null;\n}\n\nexport function patchModule(\n modulePath: string,\n className: string,\n importPath: string,\n type: 'controller' | 'provider' | 'import'\n): string | null {\n const content = fs.readFileSync(modulePath, 'utf-8');\n let newContent = content;\n\n // 1. Add Import\n const importLine = `import { ${className} } from '${importPath}';`;\n if (!content.includes(importLine)) {\n const lastImportMatches = content.match(/^import .*$/gm);\n if (lastImportMatches && lastImportMatches.length > 0) {\n const lastImport = lastImportMatches[lastImportMatches.length - 1];\n newContent = newContent.replace(lastImport, `${lastImport}\\n${importLine}`);\n } else {\n newContent = `${importLine}\\n${newContent}`;\n }\n }\n\n // 2. Add to Metadata\n const markerMap = {\n controller: '// hono-di:controllers',\n provider: '// hono-di:providers',\n import: '// hono-di:imports',\n };\n const marker = markerMap[type];\n\n if (content.includes(marker)) {\n newContent = newContent.replace(marker, `${className}, ${marker}`);\n return newContent;\n }\n\n return null; // Return null if no patch happened (no markers)\n}\n","import path from 'node:path';\n\nexport function normalizePath(p: string): string {\n return p.split(path.sep).join('/');\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { GenerateInput, GenerateResult } from './types';\nimport { toKebabCase, toPascalCase } from './utils/name.utils';\nimport { ensureDirectoryExists, fileExists } from './utils/fs.utils';\nimport { generateModule } from './schematics/module.schematic';\nimport { generateService } from './schematics/service.schematic';\nimport { generateController } from './schematics/controller.schematic';\nimport { generateClass } from './schematics/class.schematic';\nimport { generateInterface } from './schematics/interface.schematic';\nimport { generatePipe } from './schematics/pipe.schematic';\nimport { generateGuard } from './schematics/guard.schematic';\nimport { generateFilter } from './schematics/filter.schematic';\nimport { generateInterceptor } from './schematics/interceptor.schematic';\nimport { generateDecorator } from './schematics/decorator.schematic';\nimport { findNearestModule, patchModule } from './utils/patch.utils';\nimport { normalizePath } from './utils/path.utils';\n\nexport function generate(input: GenerateInput): GenerateResult {\n const result: GenerateResult = {\n success: true,\n operations: [],\n errors: [],\n warnings: [],\n };\n\n try {\n const { type, name, path: inputPath = 'src', flat, force, dryRun, skipImport } = input;\n const kebabName = toKebabCase(name);\n const pascalName = toPascalCase(name);\n\n // Determine suffix and extension\n let suffix = '';\n let ext = '.ts';\n switch (type) {\n case 'module': suffix = '.module'; break;\n case 'controller': suffix = '.controller'; break;\n case 'service':\n case 'provider': suffix = '.service'; break; // Provider alias maps to service typically\n case 'class': suffix = ''; break;\n case 'interface': suffix = '.interface'; break;\n case 'pipe': suffix = '.pipe'; break;\n case 'guard': suffix = '.guard'; break;\n case 'filter': suffix = '.filter'; break;\n case 'interceptor': suffix = '.interceptor'; break;\n case 'decorator': suffix = '.decorator'; break;\n }\n\n // Determine target directory and file name\n const fileName = `${kebabName}${suffix}${ext}`;\n let targetDir = path.join(process.cwd(), inputPath);\n\n if (!flat) {\n targetDir = path.join(targetDir, kebabName);\n }\n\n const filePath = path.join(targetDir, fileName);\n\n // Check existence\n if (fileExists(filePath) && !force) {\n result.success = false;\n result.errors?.push(`File ${filePath} already exists.`);\n result.operations.push({ action: 'error', path: filePath });\n return result;\n }\n\n // Generate content\n let content = '';\n switch (type) {\n case 'module': content = generateModule(name); break;\n case 'controller': content = generateController(name); break;\n case 'service':\n case 'provider': content = generateService(name); break;\n case 'class': content = generateClass(name); break;\n case 'interface': content = generateInterface(name); break;\n case 'pipe': content = generatePipe(name); break;\n case 'guard': content = generateGuard(name); break;\n case 'filter': content = generateFilter(name); break;\n case 'interceptor': content = generateInterceptor(name); break;\n case 'decorator': content = generateDecorator(name); break;\n }\n\n // Add operation\n result.operations.push({\n action: fileExists(filePath) ? 'overwrite' : 'create',\n path: filePath,\n content: content\n });\n\n if (input.spec !== false) { // Default true\n const specName = `${kebabName}${suffix}.spec${ext}`;\n const specPath = path.join(targetDir, specName);\n if (!fileExists(specPath) || force) {\n result.operations.push({\n action: fileExists(specPath) ? 'overwrite' : 'create',\n path: specPath,\n content: `// Tests for ${name}`\n });\n }\n }\n\n // Auto-import logic\n if (!skipImport && type !== 'module' && type !== 'class' && type !== 'interface' && type !== 'decorator') {\n const modulePath = findNearestModule(targetDir);\n if (modulePath) {\n // Calculate relative path for import\n const moduleDir = path.dirname(modulePath);\n let relativePath = path.relative(moduleDir, filePath);\n // Remove extension for import\n relativePath = relativePath.replace(/\\.ts$/, '');\n if (!relativePath.startsWith('.')) {\n relativePath = `./${relativePath}`;\n }\n relativePath = normalizePath(relativePath);\n\n let importType: 'controller' | 'provider' | 'import' = 'provider';\n if (type === 'controller') importType = 'controller';\n\n // TODO: Handle module imports into other modules if newly generated is a module\n\n const patchedContent = patchModule(modulePath, `${pascalName}${toPascalCase(type === 'provider' ? 'Service' : type)}`, relativePath, importType);\n\n if (patchedContent) {\n result.operations.push({\n action: 'overwrite',\n path: modulePath,\n content: patchedContent\n });\n }\n }\n }\n\n } catch (error: any) {\n result.success = false;\n result.errors?.push(error.message);\n }\n\n return result;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@hono-di/generate",
3
+ "version": "0.0.6",
4
+ "description": "Code generation API for Hono-DI",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "test": "bun test"
15
+ },
16
+ "devDependencies": {
17
+ "tsup": "^8.5.1",
18
+ "typescript": "^5.4.0",
19
+ "@types/node": "^22.10.6"
20
+ }
21
+ }