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 #2845 from hey-api/feat/symbol-meta-tags

feat: pass tags to symbol meta

authored by

Lubos and committed by
GitHub
dea8704d 958c70b6

+489 -421
+5
.changeset/plain-keys-camp.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + feat(parser): pass tags to symbol meta
+7 -8
packages/openapi-ts-tests/main/test/openapi-ts.config.ts
··· 141 141 if (!symbol.external && !symbol.meta?.path) { 142 142 console.log(`[${plugin.name}]:`, symbol.name, symbol.meta); 143 143 } 144 - if (symbol.meta?.path) { 145 - if (plugin.name === '@hey-api/sdk') { 146 - console.log( 147 - `[${plugin.name}]:`, 148 - symbol.name, 149 - symbol.meta.path, 150 - ); 151 - } 144 + if (symbol.meta?.tags && symbol.meta?.tags.size > 0) { 145 + console.log( 146 + `[${plugin.name}]:`, 147 + symbol.name, 148 + symbol.meta.path, 149 + symbol.meta.tags, 150 + ); 152 151 } 153 152 }, 154 153 // 'symbol:setValue:after': ({ plugin, symbol }) => {
+4
packages/openapi-ts/src/index.ts
··· 23 23 * Name of the plugin that registered this symbol. 24 24 */ 25 25 pluginName?: string; 26 + /** 27 + * Tags associated with this symbol. 28 + */ 29 + tags?: Set<string>; 26 30 } 27 31 } 28 32 // END OVERRIDES
+1
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/functions.ts
··· 46 46 const symbol = plugin.registerSymbol({ 47 47 meta: { 48 48 path: event._path, 49 + tags: event.tags, 49 50 }, 50 51 name: serviceFunctionIdentifier({ 51 52 config: plugin.context.config,
+5
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/operation.ts
··· 125 125 meta: { 126 126 kind: 'type', 127 127 path: state.path.value, 128 + tags: state.tags?.value, 128 129 }, 129 130 name: buildName({ 130 131 config: plugin.config.requests, ··· 163 164 meta: { 164 165 kind: 'type', 165 166 path: state.path.value, 167 + tags: state.tags?.value, 166 168 }, 167 169 name: buildName({ 168 170 config: plugin.config.errors, ··· 188 190 meta: { 189 191 kind: 'type', 190 192 path: state.path.value, 193 + tags: state.tags?.value, 191 194 }, 192 195 name: buildName({ 193 196 config: { ··· 222 225 meta: { 223 226 kind: 'type', 224 227 path: state.path.value, 228 + tags: state.tags?.value, 225 229 }, 226 230 name: buildName({ 227 231 config: plugin.config.responses, ··· 247 251 meta: { 248 252 kind: 'type', 249 253 path: state.path.value, 254 + tags: state.tags?.value, 250 255 }, 251 256 name: buildName({ 252 257 config: {
+4 -6
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/types.d.ts
··· 1 + import type { SymbolMeta } from '@hey-api/codegen-core'; 2 + 1 3 import type { ToRefs } from '~/plugins'; 2 4 3 5 import type { HeyApiTypeScriptPlugin } from '../types'; ··· 7 9 state: ToRefs<PluginState>; 8 10 }; 9 11 10 - export type PluginState = { 11 - /** 12 - * Path to the schema in the intermediary representation. 13 - */ 14 - path: ReadonlyArray<string | number>; 15 - }; 12 + export type PluginState = Pick<Required<SymbolMeta>, 'path'> & 13 + Pick<Partial<SymbolMeta>, 'tags'>;
+3
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/webhook.ts
··· 28 28 meta: { 29 29 kind: 'type', 30 30 path: state.path.value, 31 + tags: state.tags?.value, 31 32 }, 32 33 name: buildName({ 33 34 config: { ··· 56 57 meta: { 57 58 kind: 'type', 58 59 path: state.path.value, 60 + tags: state.tags?.value, 59 61 }, 60 62 name: symbolWebhookPayload.name, 61 63 placeholder: symbolWebhookPayload.placeholder, ··· 83 85 meta: { 84 86 kind: 'type', 85 87 path: state.path.value, 88 + tags: state.tags?.value, 86 89 }, 87 90 name: buildName({ 88 91 config: plugin.config.webhooks,
+13 -22
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/plugin.ts
··· 6 6 import type { SchemaWithType } from '~/plugins'; 7 7 import { toRefs } from '~/plugins/shared/utils/refs'; 8 8 import { tsc } from '~/tsc'; 9 - import { refToName } from '~/utils/ref'; 9 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 10 10 11 11 import { createClientOptions } from '../shared/clientOptions'; 12 12 import { exportType } from '../shared/export'; 13 13 import { operationToType } from '../shared/operation'; 14 - import type { IrSchemaToAstOptions } from '../shared/types'; 14 + import type { IrSchemaToAstOptions, PluginState } from '../shared/types'; 15 15 import { webhookToType } from '../shared/webhook'; 16 16 import { createWebhooks } from '../shared/webhooks'; 17 17 import type { HeyApiTypeScriptPlugin } from '../types'; ··· 68 68 }; 69 69 70 70 const handleComponent = ({ 71 - $ref, 72 71 plugin, 73 72 schema, 74 73 state, 75 74 }: IrSchemaToAstOptions & { 76 - $ref: string; 77 75 schema: IR.SchemaObject; 78 76 }) => { 79 77 const type = irSchemaToAst({ plugin, schema, state }); ··· 81 79 // Don't tag enums as 'type' since they export runtime artifacts (values) 82 80 const isEnum = schema.type === 'enum' && plugin.config.enums.enabled; 83 81 82 + const $ref = pathToJsonPointer(state.path.value); 84 83 const symbol = plugin.registerSymbol({ 85 84 exported: true, 86 85 meta: { 87 86 kind: isEnum ? undefined : 'type', 88 87 path: state.path.value, 88 + tags: state.tags?.value, 89 89 }, 90 90 name: buildName({ 91 91 config: plugin.config.definitions, ··· 144 144 'server', 145 145 'webhook', 146 146 (event) => { 147 + const state = toRefs<PluginState>({ 148 + path: event._path, 149 + tags: event.tags, 150 + }); 147 151 switch (event.type) { 148 152 case 'operation': 149 153 operationToType({ 150 154 operation: event.operation, 151 155 plugin, 152 - state: toRefs({ 153 - path: event._path, 154 - }), 156 + state, 155 157 }); 156 158 break; 157 159 case 'parameter': 158 160 handleComponent({ 159 - $ref: event.$ref, 160 161 plugin, 161 162 schema: event.parameter.schema, 162 - state: toRefs({ 163 - path: event._path, 164 - }), 163 + state, 165 164 }); 166 165 break; 167 166 case 'requestBody': 168 167 handleComponent({ 169 - $ref: event.$ref, 170 168 plugin, 171 169 schema: event.requestBody.schema, 172 - state: toRefs({ 173 - path: event._path, 174 - }), 170 + state, 175 171 }); 176 172 break; 177 173 case 'schema': 178 174 handleComponent({ 179 - $ref: event.$ref, 180 175 plugin, 181 176 schema: event.schema, 182 - state: toRefs({ 183 - path: event._path, 184 - }), 177 + state, 185 178 }); 186 179 break; 187 180 case 'server': ··· 192 185 webhookToType({ 193 186 operation: event.operation, 194 187 plugin, 195 - state: toRefs({ 196 - path: event._path, 197 - }), 188 + state, 198 189 }), 199 190 ); 200 191 break;
+5 -7
packages/openapi-ts/src/plugins/arktype/shared/types.d.ts
··· 1 + import type { SymbolMeta } from '@hey-api/codegen-core'; 1 2 import type ts from 'typescript'; 2 3 3 4 import type { IR } from '~/ir/types'; ··· 17 18 state: ToRefs<PluginState>; 18 19 }; 19 20 20 - export type PluginState = { 21 - /** 22 - * Path to the schema in the intermediary representation. 23 - */ 24 - _path: ReadonlyArray<string | number>; 25 - hasLazyExpression: boolean; 26 - }; 21 + export type PluginState = Pick<Required<SymbolMeta>, 'path'> & 22 + Pick<Partial<SymbolMeta>, 'tags'> & { 23 + hasLazyExpression: boolean; 24 + }; 27 25 28 26 export type ValidatorArgs = { 29 27 operation: IR.OperationObject;
+13 -21
packages/openapi-ts/src/plugins/arktype/v2/plugin.ts
··· 4 4 import type { SchemaWithType } from '~/plugins/shared/types/schema'; 5 5 import { toRefs } from '~/plugins/shared/utils/refs'; 6 6 import { tsc } from '~/tsc'; 7 - import { refToName } from '~/utils/ref'; 7 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 8 8 9 9 import { exportAst } from '../shared/export'; 10 - import type { Ast, IrSchemaToAstOptions } from '../shared/types'; 10 + import type { Ast, IrSchemaToAstOptions, PluginState } from '../shared/types'; 11 11 import type { ArktypePlugin } from '../types'; 12 12 import { irSchemaWithTypeToAst } from './toAst'; 13 13 ··· 236 236 }; 237 237 238 238 const handleComponent = ({ 239 - $ref, 240 239 plugin, 241 240 schema, 242 241 state, 243 242 }: IrSchemaToAstOptions & { 244 - $ref: string; 245 243 schema: IR.SchemaObject; 246 244 }): void => { 245 + const $ref = pathToJsonPointer(state.path.value); 247 246 const ast = irSchemaToAst({ plugin, schema, state }); 248 247 const baseName = refToName($ref); 249 248 const symbol = plugin.registerSymbol({ 250 249 exported: true, 251 250 meta: { 252 - path: state._path.value, 251 + path: state.path.value, 253 252 }, 254 253 name: buildName({ 255 254 config: plugin.config.definitions, ··· 262 261 exported: true, 263 262 meta: { 264 263 kind: 'type', 265 - path: state._path.value, 264 + path: state.path.value, 266 265 }, 267 266 name: buildName({ 268 267 config: plugin.config.definitions.types.infer, ··· 294 293 'schema', 295 294 'webhook', 296 295 (event) => { 296 + const state = toRefs<PluginState>({ 297 + hasLazyExpression: false, 298 + path: event._path, 299 + tags: event.tags, 300 + }); 297 301 switch (event.type) { 298 302 // case 'operation': 299 303 // operationToZodSchema({ ··· 311 315 // break; 312 316 case 'parameter': 313 317 handleComponent({ 314 - $ref: event.$ref, 315 318 plugin, 316 319 schema: event.parameter.schema, 317 - state: toRefs({ 318 - _path: event._path, 319 - hasLazyExpression: false, 320 - }), 320 + state, 321 321 }); 322 322 break; 323 323 case 'requestBody': 324 324 handleComponent({ 325 - $ref: event.$ref, 326 325 plugin, 327 326 schema: event.requestBody.schema, 328 - state: toRefs({ 329 - _path: event._path, 330 - hasLazyExpression: false, 331 - }), 327 + state, 332 328 }); 333 329 break; 334 330 case 'schema': 335 331 handleComponent({ 336 - $ref: event.$ref, 337 332 plugin, 338 333 schema: event.schema, 339 - state: toRefs({ 340 - _path: event._path, 341 - hasLazyExpression: false, 342 - }), 334 + state, 343 335 }); 344 336 break; 345 337 // case 'webhook':
+2 -2
packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts
··· 34 34 schema: property, 35 35 state: { 36 36 ...state, 37 - _path: toRef([...state._path.value, 'properties', name]), 37 + path: toRef([...state.path.value, 'properties', name]), 38 38 }, 39 39 }); 40 40 if (propertyAst.hasLazyExpression) { ··· 119 119 schema: schema.additionalProperties, 120 120 state: { 121 121 ...state, 122 - _path: toRef([...state._path.value, 'additionalProperties']), 122 + path: toRef([...state.path.value, 'additionalProperties']), 123 123 }, 124 124 }); 125 125 result.expression = tsc.callExpression({
+49 -41
packages/openapi-ts/src/plugins/shared/types/instance.d.ts
··· 1 1 import type { IrTopLevelKind } from '~/ir/graph'; 2 2 import type { IR } from '~/ir/types'; 3 3 4 - type WalkEvents = 5 - | { 6 - _path: ReadonlyArray<string | number>; 7 - method: keyof IR.PathItemObject; 8 - operation: IR.OperationObject; 9 - path: string; 10 - type: Extract<IrTopLevelKind, 'operation'>; 11 - } 12 - | { 13 - $ref: string; 14 - _path: ReadonlyArray<string | number>; 15 - name: string; 16 - parameter: IR.ParameterObject; 17 - type: Extract<IrTopLevelKind, 'parameter'>; 18 - } 19 - | { 20 - $ref: string; 21 - _path: ReadonlyArray<string | number>; 22 - name: string; 23 - requestBody: IR.RequestBodyObject; 24 - type: Extract<IrTopLevelKind, 'requestBody'>; 25 - } 26 - | { 27 - $ref: string; 28 - _path: ReadonlyArray<string | number>; 29 - name: string; 30 - schema: IR.SchemaObject; 31 - type: Extract<IrTopLevelKind, 'schema'>; 32 - } 33 - | { 34 - _path: ReadonlyArray<string | number>; 35 - server: IR.ServerObject; 36 - type: Extract<IrTopLevelKind, 'server'>; 37 - } 38 - | { 39 - _path: ReadonlyArray<string | number>; 40 - key: string; 41 - method: keyof IR.PathItemObject; 42 - operation: IR.OperationObject; 43 - type: Extract<IrTopLevelKind, 'webhook'>; 44 - }; 4 + export type BaseEvent = { 5 + /** 6 + * Path to the node, derived from the pointer. 7 + */ 8 + _path: ReadonlyArray<string | number>; 9 + /** 10 + * Pointer to the node in the graph. 11 + */ 12 + pointer: string; 13 + /** 14 + * Tags associated with the node. 15 + */ 16 + tags?: Set<string>; 17 + }; 18 + 19 + type WalkEvents = BaseEvent & 20 + ( 21 + | { 22 + method: keyof IR.PathItemObject; 23 + operation: IR.OperationObject; 24 + path: string; 25 + type: Extract<IrTopLevelKind, 'operation'>; 26 + } 27 + | { 28 + name: string; 29 + parameter: IR.ParameterObject; 30 + type: Extract<IrTopLevelKind, 'parameter'>; 31 + } 32 + | { 33 + name: string; 34 + requestBody: IR.RequestBodyObject; 35 + type: Extract<IrTopLevelKind, 'requestBody'>; 36 + } 37 + | { 38 + name: string; 39 + schema: IR.SchemaObject; 40 + type: Extract<IrTopLevelKind, 'schema'>; 41 + } 42 + | { 43 + server: IR.ServerObject; 44 + type: Extract<IrTopLevelKind, 'server'>; 45 + } 46 + | { 47 + key: string; 48 + method: keyof IR.PathItemObject; 49 + operation: IR.OperationObject; 50 + type: Extract<IrTopLevelKind, 'webhook'>; 51 + } 52 + ); 45 53 46 54 export type WalkEvent<T extends IrTopLevelKind = IrTopLevelKind> = Extract< 47 55 WalkEvents,
+14 -12
packages/openapi-ts/src/plugins/shared/utils/instance.ts
··· 24 24 import type { PluginConfigMap } from '~/plugins/config'; 25 25 import { jsonPointerToPath } from '~/utils/ref'; 26 26 27 - import type { WalkEvent } from '../types/instance'; 27 + import type { BaseEvent, WalkEvent } from '../types/instance'; 28 28 29 29 const defaultGetFilePath = (symbol: Symbol): string | undefined => { 30 30 if (!symbol.meta?.pluginName || typeof symbol.meta.pluginName !== 'string') { ··· 182 182 const result = matchIrPointerToGroup(pointer); 183 183 if (!result.matched || !eventSet.has(result.kind)) return; 184 184 let event: WalkEvent | undefined; 185 + const baseEvent: BaseEvent = { 186 + _path: jsonPointerToPath(pointer), 187 + pointer, 188 + tags: nodeInfo.tags, 189 + }; 185 190 switch (result.kind) { 186 191 case 'operation': 187 192 event = { 188 - _path: jsonPointerToPath(pointer), 193 + ...baseEvent, 189 194 method: nodeInfo.key as keyof IR.PathItemObject, 190 195 operation: nodeInfo.node as IR.OperationObject, 191 - path: jsonPointerToPath(pointer)[1]!, 196 + path: baseEvent._path[1] as string, 192 197 type: result.kind, 193 198 } satisfies WalkEvent<'operation'>; 194 199 break; 195 200 case 'parameter': 196 201 event = { 197 - $ref: pointer, 198 - _path: jsonPointerToPath(pointer), 202 + ...baseEvent, 199 203 name: nodeInfo.key as string, 200 204 parameter: nodeInfo.node as IR.ParameterObject, 201 205 type: result.kind, ··· 203 207 break; 204 208 case 'requestBody': 205 209 event = { 206 - $ref: pointer, 207 - _path: jsonPointerToPath(pointer), 210 + ...baseEvent, 208 211 name: nodeInfo.key as string, 209 212 requestBody: nodeInfo.node as IR.RequestBodyObject, 210 213 type: result.kind, ··· 212 215 break; 213 216 case 'schema': 214 217 event = { 215 - $ref: pointer, 216 - _path: jsonPointerToPath(pointer), 218 + ...baseEvent, 217 219 name: nodeInfo.key as string, 218 220 schema: nodeInfo.node as IR.SchemaObject, 219 221 type: result.kind, ··· 221 223 break; 222 224 case 'server': 223 225 event = { 224 - _path: jsonPointerToPath(pointer), 226 + ...baseEvent, 225 227 server: nodeInfo.node as IR.ServerObject, 226 228 type: result.kind, 227 229 } satisfies WalkEvent<'server'>; 228 230 break; 229 231 case 'webhook': 230 232 event = { 231 - _path: jsonPointerToPath(pointer), 232 - key: jsonPointerToPath(pointer)[1]!, 233 + ...baseEvent, 234 + key: baseEvent._path[1] as string, 233 235 method: nodeInfo.key as keyof IR.PathItemObject, 234 236 operation: nodeInfo.node as IR.OperationObject, 235 237 type: result.kind,
+42
packages/openapi-ts/src/plugins/valibot/shared/export.ts
··· 1 + import type { Symbol } from '@hey-api/codegen-core'; 2 + import type ts from 'typescript'; 3 + 4 + import type { IR } from '~/ir/types'; 5 + import { createSchemaComment } from '~/plugins/shared/utils/schema'; 6 + import { tsc } from '~/tsc'; 7 + 8 + import { identifiers } from '../v1/constants'; 9 + import { pipesToAst } from './pipesToAst'; 10 + import type { Ast, IrSchemaToAstOptions } from './types'; 11 + 12 + export const exportAst = ({ 13 + ast, 14 + plugin, 15 + schema, 16 + state, 17 + symbol, 18 + }: IrSchemaToAstOptions & { 19 + ast: Ast; 20 + schema: IR.SchemaObject; 21 + symbol: Symbol; 22 + }): void => { 23 + const v = plugin.referenceSymbol( 24 + plugin.api.selector('external', 'valibot.v'), 25 + ); 26 + 27 + const statement = tsc.constVariable({ 28 + comment: plugin.config.comments 29 + ? createSchemaComment({ schema }) 30 + : undefined, 31 + exportConst: symbol.exported, 32 + expression: pipesToAst({ pipes: ast.pipes, plugin }), 33 + name: symbol.placeholder, 34 + typeName: state.hasLazyExpression.value 35 + ? (tsc.propertyAccessExpression({ 36 + expression: v.placeholder, 37 + name: ast.typeName || identifiers.types.GenericSchema.text, 38 + }) as unknown as ts.TypeNode) 39 + : undefined, 40 + }); 41 + plugin.setSymbolValue(symbol, statement); 42 + };
+13 -10
packages/openapi-ts/src/plugins/valibot/shared/types.d.ts
··· 1 + import type { SymbolMeta } from '@hey-api/codegen-core'; 2 + import type ts from 'typescript'; 3 + 1 4 import type { IR } from '~/ir/types'; 2 5 import type { ToRefs } from '~/plugins'; 3 - import type { StringCase, StringName } from '~/types/case'; 4 6 5 7 import type { ValibotPlugin } from '../types'; 6 8 9 + export type Ast = { 10 + hasLazyExpression?: boolean; 11 + pipes: Array<ts.Expression>; 12 + typeName?: string | ts.Identifier; 13 + }; 14 + 7 15 export type IrSchemaToAstOptions = { 8 16 plugin: ValibotPlugin['Instance']; 9 17 state: ToRefs<PluginState>; 10 18 }; 11 19 12 - export type PluginState = { 13 - hasLazyExpression: boolean; 14 - nameCase: StringCase; 15 - nameTransformer: StringName; 16 - /** 17 - * Path to the schema in the intermediary representation. 18 - */ 19 - path: ReadonlyArray<string | number>; 20 - }; 20 + export type PluginState = Pick<Required<SymbolMeta>, 'path'> & 21 + Pick<Partial<SymbolMeta>, 'tags'> & { 22 + hasLazyExpression: boolean; 23 + }; 21 24 22 25 export type ValidatorArgs = { 23 26 operation: IR.OperationObject;
+16 -9
packages/openapi-ts/src/plugins/valibot/v1/operation.ts packages/openapi-ts/src/plugins/valibot/shared/operation.ts
··· 1 1 import { operationResponsesMap } from '~/ir/operation'; 2 2 import type { IR } from '~/ir/types'; 3 3 import { buildName } from '~/openApi/shared/utils/name'; 4 - import { toRef } from '~/plugins/shared/utils/refs'; 5 4 6 - import type { IrSchemaToAstOptions } from '../shared/types'; 7 - import { irSchemaToAst } from './plugin'; 5 + import { exportAst } from './export'; 6 + import type { Ast, IrSchemaToAstOptions } from './types'; 8 7 9 8 export const irOperationToAst = ({ 9 + getAst, 10 10 operation, 11 11 plugin, 12 12 state, 13 13 }: IrSchemaToAstOptions & { 14 + getAst: ( 15 + schema: IR.SchemaObject, 16 + path: ReadonlyArray<string | number>, 17 + ) => Ast; 14 18 operation: IR.OperationObject; 15 19 }) => { 16 20 if (plugin.config.requests.enabled) { ··· 111 115 112 116 schemaData.required = [...requiredProperties]; 113 117 118 + const ast = getAst(schemaData, state.path.value); 114 119 const symbol = plugin.registerSymbol({ 115 120 exported: true, 116 121 meta: { 117 122 path: state.path.value, 123 + tags: state.tags?.value, 118 124 }, 119 125 name: buildName({ 120 126 config: plugin.config.requests, ··· 122 128 }), 123 129 selector: plugin.api.selector('data', operation.id), 124 130 }); 125 - irSchemaToAst({ 131 + exportAst({ 132 + ast, 126 133 plugin, 127 134 schema: schemaData, 128 135 state, ··· 136 143 137 144 if (response) { 138 145 const path = [...state.path.value, 'responses']; 146 + const ast = getAst(response, path); 139 147 const symbol = plugin.registerSymbol({ 140 148 exported: true, 141 149 meta: { 142 150 path, 151 + tags: state.tags?.value, 143 152 }, 144 153 name: buildName({ 145 154 config: plugin.config.responses, ··· 147 156 }), 148 157 selector: plugin.api.selector('responses', operation.id), 149 158 }); 150 - irSchemaToAst({ 159 + exportAst({ 160 + ast, 151 161 plugin, 152 162 schema: response, 153 - state: { 154 - ...state, 155 - path: toRef(path), 156 - }, 163 + state, 157 164 symbol, 158 165 }); 159 166 }
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts packages/openapi-ts/src/plugins/valibot/shared/pipesToAst.ts
··· 3 3 import { tsc } from '~/tsc'; 4 4 5 5 import type { ValibotPlugin } from '../types'; 6 - import { identifiers } from './constants'; 6 + import { identifiers } from '../v1/constants'; 7 7 8 8 export const pipesToAst = ({ 9 9 pipes,
+83 -87
packages/openapi-ts/src/plugins/valibot/v1/plugin.ts
··· 1 - import type { Symbol } from '@hey-api/codegen-core'; 2 1 import type ts from 'typescript'; 3 2 4 3 import { deduplicateSchema } from '~/ir/schema'; ··· 6 5 import { buildName } from '~/openApi/shared/utils/name'; 7 6 import type { SchemaWithType } from '~/plugins'; 8 7 import { toRef, toRefs } from '~/plugins/shared/utils/refs'; 9 - import { createSchemaComment } from '~/plugins/shared/utils/schema'; 10 8 import { tsc } from '~/tsc'; 11 - import { refToName } from '~/utils/ref'; 9 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 12 10 11 + import { exportAst } from '../shared/export'; 13 12 import { numberParameter } from '../shared/numbers'; 14 - import type { IrSchemaToAstOptions, PluginState } from '../shared/types'; 13 + import { irOperationToAst } from '../shared/operation'; 14 + import { pipesToAst } from '../shared/pipesToAst'; 15 + import type { Ast, IrSchemaToAstOptions, PluginState } from '../shared/types'; 16 + import { irWebhookToAst } from '../shared/webhook'; 15 17 import type { ValibotPlugin } from '../types'; 16 18 import { identifiers } from './constants'; 17 - import { irOperationToAst } from './operation'; 18 - import { pipesToAst } from './pipesToAst'; 19 19 import { irSchemaWithTypeToAst } from './toAst'; 20 - import { irWebhookToAst } from './webhook'; 21 20 22 21 export const irSchemaToAst = ({ 23 - $ref, 24 22 optional, 25 23 plugin, 26 24 schema, 27 25 state, 28 - symbol, 29 26 }: IrSchemaToAstOptions & { 30 27 /** 31 - * When $ref is supplied, a node will be emitted to the file. 32 - */ 33 - $ref?: string; 34 - /** 35 28 * Accept `optional` to handle optional object properties. We can't handle 36 29 * this inside the object function because `.optional()` must come before 37 30 * `.default()` which is handled in this function. 38 31 */ 39 32 optional?: boolean; 40 33 schema: IR.SchemaObject; 41 - /** 42 - * When symbol is supplied, the AST node will be set as its value. 43 - */ 44 - symbol?: Symbol; 45 - }): Array<ts.Expression> => { 46 - if ($ref && !symbol) { 47 - const selector = plugin.api.selector('ref', $ref); 48 - symbol = plugin.getSymbol(selector) || plugin.referenceSymbol(selector); 49 - } 34 + }): Ast => { 35 + const ast: Ast = { 36 + pipes: [], 37 + }; 50 38 51 39 const v = plugin.referenceSymbol( 52 40 plugin.api.selector('external', 'valibot.v'), 53 41 ); 54 - let anyType: string | undefined; 55 - let pipes: Array<ts.Expression> = []; 56 42 57 43 if (schema.$ref) { 58 44 const selector = plugin.api.selector('ref', schema.$ref); 59 45 const refSymbol = plugin.referenceSymbol(selector); 60 46 if (plugin.isSymbolRegistered(selector)) { 61 47 const ref = tsc.identifier({ text: refSymbol.placeholder }); 62 - pipes.push(ref); 48 + ast.pipes.push(ref); 63 49 } else { 64 50 const lazyExpression = tsc.callExpression({ 65 51 functionName: tsc.propertyAccessExpression({ ··· 76 62 }), 77 63 ], 78 64 }); 79 - pipes.push(lazyExpression); 65 + ast.pipes.push(lazyExpression); 80 66 state.hasLazyExpression.value = true; 81 67 } 82 68 } else if (schema.type) { 83 - const ast = irSchemaWithTypeToAst({ 69 + const typeAst = irSchemaWithTypeToAst({ 84 70 plugin, 85 71 schema: schema as SchemaWithType, 86 72 state, 87 73 }); 88 - anyType = ast.anyType; 89 - pipes.push(ast.expression); 74 + ast.typeName = typeAst.anyType; 75 + ast.pipes.push(typeAst.expression); 90 76 91 77 if (plugin.config.metadata && schema.description) { 92 78 const expression = tsc.callExpression({ ··· 105 91 }), 106 92 ], 107 93 }); 108 - pipes.push(expression); 94 + ast.pipes.push(expression); 109 95 } 110 96 } else if (schema.items) { 111 97 schema = deduplicateSchema({ schema }); ··· 120 106 path: toRef([...state.path.value, 'items', index]), 121 107 }, 122 108 }); 123 - return pipesToAst({ pipes: itemAst, plugin }); 109 + return pipesToAst({ pipes: itemAst.pipes, plugin }); 124 110 }); 125 111 126 112 if (schema.logicalOperator === 'and') { ··· 135 121 }), 136 122 ], 137 123 }); 138 - pipes.push(intersectExpression); 124 + ast.pipes.push(intersectExpression); 139 125 } else { 140 126 const unionExpression = tsc.callExpression({ 141 127 functionName: tsc.propertyAccessExpression({ ··· 148 134 }), 149 135 ], 150 136 }); 151 - pipes.push(unionExpression); 137 + ast.pipes.push(unionExpression); 152 138 } 153 139 } else { 154 140 const schemaPipes = irSchemaToAst({ plugin, schema, state }); 155 - pipes.push(...schemaPipes); 141 + ast.pipes.push(...schemaPipes.pipes); 156 142 } 157 143 } else { 158 144 // catch-all fallback for failed schemas 159 - const ast = irSchemaWithTypeToAst({ 145 + const typeAst = irSchemaWithTypeToAst({ 160 146 plugin, 161 147 schema: { 162 148 type: 'unknown', 163 149 }, 164 150 state, 165 151 }); 166 - anyType = ast.anyType; 167 - pipes.push(ast.expression); 152 + ast.typeName = typeAst.anyType; 153 + ast.pipes.push(typeAst.expression); 168 154 } 169 155 170 - if (pipes.length) { 156 + if (ast.pipes.length) { 171 157 if (schema.accessScope === 'read') { 172 158 const readonlyExpression = tsc.callExpression({ 173 159 functionName: tsc.propertyAccessExpression({ ··· 175 161 name: identifiers.actions.readonly, 176 162 }), 177 163 }); 178 - pipes.push(readonlyExpression); 164 + ast.pipes.push(readonlyExpression); 179 165 } 180 166 181 167 let callParameter: ts.Expression | undefined; ··· 184 170 const isBigInt = schema.type === 'integer' && schema.format === 'int64'; 185 171 callParameter = numberParameter({ isBigInt, value: schema.default }); 186 172 if (callParameter) { 187 - pipes = [ 173 + ast.pipes = [ 188 174 tsc.callExpression({ 189 175 functionName: tsc.propertyAccessExpression({ 190 176 expression: v.placeholder, 191 177 name: identifiers.schemas.optional, 192 178 }), 193 - parameters: [pipesToAst({ pipes, plugin }), callParameter], 179 + parameters: [ 180 + pipesToAst({ pipes: ast.pipes, plugin }), 181 + callParameter, 182 + ], 194 183 }), 195 184 ]; 196 185 } 197 186 } 198 187 199 188 if (optional && !callParameter) { 200 - pipes = [ 189 + ast.pipes = [ 201 190 tsc.callExpression({ 202 191 functionName: tsc.propertyAccessExpression({ 203 192 expression: v.placeholder, 204 193 name: identifiers.schemas.optional, 205 194 }), 206 - parameters: [pipesToAst({ pipes, plugin })], 195 + parameters: [pipesToAst({ pipes: ast.pipes, plugin })], 207 196 }), 208 197 ]; 209 198 } 210 199 } 211 200 212 - if (symbol) { 213 - if ($ref) { 214 - symbol = plugin.registerSymbol({ 215 - exported: true, 216 - meta: { 217 - path: state.path.value, 218 - }, 219 - name: buildName({ 220 - config: { 221 - case: state.nameCase.value, 222 - name: state.nameTransformer.value, 223 - }, 224 - name: refToName($ref), 225 - }), 226 - selector: plugin.api.selector('ref', $ref), 227 - }); 228 - } 229 - const statement = tsc.constVariable({ 230 - comment: plugin.config.comments 231 - ? createSchemaComment({ schema }) 232 - : undefined, 233 - exportConst: symbol.exported, 234 - expression: pipesToAst({ pipes, plugin }), 235 - name: symbol.placeholder, 236 - typeName: state.hasLazyExpression.value 237 - ? (tsc.propertyAccessExpression({ 238 - expression: v.placeholder, 239 - name: anyType || identifiers.types.GenericSchema.text, 240 - }) as unknown as ts.TypeNode) 241 - : undefined, 242 - }); 243 - plugin.setSymbolValue(symbol, statement); 244 - return []; 245 - } 201 + return ast as Ast; 202 + }; 246 203 247 - return pipes; 204 + const handleComponent = ({ 205 + plugin, 206 + schema, 207 + state, 208 + }: IrSchemaToAstOptions & { 209 + schema: IR.SchemaObject; 210 + }): void => { 211 + const $ref = pathToJsonPointer(state.path.value); 212 + const ast = irSchemaToAst({ plugin, schema, state }); 213 + const baseName = refToName($ref); 214 + const symbol = plugin.registerSymbol({ 215 + exported: true, 216 + meta: { 217 + path: state.path.value, 218 + tags: state.tags?.value, 219 + }, 220 + name: buildName({ 221 + config: plugin.config.definitions, 222 + name: baseName, 223 + }), 224 + selector: plugin.api.selector('ref', $ref), 225 + }); 226 + exportAst({ 227 + ast, 228 + plugin, 229 + schema, 230 + state, 231 + symbol, 232 + }); 248 233 }; 249 234 250 235 export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { ··· 264 249 (event) => { 265 250 const state = toRefs<PluginState>({ 266 251 hasLazyExpression: false, 267 - nameCase: plugin.config.definitions.case, 268 - nameTransformer: plugin.config.definitions.name, 269 252 path: event._path, 253 + tags: event.tags, 270 254 }); 271 - 272 255 switch (event.type) { 273 256 case 'operation': 274 257 irOperationToAst({ 258 + getAst: (schema, path) => { 259 + const state = toRefs<PluginState>({ 260 + hasLazyExpression: false, 261 + path, 262 + tags: event.tags, 263 + }); 264 + return irSchemaToAst({ plugin, schema, state }); 265 + }, 275 266 operation: event.operation, 276 267 plugin, 277 268 state, 278 269 }); 279 270 break; 280 271 case 'parameter': 281 - irSchemaToAst({ 282 - $ref: event.$ref, 272 + handleComponent({ 283 273 plugin, 284 274 schema: event.parameter.schema, 285 275 state, 286 276 }); 287 277 break; 288 278 case 'requestBody': 289 - irSchemaToAst({ 290 - $ref: event.$ref, 279 + handleComponent({ 291 280 plugin, 292 281 schema: event.requestBody.schema, 293 282 state, 294 283 }); 295 284 break; 296 285 case 'schema': 297 - irSchemaToAst({ 298 - $ref: event.$ref, 286 + handleComponent({ 299 287 plugin, 300 288 schema: event.schema, 301 289 state, ··· 303 291 break; 304 292 case 'webhook': 305 293 irWebhookToAst({ 294 + getAst: (schema, path) => { 295 + const state = toRefs<PluginState>({ 296 + hasLazyExpression: false, 297 + path, 298 + tags: event.tags, 299 + }); 300 + return irSchemaToAst({ plugin, schema, state }); 301 + }, 306 302 operation: event.operation, 307 303 plugin, 308 304 state,
+19 -16
packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts
··· 1 - import type ts from 'typescript'; 2 - 3 1 import { deduplicateSchema } from '~/ir/schema'; 4 2 import type { SchemaWithType } from '~/plugins'; 5 3 import { toRef } from '~/plugins/shared/utils/refs'; 6 4 import { tsc } from '~/tsc'; 7 5 8 - import type { IrSchemaToAstOptions } from '../../shared/types'; 6 + import { pipesToAst } from '../../shared/pipesToAst'; 7 + import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; 9 8 import { identifiers } from '../constants'; 10 - import { pipesToAst } from '../pipesToAst'; 11 9 import { irSchemaToAst } from '../plugin'; 12 10 import { unknownToAst } from './unknown'; 13 11 ··· 17 15 state, 18 16 }: IrSchemaToAstOptions & { 19 17 schema: SchemaWithType<'array'>; 20 - }): ts.Expression => { 18 + }): Omit<Ast, 'typeName'> => { 19 + const result: Omit<Ast, 'typeName'> = { 20 + pipes: [], 21 + }; 22 + 21 23 const v = plugin.referenceSymbol( 22 24 plugin.api.selector('external', 'valibot.v'), 23 25 ); ··· 25 27 expression: v.placeholder, 26 28 name: identifiers.schemas.array, 27 29 }); 28 - 29 - const pipes: Array<ts.CallExpression> = []; 30 30 31 31 if (!schema.items) { 32 32 const expression = tsc.callExpression({ ··· 41 41 }), 42 42 ], 43 43 }); 44 - pipes.push(expression); 44 + result.pipes.push(expression); 45 45 } else { 46 46 schema = deduplicateSchema({ schema }); 47 47 48 48 // at least one item is guaranteed 49 49 const itemExpressions = schema.items!.map((item, index) => { 50 - const schemaPipes = irSchemaToAst({ 50 + const itemAst = irSchemaToAst({ 51 51 plugin, 52 52 schema: item, 53 53 state: { ··· 55 55 path: toRef([...state.path.value, 'items', index]), 56 56 }, 57 57 }); 58 - return pipesToAst({ pipes: schemaPipes, plugin }); 58 + if (itemAst.hasLazyExpression) { 59 + result.hasLazyExpression = true; 60 + } 61 + return pipesToAst({ pipes: itemAst.pipes, plugin }); 59 62 }); 60 63 61 64 if (itemExpressions.length === 1) { ··· 63 66 functionName, 64 67 parameters: itemExpressions, 65 68 }); 66 - pipes.push(expression); 69 + result.pipes.push(expression); 67 70 } else { 68 71 if (schema.logicalOperator === 'and') { 69 72 // TODO: parser - handle intersection ··· 87 90 }), 88 91 ], 89 92 }); 90 - pipes.push(expression); 93 + result.pipes.push(expression); 91 94 } 92 95 } 93 96 ··· 99 102 }), 100 103 parameters: [tsc.valueToExpression({ value: schema.minItems })], 101 104 }); 102 - pipes.push(expression); 105 + result.pipes.push(expression); 103 106 } else { 104 107 if (schema.minItems !== undefined) { 105 108 const expression = tsc.callExpression({ ··· 109 112 }), 110 113 parameters: [tsc.valueToExpression({ value: schema.minItems })], 111 114 }); 112 - pipes.push(expression); 115 + result.pipes.push(expression); 113 116 } 114 117 115 118 if (schema.maxItems !== undefined) { ··· 120 123 }), 121 124 parameters: [tsc.valueToExpression({ value: schema.maxItems })], 122 125 }); 123 - pipes.push(expression); 126 + result.pipes.push(expression); 124 127 } 125 128 } 126 129 127 - return pipesToAst({ pipes, plugin }); 130 + return result as Omit<Ast, 'typeName'>; 128 131 };
+22 -10
packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts
··· 2 2 3 3 import type { SchemaWithType } from '~/plugins'; 4 4 5 + import { pipesToAst } from '../../shared/pipesToAst'; 5 6 import type { IrSchemaToAstOptions } from '../../shared/types'; 6 7 import { arrayToAst } from './array'; 7 8 import { booleanToAst } from './boolean'; ··· 28 29 switch (schema.type) { 29 30 case 'array': 30 31 return { 31 - expression: arrayToAst({ 32 - ...args, 33 - schema: schema as SchemaWithType<'array'>, 32 + expression: pipesToAst({ 33 + pipes: arrayToAst({ 34 + ...args, 35 + schema: schema as SchemaWithType<'array'>, 36 + }).pipes, 37 + plugin: args.plugin, 34 38 }), 35 39 }; 36 40 case 'boolean': ··· 70 74 }), 71 75 }; 72 76 case 'object': 73 - return objectToAst({ 74 - ...args, 75 - schema: schema as SchemaWithType<'object'>, 76 - }); 77 + return { 78 + expression: pipesToAst({ 79 + pipes: objectToAst({ 80 + ...args, 81 + schema: schema as SchemaWithType<'object'>, 82 + }).pipes, 83 + plugin: args.plugin, 84 + }), 85 + }; 77 86 case 'string': 78 87 // For string schemas with int64/uint64 formats, use number handler to generate union with transform 79 88 if (schema.format === 'int64' || schema.format === 'uint64') { ··· 92 101 }; 93 102 case 'tuple': 94 103 return { 95 - expression: tupleToAst({ 96 - ...args, 97 - schema: schema as SchemaWithType<'tuple'>, 104 + expression: pipesToAst({ 105 + pipes: tupleToAst({ 106 + ...args, 107 + schema: schema as SchemaWithType<'tuple'>, 108 + }).pipes, 109 + plugin: args.plugin, 98 110 }), 99 111 }; 100 112 case 'undefined':
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts
··· 9 9 needsBigIntForFormat, 10 10 numberParameter, 11 11 } from '../../shared/numbers'; 12 + import { pipesToAst } from '../../shared/pipesToAst'; 12 13 import type { IrSchemaToAstOptions } from '../../shared/types'; 13 14 import { identifiers } from '../constants'; 14 - import { pipesToAst } from '../pipesToAst'; 15 15 16 16 export const numberToAst = ({ 17 17 plugin,
+41 -39
packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts
··· 5 5 import { tsc } from '~/tsc'; 6 6 import { numberRegExp } from '~/utils/regexp'; 7 7 8 - import type { IrSchemaToAstOptions } from '../../shared/types'; 8 + import { pipesToAst } from '../../shared/pipesToAst'; 9 + import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; 9 10 import { identifiers } from '../constants'; 10 - import { pipesToAst } from '../pipesToAst'; 11 11 import { irSchemaToAst } from '../plugin'; 12 12 13 13 export const objectToAst = ({ ··· 16 16 state, 17 17 }: IrSchemaToAstOptions & { 18 18 schema: SchemaWithType<'object'>; 19 - }): { 20 - anyType: string; 21 - expression: ts.CallExpression; 22 - } => { 19 + }): Omit<Ast, 'typeName'> => { 20 + const result: Partial<Omit<Ast, 'typeName'>> = {}; 21 + 23 22 // TODO: parser - handle constants 24 23 const properties: Array<ts.PropertyAssignment> = []; 25 24 ··· 29 28 const property = schema.properties[name]!; 30 29 const isRequired = required.includes(name); 31 30 32 - const schemaPipes = irSchemaToAst({ 31 + const propertyAst = irSchemaToAst({ 33 32 optional: !isRequired, 34 33 plugin, 35 34 schema: property, ··· 38 37 path: toRef([...state.path.value, 'properties', name]), 39 38 }, 40 39 }); 40 + if (propertyAst.hasLazyExpression) { 41 + result.hasLazyExpression = true; 42 + } 41 43 42 44 numberRegExp.lastIndex = 0; 43 45 let propertyName; ··· 60 62 } 61 63 properties.push( 62 64 tsc.propertyAssignment({ 63 - initializer: pipesToAst({ pipes: schemaPipes, plugin }), 65 + initializer: pipesToAst({ pipes: propertyAst.pipes, plugin }), 64 66 name: propertyName, 65 67 }), 66 68 ); ··· 75 77 schema.additionalProperties.type === 'object' && 76 78 !Object.keys(properties).length 77 79 ) { 78 - const pipes = irSchemaToAst({ 80 + const additionalAst = irSchemaToAst({ 79 81 plugin, 80 82 schema: schema.additionalProperties, 81 83 state: { ··· 83 85 path: toRef([...state.path.value, 'additionalProperties']), 84 86 }, 85 87 }); 86 - const expression = tsc.callExpression({ 87 - functionName: tsc.propertyAccessExpression({ 88 - expression: v.placeholder, 89 - name: identifiers.schemas.record, 90 - }), 91 - parameters: [ 92 - tsc.callExpression({ 93 - functionName: tsc.propertyAccessExpression({ 94 - expression: v.placeholder, 95 - name: identifiers.schemas.string, 96 - }), 97 - parameters: [], 88 + if (additionalAst.hasLazyExpression) { 89 + result.hasLazyExpression = true; 90 + } 91 + result.pipes = [ 92 + tsc.callExpression({ 93 + functionName: tsc.propertyAccessExpression({ 94 + expression: v.placeholder, 95 + name: identifiers.schemas.record, 98 96 }), 99 - pipesToAst({ pipes, plugin }), 100 - ], 101 - }); 102 - return { 103 - anyType: 'AnyZodObject', 104 - expression, 105 - }; 97 + parameters: [ 98 + tsc.callExpression({ 99 + functionName: tsc.propertyAccessExpression({ 100 + expression: v.placeholder, 101 + name: identifiers.schemas.string, 102 + }), 103 + parameters: [], 104 + }), 105 + pipesToAst({ pipes: additionalAst.pipes, plugin }), 106 + ], 107 + }), 108 + ]; 109 + return result as Omit<Ast, 'typeName'>; 106 110 } 107 111 108 - const expression = tsc.callExpression({ 109 - functionName: tsc.propertyAccessExpression({ 110 - expression: v.placeholder, 111 - name: identifiers.schemas.object, 112 + result.pipes = [ 113 + tsc.callExpression({ 114 + functionName: tsc.propertyAccessExpression({ 115 + expression: v.placeholder, 116 + name: identifiers.schemas.object, 117 + }), 118 + parameters: [ts.factory.createObjectLiteralExpression(properties, true)], 112 119 }), 113 - parameters: [ts.factory.createObjectLiteralExpression(properties, true)], 114 - }); 115 - return { 116 - // Zod uses AnyZodObject here, maybe we want to be more specific too 117 - anyType: identifiers.types.GenericSchema.text, 118 - expression, 119 - }; 120 + ]; 121 + return result as Omit<Ast, 'typeName'>; 120 122 };
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts
··· 3 3 import type { SchemaWithType } from '~/plugins'; 4 4 import { tsc } from '~/tsc'; 5 5 6 + import { pipesToAst } from '../../shared/pipesToAst'; 6 7 import type { IrSchemaToAstOptions } from '../../shared/types'; 7 8 import { identifiers } from '../constants'; 8 - import { pipesToAst } from '../pipesToAst'; 9 9 10 10 export const stringToAst = ({ 11 11 plugin,
+46 -33
packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts
··· 2 2 import { toRef } from '~/plugins/shared/utils/refs'; 3 3 import { tsc } from '~/tsc'; 4 4 5 - import type { IrSchemaToAstOptions } from '../../shared/types'; 5 + import { pipesToAst } from '../../shared/pipesToAst'; 6 + import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; 6 7 import { identifiers } from '../constants'; 7 - import { pipesToAst } from '../pipesToAst'; 8 8 import { irSchemaToAst } from '../plugin'; 9 9 import { unknownToAst } from './unknown'; 10 10 ··· 14 14 state, 15 15 }: IrSchemaToAstOptions & { 16 16 schema: SchemaWithType<'tuple'>; 17 - }) => { 17 + }): Omit<Ast, 'typeName'> => { 18 + const result: Partial<Omit<Ast, 'typeName'>> = {}; 19 + 18 20 const v = plugin.referenceSymbol( 19 21 plugin.api.selector('external', 'valibot.v'), 20 22 ); ··· 29 31 parameters: [tsc.valueToExpression({ value })], 30 32 }), 31 33 ); 32 - const expression = tsc.callExpression({ 33 - functionName: tsc.propertyAccessExpression({ 34 - expression: v.placeholder, 35 - name: identifiers.schemas.tuple, 36 - }), 37 - parameters: [ 38 - tsc.arrayLiteralExpression({ 39 - elements: tupleElements, 34 + result.pipes = [ 35 + tsc.callExpression({ 36 + functionName: tsc.propertyAccessExpression({ 37 + expression: v.placeholder, 38 + name: identifiers.schemas.tuple, 40 39 }), 41 - ], 42 - }); 43 - return expression; 40 + parameters: [ 41 + tsc.arrayLiteralExpression({ 42 + elements: tupleElements, 43 + }), 44 + ], 45 + }), 46 + ]; 47 + return result as Omit<Ast, 'typeName'>; 44 48 } 45 49 46 50 if (schema.items) { ··· 53 57 path: toRef([...state.path.value, 'items', index]), 54 58 }, 55 59 }); 56 - return pipesToAst({ pipes: schemaPipes, plugin }); 60 + if (schemaPipes.hasLazyExpression) { 61 + result.hasLazyExpression = true; 62 + } 63 + return pipesToAst({ pipes: schemaPipes.pipes, plugin }); 57 64 }); 58 - const expression = tsc.callExpression({ 59 - functionName: tsc.propertyAccessExpression({ 60 - expression: v.placeholder, 61 - name: identifiers.schemas.tuple, 62 - }), 63 - parameters: [ 64 - tsc.arrayLiteralExpression({ 65 - elements: tupleElements, 65 + result.pipes = [ 66 + tsc.callExpression({ 67 + functionName: tsc.propertyAccessExpression({ 68 + expression: v.placeholder, 69 + name: identifiers.schemas.tuple, 66 70 }), 67 - ], 68 - }); 69 - return expression; 71 + parameters: [ 72 + tsc.arrayLiteralExpression({ 73 + elements: tupleElements, 74 + }), 75 + ], 76 + }), 77 + ]; 78 + return result as Omit<Ast, 'typeName'>; 70 79 } 71 80 72 - return unknownToAst({ 73 - plugin, 74 - schema: { 75 - type: 'unknown', 76 - }, 77 - state, 78 - }); 81 + return { 82 + pipes: [ 83 + unknownToAst({ 84 + plugin, 85 + schema: { 86 + type: 'unknown', 87 + }, 88 + state, 89 + }), 90 + ], 91 + }; 79 92 };
+11 -3
packages/openapi-ts/src/plugins/valibot/v1/webhook.ts packages/openapi-ts/src/plugins/valibot/shared/webhook.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import { buildName } from '~/openApi/shared/utils/name'; 3 3 4 - import type { IrSchemaToAstOptions } from '../shared/types'; 5 - import { irSchemaToAst } from './plugin'; 4 + import { exportAst } from './export'; 5 + import type { Ast, IrSchemaToAstOptions } from './types'; 6 6 7 7 export const irWebhookToAst = ({ 8 + getAst, 8 9 operation, 9 10 plugin, 10 11 state, 11 12 }: IrSchemaToAstOptions & { 13 + getAst: ( 14 + schema: IR.SchemaObject, 15 + path: ReadonlyArray<string | number>, 16 + ) => Ast; 12 17 operation: IR.OperationObject; 13 18 }) => { 14 19 if (plugin.config.webhooks.enabled) { ··· 109 114 110 115 schemaData.required = [...requiredProperties]; 111 116 117 + const ast = getAst(schemaData, state.path.value); 112 118 const symbol = plugin.registerSymbol({ 113 119 exported: true, 114 120 meta: { 115 121 path: state.path.value, 122 + tags: state.tags?.value, 116 123 }, 117 124 name: buildName({ 118 125 config: plugin.config.webhooks, ··· 120 127 }), 121 128 selector: plugin.api.selector('webhook-request', operation.id), 122 129 }); 123 - irSchemaToAst({ 130 + exportAst({ 131 + ast, 124 132 plugin, 125 133 schema: schemaData, 126 134 state,
+16 -24
packages/openapi-ts/src/plugins/zod/mini/plugin.ts
··· 4 4 import type { SchemaWithType } from '~/plugins'; 5 5 import { toRef, toRefs } from '~/plugins/shared/utils/refs'; 6 6 import { tsc } from '~/tsc'; 7 - import { refToName } from '~/utils/ref'; 7 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 8 8 9 9 import { identifiers } from '../constants'; 10 10 import { exportAst } from '../shared/export'; ··· 226 226 }; 227 227 228 228 const handleComponent = ({ 229 - $ref, 230 229 plugin, 231 230 schema, 232 231 state, 233 232 }: IrSchemaToAstOptions & { 234 - $ref: string; 235 233 schema: IR.SchemaObject; 236 234 }): void => { 235 + const $ref = pathToJsonPointer(state.path.value); 237 236 const ast = irSchemaToAst({ plugin, schema, state }); 238 237 const baseName = refToName($ref); 239 238 const symbol = plugin.registerSymbol({ 240 239 exported: true, 241 240 meta: { 242 241 path: state.path.value, 242 + tags: state.tags?.value, 243 243 }, 244 244 name: buildName({ 245 245 config: plugin.config.definitions, ··· 253 253 meta: { 254 254 kind: 'type', 255 255 path: state.path.value, 256 + tags: state.tags?.value, 256 257 }, 257 258 name: buildName({ 258 259 config: plugin.config.definitions.types.infer, ··· 285 286 'schema', 286 287 'webhook', 287 288 (event) => { 289 + const state = toRefs<PluginState>({ 290 + hasLazyExpression: false, 291 + path: event._path, 292 + tags: event.tags, 293 + }); 288 294 switch (event.type) { 289 295 case 'operation': 290 296 irOperationToAst({ ··· 292 298 const state = toRefs<PluginState>({ 293 299 hasLazyExpression: false, 294 300 path, 301 + tags: event.tags, 295 302 }); 296 303 return irSchemaToAst({ plugin, schema, state }); 297 304 }, 298 305 operation: event.operation, 299 306 plugin, 300 - state: toRefs({ 301 - path: event._path, 302 - }), 307 + state, 303 308 }); 304 309 break; 305 310 case 'parameter': 306 311 handleComponent({ 307 - $ref: event.$ref, 308 312 plugin, 309 313 schema: event.parameter.schema, 310 - state: toRefs({ 311 - hasLazyExpression: false, 312 - path: event._path, 313 - }), 314 + state, 314 315 }); 315 316 break; 316 317 case 'requestBody': 317 318 handleComponent({ 318 - $ref: event.$ref, 319 319 plugin, 320 320 schema: event.requestBody.schema, 321 - state: toRefs({ 322 - hasLazyExpression: false, 323 - path: event._path, 324 - }), 321 + state, 325 322 }); 326 323 break; 327 324 case 'schema': 328 325 handleComponent({ 329 - $ref: event.$ref, 330 326 plugin, 331 327 schema: event.schema, 332 - state: toRefs({ 333 - hasLazyExpression: false, 334 - path: event._path, 335 - }), 328 + state, 336 329 }); 337 330 break; 338 331 case 'webhook': ··· 341 334 const state = toRefs<PluginState>({ 342 335 hasLazyExpression: false, 343 336 path, 337 + tags: event.tags, 344 338 }); 345 339 return irSchemaToAst({ plugin, schema, state }); 346 340 }, 347 341 operation: event.operation, 348 342 plugin, 349 - state: toRefs({ 350 - path: event._path, 351 - }), 343 + state, 352 344 }); 353 345 break; 354 346 }
+9 -7
packages/openapi-ts/src/plugins/zod/shared/operation.ts
··· 10 10 operation, 11 11 plugin, 12 12 state, 13 - }: Omit<IrSchemaToAstOptions, 'state'> & { 13 + }: IrSchemaToAstOptions & { 14 14 getAst: ( 15 15 schema: IR.SchemaObject, 16 16 path: ReadonlyArray<string | number>, 17 17 ) => Ast; 18 18 operation: IR.OperationObject; 19 - state: Partial<IrSchemaToAstOptions['state']>; 20 19 }): void => { 21 20 if (plugin.config.requests.enabled) { 22 21 const requiredProperties = new Set<string>(); ··· 116 115 117 116 schemaData.required = [...requiredProperties]; 118 117 119 - const path = state.path?.value || []; 120 - const ast = getAst(schemaData, path); 118 + const ast = getAst(schemaData, state.path.value); 121 119 const symbol = plugin.registerSymbol({ 122 120 exported: true, 123 121 meta: { 124 - path, 122 + path: state.path.value, 123 + tags: state.tags?.value, 125 124 }, 126 125 name: buildName({ 127 126 config: plugin.config.requests, ··· 134 133 exported: true, 135 134 meta: { 136 135 kind: 'type', 137 - path, 136 + path: state.path.value, 137 + tags: state.tags?.value, 138 138 }, 139 139 name: buildName({ 140 140 config: plugin.config.requests.types.infer, ··· 157 157 const { response } = operationResponsesMap(operation); 158 158 159 159 if (response) { 160 - const path = [...(state.path?.value || []), 'responses']; 160 + const path = [...state.path.value, 'responses']; 161 161 const ast = getAst(response, path); 162 162 const symbol = plugin.registerSymbol({ 163 163 exported: true, 164 164 meta: { 165 165 path, 166 + tags: state.tags?.value, 166 167 }, 167 168 name: buildName({ 168 169 config: plugin.config.responses, ··· 176 177 meta: { 177 178 kind: 'type', 178 179 path, 180 + tags: state.tags?.value, 179 181 }, 180 182 name: buildName({ 181 183 config: plugin.config.responses.types.infer,
+5 -7
packages/openapi-ts/src/plugins/zod/shared/types.d.ts
··· 1 + import type { SymbolMeta } from '@hey-api/codegen-core'; 1 2 import type ts from 'typescript'; 2 3 3 4 import type { IR } from '~/ir/types'; ··· 16 17 state: ToRefs<PluginState>; 17 18 }; 18 19 19 - export type PluginState = { 20 - hasLazyExpression: boolean; 21 - /** 22 - * Path to the schema in the intermediary representation. 23 - */ 24 - path: ReadonlyArray<string | number>; 25 - }; 20 + export type PluginState = Pick<Required<SymbolMeta>, 'path'> & 21 + Pick<Partial<SymbolMeta>, 'tags'> & { 22 + hasLazyExpression: boolean; 23 + }; 26 24 27 25 export type ValidatorArgs = { 28 26 operation: IR.OperationObject;
+6 -6
packages/openapi-ts/src/plugins/zod/shared/webhook.ts
··· 9 9 operation, 10 10 plugin, 11 11 state, 12 - }: Omit<IrSchemaToAstOptions, 'state'> & { 12 + }: IrSchemaToAstOptions & { 13 13 getAst: ( 14 14 schema: IR.SchemaObject, 15 15 path: ReadonlyArray<string | number>, 16 16 ) => Ast; 17 17 operation: IR.OperationObject; 18 - state: Partial<IrSchemaToAstOptions['state']>; 19 18 }) => { 20 19 if (plugin.config.webhooks.enabled) { 21 20 const requiredProperties = new Set<string>(); ··· 115 114 116 115 schemaData.required = [...requiredProperties]; 117 116 118 - const path = state.path?.value || []; 119 - const ast = getAst(schemaData, path); 117 + const ast = getAst(schemaData, state.path.value); 120 118 const symbol = plugin.registerSymbol({ 121 119 exported: true, 122 120 meta: { 123 - path, 121 + path: state.path.value, 122 + tags: state.tags?.value, 124 123 }, 125 124 name: buildName({ 126 125 config: plugin.config.webhooks, ··· 133 132 exported: true, 134 133 meta: { 135 134 kind: 'type', 136 - path, 135 + path: state.path.value, 136 + tags: state.tags?.value, 137 137 }, 138 138 name: buildName({ 139 139 config: plugin.config.webhooks.types.infer,
+16 -24
packages/openapi-ts/src/plugins/zod/v3/plugin.ts
··· 4 4 import type { SchemaWithType } from '~/plugins'; 5 5 import { toRef, toRefs } from '~/plugins/shared/utils/refs'; 6 6 import { tsc } from '~/tsc'; 7 - import { refToName } from '~/utils/ref'; 7 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 8 8 9 9 import { identifiers } from '../constants'; 10 10 import { exportAst } from '../shared/export'; ··· 200 200 }; 201 201 202 202 const handleComponent = ({ 203 - $ref, 204 203 plugin, 205 204 schema, 206 205 state, 207 206 }: IrSchemaToAstOptions & { 208 - $ref: string; 209 207 schema: IR.SchemaObject; 210 208 }): void => { 209 + const $ref = pathToJsonPointer(state.path.value); 211 210 const ast = irSchemaToAst({ plugin, schema, state }); 212 211 const baseName = refToName($ref); 213 212 const symbol = plugin.registerSymbol({ 214 213 exported: true, 215 214 meta: { 216 215 path: state.path.value, 216 + tags: state.tags?.value, 217 217 }, 218 218 name: buildName({ 219 219 config: plugin.config.definitions, ··· 227 227 meta: { 228 228 kind: 'type', 229 229 path: state.path.value, 230 + tags: state.tags?.value, 230 231 }, 231 232 name: buildName({ 232 233 config: plugin.config.definitions.types.infer, ··· 258 259 'schema', 259 260 'webhook', 260 261 (event) => { 262 + const state = toRefs<PluginState>({ 263 + hasLazyExpression: false, 264 + path: event._path, 265 + tags: event.tags, 266 + }); 261 267 switch (event.type) { 262 268 case 'operation': 263 269 irOperationToAst({ ··· 265 271 const state = toRefs<PluginState>({ 266 272 hasLazyExpression: false, 267 273 path, 274 + tags: event.tags, 268 275 }); 269 276 return irSchemaToAst({ plugin, schema, state }); 270 277 }, 271 278 operation: event.operation, 272 279 plugin, 273 - state: toRefs({ 274 - path: event._path, 275 - }), 280 + state, 276 281 }); 277 282 break; 278 283 case 'parameter': 279 284 handleComponent({ 280 - $ref: event.$ref, 281 285 plugin, 282 286 schema: event.parameter.schema, 283 - state: toRefs({ 284 - hasLazyExpression: false, 285 - path: event._path, 286 - }), 287 + state, 287 288 }); 288 289 break; 289 290 case 'requestBody': 290 291 handleComponent({ 291 - $ref: event.$ref, 292 292 plugin, 293 293 schema: event.requestBody.schema, 294 - state: toRefs({ 295 - hasLazyExpression: false, 296 - path: event._path, 297 - }), 294 + state, 298 295 }); 299 296 break; 300 297 case 'schema': 301 298 handleComponent({ 302 - $ref: event.$ref, 303 299 plugin, 304 300 schema: event.schema, 305 - state: toRefs({ 306 - hasLazyExpression: false, 307 - path: event._path, 308 - }), 301 + state, 309 302 }); 310 303 break; 311 304 case 'webhook': ··· 314 307 const state = toRefs<PluginState>({ 315 308 hasLazyExpression: false, 316 309 path, 310 + tags: event.tags, 317 311 }); 318 312 return irSchemaToAst({ plugin, schema, state }); 319 313 }, 320 314 operation: event.operation, 321 315 plugin, 322 - state: toRefs({ 323 - path: event._path, 324 - }), 316 + state, 325 317 }); 326 318 break; 327 319 }
+16 -24
packages/openapi-ts/src/plugins/zod/v4/plugin.ts
··· 4 4 import type { SchemaWithType } from '~/plugins'; 5 5 import { toRef, toRefs } from '~/plugins/shared/utils/refs'; 6 6 import { tsc } from '~/tsc'; 7 - import { refToName } from '~/utils/ref'; 7 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 8 8 9 9 import { identifiers } from '../constants'; 10 10 import { exportAst } from '../shared/export'; ··· 228 228 }; 229 229 230 230 const handleComponent = ({ 231 - $ref, 232 231 plugin, 233 232 schema, 234 233 state, 235 234 }: IrSchemaToAstOptions & { 236 - $ref: string; 237 235 schema: IR.SchemaObject; 238 236 }): void => { 237 + const $ref = pathToJsonPointer(state.path.value); 239 238 const ast = irSchemaToAst({ plugin, schema, state }); 240 239 const baseName = refToName($ref); 241 240 const symbol = plugin.registerSymbol({ 242 241 exported: true, 243 242 meta: { 244 243 path: state.path.value, 244 + tags: state.tags?.value, 245 245 }, 246 246 name: buildName({ 247 247 config: plugin.config.definitions, ··· 255 255 meta: { 256 256 kind: 'type', 257 257 path: state.path.value, 258 + tags: state.tags?.value, 258 259 }, 259 260 name: buildName({ 260 261 config: plugin.config.definitions.types.infer, ··· 286 287 'schema', 287 288 'webhook', 288 289 (event) => { 290 + const state = toRefs<PluginState>({ 291 + hasLazyExpression: false, 292 + path: event._path, 293 + tags: event.tags, 294 + }); 289 295 switch (event.type) { 290 296 case 'operation': 291 297 irOperationToAst({ ··· 293 299 const state = toRefs<PluginState>({ 294 300 hasLazyExpression: false, 295 301 path, 302 + tags: event.tags, 296 303 }); 297 304 return irSchemaToAst({ plugin, schema, state }); 298 305 }, 299 306 operation: event.operation, 300 307 plugin, 301 - state: toRefs({ 302 - path: event._path, 303 - }), 308 + state, 304 309 }); 305 310 break; 306 311 case 'parameter': 307 312 handleComponent({ 308 - $ref: event.$ref, 309 313 plugin, 310 314 schema: event.parameter.schema, 311 - state: toRefs({ 312 - hasLazyExpression: false, 313 - path: event._path, 314 - }), 315 + state, 315 316 }); 316 317 break; 317 318 case 'requestBody': 318 319 handleComponent({ 319 - $ref: event.$ref, 320 320 plugin, 321 321 schema: event.requestBody.schema, 322 - state: toRefs({ 323 - hasLazyExpression: false, 324 - path: event._path, 325 - }), 322 + state, 326 323 }); 327 324 break; 328 325 case 'schema': 329 326 handleComponent({ 330 - $ref: event.$ref, 331 327 plugin, 332 328 schema: event.schema, 333 - state: toRefs({ 334 - hasLazyExpression: false, 335 - path: event._path, 336 - }), 329 + state, 337 330 }); 338 331 break; 339 332 case 'webhook': ··· 342 335 const state = toRefs<PluginState>({ 343 336 hasLazyExpression: false, 344 337 path, 338 + tags: event.tags, 345 339 }); 346 340 return irSchemaToAst({ plugin, schema, state }); 347 341 }, 348 342 operation: event.operation, 349 343 plugin, 350 - state: toRefs({ 351 - path: event._path, 352 - }), 344 + state, 353 345 }); 354 346 break; 355 347 }