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.

chore: python sdk params

Lubos 638429d8 cf5ae58d

+662 -141
+10 -9
dev/playground.py
··· 3 3 4 4 def run(): 5 5 client = OpenCode() 6 - client.tui.publish() 7 - # body={ 8 - # "properties": { 9 - # "message": "Hello from Hey API OpenAPI TS Playground!", 10 - # "variant": "success", 11 - # }, 12 - # "type": "tui.toast.show", 13 - # }, 14 - # directory="main", 6 + client.tui.publish( 7 + body={ 8 + "properties": { 9 + "message": "Hello from Hey API OpenAPI Python Playground!", 10 + "variant": "success", 11 + }, 12 + "type": "tui.toast.show", 13 + }, 14 + directory="main", 15 + ) 15 16 16 17 run()
+1 -1
dev/playground.ts
··· 27 27 sdk.tui.publish({ 28 28 body: { 29 29 properties: { 30 - message: 'Hello from Hey API OpenAPI TS Playground!', 30 + message: 'Hello from Hey API OpenAPI TypeScript Playground!', 31 31 variant: 'success', 32 32 }, 33 33 type: 'tui.toast.show',
+171
packages/openapi-python/src/plugins/@hey-api/sdk/shared/operation.ts
··· 1 + import type { IR } from '@hey-api/shared'; 2 + import { toCase } from '@hey-api/shared'; 3 + 4 + import type { $ } from '../../../../py-dsl'; 5 + // import { py } from '../../../../ts-python'; 6 + import type { HeyApiSdkPlugin } from '../types'; 7 + import { getSignatureParameters } from './signature'; 8 + 9 + type OperationParameters = { 10 + bodyRef?: string; 11 + parameters: Array<ReturnType<typeof $.param>>; 12 + // parameters: Array<{ 13 + // annotation?: py.Expression; 14 + // defaultValue?: py.Expression; 15 + // name: string; 16 + // }>; 17 + }; 18 + 19 + const PYTHON_BUILTIN_TYPES: Record<string, string> = { 20 + array: 'list', 21 + boolean: 'bool', 22 + integer: 'int', 23 + number: 'float', 24 + object: 'dict', 25 + string: 'str', 26 + }; 27 + 28 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 29 + function schemaToPythonType(schema: IR.SchemaObject, plugin: HeyApiSdkPlugin['Instance']): string { 30 + if (schema.$ref) { 31 + return toCase(schema.$ref.split('/').pop()!, 'PascalCase'); 32 + } 33 + 34 + if (schema.type === 'array') { 35 + const itemsSchema = schema.items as IR.SchemaObject | undefined; 36 + const itemType = itemsSchema ? schemaToPythonType(itemsSchema, plugin) : 'Any'; 37 + return `list[${itemType}]`; 38 + } 39 + 40 + if (schema.type === 'object' || schema.additionalProperties) { 41 + if (schema.additionalProperties && typeof schema.additionalProperties === 'object') { 42 + const valueType = schemaToPythonType(schema.additionalProperties as IR.SchemaObject, plugin); 43 + return `dict[str, ${valueType}]`; 44 + } 45 + return 'dict[str, Any]'; 46 + } 47 + 48 + if (schema.type === 'tuple') { 49 + const itemsSchema = schema.items as IR.SchemaObject | IR.SchemaObject[] | undefined; 50 + const itemTypes = itemsSchema 51 + ? Array.isArray(itemsSchema) 52 + ? itemsSchema.map((item) => schemaToPythonType(item, plugin)) 53 + : [schemaToPythonType(itemsSchema, plugin)] 54 + : []; 55 + return `tuple[${itemTypes.join(', ')}]`; 56 + } 57 + 58 + const builtinType = schema.type ? PYTHON_BUILTIN_TYPES[schema.type] : 'Any'; 59 + return builtinType ?? 'Any'; 60 + } 61 + 62 + export function operationParameters({ 63 + operation, 64 + plugin, 65 + }: { 66 + operation: IR.OperationObject; 67 + plugin: HeyApiSdkPlugin['Instance']; 68 + }): OperationParameters { 69 + const result: OperationParameters = { 70 + parameters: [], 71 + }; 72 + 73 + if (plugin.config.paramsStructure === 'flat') { 74 + const signature = getSignatureParameters({ operation }); 75 + if (!signature) return result; 76 + 77 + // result.bodyRef = signature.bodyRef; 78 + 79 + // for (const param of opParameters.parameters) { 80 + // if (param.name === '*') { 81 + // continue; 82 + // } 83 + // node.param(param.name, (p) => p.type(param.annotation).default(param.defaultValue)); 84 + // } 85 + 86 + // const pathParams: OperationParameters['parameters'] = []; 87 + // const requiredParams: OperationParameters['parameters'] = []; 88 + // const optionalParams: OperationParameters['parameters'] = []; 89 + 90 + // const paramNames = Object.keys(signature.parameters); 91 + 92 + // for (const paramName of paramNames) { 93 + // const param = signature.parameters[paramName]!; 94 + 95 + // if (param.in === 'path') { 96 + // const type = schemaToPythonType(param.schema, plugin); 97 + // pathParams.push({ 98 + // annotation: py.factory.createIdentifier(type), 99 + // name: param.name, 100 + // }); 101 + // continue; 102 + // } 103 + 104 + // if (param.in === 'body' && param.schema.$ref) { 105 + // const refName = toCase(param.schema.$ref.split('/').pop()!, 'PascalCase'); 106 + // if (param.isRequired) { 107 + // requiredParams.push({ 108 + // annotation: py.factory.createIdentifier(refName), 109 + // name: param.name, 110 + // }); 111 + // } else { 112 + // optionalParams.push({ 113 + // annotation: py.factory.createIdentifier(`${refName} | None`), 114 + // defaultValue: py.factory.createLiteral(null), 115 + // name: param.name, 116 + // }); 117 + // } 118 + // continue; 119 + // } 120 + 121 + // const type = schemaToPythonType(param.schema, plugin); 122 + 123 + // if (param.isRequired) { 124 + // requiredParams.push({ 125 + // annotation: py.factory.createIdentifier(type), 126 + // name: param.name, 127 + // }); 128 + // } else { 129 + // let defaultValue: py.Expression = py.factory.createLiteral(null); 130 + // if (param.schema.default !== undefined) { 131 + // const defaultVal = param.schema.default; 132 + // if ( 133 + // typeof defaultVal === 'string' || 134 + // typeof defaultVal === 'number' || 135 + // typeof defaultVal === 'boolean' 136 + // ) { 137 + // defaultValue = py.factory.createLiteral(defaultVal); 138 + // } else { 139 + // defaultValue = py.factory.createLiteral(null); 140 + // } 141 + // } else if (type.startsWith('list') || type.startsWith('dict')) { 142 + // defaultValue = py.factory.createLiteral(null); 143 + // } 144 + 145 + // optionalParams.push({ 146 + // annotation: py.factory.createIdentifier(`${type} | None`), 147 + // defaultValue, 148 + // name: param.name, 149 + // }); 150 + // } 151 + // } 152 + 153 + // if (pathParams.length > 0) { 154 + // result.parameters.push(...pathParams); 155 + // } 156 + 157 + // if (requiredParams.length > 0 || optionalParams.length > 0) { 158 + // result.parameters.push({ name: '*' }); 159 + // result.parameters.push(...requiredParams); 160 + // result.parameters.push(...optionalParams); 161 + // } 162 + 163 + // result.parameters.push({ 164 + // annotation: py.factory.createIdentifier('float | None'), 165 + // defaultValue: py.factory.createLiteral(null), 166 + // name: 'timeout', 167 + // }); 168 + } 169 + 170 + return result; 171 + }
+149
packages/openapi-python/src/plugins/@hey-api/sdk/shared/signature.ts
··· 1 + import type { IR } from '@hey-api/shared'; 2 + import { refToName, toCase } from '@hey-api/shared'; 3 + 4 + type Location = keyof IR.ParametersObject | 'body'; 5 + 6 + type SignatureParameter = { 7 + in: Location; 8 + isRequired: boolean; 9 + name: string; 10 + originalName?: string; 11 + schema: IR.SchemaObject; 12 + }; 13 + 14 + type SignatureParameters = Record<string, SignatureParameter>; 15 + 16 + type Signature = { 17 + bodyRef?: string; 18 + parameters: SignatureParameters; 19 + }; 20 + 21 + export function getSignatureParameters({ 22 + operation, 23 + }: { 24 + operation: IR.OperationObject; 25 + }): Signature | undefined { 26 + const locations = ['header', 'path', 'query'] as const satisfies ReadonlyArray<Location>; 27 + const nameToLocations: Record<string, Set<Location>> = {}; 28 + 29 + const addParameter = (name: string, location: Location): void => { 30 + if (!nameToLocations[name]) { 31 + nameToLocations[name] = new Set(); 32 + } 33 + nameToLocations[name]!.add(location); 34 + }; 35 + 36 + for (const location of locations) { 37 + const parameters = operation.parameters?.[location]; 38 + if (parameters) { 39 + for (const key in parameters) { 40 + const parameter = parameters[key]!; 41 + addParameter(parameter.name, location); 42 + } 43 + } 44 + } 45 + 46 + if (operation.body) { 47 + if ( 48 + !operation.body.schema.logicalOperator && 49 + operation.body.schema.type === 'object' && 50 + operation.body.schema.properties 51 + ) { 52 + const properties = operation.body.schema.properties; 53 + for (const key in properties) { 54 + addParameter(key, 'body'); 55 + } 56 + } else if (operation.body.schema.$ref) { 57 + const name = refToName(operation.body.schema.$ref); 58 + const key = toCase(name, 'snake_case'); 59 + addParameter(key, 'body'); 60 + } else { 61 + addParameter('body', 'body'); 62 + } 63 + } 64 + 65 + const conflicts = new Set<string>(); 66 + for (const name in nameToLocations) { 67 + if (nameToLocations[name]!.size > 1) { 68 + conflicts.add(name); 69 + } 70 + } 71 + 72 + const signatureParameters: SignatureParameters = {}; 73 + 74 + for (const location of locations) { 75 + const parameters = operation.parameters?.[location]; 76 + if (parameters) { 77 + for (const key in parameters) { 78 + const parameter = parameters[key]!; 79 + const originalName = parameter.name; 80 + const name = conflicts.has(originalName) ? `${location}_${originalName}` : originalName; 81 + const signatureParameter: SignatureParameter = { 82 + in: location, 83 + isRequired: parameter.required ?? false, 84 + name, 85 + schema: parameter.schema, 86 + }; 87 + if (name !== originalName) { 88 + signatureParameter.originalName = originalName; 89 + } 90 + signatureParameters[name] = signatureParameter; 91 + } 92 + } 93 + } 94 + 95 + let bodyRef: string | undefined; 96 + 97 + if (operation.body) { 98 + const location = 'body'; 99 + if ( 100 + !operation.body.schema.logicalOperator && 101 + operation.body.schema.type === 'object' && 102 + operation.body.schema.properties 103 + ) { 104 + const properties = operation.body.schema.properties; 105 + for (const originalName in properties) { 106 + const property = properties[originalName]!; 107 + const name = conflicts.has(originalName) ? `${location}_${originalName}` : originalName; 108 + const signatureParameter: SignatureParameter = { 109 + in: location, 110 + isRequired: operation.body.schema.required?.includes(originalName) ?? false, 111 + name, 112 + schema: property, 113 + }; 114 + if (name !== originalName) { 115 + signatureParameter.originalName = originalName; 116 + } 117 + signatureParameters[name] = signatureParameter; 118 + } 119 + } else if (operation.body.schema.$ref) { 120 + const value = refToName(operation.body.schema.$ref); 121 + const originalName = toCase(value, 'snake_case'); 122 + const name = conflicts.has(originalName) ? `${location}_${originalName}` : originalName; 123 + bodyRef = toCase(value, 'PascalCase'); 124 + const signatureParameter: SignatureParameter = { 125 + in: location, 126 + isRequired: operation.body.required ?? false, 127 + name, 128 + schema: operation.body.schema, 129 + }; 130 + if (name !== originalName) { 131 + signatureParameter.originalName = originalName; 132 + } 133 + signatureParameters[name] = signatureParameter; 134 + } else { 135 + signatureParameters.body = { 136 + in: location, 137 + isRequired: operation.body.required ?? false, 138 + name: 'body', 139 + schema: operation.body.schema, 140 + }; 141 + } 142 + } 143 + 144 + if (!Object.keys(signatureParameters).length) { 145 + return; 146 + } 147 + 148 + return { bodyRef, parameters: signatureParameters }; 149 + }
+10 -7
packages/openapi-python/src/plugins/@hey-api/sdk/v1/node.ts
··· 10 10 11 11 import { $ } from '../../../../py-dsl'; 12 12 import { createOperationComment } from '../../../shared/utils/operation'; 13 + import { operationParameters } from '../shared/operation'; 13 14 import type { HeyApiSdkPlugin } from '../types'; 14 15 15 16 export interface OperationItem { ··· 114 115 operation: IR.OperationObject; 115 116 plugin: HeyApiSdkPlugin['Instance']; 116 117 }): T { 117 - const { node, operation } = args; 118 - 119 - const method = operation.method?.toLowerCase() || 'get'; 120 - 121 - node.do($('self').attr('client').attr(method).call($.literal(operation.path)).return()); 122 - 123 - return node; 118 + const { node, operation, plugin } = args; 119 + const method = operation.method.toLowerCase(); 120 + const opParameters = operationParameters({ operation, plugin }); 121 + return ( 122 + node 123 + .params(...opParameters.parameters) 124 + // TODO: extract operation statements into a separate function 125 + .do($('self').attr('client').attr(method).call($.literal(operation.path)).return()) as T 126 + ); 124 127 } 125 128 126 129 export function toNode(
+3 -3
packages/openapi-python/src/plugins/pydantic/shared/export.ts
··· 100 100 plugin: PydanticPlugin['Instance'], 101 101 ): ReturnType<typeof $.var> { 102 102 const fieldSymbol = field.name; 103 - const varStatement = $.var(fieldSymbol).$if(field.typeAnnotation, (v, a) => v.annotate(a)); 103 + const varStatement = $.var(fieldSymbol).$if(field.type, (v, a) => v.type(a)); 104 104 105 105 const originalName = field.originalName ?? fieldSymbol.name; 106 106 const needsAlias = field.originalName !== undefined && fieldSymbol.name !== originalName; ··· 135 135 }): void { 136 136 const typeAlias = plugin.external('typing.TypeAlias'); 137 137 const statement = $.var(symbol) 138 - .annotate(typeAlias) 139 - .assign(final.typeAnnotation ?? plugin.external('typing.Any')); 138 + .type(typeAlias) 139 + .assign(final.type ?? plugin.external('typing.Any')); 140 140 plugin.node(statement); 141 141 }
+2 -2
packages/openapi-python/src/plugins/pydantic/shared/types.ts
··· 1 1 import type { Symbol } from '@hey-api/codegen-core'; 2 2 3 - import type { AnnotationExpr } from '../../../py-dsl'; 3 + import type { VarType } from '../../../py-dsl'; 4 4 import type { FieldConstraints } from '../v2/constants'; 5 5 6 6 /** ··· 8 8 */ 9 9 export interface PydanticType { 10 10 fieldConstraints?: FieldConstraints; 11 - typeAnnotation?: AnnotationExpr; 11 + type?: VarType; 12 12 } 13 13 14 14 /**
+5 -5
packages/openapi-python/src/plugins/pydantic/v2/toAst/array.ts
··· 43 43 return { 44 44 childResults, 45 45 fieldConstraints: constraints, 46 - typeAnnotation: $(list).slice(any), 46 + type: $(list).slice(any), 47 47 }; 48 48 } 49 49 ··· 60 60 return { 61 61 childResults, 62 62 fieldConstraints: constraints, 63 - typeAnnotation: $(list).slice(itemResult.typeAnnotation ?? any), 63 + type: $(list).slice(itemResult.type ?? any), 64 64 }; 65 65 } 66 66 67 67 if (childResults.length > 1) { 68 68 const union = plugin.external('typing.Union'); 69 - const itemTypes = childResults.map((r) => ctx.applyModifiers(r).typeAnnotation ?? any); 69 + const itemTypes = childResults.map((r) => ctx.applyModifiers(r).type ?? any); 70 70 return { 71 71 childResults, 72 72 fieldConstraints: constraints, 73 - typeAnnotation: $(list).slice($(union).slice(...itemTypes)), 73 + type: $(list).slice($(union).slice(...itemTypes)), 74 74 }; 75 75 } 76 76 77 77 return { 78 78 childResults, 79 79 fieldConstraints: constraints, 80 - typeAnnotation: $(list).slice(any), 80 + type: $(list).slice(any), 81 81 }; 82 82 }
+2 -2
packages/openapi-python/src/plugins/pydantic/v2/toAst/boolean.ts
··· 14 14 if (typeof schema.const === 'boolean') { 15 15 const literal = plugin.external('typing.Literal'); 16 16 return { 17 - typeAnnotation: $(literal).slice($.literal(schema.const)), 17 + type: $(literal).slice($.literal(schema.const)), 18 18 }; 19 19 } 20 20 21 21 return { 22 - typeAnnotation: 'bool', 22 + type: 'bool', 23 23 }; 24 24 }
+2 -2
packages/openapi-python/src/plugins/pydantic/v2/toAst/enum.ts
··· 83 83 return { 84 84 enumMembers, 85 85 isNullable, 86 - typeAnnotation: plugin.external('typing.Any'), 86 + type: plugin.external('typing.Any'), 87 87 }; 88 88 } 89 89 ··· 91 91 return { 92 92 enumMembers, 93 93 isNullable, 94 - typeAnnotation: toLiteralType(enumMembers, plugin), 94 + type: toLiteralType(enumMembers, plugin), 95 95 }; 96 96 } 97 97
+9 -9
packages/openapi-python/src/plugins/pydantic/v2/toAst/intersection.ts
··· 1 1 import type { IR } from '@hey-api/shared'; 2 2 3 - import type { AnnotationExpr } from '../../../../py-dsl'; 3 + import type { VarType } from '../../../../py-dsl'; 4 4 import type { 5 5 PydanticField, 6 6 PydanticFinal, ··· 37 37 return { 38 38 childResults, 39 39 fieldConstraints: constraints, 40 - typeAnnotation: plugin.external('typing.Any'), 40 + type: plugin.external('typing.Any'), 41 41 }; 42 42 } 43 43 ··· 47 47 childResults, 48 48 fieldConstraints: { ...constraints, ...finalResult.fieldConstraints }, 49 49 mergedFields: finalResult.fields, 50 - typeAnnotation: finalResult.typeAnnotation, 50 + type: finalResult.type, 51 51 }; 52 52 } 53 53 ··· 59 59 const finalResult = applyModifiers(result); 60 60 61 61 // TODO: replace 62 - const typeStr = String(finalResult.typeAnnotation); 62 + const typeStr = String(finalResult.type); 63 63 const isReference = 64 64 !finalResult.fields && 65 65 typeStr !== '' && ··· 84 84 } 85 85 } 86 86 87 - let typeAnnotation: AnnotationExpr; 87 + let type: VarType; 88 88 89 89 if (baseClasses.length > 0 && mergedFields.length === 0) { 90 - typeAnnotation = baseClasses[0]!; 90 + type = baseClasses[0]!; 91 91 } else if (mergedFields.length > 0) { 92 92 // TODO: replace 93 - typeAnnotation = '__INTERSECTION_PLACEHOLDER__'; 93 + type = '__INTERSECTION_PLACEHOLDER__'; 94 94 } else { 95 - typeAnnotation = plugin.external('typing.Any'); 95 + type = plugin.external('typing.Any'); 96 96 } 97 97 98 98 return { ··· 100 100 childResults, 101 101 fieldConstraints: constraints, 102 102 mergedFields: mergedFields.length > 0 ? mergedFields : undefined, 103 - typeAnnotation, 103 + type, 104 104 }; 105 105 }
+1 -1
packages/openapi-python/src/plugins/pydantic/v2/toAst/never.ts
··· 10 10 schema: SchemaWithType<'never'>; 11 11 }): PydanticType { 12 12 return { 13 - typeAnnotation: plugin.external('typing.NoReturn'), 13 + type: plugin.external('typing.NoReturn'), 14 14 }; 15 15 }
+1 -1
packages/openapi-python/src/plugins/pydantic/v2/toAst/null.ts
··· 9 9 schema: SchemaWithType<'null'>; 10 10 }): PydanticType { 11 11 return { 12 - typeAnnotation: 'None', 12 + type: 'None', 13 13 }; 14 14 }
+2 -2
packages/openapi-python/src/plugins/pydantic/v2/toAst/number.ts
··· 17 17 if (typeof schema.const === 'number') { 18 18 const literal = plugin.external('typing.Literal'); 19 19 return { 20 - typeAnnotation: $(literal).slice($.literal(schema.const)), 20 + type: $(literal).slice($.literal(schema.const)), 21 21 }; 22 22 } 23 23 ··· 43 43 44 44 return { 45 45 fieldConstraints: constraints, 46 - typeAnnotation: schema.type === 'integer' ? 'int' : 'float', 46 + type: schema.type === 'integer' ? 'int' : 'float', 47 47 }; 48 48 }
+8 -10
packages/openapi-python/src/plugins/pydantic/v2/toAst/object.ts
··· 1 1 import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 2 import { childContext, toCase } from '@hey-api/shared'; 3 3 4 - import { $, type AnnotationExpr } from '../../../../py-dsl'; 4 + import { $, type VarType } from '../../../../py-dsl'; 5 5 import { safeRuntimeName } from '../../../../py-dsl/utils/name'; 6 6 import type { PydanticField, PydanticFinal, PydanticResult } from '../../shared/types'; 7 7 import type { PydanticPlugin } from '../../types'; ··· 15 15 walkerCtx: SchemaVisitorContext<PydanticPlugin['Instance']>; 16 16 } 17 17 18 - export interface ObjectToFieldsResult extends Pick<PydanticResult, 'fields' | 'typeAnnotation'> { 18 + export interface ObjectToFieldsResult extends Pick<PydanticResult, 'fields' | 'type'> { 19 19 childResults: Array<PydanticResult>; 20 20 } 21 21 22 - function resolveAdditionalProperties( 23 - ctx: ObjectResolverContext, 24 - ): AnnotationExpr | null | undefined { 22 + function resolveAdditionalProperties(ctx: ObjectResolverContext): VarType | null | undefined { 25 23 const { schema } = ctx; 26 24 27 25 if (!schema.additionalProperties || !schema.additionalProperties.type) return undefined; ··· 33 31 ); 34 32 ctx._childResults.push(result); 35 33 36 - return result.typeAnnotation; 34 + return result.type; 37 35 } 38 36 39 37 function resolveFields(ctx: ObjectResolverContext): Array<PydanticField> { ··· 54 52 isOptional, 55 53 name: ctx.plugin.symbol(snakeCaseName), 56 54 originalName: name, 57 - typeAnnotation: final.typeAnnotation, 55 + type: final.type, 58 56 }); 59 57 } 60 58 ··· 74 72 if (additional) { 75 73 const any = ctx.plugin.external('typing.Any'); 76 74 if (!ctx.schema.properties) { 77 - return { typeAnnotation: $('dict').slice('str', any) }; 75 + return { type: $('dict').slice('str', any) }; 78 76 } 79 - return { typeAnnotation: $('dict').slice('str', any) }; 77 + return { type: $('dict').slice('str', any) }; 80 78 } 81 79 82 80 // additionalProperties with properties → class wins, additional props ignored for now ··· 87 85 } 88 86 89 87 const any = ctx.plugin.external('typing.Any'); 90 - return { typeAnnotation: $('dict').slice('str', any) }; 88 + return { type: $('dict').slice('str', any) }; 91 89 } 92 90 93 91 export function objectToFields(
+2 -2
packages/openapi-python/src/plugins/pydantic/v2/toAst/string.ts
··· 17 17 if (typeof schema.const === 'string') { 18 18 const literal = plugin.external('typing.Literal'); 19 19 return { 20 - typeAnnotation: $(literal).slice($.literal(schema.const)), 20 + type: $(literal).slice($.literal(schema.const)), 21 21 }; 22 22 } 23 23 ··· 39 39 40 40 return { 41 41 fieldConstraints: constraints, 42 - typeAnnotation: 'str', 42 + type: 'str', 43 43 }; 44 44 }
+7 -7
packages/openapi-python/src/plugins/pydantic/v2/toAst/tuple.ts
··· 1 1 import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 2 import { childContext } from '@hey-api/shared'; 3 3 4 - import { $, type AnnotationExpr } from '../../../../py-dsl'; 4 + import { $, type VarType } from '../../../../py-dsl'; 5 5 import type { PydanticFinal, PydanticResult, PydanticType } from '../../shared/types'; 6 6 import type { PydanticPlugin } from '../../types'; 7 7 import type { FieldConstraints } from '../constants'; ··· 34 34 return { 35 35 childResults, 36 36 fieldConstraints: constraints, 37 - typeAnnotation: $(tuple).slice(), 37 + type: $(tuple).slice(), 38 38 }; 39 39 } 40 40 41 - const itemTypes: Array<AnnotationExpr> = []; 41 + const itemTypes: Array<VarType> = []; 42 42 43 43 for (let i = 0; i < schema.items.length; i++) { 44 44 const item = schema.items[i]!; ··· 46 46 childResults.push(result); 47 47 48 48 const finalResult = applyModifiers(result); 49 - if (finalResult.typeAnnotation !== undefined) { 50 - itemTypes.push(finalResult.typeAnnotation); 49 + if (finalResult.type !== undefined) { 50 + itemTypes.push(finalResult.type); 51 51 } 52 52 } 53 53 ··· 55 55 return { 56 56 childResults, 57 57 fieldConstraints: constraints, 58 - typeAnnotation: $(tuple).slice(any, '...'), 58 + type: $(tuple).slice(any, '...'), 59 59 }; 60 60 } 61 61 62 62 return { 63 63 childResults, 64 64 fieldConstraints: constraints, 65 - typeAnnotation: $(tuple).slice(...itemTypes), 65 + type: $(tuple).slice(...itemTypes), 66 66 }; 67 67 }
+1 -1
packages/openapi-python/src/plugins/pydantic/v2/toAst/undefined.ts
··· 9 9 schema: SchemaWithType<'undefined'>; 10 10 }): PydanticType { 11 11 return { 12 - typeAnnotation: 'None', 12 + type: 'None', 13 13 }; 14 14 }
+5 -5
packages/openapi-python/src/plugins/pydantic/v2/toAst/union.ts
··· 31 31 let isNullable = false; 32 32 33 33 for (const result of childResults) { 34 - if (result.typeAnnotation === 'None') { 34 + if (result.type === 'None') { 35 35 isNullable = true; 36 36 } else { 37 37 nonNullResults.push(result); ··· 45 45 childResults, 46 46 fieldConstraints: constraints, 47 47 isNullable: true, 48 - typeAnnotation: 'None', 48 + type: 'None', 49 49 }; 50 50 } 51 51 ··· 55 55 childResults, 56 56 fieldConstraints: { ...constraints, ...finalResult.fieldConstraints }, 57 57 isNullable, 58 - typeAnnotation: finalResult.typeAnnotation, 58 + type: finalResult.type, 59 59 }; 60 60 } 61 61 62 62 const union = plugin.external('typing.Union'); 63 63 const itemTypes = nonNullResults.map( 64 - (r) => applyModifiers(r).typeAnnotation ?? plugin.external('typing.Any'), 64 + (r) => applyModifiers(r).type ?? plugin.external('typing.Any'), 65 65 ); 66 66 67 67 if (isNullable) { ··· 72 72 childResults, 73 73 fieldConstraints: constraints, 74 74 isNullable, 75 - typeAnnotation: $(union).slice(...itemTypes), 75 + type: $(union).slice(...itemTypes), 76 76 }; 77 77 }
+1 -1
packages/openapi-python/src/plugins/pydantic/v2/toAst/unknown.ts
··· 10 10 schema: SchemaWithType<'unknown'>; 11 11 }): PydanticType { 12 12 return { 13 - typeAnnotation: plugin.external('typing.Any'), 13 + type: plugin.external('typing.Any'), 14 14 }; 15 15 }
+1 -1
packages/openapi-python/src/plugins/pydantic/v2/toAst/void.ts
··· 9 9 schema: SchemaWithType<'void'>; 10 10 }): PydanticType { 11 11 return { 12 - typeAnnotation: 'None', 12 + type: 'None', 13 13 }; 14 14 }
+10 -10
packages/openapi-python/src/plugins/pydantic/v2/walker.ts
··· 41 41 const needsOptional = optional || hasDefault; 42 42 const needsNullable = result.meta.nullable; 43 43 44 - let typeAnnotation = result.typeAnnotation; 44 + let type = result.type; 45 45 const fieldConstraints = { ...result.fieldConstraints }; 46 46 47 47 if (needsOptional || needsNullable) { 48 48 const optionalType = ctx.plugin.external('typing.Optional'); 49 - typeAnnotation = $(optionalType).slice(typeAnnotation ?? ctx.plugin.external('typing.Any')); 49 + type = $(optionalType).slice(type ?? ctx.plugin.external('typing.Any')); 50 50 if (needsOptional) { 51 51 fieldConstraints.default = hasDefault ? result.meta.default : null; 52 52 } ··· 56 56 enumMembers: result.enumMembers, 57 57 fieldConstraints, 58 58 fields: result.fields, 59 - typeAnnotation, 59 + type, 60 60 }; 61 61 }, 62 62 array(schema, ctx, walk) { 63 63 const applyModifiers = (result: PydanticResult, opts?: { optional?: boolean }) => 64 64 this.applyModifiers(result, ctx, opts) as PydanticFinal; 65 65 66 - const { childResults, fieldConstraints, typeAnnotation } = arrayToType({ 66 + const { childResults, fieldConstraints, type } = arrayToType({ 67 67 applyModifiers, 68 68 plugin: ctx.plugin, 69 69 schema, ··· 74 74 return { 75 75 fieldConstraints, 76 76 meta: composeMeta(childResults, { ...defaultMeta(schema) }), 77 - typeAnnotation, 77 + type, 78 78 }; 79 79 }, 80 80 boolean(schema, ctx) { ··· 160 160 const applyModifiers = (result: PydanticResult, opts?: { optional?: boolean }) => 161 161 this.applyModifiers(result, ctx, opts) as PydanticFinal; 162 162 163 - const { childResults, fields, typeAnnotation } = objectToFields({ 163 + const { childResults, fields, type } = objectToFields({ 164 164 applyModifiers, 165 165 plugin: ctx.plugin, 166 166 schema, ··· 171 171 return { 172 172 fields, 173 173 meta: inheritMeta(schema, childResults), 174 - typeAnnotation: typeAnnotation ?? '', 174 + type: type ?? '', 175 175 }; 176 176 }, 177 177 postProcess(result) { ··· 193 193 ...defaultMeta(schema), 194 194 hasForwardReference: !isRegistered, 195 195 }, 196 - typeAnnotation: refSymbol, 196 + type: refSymbol, 197 197 }; 198 198 }, 199 199 string(schema, ctx) { ··· 207 207 const applyModifiers = (result: PydanticResult, opts?: { optional?: boolean }) => 208 208 this.applyModifiers(result, ctx, opts) as PydanticFinal; 209 209 210 - const { childResults, fieldConstraints, typeAnnotation } = tupleToType({ 210 + const { childResults, fieldConstraints, type } = tupleToType({ 211 211 applyModifiers, 212 212 plugin: ctx.plugin, 213 213 schema, ··· 218 218 return { 219 219 fieldConstraints, 220 220 meta: composeMeta(childResults, { ...defaultMeta(schema) }), 221 - typeAnnotation, 221 + type, 222 222 }; 223 223 }, 224 224 undefined(schema, ctx) {
+9 -23
packages/openapi-python/src/py-dsl/decl/func.ts
··· 8 8 import { DocMixin } from '../mixins/doc'; 9 9 import { LayoutMixin } from '../mixins/layout'; 10 10 import { AsyncMixin, ExportMixin } from '../mixins/modifiers'; 11 + import { ParamMixin } from '../mixins/param'; 12 + import { ReturnsMixin } from '../mixins/returns'; 11 13 import { safeRuntimeName } from '../utils/name'; 12 14 13 15 const Mixed = AsyncMixin( 14 - DecoratorMixin(DocMixin(DoMixin(ExportMixin(LayoutMixin(PyDsl<py.FunctionDeclaration>))))), 16 + DecoratorMixin( 17 + DocMixin( 18 + DoMixin(ExportMixin(LayoutMixin(ParamMixin(ReturnsMixin(PyDsl<py.FunctionDeclaration>))))), 19 + ), 20 + ), 15 21 ); 16 22 17 23 export class FuncPyDsl extends Mixed { 18 24 readonly '~dsl' = 'FuncPyDsl'; 19 25 override readonly nameSanitizer = safeRuntimeName; 20 26 21 - protected _parameters: Array<py.FunctionParameter> = []; 22 - protected _returnType?: NodeName | py.Expression; 23 - 24 27 constructor(name: NodeName, fn?: (f: FuncPyDsl) => void) { 25 28 super(); 26 29 this.name.set(name); ··· 38 41 } finally { 39 42 ctx.popScope(); 40 43 } 41 - for (const param of this._parameters) { 42 - ctx.analyze(param); 43 - } 44 - ctx.analyze(this._returnType); 45 44 } 46 45 47 46 /** Returns true when all required builder calls are present. */ ··· 49 48 return this.missingRequiredCalls().length === 0; 50 49 } 51 50 52 - param(name: string, configure?: (p: py.FunctionParameter) => void): this { 53 - const param = py.factory.createFunctionParameter(name, undefined, undefined, undefined); 54 - if (configure) configure(param); 55 - this._parameters.push(param); 56 - return this; 57 - } 58 - 59 - returns(returnType: NodeName | py.Expression): this { 60 - this._returnType = 61 - typeof returnType === 'string' ? py.factory.createIdentifier(returnType) : returnType; 62 - return this; 63 - } 64 - 65 51 override toAst() { 66 52 this.$validate(); 67 53 return py.factory.createFunctionDeclaration( 68 54 this.name.toString(), 69 - this._parameters, 70 - this.$node(this._returnType), 55 + this.$params(), 56 + this.$returns(), 71 57 this.$do(), 72 58 this.$decorators(), 73 59 this.$docs(),
+72
packages/openapi-python/src/py-dsl/decl/param.ts
··· 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 2 + 3 + import { py } from '../../ts-python'; 4 + import { PyDsl } from '../base'; 5 + 6 + export type ParamDefaultValue = NodeName | py.Expression | undefined; 7 + export type ParamFn = (p: ParamPyDsl) => void; 8 + export type ParamName = NodeName | ParamFn; 9 + export type ParamType = NodeName | py.Expression | undefined; 10 + 11 + export type ParamCtor = (name: ParamName, fn?: ParamFn) => ParamPyDsl; 12 + 13 + export class ParamPyDsl extends PyDsl<py.FunctionParameter> { 14 + readonly '~dsl' = 'ParamPyDsl'; 15 + 16 + protected _defaultValue?: ParamDefaultValue; 17 + protected _type?: ParamType; 18 + 19 + constructor(name: ParamName, fn?: ParamFn) { 20 + super(); 21 + if (typeof name === 'function') { 22 + name(this); 23 + } else { 24 + this.name.set(name); 25 + fn?.(this); 26 + } 27 + } 28 + 29 + override analyze(ctx: AnalysisContext): void { 30 + super.analyze(ctx); 31 + ctx.analyze(this.name); 32 + ctx.analyze(this._type); 33 + ctx.analyze(this._defaultValue); 34 + } 35 + 36 + /** Sets the parameter default value. */ 37 + default(value: ParamDefaultValue): this { 38 + this._defaultValue = value; 39 + return this; 40 + } 41 + 42 + get isValid(): boolean { 43 + return this.missingRequiredCalls().length === 0; 44 + } 45 + 46 + /** Sets the parameter type. */ 47 + type(type: ParamType): this { 48 + this._type = type; 49 + return this; 50 + } 51 + 52 + override toAst() { 53 + this.$validate(); 54 + return py.factory.createFunctionParameter( 55 + this.name.toString(), 56 + this.$node(this._type), 57 + this.$node(this._defaultValue), 58 + ); 59 + } 60 + 61 + $validate(): asserts this { 62 + const missing = this.missingRequiredCalls(); 63 + if (missing.length === 0) return; 64 + throw new Error(`Parameter missing ${missing.join(' and ')}`); 65 + } 66 + 67 + private missingRequiredCalls(): ReadonlyArray<string> { 68 + const missing: Array<string> = []; 69 + if (!this.name.toString()) missing.push('name'); 70 + return missing; 71 + } 72 + }
+4 -4
packages/openapi-python/src/py-dsl/index.ts
··· 6 6 // import { EnumPyDsl } from './decl/enum'; 7 7 // import { FieldPyDsl } from './decl/field'; 8 8 import { FuncPyDsl } from './decl/func'; 9 - import { AttrPyDsl } from './expr/attr'; 9 + import { ParamPyDsl } from './decl/param'; 10 10 // import { GetterPyDsl } from './decl/getter'; 11 11 // import { InitPyDsl } from './decl/init'; 12 12 // import { EnumMemberPyDsl } from './decl/member'; 13 13 // import { MethodPyDsl } from './decl/method'; 14 - // import { ParamPyDsl } from './decl/param'; 15 14 // import { PatternPyDsl } from './decl/pattern'; 16 15 // import { SetterPyDsl } from './decl/setter'; 17 16 // import { ArrayPyDsl } from './expr/array'; 18 17 // import { AsPyDsl } from './expr/as'; 18 + import { AttrPyDsl } from './expr/attr'; 19 19 // import { AwaitPyDsl } from './expr/await'; 20 20 import { BinaryPyDsl } from './expr/binary'; 21 21 import { CallPyDsl } from './expr/call'; ··· 185 185 // object: (...args: ConstructorParameters<typeof ObjectTsDsl>) => new ObjectTsDsl(...args), 186 186 187 187 /** Creates a parameter declaration for functions or methods. */ 188 - // param: (...args: ConstructorParameters<typeof ParamTsDsl>) => new ParamTsDsl(...args), 188 + param: (...args: ConstructorParameters<typeof ParamPyDsl>) => new ParamPyDsl(...args), 189 189 190 190 /** Creates a pattern for destructuring or matching. */ 191 191 // pattern: (...args: ConstructorParameters<typeof PatternTsDsl>) => new PatternTsDsl(...args), ··· 320 320 // export type { MaybePyDsl, TypePyDsl } from './base'; 321 321 export { PyDsl } from './base'; 322 322 export type { CallArgs } from './expr/call'; 323 - export type { AnnotationExpr } from './stmt/var'; 323 + export type { VarType } from './stmt/var'; 324 324 export type { ExampleOptions } from './utils/context'; 325 325 export { ctx, PyDslContext } from './utils/context'; 326 326 export { keywords } from './utils/keywords';
+46
packages/openapi-python/src/py-dsl/mixins/param.ts
··· 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 + 3 + import type { py } from '../../ts-python'; 4 + import type { MaybePyDsl } from '../base'; 5 + import type { ParamCtor, ParamFn, ParamName } from '../decl/param'; 6 + import { ParamPyDsl } from '../decl/param'; 7 + import type { BaseCtor, MixinCtor } from './types'; 8 + 9 + export interface ParamMethods extends Node { 10 + /** Renders the parameters into an array of `FunctionParameter`s. */ 11 + $params(): ReadonlyArray<py.FunctionParameter>; 12 + /** Adds a parameter. */ 13 + param(...args: Parameters<ParamCtor>): this; 14 + /** Adds multiple parameters. */ 15 + params(...params: ReadonlyArray<MaybePyDsl<py.FunctionParameter>>): this; 16 + } 17 + 18 + export function ParamMixin<T extends py.Node, TBase extends BaseCtor<T>>(Base: TBase) { 19 + abstract class Param extends Base { 20 + protected _params: Array<MaybePyDsl<py.FunctionParameter>> = []; 21 + 22 + override analyze(ctx: AnalysisContext): void { 23 + super.analyze(ctx); 24 + for (const param of this._params) { 25 + ctx.analyze(param); 26 + } 27 + } 28 + 29 + protected param(name: ParamName, fn?: ParamFn): this { 30 + const p = typeof name === 'function' ? new ParamPyDsl(name) : new ParamPyDsl(name, fn); 31 + this._params.push(p); 32 + return this; 33 + } 34 + 35 + protected params(...params: ReadonlyArray<MaybePyDsl<py.FunctionParameter>>): this { 36 + this._params.push(...params); 37 + return this; 38 + } 39 + 40 + protected $params(): ReadonlyArray<py.FunctionParameter> { 41 + return this.$node(this._params); 42 + } 43 + } 44 + 45 + return Param as unknown as MixinCtor<TBase, ParamMethods>; 46 + }
+43
packages/openapi-python/src/py-dsl/mixins/returns.ts
··· 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 3 + 4 + import type { py } from '../../ts-python'; 5 + import { IdPyDsl } from '../expr/identifier'; 6 + import type { BaseCtor, MixinCtor } from './types'; 7 + 8 + export interface ReturnsMethods extends Node { 9 + $returns(): py.Expression | undefined; 10 + returns(type: NodeName | py.Expression): this; 11 + } 12 + 13 + export function ReturnsMixin<T extends py.Node, TBase extends BaseCtor<T>>(Base: TBase) { 14 + abstract class Returns extends Base { 15 + protected _returns?: IdPyDsl | py.Expression; 16 + 17 + override analyze(ctx: AnalysisContext): void { 18 + super.analyze(ctx); 19 + ctx.analyze(this._returns); 20 + } 21 + 22 + returns(type: NodeName | py.Expression): this { 23 + if (typeof type === 'string' || isSymbol(type)) { 24 + this._returns = new IdPyDsl(type); 25 + } else { 26 + this._returns = type as py.Expression; 27 + } 28 + return this; 29 + } 30 + 31 + protected $returns(): py.Expression | undefined { 32 + if (!this._returns) { 33 + return undefined; 34 + } 35 + if (this._returns instanceof IdPyDsl) { 36 + return this._returns.toAst(); 37 + } 38 + return this._returns as py.Expression; 39 + } 40 + } 41 + 42 + return Returns as unknown as MixinCtor<TBase, ReturnsMethods>; 43 + }
+10 -10
packages/openapi-python/src/py-dsl/stmt/var.ts
··· 9 9 10 10 const Mixed = ValueMixin(PyDsl<py.Assignment>); 11 11 12 - export type AnnotationExpr = NodeName | MaybePyDsl<py.Expression>; 12 + export type VarType = NodeName | MaybePyDsl<py.Expression>; 13 13 14 14 export class VarPyDsl extends Mixed { 15 15 readonly '~dsl' = 'VarPyDsl'; 16 16 override readonly nameSanitizer = safeRuntimeName; 17 17 18 - protected _annotation?: AnnotationExpr; 18 + protected _type?: VarType; 19 19 20 20 constructor(name?: NodeName) { 21 21 super(); ··· 28 28 override analyze(ctx: AnalysisContext): void { 29 29 super.analyze(ctx); 30 30 ctx.analyze(this.name); 31 - ctx.analyze(this._annotation); 31 + ctx.analyze(this._type); 32 32 } 33 33 34 34 /** Returns true when all required builder calls are present. */ ··· 37 37 } 38 38 39 39 /** Sets the type annotation for the variable. */ 40 - annotate(annotation: AnnotationExpr): this { 41 - this._annotation = annotation; 40 + type(type: VarType): this { 41 + this._type = type; 42 42 return this; 43 43 } 44 44 45 45 override toAst() { 46 46 this.$validate(); 47 47 const target = this.$node(this.name)!; 48 - const annotation = this.$annotation(); 48 + const annotation = this.$type(); 49 49 const value = this.$value(); 50 50 51 51 return py.factory.createAssignment(target, annotation, value); ··· 57 57 throw new Error(`Variable assignment missing ${missing.join(' and ')}`); 58 58 } 59 59 60 - protected $annotation(): py.Expression | undefined { 61 - return this.$node(this._annotation); 60 + protected $type(): py.Expression | undefined { 61 + return this.$node(this._type); 62 62 } 63 63 64 64 private missingRequiredCalls(): ReadonlyArray<string> { 65 65 const missing: Array<string> = []; 66 66 if (!this.$node(this.name)) missing.push('name'); 67 - const hasAnnotation = this.$annotation(); 67 + const hasAnnotation = this.$type(); 68 68 const hasValue = this.$value(); 69 69 if (!hasAnnotation && !hasValue) { 70 - missing.push('.annotate() or .assign()'); 70 + missing.push('.type() or .assign()'); 71 71 } 72 72 return missing; 73 73 }
+6 -1
packages/openapi-python/src/ts-python/__snapshots__/nodes/statements/with/with-alias.py
··· 1 1 class context_manager: 2 2 def __enter__(self): 3 3 return self 4 - def __exit__(self, exc_type, exc_val, exc_tb): 4 + def __exit__( 5 + self, 6 + exc_type, 7 + exc_val, 8 + exc_tb, 9 + ): 5 10 return False 6 11 7 12 with context_manager() as alias:
+6 -1
packages/openapi-python/src/ts-python/__snapshots__/nodes/statements/with/with-async.py
··· 1 1 class context_manager: 2 2 def __enter__(self): 3 3 return self 4 - def __exit__(self, exc_type, exc_val, exc_tb): 4 + def __exit__( 5 + self, 6 + exc_type, 7 + exc_val, 8 + exc_tb, 9 + ): 5 10 return False 6 11 7 12 async def foo():
+18 -3
packages/openapi-python/src/ts-python/__snapshots__/nodes/statements/with/with-many-items.py
··· 1 1 class context_manager: 2 2 def __enter__(self): 3 3 return self 4 - def __exit__(self, exc_type, exc_val, exc_tb): 4 + def __exit__( 5 + self, 6 + exc_type, 7 + exc_val, 8 + exc_tb, 9 + ): 5 10 return False 6 11 7 12 class context_manager2: 8 13 def __enter__(self): 9 14 return self 10 - def __exit__(self, exc_type, exc_val, exc_tb): 15 + def __exit__( 16 + self, 17 + exc_type, 18 + exc_val, 19 + exc_tb, 20 + ): 11 21 return False 12 22 13 23 class context_manager3: 14 24 def __enter__(self): 15 25 return self 16 - def __exit__(self, exc_type, exc_val, exc_tb): 26 + def __exit__( 27 + self, 28 + exc_type, 29 + exc_val, 30 + exc_tb, 31 + ): 17 32 return False 18 33 19 34 with context_manager() as alias, context_manager2() as (a, b), context_manager3():
+6 -1
packages/openapi-python/src/ts-python/__snapshots__/nodes/statements/with/with-tuple-alias.py
··· 1 1 class context_manager: 2 2 def __enter__(self): 3 3 return self 4 - def __exit__(self, exc_type, exc_val, exc_tb): 4 + def __exit__( 5 + self, 6 + exc_type, 7 + exc_val, 8 + exc_tb, 9 + ): 5 10 return False 6 11 7 12 with context_manager() as (a, b):
+6 -1
packages/openapi-python/src/ts-python/__snapshots__/nodes/statements/with/with.py
··· 1 1 class context_manager: 2 2 def __enter__(self): 3 3 return self 4 - def __exit__(self, exc_type, exc_val, exc_tb): 4 + def __exit__( 5 + self, 6 + exc_type, 7 + exc_val, 8 + exc_tb, 9 + ): 5 10 return False 6 11 7 12 with context_manager():
+2
packages/openapi-python/src/ts-python/nodes/statement.ts
··· 1 1 import type { PyClassDeclaration } from './declarations/class'; 2 2 import type { PyFunctionDeclaration } from './declarations/function'; 3 + import type { PyFunctionParameter } from './declarations/functionParameter'; 3 4 import type { PyAssignment } from './statements/assignment'; 4 5 import type { PyAugmentedAssignment } from './statements/augmentedAssignment'; 5 6 import type { PyBreakStatement } from './statements/break'; ··· 27 28 | PyExpressionStatement 28 29 | PyForStatement 29 30 | PyFunctionDeclaration 31 + | PyFunctionParameter 30 32 | PyIfStatement 31 33 | PyImportStatement 32 34 | PyRaiseStatement
+22 -5
packages/openapi-python/src/ts-python/printer.ts
··· 5 5 indentSize?: number; 6 6 } 7 7 8 + const PARAMS_MULTILINE_THRESHOLD = 3; 9 + 8 10 export function createPrinter(options?: PyPrinterOptions) { 9 11 const indentSize = options?.indentSize ?? 4; 10 12 ··· 178 180 } 179 181 const modifiers = node.modifiers?.map(printNode).join(' ') ?? ''; 180 182 const defPrefix = modifiers ? `${modifiers} def` : 'def'; 181 - const parameters = node.parameters.map((parameter) => { 183 + const formatParameter = (parameter: (typeof node.parameters)[number]): string => { 182 184 const children: Array<string> = [parameter.name]; 183 185 if (parameter.annotation) children.push(`: ${printNode(parameter.annotation)}`); 184 186 if (parameter.defaultValue) children.push(` = ${printNode(parameter.defaultValue)}`); 185 187 return children.join(''); 186 - }); 188 + }; 187 189 const returnAnnotation = node.returnType ? ` -> ${printNode(node.returnType)}` : ''; 188 - parts.push( 189 - printLine(`${defPrefix} ${node.name}(${parameters.join(', ')})${returnAnnotation}:`), 190 - ); 190 + const preParams = `${defPrefix} ${node.name}(`; 191 + const postParams = `)${returnAnnotation}:`; 192 + 193 + if (node.parameters.length > PARAMS_MULTILINE_THRESHOLD) { 194 + parts.push(printLine(preParams)); 195 + indentLevel += 1; 196 + const params = node.parameters.map((parameter) => 197 + printLine(`${formatParameter(parameter)},`), 198 + ); 199 + parts.push(...params); 200 + indentLevel -= 1; 201 + parts.push(printLine(postParams)); 202 + } else { 203 + const parameters = node.parameters.map((parameter) => formatParameter(parameter)); 204 + const params = parameters.join(', '); 205 + parts.push(printLine(`${preParams}${params}${postParams}`)); 206 + } 207 + 191 208 if (node.docstring) { 192 209 indentLevel += 1; 193 210 parts.push(...printDocstring(node.docstring));
+5 -5
packages/openapi-ts/src/ts-dsl/decl/param.ts
··· 9 9 import { TokenTsDsl } from '../token'; 10 10 import { TypeExprTsDsl } from '../type/expr'; 11 11 12 - export type ParamCtor = ( 13 - name: NodeName | ((p: ParamTsDsl) => void), 14 - fn?: (p: ParamTsDsl) => void, 15 - ) => ParamTsDsl; 12 + export type ParamName = NodeName | ParamFn; 13 + export type ParamFn = (p: ParamTsDsl) => void; 14 + 15 + export type ParamCtor = (name: ParamName, fn?: ParamFn) => ParamTsDsl; 16 16 17 17 const Mixed = DecoratorMixin( 18 18 OptionalMixin(PatternMixin(ValueMixin(TsDsl<ts.ParameterDeclaration>))), ··· 23 23 24 24 protected _type?: TypeTsDsl; 25 25 26 - constructor(name: NodeName | ((p: ParamTsDsl) => void), fn?: (p: ParamTsDsl) => void) { 26 + constructor(name: ParamName, fn?: ParamFn) { 27 27 super(); 28 28 if (typeof name === 'function') { 29 29 name(this);
+3 -6
packages/openapi-ts/src/ts-dsl/mixins/param.ts
··· 1 - import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; 5 - import type { ParamCtor } from '../decl/param'; 5 + import type { ParamCtor, ParamFn, ParamName } from '../decl/param'; 6 6 import { ParamTsDsl } from '../decl/param'; 7 7 import type { BaseCtor, MixinCtor } from './types'; 8 8 ··· 26 26 } 27 27 } 28 28 29 - protected param( 30 - name: NodeName | ((p: ParamTsDsl) => void), 31 - fn?: (p: ParamTsDsl) => void, 32 - ): this { 29 + protected param(name: ParamName, fn?: ParamFn): this { 33 30 const p = new ParamTsDsl(name, fn); 34 31 this._params.push(p); 35 32 return this;
+1
packages/openapi-ts/src/ts-dsl/type/param.ts
··· 29 29 ctx.analyze(this.defaultValue); 30 30 } 31 31 32 + /** Sets the parameter default value. */ 32 33 default(value: TypeParamExpr): this { 33 34 this.defaultValue = ref(value); 34 35 return this;