astahmer / openapi-zod-client

Generate a zodios (typescript http client with zod validation) from an OpenAPI spec (json/yaml)
openapi-zod-client.vercel.app
717 stars 80 forks source link

feat: add typescriptDefinitionRefiner to TemplateContext options #278

Open senecolas opened 4 months ago

senecolas commented 4 months ago

Implement typescriptDefinitionRefiner Functionality

Overview

Following the discussion in PR #275 , this pull request introduces the typescriptDefinitionRefiner function, allowing for the customization of TypeScript type definitions generated by the openapi-zod-client tool. This functionality supports all type description customization needs, including the ability to add JSDoc comments based on OpenAPI schema properties.

Functionality

The typescriptDefinitionRefiner function is called within the recursive getTypescriptFromOpenApi before returning each Tanu type result. It enables users to modify the TypeScript definitions on the fly, providing a flexible approach to enhance the generated types, such as by adding comprehensive JSDoc comments.

Function signature

/**
 * A function to refine each Tanu type definition. Mostly useful for adding fields from SchemaObject
 * that aren't defined yet in the default type definition.
 */
typescriptDefinitionRefiner?: (
    defaultTs: ts.Node | TypeDefinitionObject | string,
    schema: SchemaObject
) => ts.Node | TypeDefinitionObject | string;

Example Usage

Below is an example demonstrating how to use the typescriptDefinitionRefiner option to add JSDoc comments to the generated TypeScript types with the help of a generateJSDocArray function. This example showcases how to dynamically generate JSDoc comments based on the OpenAPI schema, including descriptions, examples, deprecation notices, and more:

// Generate JSDoc comments from a schema object.
function generateJSDocArray(schema: SchemaObject, withTypesAndFormat = false): string[] {
  const comments: string[] = [];
  // Mapping from schema properties to JSDoc comments
  const mapping = {
      description: (value: string) => `${value}`,
      example: (value: any) => `@example ${JSON.stringify(value)}`,
      examples: (value: any[]) => value.map((example, index) => `@example Example ${index + 1}: ${JSON.stringify(example)}`).join("\n"),
      deprecated: (value: boolean) => (value ? "@deprecated" : ""),
      externalDocs: (value: { url: string }) => `@see ${value.url}`,
      // Handle additional attributes based on `withTypesAndFormat`
      type: withTypesAndFormat ? (value: string | string[]) => `@type {${Array.isArray(value) ? value.join("|") : value}}` : undefined,
      format: withTypesAndFormat ? (value: string) => `@format ${value}` : undefined,
      minimum: (value: number) => `@minimum ${value}`,
      maximum: (value: number) => `@maximum ${value}`,
      minLength: (value: number) => `@minLength ${value}`,
      maxLength: (value: number) => `@maxLength ${value}`,
      pattern: (value: string) => `@pattern ${value}`,
      enum: (value: string[]) => `@enum ${value.join(", ")}`,
  };

  // Generate JSDoc comments based on schema properties
  Object.entries(mapping).forEach(([key, mappingFunction]) => {
      const schemaValue = schema[key as keyof SchemaObject];
      if (schemaValue !== undefined && mappingFunction) {
          const result = mappingFunction(schemaValue);
          if (result) comments.push(result);
      }
  });

  // Add an empty line after the description if there are additional comments
  if (comments.length > 1 && schema.description) {
      comments.splice(1, 0, "");
  }

  return comments;
}

// Options for the `openapi-zod-client` tool, including the new `typescriptDefinitionRefiner`
const options = {
  groupStrategy: "tag-file",
  shouldExportAllSchemas: true,
  shouldExportAllTypes: true,
  typescriptDefinitionRefiner: (tsResult, schema) => {
    const jsDocComments = generateJSDocArray(schema);
    if (jsDocComments.length > 0 && typeof tsResult === "object") {
        // Add JSDoc comments to the TypeScript definition
        return t.comment(tsResult, jsDocComments);
    }
    return tsResult;
  },
};

This approach allows for significant flexibility in customizing the generated types, addressing the original concern about increasing configuration complexity and maintenance overhead.

Request for Comments

I welcome feedback on this feature, including suggestions for improvement or concerns about potential impacts. Please feel free to leave comments or questions in this pull request.

Thank you for considering this contribution to this project.

vercel[bot] commented 4 months ago

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
openapi-zod-client-rim4 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 29, 2024 1:46pm
astahmer commented 4 months ago

thank you, this looks great ! 🙏 could you add a test with your example from the PR description ?