@hono-di/client 0.0.6 → 0.0.7

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 CHANGED
@@ -190,5 +190,3 @@ var TypeGenerator = class {
190
190
  };
191
191
 
192
192
  exports.TypeGenerator = TypeGenerator;
193
- //# sourceMappingURL=index.cjs.map
194
- //# sourceMappingURL=index.cjs.map
package/dist/index.js CHANGED
@@ -188,5 +188,3 @@ var TypeGenerator = class {
188
188
  };
189
189
 
190
190
  export { TypeGenerator };
191
- //# sourceMappingURL=index.js.map
192
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hono-di/client",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Type-safe client generator for Hono-DI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/generator.ts"],"names":["Project"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Project, ClassDeclaration, MethodDeclaration, Decorator } from 'ts-morph';\nimport * as path from 'path';\n\nexport interface GeneratorOptions {\n tsConfigFilePath: string;\n output?: string;\n}\n\nexport class TypeGenerator {\n constructor(private readonly options: GeneratorOptions) { }\n\n async generate(): Promise<string> {\n const project = new Project({\n tsConfigFilePath: this.options.tsConfigFilePath,\n });\n\n const controllers = this.findControllers(project);\n const routes = this.extractRoutes(controllers, project);\n\n return this.buildTypeDefinition(routes);\n }\n\n private findControllers(project: Project): ClassDeclaration[] {\n const controllers: ClassDeclaration[] = [];\n const sourceFiles = project.getSourceFiles();\n\n for (const sourceFile of sourceFiles) {\n const classes = sourceFile.getClasses();\n for (const cls of classes) {\n if (this.hasDecorator(cls, 'Controller')) {\n controllers.push(cls);\n }\n }\n }\n return controllers;\n }\n\n private extractRoutes(controllers: ClassDeclaration[], project: Project) {\n const routes: any[] = [];\n\n for (const controller of controllers) {\n const controllerPrefix = this.getDecoratorArgument(controller, 'Controller') || '';\n const methods = controller.getMethods();\n\n for (const method of methods) {\n const methodDecorator = this.getMethodDecorator(method);\n if (methodDecorator) {\n const methodPath = this.getDecoratorArgument(method, methodDecorator.getName()) || '/';\n const httpMethod = methodDecorator.getName().toLowerCase(); // Get, Post -> get, post\n const returnType = method.getReturnType().getText();\n\n // Normalize path\n const fullPath = this.normalizePath(controllerPrefix, methodPath);\n\n // Collect return type\n this.collectType(returnType, project);\n\n routes.push({\n path: fullPath,\n method: httpMethod,\n returnType,\n methodName: method.getName(),\n parameters: method.getParameters().map(p => {\n const typeText = p.getType().getText();\n this.collectType(typeText, project);\n return {\n name: p.getName(),\n type: typeText,\n decorators: p.getDecorators().map(d => ({\n name: d.getName(),\n arguments: d.getArguments().map(arg => arg.getText().replace(/^['\"]|['\"]$/g, ''))\n }))\n };\n })\n });\n }\n }\n }\n return routes;\n }\n\n private extraTypes = new Map<string, string>();\n\n private collectType(typeText: string, project: Project) {\n // Simple heuristic: if it starts with uppercase and is not a primitive\n const primitives = ['string', 'number', 'boolean', 'any', 'void', 'null', 'undefined', 'Date'];\n const isArray = typeText.endsWith('[]');\n const baseType = isArray ? typeText.slice(0, -2) : typeText;\n\n if (primitives.includes(baseType) || baseType.startsWith('{') || baseType.includes('<')) {\n return;\n }\n\n if (this.extraTypes.has(baseType)) {\n return;\n }\n\n // Find the class or interface in the project\n for (const sourceFile of project.getSourceFiles()) {\n const cls = sourceFile.getClass(baseType);\n if (cls) {\n const properties = cls.getProperties().map(p => {\n return `${p.getName()}: ${p.getType().getText()};`;\n });\n this.extraTypes.set(baseType, `export interface ${baseType} {\\n ${properties.join('\\n ')}\\n}`);\n return;\n }\n\n const intf = sourceFile.getInterface(baseType);\n if (intf) {\n const properties = intf.getProperties().map(p => {\n return `${p.getName()}: ${p.getType().getText()};`;\n });\n this.extraTypes.set(baseType, `export interface ${baseType} {\\n ${properties.join('\\n ')}\\n}`);\n return;\n }\n }\n }\n\n private buildTypeDefinition(routes: any[]): string {\n let typeDef = `import { Hono } from 'hono';\\n`;\n typeDef += `import type { InferRequestType, InferResponseType } from 'hono/client';\\n\\n`;\n\n // Add extra types\n for (const [name, def] of this.extraTypes) {\n typeDef += `${def}\\n\\n`;\n }\n\n typeDef += `export type AppType = Hono<any, {\\n`;\n\n // Group routes by path\n const routesByPath: Record<string, any[]> = {};\n for (const route of routes) {\n if (!routesByPath[route.path]) {\n routesByPath[route.path] = [];\n }\n routesByPath[route.path].push(route);\n }\n\n for (const [path, pathRoutes] of Object.entries(routesByPath)) {\n typeDef += ` '${path}': {\\n`;\n for (const route of pathRoutes) {\n let inputType = '';\n const inputFields: string[] = [];\n\n // Handle Body\n const bodyParam = route.parameters.find((p: any) => p.decorators.some((d: any) => d.name === 'Body'));\n if (bodyParam) {\n inputFields.push(`json: ${bodyParam.type}`);\n }\n\n // Handle Query\n const queryParam = route.parameters.find((p: any) => p.decorators.some((d: any) => d.name === 'Query'));\n if (queryParam) {\n inputFields.push(`query: ${queryParam.type}`);\n }\n\n // Handle Param\n const paramParams = route.parameters.filter((p: any) => p.decorators.some((d: any) => d.name === 'Param'));\n if (paramParams.length > 0) {\n const paramFields = paramParams.map((p: any) => {\n const decorator = p.decorators.find((d: any) => d.name === 'Param');\n const paramName = decorator.arguments[0] || p.name;\n return `${paramName}: ${p.type}`;\n });\n inputFields.push(`param: { ${paramFields.join('; ')} }`);\n }\n\n inputType = `{ ${inputFields.join(', ')} }`;\n\n typeDef += ` $${route.method}: {\\n`;\n typeDef += ` input: ${inputType},\\n`;\n typeDef += ` output: { json: ${route.returnType} },\\n`;\n typeDef += ` outputFormat: 'json',\\n`;\n typeDef += ` status: 200\\n`;\n typeDef += ` },\\n`;\n }\n typeDef += ` },\\n`;\n }\n\n typeDef += `}>;\\n`;\n return typeDef;\n }\n\n private hasDecorator(node: ClassDeclaration | MethodDeclaration, name: string): boolean {\n return node.getDecorators().some(d => d.getName() === name);\n }\n\n private getMethodDecorator(method: MethodDeclaration): Decorator | undefined {\n const methods = ['Get', 'Post', 'Put', 'Delete', 'Patch'];\n return method.getDecorators().find(d => methods.includes(d.getName()));\n }\n\n private getDecoratorArgument(node: ClassDeclaration | MethodDeclaration, name: string): string | undefined {\n const decorator = node.getDecorator(name);\n if (!decorator) return undefined;\n const args = decorator.getArguments();\n if (args.length > 0) {\n const text = args[0].getText();\n // Remove quotes\n return text.replace(/^['\"]|['\"]$/g, '');\n }\n return undefined;\n }\n\n private normalizePath(prefix: string, path: string): string {\n const cleanPrefix = prefix ? prefix.replace(/^\\/+/, '').replace(/\\/+$/, '') : '';\n const cleanPath = path ? path.replace(/^\\/+/, '').replace(/\\/+$/, '') : '';\n let result = '';\n if (cleanPrefix) result += `/${cleanPrefix}`;\n if (cleanPath) result += `/${cleanPath}`;\n return result || '/';\n }\n}\n"]}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/generator.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { Project, ClassDeclaration, MethodDeclaration, Decorator } from 'ts-morph';\nimport * as path from 'path';\n\nexport interface GeneratorOptions {\n tsConfigFilePath: string;\n output?: string;\n}\n\nexport class TypeGenerator {\n constructor(private readonly options: GeneratorOptions) { }\n\n async generate(): Promise<string> {\n const project = new Project({\n tsConfigFilePath: this.options.tsConfigFilePath,\n });\n\n const controllers = this.findControllers(project);\n const routes = this.extractRoutes(controllers, project);\n\n return this.buildTypeDefinition(routes);\n }\n\n private findControllers(project: Project): ClassDeclaration[] {\n const controllers: ClassDeclaration[] = [];\n const sourceFiles = project.getSourceFiles();\n\n for (const sourceFile of sourceFiles) {\n const classes = sourceFile.getClasses();\n for (const cls of classes) {\n if (this.hasDecorator(cls, 'Controller')) {\n controllers.push(cls);\n }\n }\n }\n return controllers;\n }\n\n private extractRoutes(controllers: ClassDeclaration[], project: Project) {\n const routes: any[] = [];\n\n for (const controller of controllers) {\n const controllerPrefix = this.getDecoratorArgument(controller, 'Controller') || '';\n const methods = controller.getMethods();\n\n for (const method of methods) {\n const methodDecorator = this.getMethodDecorator(method);\n if (methodDecorator) {\n const methodPath = this.getDecoratorArgument(method, methodDecorator.getName()) || '/';\n const httpMethod = methodDecorator.getName().toLowerCase(); // Get, Post -> get, post\n const returnType = method.getReturnType().getText();\n\n // Normalize path\n const fullPath = this.normalizePath(controllerPrefix, methodPath);\n\n // Collect return type\n this.collectType(returnType, project);\n\n routes.push({\n path: fullPath,\n method: httpMethod,\n returnType,\n methodName: method.getName(),\n parameters: method.getParameters().map(p => {\n const typeText = p.getType().getText();\n this.collectType(typeText, project);\n return {\n name: p.getName(),\n type: typeText,\n decorators: p.getDecorators().map(d => ({\n name: d.getName(),\n arguments: d.getArguments().map(arg => arg.getText().replace(/^['\"]|['\"]$/g, ''))\n }))\n };\n })\n });\n }\n }\n }\n return routes;\n }\n\n private extraTypes = new Map<string, string>();\n\n private collectType(typeText: string, project: Project) {\n // Simple heuristic: if it starts with uppercase and is not a primitive\n const primitives = ['string', 'number', 'boolean', 'any', 'void', 'null', 'undefined', 'Date'];\n const isArray = typeText.endsWith('[]');\n const baseType = isArray ? typeText.slice(0, -2) : typeText;\n\n if (primitives.includes(baseType) || baseType.startsWith('{') || baseType.includes('<')) {\n return;\n }\n\n if (this.extraTypes.has(baseType)) {\n return;\n }\n\n // Find the class or interface in the project\n for (const sourceFile of project.getSourceFiles()) {\n const cls = sourceFile.getClass(baseType);\n if (cls) {\n const properties = cls.getProperties().map(p => {\n return `${p.getName()}: ${p.getType().getText()};`;\n });\n this.extraTypes.set(baseType, `export interface ${baseType} {\\n ${properties.join('\\n ')}\\n}`);\n return;\n }\n\n const intf = sourceFile.getInterface(baseType);\n if (intf) {\n const properties = intf.getProperties().map(p => {\n return `${p.getName()}: ${p.getType().getText()};`;\n });\n this.extraTypes.set(baseType, `export interface ${baseType} {\\n ${properties.join('\\n ')}\\n}`);\n return;\n }\n }\n }\n\n private buildTypeDefinition(routes: any[]): string {\n let typeDef = `import { Hono } from 'hono';\\n`;\n typeDef += `import type { InferRequestType, InferResponseType } from 'hono/client';\\n\\n`;\n\n // Add extra types\n for (const [name, def] of this.extraTypes) {\n typeDef += `${def}\\n\\n`;\n }\n\n typeDef += `export type AppType = Hono<any, {\\n`;\n\n // Group routes by path\n const routesByPath: Record<string, any[]> = {};\n for (const route of routes) {\n if (!routesByPath[route.path]) {\n routesByPath[route.path] = [];\n }\n routesByPath[route.path].push(route);\n }\n\n for (const [path, pathRoutes] of Object.entries(routesByPath)) {\n typeDef += ` '${path}': {\\n`;\n for (const route of pathRoutes) {\n let inputType = '';\n const inputFields: string[] = [];\n\n // Handle Body\n const bodyParam = route.parameters.find((p: any) => p.decorators.some((d: any) => d.name === 'Body'));\n if (bodyParam) {\n inputFields.push(`json: ${bodyParam.type}`);\n }\n\n // Handle Query\n const queryParam = route.parameters.find((p: any) => p.decorators.some((d: any) => d.name === 'Query'));\n if (queryParam) {\n inputFields.push(`query: ${queryParam.type}`);\n }\n\n // Handle Param\n const paramParams = route.parameters.filter((p: any) => p.decorators.some((d: any) => d.name === 'Param'));\n if (paramParams.length > 0) {\n const paramFields = paramParams.map((p: any) => {\n const decorator = p.decorators.find((d: any) => d.name === 'Param');\n const paramName = decorator.arguments[0] || p.name;\n return `${paramName}: ${p.type}`;\n });\n inputFields.push(`param: { ${paramFields.join('; ')} }`);\n }\n\n inputType = `{ ${inputFields.join(', ')} }`;\n\n typeDef += ` $${route.method}: {\\n`;\n typeDef += ` input: ${inputType},\\n`;\n typeDef += ` output: { json: ${route.returnType} },\\n`;\n typeDef += ` outputFormat: 'json',\\n`;\n typeDef += ` status: 200\\n`;\n typeDef += ` },\\n`;\n }\n typeDef += ` },\\n`;\n }\n\n typeDef += `}>;\\n`;\n return typeDef;\n }\n\n private hasDecorator(node: ClassDeclaration | MethodDeclaration, name: string): boolean {\n return node.getDecorators().some(d => d.getName() === name);\n }\n\n private getMethodDecorator(method: MethodDeclaration): Decorator | undefined {\n const methods = ['Get', 'Post', 'Put', 'Delete', 'Patch'];\n return method.getDecorators().find(d => methods.includes(d.getName()));\n }\n\n private getDecoratorArgument(node: ClassDeclaration | MethodDeclaration, name: string): string | undefined {\n const decorator = node.getDecorator(name);\n if (!decorator) return undefined;\n const args = decorator.getArguments();\n if (args.length > 0) {\n const text = args[0].getText();\n // Remove quotes\n return text.replace(/^['\"]|['\"]$/g, '');\n }\n return undefined;\n }\n\n private normalizePath(prefix: string, path: string): string {\n const cleanPrefix = prefix ? prefix.replace(/^\\/+/, '').replace(/\\/+$/, '') : '';\n const cleanPath = path ? path.replace(/^\\/+/, '').replace(/\\/+$/, '') : '';\n let result = '';\n if (cleanPrefix) result += `/${cleanPrefix}`;\n if (cleanPath) result += `/${cleanPath}`;\n return result || '/';\n }\n}\n"]}