fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #3531 from hey-api/feat/plugin-typescript-resolvers

feat: add resolvers to typescript plugin

authored by

Lubos and committed by
GitHub
2f155458 8b58945f

+443 -160
+5
.changeset/better-spiders-relax.md
··· 1 + --- 2 + "@hey-api/openapi-ts": patch 3 + --- 4 + 5 + **plugin(@hey-api/typescript)**: add Resolvers API
+3 -9
packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts
··· 1 1 import type { IR } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../ts-dsl'; 4 - import type { TypeScriptResult } from './shared/types'; 4 + import type { Type } from './shared/types'; 5 5 import type { HeyApiTypeScriptPlugin } from './types'; 6 6 import { createProcessor } from './v1/processor'; 7 7 8 8 export type IApi = { 9 - schemaToType: ( 10 - plugin: HeyApiTypeScriptPlugin['Instance'], 11 - schema: IR.SchemaObject, 12 - ) => TypeScriptResult['type']; 9 + schemaToType: (plugin: HeyApiTypeScriptPlugin['Instance'], schema: IR.SchemaObject) => Type; 13 10 }; 14 11 15 12 export class Api implements IApi { 16 - schemaToType( 17 - plugin: HeyApiTypeScriptPlugin['Instance'], 18 - schema: IR.SchemaObject, 19 - ): TypeScriptResult['type'] { 13 + schemaToType(plugin: HeyApiTypeScriptPlugin['Instance'], schema: IR.SchemaObject): Type { 20 14 const processor = createProcessor(plugin); 21 15 const result = processor.process({ 22 16 export: false,
+7
packages/openapi-ts/src/plugins/@hey-api/typescript/index.ts
··· 1 1 export { defaultConfig, defineConfig } from './config'; 2 + export type { 3 + EnumResolverContext, 4 + NumberResolverContext, 5 + ObjectResolverContext, 6 + Resolvers, 7 + StringResolverContext, 8 + } from './resolvers'; 2 9 export type { HeyApiTypeScriptPlugin } from './types';
+127
packages/openapi-ts/src/plugins/@hey-api/typescript/resolvers.ts
··· 1 + import type { Plugin, SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 + 3 + import type { $, DollarTsDsl } from '../../../ts-dsl'; 4 + import type { HeyApiTypeScriptPlugin, Type, TypeScriptResult } from './shared/types'; 5 + 6 + export type Resolvers = Plugin.Resolvers<{ 7 + /** 8 + * Resolver for enum schemas. 9 + * 10 + * Allows customization of how enum types are rendered. 11 + * 12 + * Returning `undefined` will execute the default resolver logic. 13 + */ 14 + enum?: (ctx: EnumResolverContext) => Type | undefined; 15 + /** 16 + * Resolver for number schemas. 17 + * 18 + * Allows customization of how number types are rendered. 19 + * 20 + * Returning `undefined` will execute the default resolver logic. 21 + */ 22 + number?: (ctx: NumberResolverContext) => Type | undefined; 23 + /** 24 + * Resolver for object schemas. 25 + * 26 + * Allows customization of how object types are rendered. 27 + * 28 + * Returning `undefined` will execute the default resolver logic. 29 + */ 30 + object?: (ctx: ObjectResolverContext) => Type | undefined; 31 + /** 32 + * Resolver for string schemas. 33 + * 34 + * Allows customization of how string types are rendered. 35 + * 36 + * Returning `undefined` will execute the default resolver logic. 37 + */ 38 + string?: (ctx: StringResolverContext) => Type | undefined; 39 + }>; 40 + 41 + interface BaseContext extends DollarTsDsl { 42 + /** The plugin instance. */ 43 + plugin: HeyApiTypeScriptPlugin['Instance']; 44 + } 45 + 46 + export interface EnumResolverContext extends BaseContext { 47 + /** 48 + * Nodes used to build different parts of the result. 49 + */ 50 + nodes: { 51 + /** 52 + * Returns the base enum type expression. 53 + */ 54 + base: (ctx: EnumResolverContext) => Type; 55 + /** 56 + * Returns parsed enum items with metadata about the enum members. 57 + */ 58 + items: (ctx: EnumResolverContext) => { 59 + /** 60 + * String literal values for use with union types. 61 + */ 62 + enumMembers: Array<ReturnType<typeof $.type.literal>>; 63 + /** 64 + * Whether the enum includes a null value. 65 + */ 66 + isNullable: boolean; 67 + }; 68 + }; 69 + schema: SchemaWithType<'enum'>; 70 + } 71 + 72 + export interface NumberResolverContext extends BaseContext { 73 + /** 74 + * Nodes used to build different parts of the result. 75 + */ 76 + nodes: { 77 + /** 78 + * Returns the base number type expression. 79 + */ 80 + base: (ctx: NumberResolverContext) => Type; 81 + /** 82 + * Returns the literal type for const values. 83 + */ 84 + const: (ctx: NumberResolverContext) => Type | undefined; 85 + }; 86 + schema: SchemaWithType<'integer' | 'number'>; 87 + } 88 + 89 + export interface ObjectResolverContext extends BaseContext { 90 + /** 91 + * Nodes used to build different parts of the result. 92 + */ 93 + nodes: { 94 + /** 95 + * Returns the base object type expression. 96 + */ 97 + base: (ctx: ObjectResolverContext) => Type; 98 + /** 99 + * Returns the shape (properties) of the object type. 100 + */ 101 + shape: (ctx: ObjectResolverContext) => ReturnType<typeof $.type.object>; 102 + }; 103 + schema: SchemaWithType<'object'>; 104 + walk: Walker<TypeScriptResult, HeyApiTypeScriptPlugin['Instance']>; 105 + walkerCtx: SchemaVisitorContext<HeyApiTypeScriptPlugin['Instance']>; 106 + } 107 + 108 + export interface StringResolverContext extends BaseContext { 109 + /** 110 + * Nodes used to build different parts of the result. 111 + */ 112 + nodes: { 113 + /** 114 + * Returns the base string type expression. 115 + */ 116 + base: (ctx: StringResolverContext) => Type; 117 + /** 118 + * Returns the literal type for const values. 119 + */ 120 + const: (ctx: StringResolverContext) => Type | undefined; 121 + /** 122 + * Returns the format-specific type expression. 123 + */ 124 + format: (ctx: StringResolverContext) => Type | undefined; 125 + }; 126 + schema: SchemaWithType<'string'>; 127 + }
+3 -1
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/types.ts
··· 4 4 5 5 export type { HeyApiTypeScriptPlugin } from '../types'; 6 6 7 + export type Type = MaybeTsDsl<TypeTsDsl>; 8 + 7 9 /** 8 10 * Metadata that flows through schema walking. 9 11 */ ··· 25 27 export interface TypeScriptResult { 26 28 enumData?: TypeScriptEnumData; 27 29 meta: TypeScriptMeta; 28 - type: MaybeTsDsl<TypeTsDsl>; 30 + type: Type; 29 31 } 30 32 31 33 /**
+5 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/types.ts
··· 2 2 import type { DefinePlugin, Plugin } from '@hey-api/shared'; 3 3 4 4 import type { IApi } from './api'; 5 + import type { Resolvers } from './resolvers'; 5 6 6 7 export type EnumsType = 'javascript' | 'typescript' | 'typescript-const'; 7 8 8 9 export type UserConfig = Plugin.Name<'@hey-api/typescript'> & 9 10 Plugin.Hooks & 10 11 Plugin.UserComments & 11 - Plugin.UserExports & { 12 + Plugin.UserExports & 13 + Resolvers & { 12 14 /** 13 15 * Casing convention for generated names. 14 16 * ··· 232 234 export type Config = Plugin.Name<'@hey-api/typescript'> & 233 235 Plugin.Hooks & 234 236 Plugin.Comments & 235 - Plugin.Exports & { 237 + Plugin.Exports & 238 + Resolvers & { 236 239 /** 237 240 * Casing convention for generated names. 238 241 */
+2 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/array.ts
··· 4 4 import { deduplicateSchema } from '@hey-api/shared'; 5 5 6 6 import { $ } from '../../../../../ts-dsl'; 7 - import type { HeyApiTypeScriptPlugin } from '../../shared/types'; 7 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 8 8 import type { TypeScriptResult } from '../../shared/types'; 9 9 10 10 export function arrayToAst({ ··· 15 15 plugin: HeyApiTypeScriptPlugin['Instance']; 16 16 schema: SchemaWithType<'array'>; 17 17 walk: Walker<TypeScriptResult, HeyApiTypeScriptPlugin['Instance']>; 18 - }): TypeScriptResult['type'] { 18 + }): Type { 19 19 if (!schema.items) { 20 20 return $.type('Array').generic($.type(plugin.config.topType)); 21 21 }
+2 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/boolean.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../../ts-dsl'; 4 - import type { HeyApiTypeScriptPlugin, TypeScriptResult } from '../../shared/types'; 4 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 5 5 6 6 export function booleanToAst({ 7 7 schema, 8 8 }: { 9 9 plugin: HeyApiTypeScriptPlugin['Instance']; 10 10 schema: SchemaWithType<'boolean'>; 11 - }): TypeScriptResult['type'] { 11 + }): Type { 12 12 if (schema.const !== undefined) { 13 13 return $.type.fromValue(schema.const); 14 14 }
+54 -12
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/enum.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../../ts-dsl'; 4 - import type { HeyApiTypeScriptPlugin, TypeScriptResult } from '../../shared/types'; 4 + import type { EnumResolverContext } from '../../resolvers'; 5 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 5 6 import type { TypeScriptEnumData } from '../../shared/types'; 6 7 7 8 function buildEnumData( ··· 35 36 }; 36 37 } 37 38 39 + function itemsNode(ctx: EnumResolverContext): ReturnType<EnumResolverContext['nodes']['items']> { 40 + const { schema } = ctx; 41 + const items = schema.items ?? []; 42 + 43 + const enumMembers: Array<ReturnType<typeof $.type.literal>> = []; 44 + let isNullable = false; 45 + 46 + for (const item of items) { 47 + if (item.type === 'string' && typeof item.const === 'string') { 48 + enumMembers.push($.type.literal(item.const)); 49 + } else if (item.type === 'number' && typeof item.const === 'number') { 50 + enumMembers.push($.type.literal(item.const)); 51 + } else if (item.type === 'boolean' && typeof item.const === 'boolean') { 52 + enumMembers.push($.type.literal(item.const)); 53 + } else if (item.type === 'null' || item.const === null) { 54 + isNullable = true; 55 + } 56 + } 57 + 58 + return { enumMembers, isNullable }; 59 + } 60 + 61 + function baseNode(ctx: EnumResolverContext): Type { 62 + const { schema } = ctx; 63 + const items = schema.items ?? []; 64 + 65 + if (items.length === 0) { 66 + return $.type('never'); 67 + } 68 + 69 + const literalTypes = items 70 + .filter((item) => item.const !== undefined) 71 + .map((item) => $.type.fromValue(item.const)); 72 + return literalTypes.length > 0 ? $.type.or(...literalTypes) : $.type('string'); 73 + } 74 + 75 + function enumResolver(ctx: EnumResolverContext): Type { 76 + return ctx.nodes.base(ctx); 77 + } 78 + 38 79 export function enumToAst({ 39 80 plugin, 40 81 schema, ··· 43 84 schema: SchemaWithType<'enum'>; 44 85 }): { 45 86 enumData?: TypeScriptEnumData; 46 - type: TypeScriptResult['type']; 87 + type: Type; 47 88 } { 48 - const items = schema.items ?? []; 49 89 const enumData = buildEnumData(plugin, schema); 50 90 51 - let type: TypeScriptResult['type']; 91 + const ctx: EnumResolverContext = { 92 + $, 93 + nodes: { 94 + base: baseNode, 95 + items: itemsNode, 96 + }, 97 + plugin, 98 + schema, 99 + }; 52 100 53 - if (items.length === 0) { 54 - type = $.type('never'); 55 - } else { 56 - const literalTypes = items 57 - .filter((item) => item.const !== undefined) 58 - .map((item) => $.type.fromValue(item.const)); 59 - type = literalTypes.length > 0 ? $.type.or(...literalTypes) : $.type('string'); 60 - } 101 + const resolver = plugin.config['~resolvers']?.enum; 102 + const type = resolver?.(ctx) ?? enumResolver(ctx); 61 103 62 104 return { 63 105 enumData,
+2 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/never.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../../ts-dsl'; 4 - import type { HeyApiTypeScriptPlugin, TypeScriptResult } from '../../shared/types'; 4 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 5 5 6 6 // eslint-disable-next-line @typescript-eslint/no-unused-vars 7 7 export function neverToAst(args: { 8 8 plugin: HeyApiTypeScriptPlugin['Instance']; 9 9 schema: SchemaWithType<'never'>; 10 - }): TypeScriptResult['type'] { 10 + }): Type { 11 11 return $.type('never'); 12 12 }
+2 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/null.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../../ts-dsl'; 4 - import type { HeyApiTypeScriptPlugin, TypeScriptResult } from '../../shared/types'; 4 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 5 5 6 6 // eslint-disable-next-line @typescript-eslint/no-unused-vars 7 7 export function nullToAst(args: { 8 8 plugin: HeyApiTypeScriptPlugin['Instance']; 9 9 schema: SchemaWithType<'null'>; 10 - }): TypeScriptResult['type'] { 10 + }): Type { 11 11 return $.type.literal(null); 12 12 }
+38 -9
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/number.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../../ts-dsl'; 4 - import type { HeyApiTypeScriptPlugin, TypeScriptResult } from '../../shared/types'; 4 + import type { NumberResolverContext } from '../../resolvers'; 5 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 5 6 6 - export function numberToAst({ 7 - plugin, 8 - schema, 9 - }: { 10 - plugin: HeyApiTypeScriptPlugin['Instance']; 11 - schema: SchemaWithType<'integer' | 'number'>; 12 - }): TypeScriptResult['type'] { 7 + function constNode(ctx: NumberResolverContext): Type | undefined { 8 + const { schema } = ctx; 13 9 if (schema.const !== undefined) { 14 10 return $.type.fromValue(schema.const); 15 11 } 12 + return undefined; 13 + } 14 + 15 + function baseNode(ctx: NumberResolverContext): Type { 16 + const { plugin, schema } = ctx; 16 17 17 18 if (schema.type === 'integer' && schema.format === 'int64') { 18 - // TODO: parser - add ability to skip type transformers 19 19 if (plugin.getPlugin('@hey-api/transformers')?.config.bigInt) { 20 20 return $.type('bigint'); 21 21 } ··· 23 23 24 24 return $.type('number'); 25 25 } 26 + 27 + function numberResolver(ctx: NumberResolverContext): Type { 28 + const constResult = ctx.nodes.const(ctx); 29 + if (constResult) return constResult; 30 + 31 + return ctx.nodes.base(ctx); 32 + } 33 + 34 + export function numberToAst({ 35 + plugin, 36 + schema, 37 + }: { 38 + plugin: HeyApiTypeScriptPlugin['Instance']; 39 + schema: SchemaWithType<'integer' | 'number'>; 40 + }): Type { 41 + const ctx: NumberResolverContext = { 42 + $, 43 + nodes: { 44 + base: baseNode, 45 + const: constNode, 46 + }, 47 + plugin, 48 + schema, 49 + }; 50 + 51 + const resolver = plugin.config['~resolvers']?.number; 52 + const result = resolver?.(ctx); 53 + return result ?? numberResolver(ctx); 54 + }
+56 -17
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/object.ts
··· 1 1 import { ref } from '@hey-api/codegen-core'; 2 - import type { IR, SchemaWithType, Walker } from '@hey-api/shared'; 2 + import type { IR, SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 3 3 import { deduplicateSchema } from '@hey-api/shared'; 4 4 5 5 import { createSchemaComment } from '../../../../../plugins/shared/utils/schema'; 6 6 import { $ } from '../../../../../ts-dsl'; 7 - import type { HeyApiTypeScriptPlugin } from '../../shared/types'; 7 + import type { ObjectResolverContext } from '../../resolvers'; 8 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 8 9 import type { TypeScriptResult } from '../../shared/types'; 9 10 10 - export function objectToAst({ 11 - plugin, 12 - schema, 13 - walk, 14 - }: { 15 - plugin: HeyApiTypeScriptPlugin['Instance']; 16 - schema: SchemaWithType<'object'>; 17 - walk: Walker<TypeScriptResult, HeyApiTypeScriptPlugin['Instance']>; 18 - }): TypeScriptResult['type'] { 11 + function shapeNode(ctx: ObjectResolverContext): ReturnType<typeof $.type.object> { 12 + const { schema, walk, walkerCtx } = ctx; 19 13 const shape = $.type.object(); 20 14 const required = schema.required ?? []; 21 - let indexSchemas: Array<IR.SchemaObject> = []; 22 - let hasOptionalProperties = false; 23 15 24 16 for (const name in schema.properties) { 25 17 const property = schema.properties[name]!; 26 - const propertyResult = walk(property, { path: ref([]), plugin }); 18 + const propertyResult = walk(property, { path: ref([]), plugin: walkerCtx.plugin }); 27 19 const isRequired = required.includes(name); 28 20 shape.prop(name, (p) => 29 21 p 30 - .$if(plugin.config.comments && createSchemaComment(property), (p, v) => p.doc(v)) 22 + .$if(walkerCtx.plugin.config.comments && createSchemaComment(property), (p, v) => p.doc(v)) 31 23 .readonly(property.accessScope === 'read') 32 24 .required(isRequired) 33 25 .type(propertyResult.type), 34 26 ); 27 + } 28 + 29 + return shape; 30 + } 31 + 32 + function baseNode(ctx: ObjectResolverContext): Type { 33 + const { schema, walk, walkerCtx } = ctx; 34 + const shape = shapeNode(ctx); 35 + const required = schema.required ?? []; 36 + let indexSchemas: Array<IR.SchemaObject> = []; 37 + let hasOptionalProperties = false; 38 + 39 + for (const name in schema.properties) { 40 + const property = schema.properties[name]!; 41 + const isRequired = required.includes(name); 35 42 indexSchemas.push(property); 36 43 37 44 if (!isRequired) { ··· 80 87 ? indexSchemas[0]! 81 88 : deduplicateSchema({ schema: { items: indexSchemas, logicalOperator: 'or' } }); 82 89 83 - const indexType = walk(unionSchema, { path: ref([]), plugin }).type; 90 + const indexType = walk(unionSchema, { path: ref([]), plugin: walkerCtx.plugin }).type; 84 91 85 92 if (schema.propertyNames?.$ref) { 86 93 const propertyNamesResult = walk( 87 94 { $ref: schema.propertyNames.$ref }, 88 - { path: ref([]), plugin }, 95 + { path: ref([]), plugin: walkerCtx.plugin }, 89 96 ); 90 97 return $.type.mapped('key').key(propertyNamesResult.type).optional().type(indexType); 91 98 } ··· 96 103 97 104 return shape; 98 105 } 106 + 107 + function objectResolver(ctx: ObjectResolverContext): Type { 108 + return ctx.nodes.base(ctx); 109 + } 110 + 111 + export function objectToAst({ 112 + plugin, 113 + schema, 114 + walk, 115 + walkerCtx, 116 + }: { 117 + plugin: HeyApiTypeScriptPlugin['Instance']; 118 + schema: SchemaWithType<'object'>; 119 + walk: Walker<TypeScriptResult, HeyApiTypeScriptPlugin['Instance']>; 120 + walkerCtx: SchemaVisitorContext<HeyApiTypeScriptPlugin['Instance']>; 121 + }): Type { 122 + const ctx: ObjectResolverContext = { 123 + $, 124 + nodes: { 125 + base: baseNode, 126 + shape: shapeNode, 127 + }, 128 + plugin, 129 + schema, 130 + walk, 131 + walkerCtx, 132 + }; 133 + 134 + const resolver = plugin.config['~resolvers']?.object; 135 + const result = resolver?.(ctx); 136 + return result ?? objectResolver(ctx); 137 + }
+91 -50
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/string.ts
··· 3 3 import { toCase } from '@hey-api/shared'; 4 4 5 5 import { $ } from '../../../../../ts-dsl'; 6 - import type { HeyApiTypeScriptPlugin, TypeScriptResult } from '../../shared/types'; 6 + import type { StringResolverContext } from '../../resolvers'; 7 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 7 8 8 - export function stringToAst({ 9 - plugin, 10 - schema, 11 - }: { 12 - plugin: HeyApiTypeScriptPlugin['Instance']; 13 - schema: SchemaWithType<'string'>; 14 - }): TypeScriptResult['type'] { 9 + function constNode(ctx: StringResolverContext): Type | undefined { 10 + const { schema } = ctx; 15 11 if (schema.const !== undefined) { 16 12 return $.type.fromValue(schema.const); 17 13 } 14 + return undefined; 15 + } 18 16 19 - if (schema.format) { 20 - if (schema.format === 'binary') { 21 - return $.type.or($.type('Blob'), $.type('File')); 22 - } 17 + function formatNode(ctx: StringResolverContext): Type | undefined { 18 + const { plugin, schema } = ctx; 19 + const { format } = schema; 23 20 24 - if (schema.format === 'date-time' || schema.format === 'date') { 25 - // TODO: parser - add ability to skip type transformers 26 - if (plugin.getPlugin('@hey-api/transformers')?.config.dates) { 27 - return $.type('Date'); 28 - } 21 + if (!format) return undefined; 22 + 23 + if (format === 'binary') { 24 + return $.type.or($.type('Blob'), $.type('File')); 25 + } 26 + 27 + if (format === 'date-time' || format === 'date') { 28 + if (plugin.getPlugin('@hey-api/transformers')?.config.dates) { 29 + return $.type('Date'); 29 30 } 31 + } 30 32 31 - if (schema.format === 'typeid' && typeof schema.example === 'string') { 32 - const parts = String(schema.example).split('_'); 33 - parts.pop(); // remove the ID part 34 - const typeidBase = parts.join('_'); 33 + if (format === 'typeid' && typeof schema.example === 'string') { 34 + const parts = String(schema.example).split('_'); 35 + parts.pop(); 36 + const typeidBase = parts.join('_'); 35 37 36 - const typeidQuery: SymbolMeta = { 38 + const typeidQuery: SymbolMeta = { 39 + category: 'type', 40 + resource: 'type-id', 41 + resourceId: typeidBase, 42 + tool: 'typescript', 43 + }; 44 + if (!plugin.getSymbol(typeidQuery)) { 45 + const containerQuery: SymbolMeta = { 37 46 category: 'type', 38 47 resource: 'type-id', 39 - resourceId: typeidBase, 40 48 tool: 'typescript', 49 + variant: 'container', 41 50 }; 42 - if (!plugin.getSymbol(typeidQuery)) { 43 - const containerQuery: SymbolMeta = { 44 - category: 'type', 45 - resource: 'type-id', 46 - tool: 'typescript', 47 - variant: 'container', 48 - }; 49 51 50 - if (!plugin.getSymbol(containerQuery)) { 51 - const symbolTypeId = plugin.symbol('TypeID', { 52 - meta: containerQuery, 53 - }); 54 - const nodeTypeId = $.type 55 - .alias(symbolTypeId) 56 - .export() 57 - .generic('T', (g) => g.extends('string')) 58 - .type($.type.template().add($.type('T')).add('_').add($.type('string'))); 59 - plugin.node(nodeTypeId); 60 - } 61 - 62 - const refSymbol = plugin.referenceSymbol(containerQuery); 63 - const symbolTypeName = plugin.symbol(toCase(`${typeidBase}_id`, plugin.config.case), { 64 - meta: typeidQuery, 52 + if (!plugin.getSymbol(containerQuery)) { 53 + const symbolTypeId = plugin.symbol('TypeID', { 54 + meta: containerQuery, 65 55 }); 66 - const node = $.type 67 - .alias(symbolTypeName) 56 + const nodeTypeId = $.type 57 + .alias(symbolTypeId) 68 58 .export() 69 - .type($.type(refSymbol).generic($.type.literal(typeidBase))); 70 - plugin.node(node); 59 + .generic('T', (g) => g.extends('string')) 60 + .type($.type.template().add($.type('T')).add('_').add($.type('string'))); 61 + plugin.node(nodeTypeId); 71 62 } 72 - return $.type(plugin.referenceSymbol(typeidQuery)); 63 + 64 + const refSymbol = plugin.referenceSymbol(containerQuery); 65 + const symbolTypeName = plugin.symbol(toCase(`${typeidBase}_id`, plugin.config.case), { 66 + meta: typeidQuery, 67 + }); 68 + const node = $.type 69 + .alias(symbolTypeName) 70 + .export() 71 + .type($.type(refSymbol).generic($.type.literal(typeidBase))); 72 + plugin.node(node); 73 73 } 74 + return $.type(plugin.referenceSymbol(typeidQuery)); 74 75 } 75 76 77 + return undefined; 78 + } 79 + 80 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 81 + function baseNode(ctx: StringResolverContext): Type { 76 82 return $.type('string'); 77 83 } 84 + 85 + function stringResolver(ctx: StringResolverContext): Type { 86 + if (ctx.schema.const !== undefined) { 87 + const constResult = ctx.nodes.const(ctx); 88 + if (constResult) return constResult; 89 + } 90 + 91 + const formatResult = ctx.nodes.format(ctx); 92 + if (formatResult) return formatResult; 93 + 94 + return ctx.nodes.base(ctx); 95 + } 96 + 97 + export function stringToAst({ 98 + plugin, 99 + schema, 100 + }: { 101 + plugin: HeyApiTypeScriptPlugin['Instance']; 102 + schema: SchemaWithType<'string'>; 103 + }): Type { 104 + const ctx: StringResolverContext = { 105 + $, 106 + nodes: { 107 + base: baseNode, 108 + const: constNode, 109 + format: formatNode, 110 + }, 111 + plugin, 112 + schema, 113 + }; 114 + 115 + const resolver = plugin.config['~resolvers']?.string; 116 + const result = resolver?.(ctx); 117 + return result ?? stringResolver(ctx); 118 + }
+3 -3
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/tuple.ts
··· 3 3 import type { Walker } from '@hey-api/shared'; 4 4 5 5 import { $ } from '../../../../../ts-dsl'; 6 - import type { HeyApiTypeScriptPlugin } from '../../shared/types'; 6 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 7 7 import type { TypeScriptResult } from '../../shared/types'; 8 8 9 9 export function tupleToAst({ ··· 14 14 plugin: HeyApiTypeScriptPlugin['Instance']; 15 15 schema: SchemaWithType<'tuple'>; 16 16 walk: Walker<TypeScriptResult, HeyApiTypeScriptPlugin['Instance']>; 17 - }): TypeScriptResult['type'] { 18 - let itemTypes: Array<TypeScriptResult['type']> = []; 17 + }): Type { 18 + let itemTypes: Array<Type> = []; 19 19 20 20 if (schema.const && Array.isArray(schema.const)) { 21 21 itemTypes = schema.const.map((value) => $.type.fromValue(value));
+2 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/undefined.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../../ts-dsl'; 4 - import type { HeyApiTypeScriptPlugin, TypeScriptResult } from '../../shared/types'; 4 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 5 5 6 6 // eslint-disable-next-line @typescript-eslint/no-unused-vars 7 7 export function undefinedToAst(args: { 8 8 plugin: HeyApiTypeScriptPlugin['Instance']; 9 9 schema: SchemaWithType<'undefined'>; 10 - }): TypeScriptResult['type'] { 10 + }): Type { 11 11 return $.type('undefined'); 12 12 }
+2 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/unknown.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../../ts-dsl'; 4 - import type { HeyApiTypeScriptPlugin, TypeScriptResult } from '../../shared/types'; 4 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 5 5 6 6 export function unknownToAst({ 7 7 plugin, 8 8 }: { 9 9 plugin: HeyApiTypeScriptPlugin['Instance']; 10 10 schema: SchemaWithType<'unknown'>; 11 - }): TypeScriptResult['type'] { 11 + }): Type { 12 12 return $.type(plugin.config.topType); 13 13 }
+2 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/void.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../../ts-dsl'; 4 - import type { HeyApiTypeScriptPlugin, TypeScriptResult } from '../../shared/types'; 4 + import type { HeyApiTypeScriptPlugin, Type } from '../../shared/types'; 5 5 6 6 // eslint-disable-next-line @typescript-eslint/no-unused-vars 7 7 export function voidToAst(args: { 8 8 plugin: HeyApiTypeScriptPlugin['Instance']; 9 9 schema: SchemaWithType<'void'>; 10 - }): TypeScriptResult['type'] { 10 + }): Type { 11 11 return $.type('void'); 12 12 }
+1
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/walker.ts
··· 131 131 plugin: ctx.plugin, 132 132 schema, 133 133 walk, 134 + walkerCtx: ctx, 134 135 }); 135 136 return { 136 137 meta: defaultMeta(schema),
+4 -4
packages/openapi-ts/src/plugins/valibot/resolvers.ts
··· 98 98 99 99 export interface EnumResolverContext extends BaseContext { 100 100 /** 101 - * Nodes used to build different parts of the schema. 101 + * Nodes used to build different parts of the result. 102 102 */ 103 103 nodes: { 104 104 /** ··· 124 124 125 125 export interface NumberResolverContext extends BaseContext { 126 126 /** 127 - * Nodes used to build different parts of the schema. 127 + * Nodes used to build different parts of the result. 128 128 */ 129 129 nodes: { 130 130 base: (ctx: NumberResolverContext) => PipeResult; ··· 147 147 _childResults: Array<ValibotResult>; 148 148 applyModifiers: (result: ValibotResult, opts: { optional?: boolean }) => ValibotFinal; 149 149 /** 150 - * Nodes used to build different parts of the schema. 150 + * Nodes used to build different parts of the result. 151 151 */ 152 152 nodes: { 153 153 additionalProperties: (ctx: ObjectResolverContext) => Pipe | null | undefined; ··· 167 167 168 168 export interface StringResolverContext extends BaseContext { 169 169 /** 170 - * Nodes used to build different parts of the schema. 170 + * Nodes used to build different parts of the result. 171 171 */ 172 172 nodes: { 173 173 base: (ctx: StringResolverContext) => PipeResult;
-8
packages/openapi-ts/src/plugins/zod/resolvers/index.ts
··· 1 - export type { 2 - EnumResolverContext, 3 - NumberResolverContext, 4 - ObjectResolverContext, 5 - Resolvers, 6 - StringResolverContext, 7 - ValidatorResolverContext, 8 - } from './types';
+32 -31
packages/openapi-ts/src/plugins/zod/resolvers/types.ts packages/openapi-ts/src/plugins/zod/resolvers.ts
··· 3 3 import type { MaybeArray } from '@hey-api/types'; 4 4 import type ts from 'typescript'; 5 5 6 - import type { MaybeBigInt, ShouldCoerceToBigInt } from '../../../plugins/shared/utils/coerce'; 7 - import type { GetIntegerLimit } from '../../../plugins/shared/utils/formats'; 8 - import type { $, DollarTsDsl, TsDsl } from '../../../ts-dsl'; 9 - import type { Chain } from '../shared/chain'; 10 - import type { Ast, IrSchemaToAstOptions, PluginState, ZodSchemaResult } from '../shared/types'; 11 - import type { ZodPlugin } from '../types'; 6 + import type { MaybeBigInt, ShouldCoerceToBigInt } from '../../plugins/shared/utils/coerce'; 7 + import type { GetIntegerLimit } from '../../plugins/shared/utils/formats'; 8 + import type { $, DollarTsDsl, TsDsl } from '../../ts-dsl'; 9 + import type { Chain } from './shared/chain'; 10 + import type { Ast, PluginState, ZodSchemaResult } from './shared/types'; 11 + import type { ZodPlugin } from './types'; 12 12 13 13 export type Resolvers = Plugin.Resolvers<{ 14 14 /** ··· 74 74 ctx: ValidatorResolverContext, 75 75 ) => MaybeArray<TsDsl<ts.Statement>> | null | undefined; 76 76 77 - type BaseContext = DollarTsDsl & 78 - Pick<IrSchemaToAstOptions, 'plugin'> & { 77 + interface BaseContext extends DollarTsDsl { 78 + /** 79 + * Functions for working with chains. 80 + */ 81 + chain: { 79 82 /** 80 - * Functions for working with chains. 83 + * The current chain. 84 + * 85 + * In Zod, this represents a chain of call expressions ("chains") 86 + * being assembled to form a schema definition. 87 + * 88 + * Each chain can be extended, modified, or replaced to customize 89 + * the resulting schema. 81 90 */ 82 - chain: { 83 - /** 84 - * The current chain. 85 - * 86 - * In Zod, this represents a chain of call expressions ("chains") 87 - * being assembled to form a schema definition. 88 - * 89 - * Each chain can be extended, modified, or replaced to customize 90 - * the resulting schema. 91 - */ 92 - current: Chain; 93 - }; 94 - /** 95 - * Provides access to commonly used symbols within the plugin. 96 - */ 97 - symbols: { 98 - z: Symbol; 99 - }; 91 + current: Chain; 92 + }; 93 + /** The plugin instance. */ 94 + plugin: ZodPlugin['Instance']; 95 + /** 96 + * Provides access to commonly used symbols within the plugin. 97 + */ 98 + symbols: { 99 + z: Symbol; 100 100 }; 101 + } 101 102 102 103 export interface EnumResolverContext extends BaseContext { 103 104 /** 104 - * Nodes used to build different parts of the enum schema. 105 + * Nodes used to build different parts of the result. 105 106 */ 106 107 nodes: { 107 108 /** ··· 142 143 143 144 export interface NumberResolverContext extends BaseContext { 144 145 /** 145 - * Nodes used to build different parts of the number schema. 146 + * Nodes used to build different parts of the result. 146 147 */ 147 148 nodes: { 148 149 base: (ctx: NumberResolverContext) => Chain; ··· 166 167 export interface ObjectResolverContext extends BaseContext { 167 168 applyModifiers: (result: ZodSchemaResult, opts: { optional?: boolean }) => Ast; 168 169 /** 169 - * Nodes used to build different parts of the object schema. 170 + * Nodes used to build different parts of the result. 170 171 */ 171 172 nodes: { 172 173 /** ··· 191 192 192 193 export interface StringResolverContext extends BaseContext { 193 194 /** 194 - * Nodes used to build different parts of the string schema. 195 + * Nodes used to build different parts of the result. 195 196 */ 196 197 nodes: { 197 198 base: (ctx: StringResolverContext) => Chain;