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 branch 'main' of https://github.com/hey-api/openapi-ts into fix/querySerializer

Lubos 2f3cf012 ecaab8a4

+2211 -1936
+5
.changeset/plain-keys-camp.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + feat(parser): pass tags to symbol meta
+1 -1
docs/package.json
··· 20 20 "vitepress-plugin-llms": "1.7.3", 21 21 "vue": "3.5.13" 22 22 }, 23 - "packageManager": "pnpm@10.18.3" 23 + "packageManager": "pnpm@10.19.0" 24 24 }
+2 -2
package.json
··· 40 40 "node": ">=20.19.0" 41 41 }, 42 42 "devDependencies": { 43 - "@arethetypeswrong/cli": "0.17.4", 43 + "@arethetypeswrong/cli": "0.18.2", 44 44 "@changesets/cli": "2.29.7", 45 45 "@changesets/get-github-info": "0.6.0", 46 46 "@changesets/parse": "0.4.1", ··· 71 71 "typescript-eslint": "8.29.1", 72 72 "vitest": "3.1.1" 73 73 }, 74 - "packageManager": "pnpm@10.18.3" 74 + "packageManager": "pnpm@10.19.0" 75 75 }
+19 -6
packages/openapi-ts-tests/main/test/openapi-ts.config.ts
··· 134 134 // 'symbol:register:after': ({ plugin, symbol }) => { 135 135 // console.log(`(global, ${plugin.name}) registered:`, symbol.id); 136 136 // }, 137 - // 'symbol:register:before': ({ plugin, symbol }) => { 138 - // console.log(`(global, ${plugin.name}):`, symbol.name); 139 - // }, 137 + 'symbol:register:before': ({ plugin, symbol }) => { 138 + // if (!symbol.external && !symbol.meta?.resourceType) { 139 + // console.log(`[${plugin.name}]:`, symbol.name); 140 + // } 141 + if (!symbol.external && !symbol.meta?.path) { 142 + console.log(`[${plugin.name}]:`, symbol.name, symbol.meta); 143 + } 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 + ); 151 + } 152 + }, 140 153 // 'symbol:setValue:after': ({ plugin, symbol }) => { 141 154 // console.log(`(${plugin.name}) set value:`, symbol.id); 142 155 // }, ··· 221 234 // error: '他們_error_{{name}}', 222 235 // name: '你們_errors_{{name}}', 223 236 // }, 224 - name: '@hey-api/typescript', 237 + // name: '@hey-api/typescript', 225 238 // requests: '我們_data_{{name}}', 226 239 // responses: { 227 240 // name: '我_responses_{{name}}', ··· 247 260 // }, 248 261 // include... 249 262 // instance: true, 250 - // name: '@hey-api/sdk', 263 + name: '@hey-api/sdk', 251 264 // operationId: false, 252 265 // params_EXPERIMENTAL: 'experiment', 253 266 // responseStyle: 'data', ··· 295 308 // mutationOptions: { 296 309 // name: '{{name}}MO', 297 310 // }, 298 - name: '@tanstack/react-query', 311 + // name: '@tanstack/react-query', 299 312 // queryKeys: { 300 313 // name: '{{name}}QK', 301 314 // },
+1 -1
packages/openapi-ts/package.json
··· 112 112 "@angular/platform-browser-dynamic": "19.2.15", 113 113 "@angular/router": "19.2.15", 114 114 "@config/vite-base": "workspace:*", 115 - "@types/bun": "1.2.23", 115 + "@types/bun": "1.3.0", 116 116 "@types/cross-spawn": "6.0.6", 117 117 "@types/semver": "7.7.1", 118 118 "axios": "1.8.2",
-1
packages/openapi-ts/src/generate/__tests__/class.test.ts
··· 97 97 }, 98 98 '@hey-api/sdk': { 99 99 api: { 100 - createOperationComment: () => undefined, 101 100 selector: () => [], 102 101 }, 103 102 config: {
-3
packages/openapi-ts/src/generate/__tests__/core.test.ts
··· 112 112 }, 113 113 '@hey-api/sdk': { 114 114 api: { 115 - createOperationComment: () => undefined, 116 115 selector: () => [], 117 116 }, 118 117 config: { ··· 270 269 }, 271 270 '@hey-api/sdk': { 272 271 api: { 273 - createOperationComment: () => undefined, 274 272 selector: () => [], 275 273 }, 276 274 config: { ··· 411 409 }, 412 410 '@hey-api/sdk': { 413 411 api: { 414 - createOperationComment: () => undefined, 415 412 selector: () => [], 416 413 }, 417 414 config: {
+1 -1
packages/openapi-ts/src/generate/client.ts
··· 4 4 5 5 import type { IProject, ProjectRenderMeta } from '@hey-api/codegen-core'; 6 6 7 + import type { DefinePlugin } from '~/plugins'; 7 8 import type { Client } from '~/plugins/@hey-api/client-core/types'; 8 9 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 9 - import type { DefinePlugin } from '~/plugins/types'; 10 10 import type { Config } from '~/types/config'; 11 11 12 12 import { ensureDirSync, relativeModulePath } from './utils';
-1
packages/openapi-ts/src/generate/legacy/__tests__/index.test.ts
··· 96 96 }, 97 97 '@hey-api/sdk': { 98 98 api: { 99 - createOperationComment: () => undefined, 100 99 selector: () => [], 101 100 }, 102 101 config: {
-1
packages/openapi-ts/src/generate/legacy/__tests__/output.test.ts
··· 109 109 }, 110 110 '@hey-api/sdk': { 111 111 api: { 112 - createOperationComment: () => undefined, 113 112 selector: () => [], 114 113 }, 115 114 config: {
+33 -3
packages/openapi-ts/src/index.ts
··· 1 - // eslint-disable-next-line @typescript-eslint/triple-slash-reference 2 - /// <reference path="./overrides.d.ts" /> 1 + // OVERRIDES 2 + // hard-coded here because build process doesn't pick up overrides from separate files 3 + import '@hey-api/codegen-core'; 4 + 5 + declare module '@hey-api/codegen-core' { 6 + interface ProjectRenderMeta { 7 + /** 8 + * If specified, this will be the file extension used when importing 9 + * other modules. By default, we don't add a file extension and let the 10 + * runtime resolve it. 11 + * 12 + * @default null 13 + */ 14 + importFileExtension?: (string & {}) | null; 15 + } 16 + 17 + interface SymbolMeta { 18 + /** 19 + * Path to the resource this symbol represents. 20 + */ 21 + path?: ReadonlyArray<string | number>; 22 + /** 23 + * Name of the plugin that registered this symbol. 24 + */ 25 + pluginName?: string; 26 + /** 27 + * Tags associated with this symbol. 28 + */ 29 + tags?: Set<string>; 30 + } 31 + } 32 + // END OVERRIDES 3 33 4 34 import colors from 'ansi-colors'; 5 35 // @ts-expect-error ··· 160 190 OpenApiResponseObject, 161 191 OpenApiSchemaObject, 162 192 } from './openApi/types'; 193 + export type { DefinePlugin, Plugin } from './plugins'; 163 194 export type { AngularClient } from './plugins/@hey-api/client-angular'; 164 195 export type { AxiosClient } from './plugins/@hey-api/client-axios'; 165 196 export { ··· 175 206 export type { ExpressionTransformer } from './plugins/@hey-api/transformers/expressions'; 176 207 export type { TypeTransformer } from './plugins/@hey-api/transformers/types'; 177 208 export { definePluginConfig } from './plugins/shared/utils/config'; 178 - export type { DefinePlugin, Plugin } from './plugins/types'; 179 209 export { compiler, tsc } from './tsc'; 180 210 export type { UserConfig } from './types/config'; 181 211 export type { LegacyIR } from './types/types';
-31
packages/openapi-ts/src/overrides.d.ts
··· 1 - import '@hey-api/codegen-core'; 2 - 3 - declare module '@hey-api/codegen-core' { 4 - interface ProjectRenderMeta { 5 - /** 6 - * If specified, this will be the file extension used when importing 7 - * other modules. By default, we don't add a file extension and let the 8 - * runtime resolve it. 9 - * 10 - * @default null 11 - */ 12 - importFileExtension?: (string & {}) | null; 13 - } 14 - 15 - interface SymbolMeta { 16 - /** 17 - * Name of the plugin that registered this symbol. 18 - */ 19 - pluginName?: string; 20 - /** 21 - * Type of resource this symbol represents. 22 - */ 23 - resourceType?: 24 - | 'operation' 25 - | 'parameter' 26 - | 'requestBody' 27 - | 'schema' 28 - | 'server' 29 - | 'webhook'; 30 - } 31 - }
+1 -1
packages/openapi-ts/src/plugins/@angular/common/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 6 6 | 'class'
+7 -4
packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts
··· 4 4 import type { IR } from '~/ir/types'; 5 5 import { buildName } from '~/openApi/shared/utils/name'; 6 6 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 7 - import { operationClasses } from '~/plugins/@hey-api/sdk/operation'; 8 - import { isOperationOptionsRequired } from '~/plugins/shared/utils/operation'; 7 + import { operationClasses } from '~/plugins/@hey-api/sdk/shared/operation'; 8 + import { 9 + createOperationComment, 10 + isOperationOptionsRequired, 11 + } from '~/plugins/shared/utils/operation'; 9 12 import { tsc } from '~/tsc'; 10 13 import { stringCase } from '~/utils/stringCase'; 11 14 ··· 293 296 294 297 return tsc.methodDeclaration({ 295 298 accessLevel: 'public', 296 - comment: sdkPlugin.api.createOperationComment({ operation }), 299 + comment: createOperationComment({ operation }), 297 300 name: methodName, 298 301 parameters: [ 299 302 { ··· 349 352 const dataType = symbolDataType?.placeholder || 'unknown'; 350 353 351 354 return tsc.constVariable({ 352 - comment: sdkPlugin.api.createOperationComment({ operation }), 355 + comment: createOperationComment({ operation }), 353 356 exportConst: symbol.exported, 354 357 expression: tsc.arrowFunction({ 355 358 parameters: [
+7 -4
packages/openapi-ts/src/plugins/@angular/common/httpResources.ts
··· 3 3 4 4 import type { IR } from '~/ir/types'; 5 5 import { buildName } from '~/openApi/shared/utils/name'; 6 - import { operationClasses } from '~/plugins/@hey-api/sdk/operation'; 7 - import { isOperationOptionsRequired } from '~/plugins/shared/utils/operation'; 6 + import { operationClasses } from '~/plugins/@hey-api/sdk/shared/operation'; 7 + import { 8 + createOperationComment, 9 + isOperationOptionsRequired, 10 + } from '~/plugins/shared/utils/operation'; 8 11 import { tsc } from '~/tsc'; 9 12 import { stringCase } from '~/utils/stringCase'; 10 13 ··· 374 377 375 378 return tsc.methodDeclaration({ 376 379 accessLevel: 'public', 377 - comment: sdkPlugin.api.createOperationComment({ operation }), 380 + comment: createOperationComment({ operation }), 378 381 name: methodName, 379 382 parameters: [ 380 383 { ··· 426 429 const dataType = symbolDataType?.placeholder || 'unknown'; 427 430 428 431 return tsc.constVariable({ 429 - comment: sdkPlugin.api.createOperationComment({ operation }), 432 + comment: createOperationComment({ operation }), 430 433 exportConst: symbol.exported, 431 434 expression: tsc.arrowFunction({ 432 435 parameters: [
+1 -1
packages/openapi-ts/src/plugins/@angular/common/types.d.ts
··· 1 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 2 2 import type { StringName } from '~/types/case'; 3 3 4 4 import type { IApi } from './api';
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-angular/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 'client'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-angular/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 import type { IApi } from './api'; 5 5
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-axios/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 'client'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-axios/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 import type { IApi } from './api'; 5 5
+3
packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts
··· 93 93 ]; 94 94 95 95 const symbolClient = plugin.registerSymbol({ 96 + meta: { 97 + path: [], 98 + }, 96 99 name: 'client', 97 100 selector: plugin.api.selector('client'), 98 101 });
+1
packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts
··· 29 29 exported: true, 30 30 meta: { 31 31 kind: 'type', 32 + path: [], 32 33 }, 33 34 name: 'CreateClientConfig', 34 35 });
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-core/types.d.ts
··· 1 + import type { Plugin } from '~/plugins'; 1 2 import type { HeyApiClientAngularPlugin } from '~/plugins/@hey-api/client-angular'; 2 3 import type { HeyApiClientAxiosPlugin } from '~/plugins/@hey-api/client-axios'; 3 4 import type { HeyApiClientFetchPlugin } from '~/plugins/@hey-api/client-fetch'; 4 5 import type { HeyApiClientNextPlugin } from '~/plugins/@hey-api/client-next'; 5 6 import type { HeyApiClientNuxtPlugin } from '~/plugins/@hey-api/client-nuxt'; 6 7 import type { HeyApiClientOfetchPlugin } from '~/plugins/@hey-api/client-ofetch'; 7 - import type { Plugin } from '~/plugins/types'; 8 8 9 9 export interface PluginHandler { 10 10 (...args: Parameters<HeyApiClientAngularPlugin['Handler']>): void;
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-fetch/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 'client'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-fetch/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 import type { IApi } from './api'; 5 5
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-next/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 'client'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-next/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 import type { IApi } from './api'; 5 5
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-nuxt/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 'client'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-nuxt/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 import type { IApi } from './api'; 5 5
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-ofetch/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 'client'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-ofetch/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 import type { IApi } from './api'; 5 5
+1 -1
packages/openapi-ts/src/plugins/@hey-api/legacy-angular/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 export type UserConfig = Plugin.Name<'legacy/angular'> & 5 5 Pick<Client.Config, 'output'>;
+1 -1
packages/openapi-ts/src/plugins/@hey-api/legacy-axios/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 export type UserConfig = Plugin.Name<'legacy/axios'> & 5 5 Pick<Client.Config, 'output'>;
+1 -1
packages/openapi-ts/src/plugins/@hey-api/legacy-fetch/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 export type UserConfig = Plugin.Name<'legacy/fetch'> & 5 5 Pick<Client.Config, 'output'>;
+1 -1
packages/openapi-ts/src/plugins/@hey-api/legacy-node/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 export type UserConfig = Plugin.Name<'legacy/node'> & 5 5 Pick<Client.Config, 'output'>;
+1 -1
packages/openapi-ts/src/plugins/@hey-api/legacy-xhr/types.d.ts
··· 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 1 2 import type { Client } from '~/plugins/@hey-api/client-core/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 3 3 4 4 export type UserConfig = Plugin.Name<'legacy/xhr'> & 5 5 Pick<Client.Config, 'output'>;
-2
packages/openapi-ts/src/plugins/@hey-api/schemas/__tests__/schemas.test.ts
··· 100 100 }, 101 101 '@hey-api/sdk': { 102 102 api: { 103 - createOperationComment: () => undefined, 104 103 selector: () => [], 105 104 }, 106 105 config: { ··· 271 270 }, 272 271 '@hey-api/sdk': { 273 272 api: { 274 - createOperationComment: () => undefined, 275 273 selector: () => [], 276 274 }, 277 275 config: {
+1 -1
packages/openapi-ts/src/plugins/@hey-api/schemas/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 'ref'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@hey-api/schemas/types.d.ts
··· 2 2 import type { OpenApiV2_0_XTypes } from '~/openApi/2.0.x'; 3 3 import type { OpenApiV3_0_XTypes } from '~/openApi/3.0.x'; 4 4 import type { OpenApiV3_1_XTypes } from '~/openApi/3.1.x'; 5 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 5 + import type { DefinePlugin, Plugin } from '~/plugins'; 6 6 7 7 import type { IApi } from './api'; 8 8
-4
packages/openapi-ts/src/plugins/@hey-api/sdk/__tests__/plugin.test.ts
··· 102 102 }, 103 103 '@hey-api/sdk': { 104 104 api: { 105 - createOperationComment: () => undefined, 106 105 selector: () => [], 107 106 }, 108 107 config: { ··· 345 344 }, 346 345 '@hey-api/sdk': { 347 346 api: { 348 - createOperationComment: () => undefined, 349 347 selector: () => [], 350 348 }, 351 349 config: { ··· 510 508 }, 511 509 '@hey-api/sdk': { 512 510 api: { 513 - createOperationComment: () => undefined, 514 511 selector: () => [], 515 512 }, 516 513 config: { ··· 678 675 }, 679 676 '@hey-api/sdk': { 680 677 api: { 681 - createOperationComment: () => undefined, 682 678 selector: () => [], 683 679 }, 684 680 config: {
+1 -10
packages/openapi-ts/src/plugins/@hey-api/sdk/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 4 - 5 - import { createOperationComment } from './comment'; 3 + import type { Plugin } from '~/plugins'; 6 4 7 5 type SelectorType = 8 6 | 'buildClientParams' ··· 16 14 | 'urlSearchParamsBodySerializer'; 17 15 18 16 export type IApi = { 19 - createOperationComment: typeof createOperationComment; 20 17 /** 21 18 * @param type Selector type. 22 19 * @param value Depends on `type`: ··· 36 33 37 34 export class Api implements IApi { 38 35 constructor(public meta: Plugin.Name<'@hey-api/sdk'>) {} 39 - 40 - createOperationComment( 41 - ...args: Parameters<typeof createOperationComment> 42 - ): ReturnType<typeof createOperationComment> { 43 - return createOperationComment(...args); 44 - } 45 36 46 37 selector(...args: ReadonlyArray<string | undefined>): Selector { 47 38 return [this.meta.name, ...(args as Selector)];
+2 -2
packages/openapi-ts/src/plugins/@hey-api/sdk/auth.ts packages/openapi-ts/src/plugins/@hey-api/sdk/shared/auth.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 3 - import type { Auth } from '../client-core/bundle/auth'; 4 - import type { HeyApiSdkPlugin } from './types'; 3 + import type { Auth } from '../../client-core/bundle/auth'; 4 + import type { HeyApiSdkPlugin } from '../types'; 5 5 6 6 // TODO: parser - handle more security types 7 7 const securitySchemeObjectToAuthObject = ({
-37
packages/openapi-ts/src/plugins/@hey-api/sdk/comment.ts
··· 1 - import type { IR } from '~/ir/types'; 2 - import type { Comments } from '~/tsc'; 3 - import { escapeComment } from '~/utils/escape'; 4 - 5 - export const createOperationComment = ({ 6 - operation, 7 - }: { 8 - operation: IR.OperationObject; 9 - }): Comments | undefined => { 10 - const comments: Array<string> = []; 11 - 12 - if (operation.summary) { 13 - comments.push(escapeComment(operation.summary)); 14 - } 15 - 16 - if (operation.description) { 17 - if (comments.length) { 18 - comments.push(''); // Add an empty line between summary and description 19 - } 20 - 21 - comments.push(escapeComment(operation.description)); 22 - } 23 - 24 - if (operation.deprecated) { 25 - if (comments.length) { 26 - comments.push(''); // Add an empty line before deprecated 27 - } 28 - 29 - comments.push('@deprecated'); 30 - } 31 - 32 - if (!comments.length) { 33 - return; 34 - } 35 - 36 - return comments; 37 - };
packages/openapi-ts/src/plugins/@hey-api/sdk/constants.ts packages/openapi-ts/src/plugins/@hey-api/sdk/shared/constants.ts
+18 -3
packages/openapi-ts/src/plugins/@hey-api/sdk/operation.ts packages/openapi-ts/src/plugins/@hey-api/sdk/shared/operation.ts
··· 11 11 import { stringCase } from '~/utils/stringCase'; 12 12 import { transformClassName } from '~/utils/transform'; 13 13 14 - import type { Field, Fields } from '../client-core/bundle/params'; 14 + import type { Field, Fields } from '../../client-core/bundle/params'; 15 + // import { getSignatureParameters } from './signature'; 16 + import type { HeyApiSdkPlugin } from '../types'; 15 17 import { operationAuth } from './auth'; 16 18 import { nuxtTypeComposable, nuxtTypeDefault } from './constants'; 17 - // import { getSignatureParameters } from './signature'; 18 - import type { HeyApiSdkPlugin } from './types'; 19 19 import { createRequestValidator, createResponseValidator } from './validator'; 20 20 21 21 interface ClassNameEntry { ··· 228 228 type: pluginTypeScript.api.schemaToType({ 229 229 plugin: pluginTypeScript, 230 230 schema: parameter.schema, 231 + state: { 232 + path: { 233 + value: [], 234 + }, 235 + }, 231 236 }), 232 237 }); 233 238 } ··· 249 254 type: pluginTypeScript.api.schemaToType({ 250 255 plugin: pluginTypeScript, 251 256 schema: parameter.schema, 257 + state: { 258 + path: { 259 + value: [], 260 + }, 261 + }, 252 262 }), 253 263 }); 254 264 } ··· 265 275 type: pluginTypeScript.api.schemaToType({ 266 276 plugin: pluginTypeScript, 267 277 schema: operation.body.schema, 278 + state: { 279 + path: { 280 + value: [], 281 + }, 282 + }, 268 283 }), 269 284 }); 270 285 }
+2 -512
packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts
··· 1 - import type ts from 'typescript'; 2 - 3 - import { clientFolderAbsolutePath } from '~/generate/client'; 4 - import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 5 - import { isOperationOptionsRequired } from '~/plugins/shared/utils/operation'; 6 - import { tsc } from '~/tsc'; 7 - import { stringCase } from '~/utils/stringCase'; 8 - 9 - import { nuxtTypeComposable, nuxtTypeDefault } from './constants'; 10 - import { 11 - operationClasses, 12 - operationParameters, 13 - operationStatements, 14 - } from './operation'; 15 - import { serviceFunctionIdentifier } from './plugin-legacy'; 16 - import { createTypeOptions } from './typeOptions'; 17 1 import type { HeyApiSdkPlugin } from './types'; 18 - 19 - const createClientClassNodes = ({ 20 - plugin, 21 - }: { 22 - plugin: HeyApiSdkPlugin['Instance']; 23 - }): ReadonlyArray<ts.ClassElement> => { 24 - const clientAssignmentStatement = tsc.expressionToStatement({ 25 - expression: tsc.binaryExpression({ 26 - left: tsc.propertyAccessExpression({ 27 - expression: tsc.this(), 28 - name: '_client', 29 - }), 30 - operator: '=', 31 - right: tsc.propertyAccessExpression({ 32 - expression: tsc.identifier({ text: 'args' }), 33 - name: 'client', 34 - }), 35 - }), 36 - }); 37 - 38 - const symbolClient = plugin.referenceSymbol(plugin.api.selector('Client')); 39 - const client = getClientPlugin(plugin.context.config); 40 - const symClient = 41 - client.api && 'selector' in client.api 42 - ? plugin.getSymbol( 43 - // @ts-expect-error 44 - client.api.selector('client'), 45 - ) 46 - : undefined; 47 - 48 - return [ 49 - tsc.propertyDeclaration({ 50 - initializer: symClient 51 - ? tsc.identifier({ text: symClient.placeholder }) 52 - : undefined, 53 - modifier: 'protected', 54 - name: '_client', 55 - type: tsc.typeReferenceNode({ typeName: symbolClient.placeholder }), 56 - }), 57 - // @ts-expect-error 58 - tsc.identifier({ text: '\n' }), 59 - tsc.constructorDeclaration({ 60 - multiLine: true, 61 - parameters: [ 62 - { 63 - isRequired: !plugin.config.client, 64 - name: 'args', 65 - type: tsc.typeInterfaceNode({ 66 - properties: [ 67 - { 68 - isRequired: !plugin.config.client, 69 - name: 'client', 70 - type: symbolClient.placeholder, 71 - }, 72 - ], 73 - useLegacyResolution: false, 74 - }), 75 - }, 76 - ], 77 - statements: [ 78 - !plugin.config.client 79 - ? clientAssignmentStatement 80 - : tsc.ifStatement({ 81 - expression: tsc.propertyAccessExpression({ 82 - expression: tsc.identifier({ text: 'args' }), 83 - isOptional: true, 84 - name: 'client', 85 - }), 86 - thenStatement: tsc.block({ 87 - statements: [clientAssignmentStatement], 88 - }), 89 - }), 90 - ], 91 - }), 92 - ]; 93 - }; 94 - 95 - interface SdkClassEntry { 96 - /** 97 - * Name of the class. 98 - */ 99 - className: string; 100 - /** 101 - * Symbol IDs for child classes located inside this class. 102 - */ 103 - classes: Set<number>; 104 - /** 105 - * Symbol ID for the class. 106 - */ 107 - id: number; 108 - /** 109 - * Track unique added method nodes. 110 - */ 111 - methods: Set<string>; 112 - /** 113 - * List of class nodes containing methods. 114 - */ 115 - nodes: Array<ts.ClassElement>; 116 - /** 117 - * Is this a root class? 118 - */ 119 - root: boolean; 120 - } 121 - 122 - const generateClassSdk = ({ 123 - plugin, 124 - }: { 125 - plugin: HeyApiSdkPlugin['Instance']; 126 - }) => { 127 - const client = getClientPlugin(plugin.context.config); 128 - const isAngularClient = client.name === '@hey-api/client-angular'; 129 - const isNuxtClient = client.name === '@hey-api/client-nuxt'; 130 - const sdkClasses = new Map<number, SdkClassEntry>(); 131 - /** 132 - * Track unique added classes. 133 - */ 134 - const generatedClasses = new Set<number>(); 135 - 136 - const clientClassNodes = plugin.config.instance 137 - ? createClientClassNodes({ plugin }) 138 - : []; 139 - 140 - plugin.forEach( 141 - 'operation', 142 - ({ operation }) => { 143 - const isRequiredOptions = isOperationOptionsRequired({ 144 - context: plugin.context, 145 - operation, 146 - }); 147 - const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); 148 - const symbolResponse = isNuxtClient 149 - ? plugin.getSymbol( 150 - pluginTypeScript.api.selector('response', operation.id), 151 - ) 152 - : undefined; 153 - 154 - const classes = operationClasses({ 155 - context: plugin.context, 156 - operation, 157 - plugin, 158 - }); 159 - 160 - for (const entry of classes.values()) { 161 - entry.path.forEach((currentClassName, index) => { 162 - const symbolCurrentClass = plugin.referenceSymbol( 163 - plugin.api.selector('class', currentClassName), 164 - ); 165 - if (!sdkClasses.has(symbolCurrentClass.id)) { 166 - sdkClasses.set(symbolCurrentClass.id, { 167 - className: currentClassName, 168 - classes: new Set(), 169 - id: symbolCurrentClass.id, 170 - methods: new Set(), 171 - nodes: [], 172 - root: !index, 173 - }); 174 - } 175 - 176 - const parentClassName = entry.path[index - 1]; 177 - if (parentClassName) { 178 - const symbolParentClass = plugin.referenceSymbol( 179 - plugin.api.selector('class', parentClassName), 180 - ); 181 - if ( 182 - symbolParentClass.placeholder !== symbolCurrentClass.placeholder 183 - ) { 184 - const parentClass = sdkClasses.get(symbolParentClass.id)!; 185 - parentClass.classes.add(symbolCurrentClass.id); 186 - sdkClasses.set(symbolParentClass.id, parentClass); 187 - } 188 - } 189 - 190 - const isLast = entry.path.length === index + 1; 191 - // add methods only to the last class 192 - if (!isLast) { 193 - return; 194 - } 195 - 196 - const currentClass = sdkClasses.get(symbolCurrentClass.id)!; 197 - 198 - // avoid duplicate methods 199 - if (currentClass.methods.has(entry.methodName)) { 200 - return; 201 - } 202 - 203 - const opParameters = operationParameters({ 204 - isRequiredOptions, 205 - operation, 206 - plugin, 207 - }); 208 - const statements = operationStatements({ 209 - isRequiredOptions, 210 - opParameters, 211 - operation, 212 - plugin, 213 - }); 214 - const functionNode = tsc.methodDeclaration({ 215 - accessLevel: 'public', 216 - comment: plugin.api.createOperationComment({ operation }), 217 - isStatic: isAngularClient ? false : !plugin.config.instance, 218 - name: entry.methodName, 219 - parameters: opParameters.parameters, 220 - returnType: undefined, 221 - statements, 222 - types: isNuxtClient 223 - ? [ 224 - { 225 - default: tsc.ots.string('$fetch'), 226 - extends: tsc.typeNode( 227 - plugin.referenceSymbol(plugin.api.selector('Composable')) 228 - .placeholder, 229 - ), 230 - name: nuxtTypeComposable, 231 - }, 232 - { 233 - default: symbolResponse 234 - ? tsc.typeReferenceNode({ 235 - typeName: symbolResponse.placeholder, 236 - }) 237 - : tsc.typeNode('undefined'), 238 - extends: symbolResponse 239 - ? tsc.typeReferenceNode({ 240 - typeName: symbolResponse.placeholder, 241 - }) 242 - : undefined, 243 - name: nuxtTypeDefault, 244 - }, 245 - ] 246 - : [ 247 - { 248 - default: 249 - ('throwOnError' in client.config 250 - ? client.config.throwOnError 251 - : false) ?? false, 252 - extends: 'boolean', 253 - name: 'ThrowOnError', 254 - }, 255 - ], 256 - }); 257 - 258 - if (!currentClass.nodes.length) { 259 - currentClass.nodes.push(functionNode); 260 - } else { 261 - currentClass.nodes.push( 262 - // @ts-expect-error 263 - tsc.identifier({ text: '\n' }), 264 - functionNode, 265 - ); 266 - } 2 + import { handlerV1 } from './v1/plugin'; 267 3 268 - currentClass.methods.add(entry.methodName); 269 - 270 - sdkClasses.set(symbolCurrentClass.id, currentClass); 271 - }); 272 - } 273 - }, 274 - { 275 - order: 'declarations', 276 - }, 277 - ); 278 - 279 - const symbolHeyApiClient = plugin.registerSymbol({ 280 - exported: false, 281 - name: '_HeyApiClient', 282 - }); 283 - 284 - const generateClass = (currentClass: SdkClassEntry) => { 285 - if (generatedClasses.has(currentClass.id)) { 286 - return; 287 - } 288 - 289 - if (currentClass.classes.size) { 290 - for (const childClassName of currentClass.classes) { 291 - const childClass = sdkClasses.get(childClassName)!; 292 - generateClass(childClass); 293 - 294 - currentClass.nodes.push( 295 - tsc.propertyDeclaration({ 296 - initializer: plugin.config.instance 297 - ? tsc.newExpression({ 298 - argumentsArray: plugin.config.instance 299 - ? [ 300 - tsc.objectExpression({ 301 - multiLine: false, 302 - obj: [ 303 - { 304 - key: 'client', 305 - value: tsc.propertyAccessExpression({ 306 - expression: tsc.this(), 307 - name: '_client', 308 - }), 309 - }, 310 - ], 311 - }), 312 - ] 313 - : [], 314 - expression: tsc.identifier({ 315 - text: plugin.referenceSymbol(childClass.id).placeholder, 316 - }), 317 - }) 318 - : tsc.identifier({ 319 - text: plugin.referenceSymbol(childClass.id).placeholder, 320 - }), 321 - modifier: plugin.config.instance ? undefined : 'static', 322 - name: stringCase({ 323 - case: 'camelCase', 324 - value: childClass.className, 325 - }), 326 - }), 327 - ); 328 - } 329 - } 330 - 331 - const symbol = plugin.registerSymbol({ 332 - exported: true, 333 - name: currentClass.className, 334 - selector: plugin.api.selector('class', currentClass.className), 335 - }); 336 - const node = tsc.classDeclaration({ 337 - decorator: 338 - currentClass.root && isAngularClient 339 - ? { 340 - args: [ 341 - { 342 - providedIn: 'root', 343 - }, 344 - ], 345 - name: plugin.referenceSymbol(plugin.api.selector('Injectable')) 346 - .placeholder, 347 - } 348 - : undefined, 349 - exportClass: symbol.exported, 350 - extendedClasses: plugin.config.instance 351 - ? [symbolHeyApiClient.placeholder] 352 - : undefined, 353 - name: symbol.placeholder, 354 - nodes: currentClass.nodes, 355 - }); 356 - plugin.setSymbolValue(symbol, node); 357 - generatedClasses.add(symbol.id); 358 - }; 359 - 360 - if (clientClassNodes.length) { 361 - const node = tsc.classDeclaration({ 362 - exportClass: symbolHeyApiClient.exported, 363 - name: symbolHeyApiClient.placeholder, 364 - nodes: clientClassNodes, 365 - }); 366 - plugin.setSymbolValue(symbolHeyApiClient, node); 367 - } 368 - 369 - for (const sdkClass of sdkClasses.values()) { 370 - generateClass(sdkClass); 371 - } 372 - }; 373 - 374 - const generateFlatSdk = ({ 375 - plugin, 376 - }: { 377 - plugin: HeyApiSdkPlugin['Instance']; 378 - }) => { 379 - const client = getClientPlugin(plugin.context.config); 380 - const isNuxtClient = client.name === '@hey-api/client-nuxt'; 381 - 382 - plugin.forEach( 383 - 'operation', 384 - ({ operation }) => { 385 - const isRequiredOptions = isOperationOptionsRequired({ 386 - context: plugin.context, 387 - operation, 388 - }); 389 - const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); 390 - const symbolResponse = isNuxtClient 391 - ? plugin.getSymbol( 392 - pluginTypeScript.api.selector('response', operation.id), 393 - ) 394 - : undefined; 395 - const opParameters = operationParameters({ 396 - isRequiredOptions, 397 - operation, 398 - plugin, 399 - }); 400 - const statements = operationStatements({ 401 - isRequiredOptions, 402 - opParameters, 403 - operation, 404 - plugin, 405 - }); 406 - const symbol = plugin.registerSymbol({ 407 - name: serviceFunctionIdentifier({ 408 - config: plugin.context.config, 409 - handleIllegal: true, 410 - id: operation.id, 411 - operation, 412 - }), 413 - selector: plugin.api.selector('function', operation.id), 414 - }); 415 - const node = tsc.constVariable({ 416 - comment: plugin.api.createOperationComment({ operation }), 417 - exportConst: true, 418 - expression: tsc.arrowFunction({ 419 - parameters: opParameters.parameters, 420 - returnType: undefined, 421 - statements, 422 - types: isNuxtClient 423 - ? [ 424 - { 425 - default: tsc.ots.string('$fetch'), 426 - extends: tsc.typeNode( 427 - plugin.referenceSymbol(plugin.api.selector('Composable')) 428 - .placeholder, 429 - ), 430 - name: nuxtTypeComposable, 431 - }, 432 - { 433 - default: symbolResponse 434 - ? tsc.typeReferenceNode({ 435 - typeName: symbolResponse.placeholder, 436 - }) 437 - : tsc.typeNode('undefined'), 438 - extends: symbolResponse 439 - ? tsc.typeReferenceNode({ 440 - typeName: symbolResponse.placeholder, 441 - }) 442 - : undefined, 443 - name: nuxtTypeDefault, 444 - }, 445 - ] 446 - : [ 447 - { 448 - default: 449 - ('throwOnError' in client.config 450 - ? client.config.throwOnError 451 - : false) ?? false, 452 - extends: 'boolean', 453 - name: 'ThrowOnError', 454 - }, 455 - ], 456 - }), 457 - name: symbol.placeholder, 458 - }); 459 - plugin.setSymbolValue(symbol, node); 460 - }, 461 - { 462 - order: 'declarations', 463 - }, 464 - ); 465 - }; 466 - 467 - export const handler: HeyApiSdkPlugin['Handler'] = ({ plugin }) => { 468 - const clientModule = clientFolderAbsolutePath(plugin.context.config); 469 - plugin.registerSymbol({ 470 - external: clientModule, 471 - name: 'formDataBodySerializer', 472 - selector: plugin.api.selector('formDataBodySerializer'), 473 - }); 474 - plugin.registerSymbol({ 475 - external: clientModule, 476 - name: 'urlSearchParamsBodySerializer', 477 - selector: plugin.api.selector('urlSearchParamsBodySerializer'), 478 - }); 479 - plugin.registerSymbol({ 480 - external: clientModule, 481 - name: 'buildClientParams', 482 - selector: plugin.api.selector('buildClientParams'), 483 - }); 484 - 485 - const client = getClientPlugin(plugin.context.config); 486 - const isAngularClient = client.name === '@hey-api/client-angular'; 487 - const isNuxtClient = client.name === '@hey-api/client-nuxt'; 488 - if (isNuxtClient) { 489 - plugin.registerSymbol({ 490 - external: clientModule, 491 - meta: { 492 - kind: 'type', 493 - }, 494 - name: 'Composable', 495 - selector: plugin.api.selector('Composable'), 496 - }); 497 - } 498 - 499 - if (isAngularClient && plugin.config.asClass) { 500 - plugin.registerSymbol({ 501 - external: '@angular/core', 502 - name: 'Injectable', 503 - selector: plugin.api.selector('Injectable'), 504 - }); 505 - } 506 - 507 - createTypeOptions({ plugin }); 508 - 509 - if (plugin.config.asClass) { 510 - generateClassSdk({ plugin }); 511 - } else { 512 - generateFlatSdk({ plugin }); 513 - } 514 - }; 4 + export const handler: HeyApiSdkPlugin['Handler'] = (args) => handlerV1(args);
+378
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/class.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 4 + import { 5 + createOperationComment, 6 + isOperationOptionsRequired, 7 + } from '~/plugins/shared/utils/operation'; 8 + import { tsc } from '~/tsc'; 9 + import { stringCase } from '~/utils/stringCase'; 10 + 11 + import type { HeyApiSdkPlugin } from '../types'; 12 + import { nuxtTypeComposable, nuxtTypeDefault } from './constants'; 13 + import { 14 + operationClasses, 15 + operationParameters, 16 + operationStatements, 17 + } from './operation'; 18 + 19 + type SdkClassEntry = { 20 + /** 21 + * Name of the class. 22 + */ 23 + className: string; 24 + /** 25 + * Symbol IDs for child classes located inside this class. 26 + */ 27 + classes: Set<number>; 28 + /** 29 + * Symbol ID for the class. 30 + */ 31 + id: number; 32 + /** 33 + * Track unique added method nodes. 34 + */ 35 + methods: Set<string>; 36 + /** 37 + * List of class nodes containing methods. 38 + */ 39 + nodes: Array<ts.ClassElement>; 40 + /** 41 + * Is this a root class? 42 + */ 43 + root: boolean; 44 + }; 45 + 46 + const createClientClassNodes = ({ 47 + plugin, 48 + }: { 49 + plugin: HeyApiSdkPlugin['Instance']; 50 + }): ReadonlyArray<ts.ClassElement> => { 51 + const clientAssignmentStatement = tsc.expressionToStatement({ 52 + expression: tsc.binaryExpression({ 53 + left: tsc.propertyAccessExpression({ 54 + expression: tsc.this(), 55 + name: '_client', 56 + }), 57 + operator: '=', 58 + right: tsc.propertyAccessExpression({ 59 + expression: tsc.identifier({ text: 'args' }), 60 + name: 'client', 61 + }), 62 + }), 63 + }); 64 + 65 + const symbolClient = plugin.referenceSymbol(plugin.api.selector('Client')); 66 + const client = getClientPlugin(plugin.context.config); 67 + const symClient = 68 + client.api && 'selector' in client.api 69 + ? plugin.getSymbol( 70 + // @ts-expect-error 71 + client.api.selector('client'), 72 + ) 73 + : undefined; 74 + 75 + return [ 76 + tsc.propertyDeclaration({ 77 + initializer: symClient 78 + ? tsc.identifier({ text: symClient.placeholder }) 79 + : undefined, 80 + modifier: 'protected', 81 + name: '_client', 82 + type: tsc.typeReferenceNode({ typeName: symbolClient.placeholder }), 83 + }), 84 + // @ts-expect-error 85 + tsc.identifier({ text: '\n' }), 86 + tsc.constructorDeclaration({ 87 + multiLine: true, 88 + parameters: [ 89 + { 90 + isRequired: !plugin.config.client, 91 + name: 'args', 92 + type: tsc.typeInterfaceNode({ 93 + properties: [ 94 + { 95 + isRequired: !plugin.config.client, 96 + name: 'client', 97 + type: symbolClient.placeholder, 98 + }, 99 + ], 100 + useLegacyResolution: false, 101 + }), 102 + }, 103 + ], 104 + statements: [ 105 + !plugin.config.client 106 + ? clientAssignmentStatement 107 + : tsc.ifStatement({ 108 + expression: tsc.propertyAccessExpression({ 109 + expression: tsc.identifier({ text: 'args' }), 110 + isOptional: true, 111 + name: 'client', 112 + }), 113 + thenStatement: tsc.block({ 114 + statements: [clientAssignmentStatement], 115 + }), 116 + }), 117 + ], 118 + }), 119 + ]; 120 + }; 121 + 122 + export const generateClassSdk = ({ 123 + plugin, 124 + }: { 125 + plugin: HeyApiSdkPlugin['Instance']; 126 + }): void => { 127 + const client = getClientPlugin(plugin.context.config); 128 + const isAngularClient = client.name === '@hey-api/client-angular'; 129 + const isNuxtClient = client.name === '@hey-api/client-nuxt'; 130 + const sdkClasses = new Map<number, SdkClassEntry>(); 131 + /** 132 + * Track unique added classes. 133 + */ 134 + const generatedClasses = new Set<number>(); 135 + 136 + const clientClassNodes = plugin.config.instance 137 + ? createClientClassNodes({ plugin }) 138 + : []; 139 + 140 + plugin.forEach( 141 + 'operation', 142 + ({ operation }) => { 143 + const isRequiredOptions = isOperationOptionsRequired({ 144 + context: plugin.context, 145 + operation, 146 + }); 147 + const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); 148 + const symbolResponse = isNuxtClient 149 + ? plugin.getSymbol( 150 + pluginTypeScript.api.selector('response', operation.id), 151 + ) 152 + : undefined; 153 + 154 + const classes = operationClasses({ 155 + context: plugin.context, 156 + operation, 157 + plugin, 158 + }); 159 + 160 + for (const entry of classes.values()) { 161 + entry.path.forEach((currentClassName, index) => { 162 + const symbolCurrentClass = plugin.referenceSymbol( 163 + plugin.api.selector('class', currentClassName), 164 + ); 165 + if (!sdkClasses.has(symbolCurrentClass.id)) { 166 + sdkClasses.set(symbolCurrentClass.id, { 167 + className: currentClassName, 168 + classes: new Set(), 169 + id: symbolCurrentClass.id, 170 + methods: new Set(), 171 + nodes: [], 172 + root: !index, 173 + }); 174 + } 175 + 176 + const parentClassName = entry.path[index - 1]; 177 + if (parentClassName) { 178 + const symbolParentClass = plugin.referenceSymbol( 179 + plugin.api.selector('class', parentClassName), 180 + ); 181 + if ( 182 + symbolParentClass.placeholder !== symbolCurrentClass.placeholder 183 + ) { 184 + const parentClass = sdkClasses.get(symbolParentClass.id)!; 185 + parentClass.classes.add(symbolCurrentClass.id); 186 + sdkClasses.set(symbolParentClass.id, parentClass); 187 + } 188 + } 189 + 190 + const isLast = entry.path.length === index + 1; 191 + // add methods only to the last class 192 + if (!isLast) { 193 + return; 194 + } 195 + 196 + const currentClass = sdkClasses.get(symbolCurrentClass.id)!; 197 + 198 + // avoid duplicate methods 199 + if (currentClass.methods.has(entry.methodName)) { 200 + return; 201 + } 202 + 203 + const opParameters = operationParameters({ 204 + isRequiredOptions, 205 + operation, 206 + plugin, 207 + }); 208 + const statements = operationStatements({ 209 + isRequiredOptions, 210 + opParameters, 211 + operation, 212 + plugin, 213 + }); 214 + const functionNode = tsc.methodDeclaration({ 215 + accessLevel: 'public', 216 + comment: createOperationComment({ operation }), 217 + isStatic: isAngularClient ? false : !plugin.config.instance, 218 + name: entry.methodName, 219 + parameters: opParameters.parameters, 220 + returnType: undefined, 221 + statements, 222 + types: isNuxtClient 223 + ? [ 224 + { 225 + default: tsc.ots.string('$fetch'), 226 + extends: tsc.typeNode( 227 + plugin.referenceSymbol(plugin.api.selector('Composable')) 228 + .placeholder, 229 + ), 230 + name: nuxtTypeComposable, 231 + }, 232 + { 233 + default: symbolResponse 234 + ? tsc.typeReferenceNode({ 235 + typeName: symbolResponse.placeholder, 236 + }) 237 + : tsc.typeNode('undefined'), 238 + extends: symbolResponse 239 + ? tsc.typeReferenceNode({ 240 + typeName: symbolResponse.placeholder, 241 + }) 242 + : undefined, 243 + name: nuxtTypeDefault, 244 + }, 245 + ] 246 + : [ 247 + { 248 + default: 249 + ('throwOnError' in client.config 250 + ? client.config.throwOnError 251 + : false) ?? false, 252 + extends: 'boolean', 253 + name: 'ThrowOnError', 254 + }, 255 + ], 256 + }); 257 + 258 + if (!currentClass.nodes.length) { 259 + currentClass.nodes.push(functionNode); 260 + } else { 261 + currentClass.nodes.push( 262 + // @ts-expect-error 263 + tsc.identifier({ text: '\n' }), 264 + functionNode, 265 + ); 266 + } 267 + 268 + currentClass.methods.add(entry.methodName); 269 + 270 + sdkClasses.set(symbolCurrentClass.id, currentClass); 271 + }); 272 + } 273 + }, 274 + { 275 + order: 'declarations', 276 + }, 277 + ); 278 + 279 + const symbolHeyApiClient = plugin.registerSymbol({ 280 + exported: false, 281 + meta: { 282 + path: [], 283 + }, 284 + name: '_HeyApiClient', 285 + }); 286 + 287 + const generateClass = (currentClass: SdkClassEntry) => { 288 + if (generatedClasses.has(currentClass.id)) { 289 + return; 290 + } 291 + 292 + if (currentClass.classes.size) { 293 + for (const childClassName of currentClass.classes) { 294 + const childClass = sdkClasses.get(childClassName)!; 295 + generateClass(childClass); 296 + 297 + currentClass.nodes.push( 298 + tsc.propertyDeclaration({ 299 + initializer: plugin.config.instance 300 + ? tsc.newExpression({ 301 + argumentsArray: plugin.config.instance 302 + ? [ 303 + tsc.objectExpression({ 304 + multiLine: false, 305 + obj: [ 306 + { 307 + key: 'client', 308 + value: tsc.propertyAccessExpression({ 309 + expression: tsc.this(), 310 + name: '_client', 311 + }), 312 + }, 313 + ], 314 + }), 315 + ] 316 + : [], 317 + expression: tsc.identifier({ 318 + text: plugin.referenceSymbol(childClass.id).placeholder, 319 + }), 320 + }) 321 + : tsc.identifier({ 322 + text: plugin.referenceSymbol(childClass.id).placeholder, 323 + }), 324 + modifier: plugin.config.instance ? undefined : 'static', 325 + name: stringCase({ 326 + case: 'camelCase', 327 + value: childClass.className, 328 + }), 329 + }), 330 + ); 331 + } 332 + } 333 + 334 + const symbol = plugin.registerSymbol({ 335 + exported: true, 336 + meta: { 337 + path: [], 338 + }, 339 + name: currentClass.className, 340 + selector: plugin.api.selector('class', currentClass.className), 341 + }); 342 + const node = tsc.classDeclaration({ 343 + decorator: 344 + currentClass.root && isAngularClient 345 + ? { 346 + args: [ 347 + { 348 + providedIn: 'root', 349 + }, 350 + ], 351 + name: plugin.referenceSymbol(plugin.api.selector('Injectable')) 352 + .placeholder, 353 + } 354 + : undefined, 355 + exportClass: symbol.exported, 356 + extendedClasses: plugin.config.instance 357 + ? [symbolHeyApiClient.placeholder] 358 + : undefined, 359 + name: symbol.placeholder, 360 + nodes: currentClass.nodes, 361 + }); 362 + plugin.setSymbolValue(symbol, node); 363 + generatedClasses.add(symbol.id); 364 + }; 365 + 366 + if (clientClassNodes.length) { 367 + const node = tsc.classDeclaration({ 368 + exportClass: symbolHeyApiClient.exported, 369 + name: symbolHeyApiClient.placeholder, 370 + nodes: clientClassNodes, 371 + }); 372 + plugin.setSymbolValue(symbolHeyApiClient, node); 373 + } 374 + 375 + for (const sdkClass of sdkClasses.values()) { 376 + generateClass(sdkClass); 377 + } 378 + };
+109
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/functions.ts
··· 1 + import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 2 + import { 3 + createOperationComment, 4 + isOperationOptionsRequired, 5 + } from '~/plugins/shared/utils/operation'; 6 + import { tsc } from '~/tsc'; 7 + 8 + import { serviceFunctionIdentifier } from '../plugin-legacy'; 9 + import type { HeyApiSdkPlugin } from '../types'; 10 + import { nuxtTypeComposable, nuxtTypeDefault } from './constants'; 11 + import { operationParameters, operationStatements } from './operation'; 12 + 13 + export const generateFlatSdk = ({ 14 + plugin, 15 + }: { 16 + plugin: HeyApiSdkPlugin['Instance']; 17 + }): void => { 18 + const client = getClientPlugin(plugin.context.config); 19 + const isNuxtClient = client.name === '@hey-api/client-nuxt'; 20 + 21 + plugin.forEach( 22 + 'operation', 23 + (event) => { 24 + const { operation } = event; 25 + const isRequiredOptions = isOperationOptionsRequired({ 26 + context: plugin.context, 27 + operation, 28 + }); 29 + const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); 30 + const symbolResponse = isNuxtClient 31 + ? plugin.getSymbol( 32 + pluginTypeScript.api.selector('response', operation.id), 33 + ) 34 + : undefined; 35 + const opParameters = operationParameters({ 36 + isRequiredOptions, 37 + operation, 38 + plugin, 39 + }); 40 + const statements = operationStatements({ 41 + isRequiredOptions, 42 + opParameters, 43 + operation, 44 + plugin, 45 + }); 46 + const symbol = plugin.registerSymbol({ 47 + meta: { 48 + path: event._path, 49 + tags: event.tags, 50 + }, 51 + name: serviceFunctionIdentifier({ 52 + config: plugin.context.config, 53 + handleIllegal: true, 54 + id: operation.id, 55 + operation, 56 + }), 57 + selector: plugin.api.selector('function', operation.id), 58 + }); 59 + const node = tsc.constVariable({ 60 + comment: createOperationComment({ operation }), 61 + exportConst: true, 62 + expression: tsc.arrowFunction({ 63 + parameters: opParameters.parameters, 64 + returnType: undefined, 65 + statements, 66 + types: isNuxtClient 67 + ? [ 68 + { 69 + default: tsc.ots.string('$fetch'), 70 + extends: tsc.typeNode( 71 + plugin.referenceSymbol(plugin.api.selector('Composable')) 72 + .placeholder, 73 + ), 74 + name: nuxtTypeComposable, 75 + }, 76 + { 77 + default: symbolResponse 78 + ? tsc.typeReferenceNode({ 79 + typeName: symbolResponse.placeholder, 80 + }) 81 + : tsc.typeNode('undefined'), 82 + extends: symbolResponse 83 + ? tsc.typeReferenceNode({ 84 + typeName: symbolResponse.placeholder, 85 + }) 86 + : undefined, 87 + name: nuxtTypeDefault, 88 + }, 89 + ] 90 + : [ 91 + { 92 + default: 93 + ('throwOnError' in client.config 94 + ? client.config.throwOnError 95 + : false) ?? false, 96 + extends: 'boolean', 97 + name: 'ThrowOnError', 98 + }, 99 + ], 100 + }), 101 + name: symbol.placeholder, 102 + }); 103 + plugin.setSymbolValue(symbol, node); 104 + }, 105 + { 106 + order: 'declarations', 107 + }, 108 + ); 109 + };
packages/openapi-ts/src/plugins/@hey-api/sdk/signature.ts packages/openapi-ts/src/plugins/@hey-api/sdk/shared/signature.ts
+5 -1
packages/openapi-ts/src/plugins/@hey-api/sdk/typeOptions.ts packages/openapi-ts/src/plugins/@hey-api/sdk/shared/typeOptions.ts
··· 2 2 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 3 3 import { tsc } from '~/tsc'; 4 4 5 + import type { HeyApiSdkPlugin } from '../types'; 5 6 import { nuxtTypeDefault, nuxtTypeResponse } from './constants'; 6 - import type { HeyApiSdkPlugin } from './types'; 7 7 8 8 export const createTypeOptions = ({ 9 9 plugin, ··· 18 18 external: clientModule, 19 19 meta: { 20 20 kind: 'type', 21 + path: [], 21 22 }, 22 23 name: 'TDataShape', 23 24 }); ··· 25 26 external: clientModule, 26 27 meta: { 27 28 kind: 'type', 29 + path: [], 28 30 }, 29 31 name: 'Client', 30 32 selector: plugin.api.selector('Client'), ··· 33 35 external: clientModule, 34 36 meta: { 35 37 kind: 'type', 38 + path: [], 36 39 }, 37 40 name: 'Options', 38 41 }); ··· 40 43 exported: true, 41 44 meta: { 42 45 kind: 'type', 46 + path: [], 43 47 }, 44 48 name: 'Options', 45 49 selector: plugin.api.selector('Options'),
+2 -6
packages/openapi-ts/src/plugins/@hey-api/sdk/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import type { 3 - DefinePlugin, 4 - Plugin, 5 - PluginClientNames, 6 - PluginValidatorNames, 7 - } from '~/plugins/types'; 2 + import type { DefinePlugin, Plugin } from '~/plugins'; 3 + import type { PluginClientNames, PluginValidatorNames } from '~/plugins/types'; 8 4 import type { StringName } from '~/types/case'; 9 5 import type { Operation } from '~/types/client'; 10 6
+56
packages/openapi-ts/src/plugins/@hey-api/sdk/v1/plugin.ts
··· 1 + import { clientFolderAbsolutePath } from '~/generate/client'; 2 + import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 3 + 4 + import { generateClassSdk } from '../shared/class'; 5 + import { generateFlatSdk } from '../shared/functions'; 6 + import { createTypeOptions } from '../shared/typeOptions'; 7 + import type { HeyApiSdkPlugin } from '../types'; 8 + 9 + export const handlerV1: HeyApiSdkPlugin['Handler'] = ({ plugin }) => { 10 + const clientModule = clientFolderAbsolutePath(plugin.context.config); 11 + plugin.registerSymbol({ 12 + external: clientModule, 13 + name: 'formDataBodySerializer', 14 + selector: plugin.api.selector('formDataBodySerializer'), 15 + }); 16 + plugin.registerSymbol({ 17 + external: clientModule, 18 + name: 'urlSearchParamsBodySerializer', 19 + selector: plugin.api.selector('urlSearchParamsBodySerializer'), 20 + }); 21 + plugin.registerSymbol({ 22 + external: clientModule, 23 + name: 'buildClientParams', 24 + selector: plugin.api.selector('buildClientParams'), 25 + }); 26 + 27 + const client = getClientPlugin(plugin.context.config); 28 + const isAngularClient = client.name === '@hey-api/client-angular'; 29 + const isNuxtClient = client.name === '@hey-api/client-nuxt'; 30 + if (isNuxtClient) { 31 + plugin.registerSymbol({ 32 + external: clientModule, 33 + meta: { 34 + kind: 'type', 35 + }, 36 + name: 'Composable', 37 + selector: plugin.api.selector('Composable'), 38 + }); 39 + } 40 + 41 + if (isAngularClient && plugin.config.asClass) { 42 + plugin.registerSymbol({ 43 + external: '@angular/core', 44 + name: 'Injectable', 45 + selector: plugin.api.selector('Injectable'), 46 + }); 47 + } 48 + 49 + createTypeOptions({ plugin }); 50 + 51 + if (plugin.config.asClass) { 52 + generateClassSdk({ plugin }); 53 + } else { 54 + generateFlatSdk({ plugin }); 55 + } 56 + };
+1 -1
packages/openapi-ts/src/plugins/@hey-api/sdk/validator.ts packages/openapi-ts/src/plugins/@hey-api/sdk/shared/validator.ts
··· 2 2 3 3 import type { IR } from '~/ir/types'; 4 4 5 - import type { HeyApiSdkPlugin } from './types'; 5 + import type { HeyApiSdkPlugin } from '../types'; 6 6 7 7 interface ValidatorProps { 8 8 operation: IR.OperationObject;
+1 -1
packages/openapi-ts/src/plugins/@hey-api/transformers/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 'response' | 'response-ref'; 6 6
+1
packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts
··· 61 61 const selector = plugin.api.selector('response-ref', schema.$ref); 62 62 63 63 if (!plugin.getSymbol(selector)) { 64 + // TODO: remove 64 65 // create each schema response transformer only once 65 66 const refSchema = plugin.context.resolveIrRef<IR.SchemaObject>( 66 67 schema.$ref,
+1 -1
packages/openapi-ts/src/plugins/@hey-api/transformers/types.d.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 3 import type { IR } from '~/ir/types'; 4 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 4 + import type { DefinePlugin, Plugin } from '~/plugins'; 5 5 6 6 import type { IApi } from './api'; 7 7 import type { ExpressionTransformer } from './expressions';
-1
packages/openapi-ts/src/plugins/@hey-api/typescript/__tests__/plugin.test.ts
··· 100 100 }, 101 101 '@hey-api/sdk': { 102 102 api: { 103 - createOperationComment: () => undefined, 104 103 selector: () => [], 105 104 }, 106 105 config: {
+5 -5
packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 - import type { Plugin } from '~/plugins/types'; 4 + import type { Plugin } from '~/plugins'; 5 5 6 - import { schemaToType } from './plugin'; 6 + import { irSchemaToAstV1 } from './v1/api'; 7 7 8 8 type SelectorType = 9 9 | 'ClientOptions' ··· 19 19 | 'Webhooks'; 20 20 21 21 export type IApi = { 22 - schemaToType: (args: Parameters<typeof schemaToType>[0]) => ts.TypeNode; 22 + schemaToType: (args: Parameters<typeof irSchemaToAstV1>[0]) => ts.TypeNode; 23 23 /** 24 24 * @param type Selector type. 25 25 * @param value Depends on `type`: ··· 46 46 return [this.meta.name, ...(args as Selector)]; 47 47 } 48 48 49 - schemaToType(args: Parameters<typeof schemaToType>[0]): ts.TypeNode { 50 - return schemaToType(args); 49 + schemaToType(args: Parameters<typeof irSchemaToAstV1>[0]): ts.TypeNode { 50 + return irSchemaToAstV1(args); 51 51 } 52 52 }
+1 -1
packages/openapi-ts/src/plugins/@hey-api/typescript/clientOptions.ts packages/openapi-ts/src/plugins/@hey-api/typescript/shared/clientOptions.ts
··· 9 9 import { tsc } from '~/tsc'; 10 10 import { parseUrl } from '~/utils/url'; 11 11 12 - import type { HeyApiTypeScriptPlugin } from './types'; 12 + import type { HeyApiTypeScriptPlugin } from '../types'; 13 13 14 14 const stringType = tsc.keywordTypeNode({ keyword: 'string' }); 15 15
+1 -1
packages/openapi-ts/src/plugins/@hey-api/typescript/export.ts packages/openapi-ts/src/plugins/@hey-api/typescript/shared/export.ts
··· 7 7 import { numberRegExp } from '~/utils/regexp'; 8 8 import { stringCase } from '~/utils/stringCase'; 9 9 10 - import type { HeyApiTypeScriptPlugin } from './types'; 10 + import type { HeyApiTypeScriptPlugin } from '../types'; 11 11 12 12 const schemaToEnumObject = ({ 13 13 plugin,
+23 -10
packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts packages/openapi-ts/src/plugins/@hey-api/typescript/shared/operation.ts
··· 4 4 import { buildName } from '~/openApi/shared/utils/name'; 5 5 import { tsc } from '~/tsc'; 6 6 7 - import { schemaToType } from './plugin'; 8 - import type { HeyApiTypeScriptPlugin } from './types'; 7 + import { irSchemaToAst } from '../v1/plugin'; 8 + import type { IrSchemaToAstOptions } from './types'; 9 9 10 10 const irParametersToIrSchema = ({ 11 11 parameters, ··· 46 46 const operationToDataType = ({ 47 47 operation, 48 48 plugin, 49 - }: { 49 + state, 50 + }: IrSchemaToAstOptions & { 50 51 operation: IR.OperationObject; 51 - plugin: HeyApiTypeScriptPlugin['Instance']; 52 52 }) => { 53 53 const data: IR.SchemaObject = { 54 54 type: 'object', ··· 124 124 exported: true, 125 125 meta: { 126 126 kind: 'type', 127 + path: state.path.value, 128 + tags: state.tags?.value, 127 129 }, 128 130 name: buildName({ 129 131 config: plugin.config.requests, ··· 131 133 }), 132 134 selector: plugin.api.selector('data', operation.id), 133 135 }); 134 - const type = schemaToType({ 136 + const type = irSchemaToAst({ 135 137 plugin, 136 138 schema: data, 139 + state, 137 140 }); 138 141 const node = tsc.typeAliasDeclaration({ 139 142 exportType: symbol.exported, ··· 146 149 export const operationToType = ({ 147 150 operation, 148 151 plugin, 149 - }: { 152 + state, 153 + }: IrSchemaToAstOptions & { 150 154 operation: IR.OperationObject; 151 - plugin: HeyApiTypeScriptPlugin['Instance']; 152 155 }) => { 153 - operationToDataType({ operation, plugin }); 156 + operationToDataType({ operation, plugin, state }); 154 157 155 158 const { error, errors, response, responses } = 156 159 operationResponsesMap(operation); ··· 160 163 exported: true, 161 164 meta: { 162 165 kind: 'type', 166 + path: state.path.value, 167 + tags: state.tags?.value, 163 168 }, 164 169 name: buildName({ 165 170 config: plugin.config.errors, ··· 167 172 }), 168 173 selector: plugin.api.selector('errors', operation.id), 169 174 }); 170 - const type = schemaToType({ 175 + const type = irSchemaToAst({ 171 176 plugin, 172 177 schema: errors, 178 + state, 173 179 }); 174 180 const node = tsc.typeAliasDeclaration({ 175 181 exportType: symbolErrors.exported, ··· 183 189 exported: true, 184 190 meta: { 185 191 kind: 'type', 192 + path: state.path.value, 193 + tags: state.tags?.value, 186 194 }, 187 195 name: buildName({ 188 196 config: { ··· 216 224 exported: true, 217 225 meta: { 218 226 kind: 'type', 227 + path: state.path.value, 228 + tags: state.tags?.value, 219 229 }, 220 230 name: buildName({ 221 231 config: plugin.config.responses, ··· 223 233 }), 224 234 selector: plugin.api.selector('responses', operation.id), 225 235 }); 226 - const type = schemaToType({ 236 + const type = irSchemaToAst({ 227 237 plugin, 228 238 schema: responses, 239 + state, 229 240 }); 230 241 const node = tsc.typeAliasDeclaration({ 231 242 exportType: symbolResponses.exported, ··· 239 250 exported: true, 240 251 meta: { 241 252 kind: 'type', 253 + path: state.path.value, 254 + tags: state.tags?.value, 242 255 }, 243 256 name: buildName({ 244 257 config: {
+3 -604
packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts
··· 1 - import type ts from 'typescript'; 2 - 3 - import { deduplicateSchema } from '~/ir/schema'; 4 - import type { IR } from '~/ir/types'; 5 - import { buildName } from '~/openApi/shared/utils/name'; 6 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 7 - import { fieldName } from '~/plugins/shared/utils/case'; 8 - import { createSchemaComment } from '~/plugins/shared/utils/schema'; 9 - import type { Property } from '~/tsc'; 10 - import { tsc } from '~/tsc'; 11 - import { refToName } from '~/utils/ref'; 12 - import { stringCase } from '~/utils/stringCase'; 13 - 14 - import { createClientOptions } from './clientOptions'; 15 - import { exportType } from './export'; 16 - import { operationToType } from './operation'; 17 1 import type { HeyApiTypeScriptPlugin } from './types'; 18 - import { webhookToType } from './webhook'; 19 - import { createWebhooks } from './webhooks'; 20 - 21 - const arrayTypeToIdentifier = ({ 22 - plugin, 23 - schema, 24 - }: { 25 - plugin: HeyApiTypeScriptPlugin['Instance']; 26 - schema: SchemaWithType<'array'>; 27 - }): ts.TypeNode => { 28 - if (!schema.items) { 29 - return tsc.typeArrayNode( 30 - tsc.keywordTypeNode({ keyword: plugin.config.topType }), 31 - ); 32 - } 33 - 34 - schema = deduplicateSchema({ detectFormat: true, schema }); 35 - 36 - const itemTypes: Array<ts.TypeNode> = []; 37 - 38 - for (const item of schema.items!) { 39 - const type = schemaToType({ 40 - plugin, 41 - schema: item, 42 - }); 43 - itemTypes.push(type); 44 - } 45 - 46 - if (itemTypes.length === 1) { 47 - return tsc.typeArrayNode(itemTypes[0]!); 48 - } 49 - 50 - if (schema.logicalOperator === 'and') { 51 - return tsc.typeArrayNode(tsc.typeIntersectionNode({ types: itemTypes })); 52 - } 53 - 54 - return tsc.typeArrayNode(tsc.typeUnionNode({ types: itemTypes })); 55 - }; 56 - 57 - const booleanTypeToIdentifier = ({ 58 - schema, 59 - }: { 60 - schema: SchemaWithType<'boolean'>; 61 - }): ts.TypeNode => { 62 - if (schema.const !== undefined) { 63 - return tsc.literalTypeNode({ 64 - literal: tsc.ots.boolean(schema.const as boolean), 65 - }); 66 - } 67 - 68 - return tsc.keywordTypeNode({ 69 - keyword: 'boolean', 70 - }); 71 - }; 72 - 73 - const enumTypeToIdentifier = ({ 74 - plugin, 75 - schema, 76 - }: { 77 - plugin: HeyApiTypeScriptPlugin['Instance']; 78 - schema: SchemaWithType<'enum'>; 79 - }): ts.TypeNode => { 80 - const type = schemaToType({ 81 - plugin, 82 - schema: { 83 - ...schema, 84 - type: undefined, 85 - }, 86 - }); 87 - return type; 88 - }; 89 - 90 - const numberTypeToIdentifier = ({ 91 - plugin, 92 - schema, 93 - }: { 94 - plugin: HeyApiTypeScriptPlugin['Instance']; 95 - schema: SchemaWithType<'integer' | 'number'>; 96 - }): ts.TypeNode => { 97 - if (schema.const !== undefined) { 98 - return tsc.literalTypeNode({ 99 - literal: tsc.ots.number(schema.const as number), 100 - }); 101 - } 102 - 103 - if (schema.type === 'integer' && schema.format === 'int64') { 104 - // TODO: parser - add ability to skip type transformers 105 - if (plugin.getPlugin('@hey-api/transformers')?.config.bigInt) { 106 - return tsc.typeReferenceNode({ typeName: 'bigint' }); 107 - } 108 - } 109 - 110 - return tsc.keywordTypeNode({ 111 - keyword: 'number', 112 - }); 113 - }; 114 - 115 - const objectTypeToIdentifier = ({ 116 - plugin, 117 - schema, 118 - }: { 119 - plugin: HeyApiTypeScriptPlugin['Instance']; 120 - schema: SchemaWithType<'object'>; 121 - }): ts.TypeNode => { 122 - // TODO: parser - handle constants 123 - let indexKey: ts.TypeReferenceNode | undefined; 124 - let indexProperty: Property | undefined; 125 - const schemaProperties: Array<Property> = []; 126 - let indexPropertyItems: Array<IR.SchemaObject> = []; 127 - const required = schema.required ?? []; 128 - let hasOptionalProperties = false; 129 - 130 - for (const name in schema.properties) { 131 - const property = schema.properties[name]!; 132 - const propertyType = schemaToType({ 133 - plugin, 134 - schema: property, 135 - }); 136 - const isRequired = required.includes(name); 137 - schemaProperties.push({ 138 - comment: createSchemaComment({ schema: property }), 139 - isReadOnly: property.accessScope === 'read', 140 - isRequired, 141 - name: fieldName({ context: plugin.context, name }), 142 - type: propertyType, 143 - }); 144 - indexPropertyItems.push(property); 145 - 146 - if (!isRequired) { 147 - hasOptionalProperties = true; 148 - } 149 - } 150 - 151 - // include pattern value schemas into the index union 152 - if (schema.patternProperties) { 153 - for (const pattern in schema.patternProperties) { 154 - const ir = schema.patternProperties[pattern]!; 155 - indexPropertyItems.unshift(ir); 156 - } 157 - } 158 - 159 - const hasPatterns = 160 - !!schema.patternProperties && 161 - Object.keys(schema.patternProperties).length > 0; 162 - 163 - const addPropsRaw = schema.additionalProperties; 164 - const addPropsObj = 165 - addPropsRaw !== false && addPropsRaw 166 - ? (addPropsRaw as IR.SchemaObject) 167 - : undefined; 168 - const shouldCreateIndex = 169 - hasPatterns || 170 - (!!addPropsObj && 171 - (addPropsObj.type !== 'never' || !indexPropertyItems.length)); 172 - 173 - if (shouldCreateIndex) { 174 - // only inject additionalProperties when it’s not "never" 175 - const addProps = addPropsObj; 176 - if (addProps && addProps.type !== 'never') { 177 - indexPropertyItems.unshift(addProps); 178 - } else if ( 179 - !hasPatterns && 180 - !indexPropertyItems.length && 181 - addProps && 182 - addProps.type === 'never' 183 - ) { 184 - // keep "never" only when there are NO patterns and NO explicit properties 185 - indexPropertyItems = [addProps]; 186 - } 187 - 188 - if (hasOptionalProperties) { 189 - indexPropertyItems.push({ 190 - type: 'undefined', 191 - }); 192 - } 193 - 194 - indexProperty = { 195 - isRequired: !schema.propertyNames, 196 - name: 'key', 197 - type: 198 - indexPropertyItems.length === 1 199 - ? schemaToType({ 200 - plugin, 201 - schema: indexPropertyItems[0]!, 202 - }) 203 - : schemaToType({ 204 - plugin, 205 - schema: { items: indexPropertyItems, logicalOperator: 'or' }, 206 - }), 207 - }; 208 - 209 - if (schema.propertyNames?.$ref) { 210 - indexKey = schemaToType({ 211 - plugin, 212 - schema: { 213 - $ref: schema.propertyNames.$ref, 214 - }, 215 - }) as ts.TypeReferenceNode; 216 - } 217 - } 218 - 219 - return tsc.typeInterfaceNode({ 220 - indexKey, 221 - indexProperty, 222 - properties: schemaProperties, 223 - useLegacyResolution: false, 224 - }); 225 - }; 226 - 227 - const stringTypeToIdentifier = ({ 228 - plugin, 229 - schema, 230 - }: { 231 - plugin: HeyApiTypeScriptPlugin['Instance']; 232 - schema: SchemaWithType<'string'>; 233 - }): ts.TypeNode => { 234 - if (schema.const !== undefined) { 235 - return tsc.literalTypeNode({ 236 - literal: tsc.stringLiteral({ text: schema.const as string }), 237 - }); 238 - } 239 - 240 - if (schema.format) { 241 - if (schema.format === 'binary') { 242 - return tsc.typeUnionNode({ 243 - types: [ 244 - tsc.typeReferenceNode({ 245 - typeName: 'Blob', 246 - }), 247 - tsc.typeReferenceNode({ 248 - typeName: 'File', 249 - }), 250 - ], 251 - }); 252 - } 253 - 254 - if (schema.format === 'date-time' || schema.format === 'date') { 255 - // TODO: parser - add ability to skip type transformers 256 - if (plugin.getPlugin('@hey-api/transformers')?.config.dates) { 257 - return tsc.typeReferenceNode({ typeName: 'Date' }); 258 - } 259 - } 260 - 261 - if (schema.format === 'typeid' && typeof schema.example === 'string') { 262 - const parts = String(schema.example).split('_'); 263 - parts.pop(); // remove the ID part 264 - const type = parts.join('_'); 265 - 266 - const selector = plugin.api.selector('TypeID', type); 267 - if (!plugin.getSymbol(selector)) { 268 - const selectorTypeId = plugin.api.selector('TypeID'); 269 - 270 - if (!plugin.getSymbol(selectorTypeId)) { 271 - const symbolTypeId = plugin.registerSymbol({ 272 - exported: true, 273 - meta: { 274 - kind: 'type', 275 - }, 276 - name: 'TypeID', 277 - selector: selectorTypeId, 278 - }); 279 - const nodeTypeId = tsc.typeAliasDeclaration({ 280 - exportType: symbolTypeId.exported, 281 - name: symbolTypeId.placeholder, 282 - type: tsc.templateLiteralType({ 283 - value: [ 284 - tsc.typeReferenceNode({ typeName: 'T' }), 285 - '_', 286 - tsc.keywordTypeNode({ keyword: 'string' }), 287 - ], 288 - }), 289 - typeParameters: [ 290 - tsc.typeParameterDeclaration({ 291 - constraint: tsc.keywordTypeNode({ 292 - keyword: 'string', 293 - }), 294 - name: 'T', 295 - }), 296 - ], 297 - }); 298 - plugin.setSymbolValue(symbolTypeId, nodeTypeId); 299 - } 2 + import { handlerV1 } from './v1/plugin'; 300 3 301 - const symbolTypeId = plugin.referenceSymbol(selectorTypeId); 302 - const symbolTypeName = plugin.registerSymbol({ 303 - exported: true, 304 - meta: { 305 - kind: 'type', 306 - }, 307 - name: stringCase({ 308 - case: plugin.config.case, 309 - value: `${type}_id`, 310 - }), 311 - selector, 312 - }); 313 - const node = tsc.typeAliasDeclaration({ 314 - exportType: symbolTypeName.exported, 315 - name: symbolTypeName.placeholder, 316 - type: tsc.typeReferenceNode({ 317 - typeArguments: [ 318 - tsc.literalTypeNode({ 319 - literal: tsc.stringLiteral({ text: type }), 320 - }), 321 - ], 322 - typeName: symbolTypeId.placeholder, 323 - }), 324 - }); 325 - plugin.setSymbolValue(symbolTypeName, node); 326 - } 327 - const symbol = plugin.referenceSymbol(selector); 328 - return tsc.typeReferenceNode({ typeName: symbol.placeholder }); 329 - } 330 - } 331 - 332 - return tsc.keywordTypeNode({ 333 - keyword: 'string', 334 - }); 335 - }; 336 - 337 - const tupleTypeToIdentifier = ({ 338 - plugin, 339 - schema, 340 - }: { 341 - plugin: HeyApiTypeScriptPlugin['Instance']; 342 - schema: SchemaWithType<'tuple'>; 343 - }): ts.TypeNode => { 344 - let itemTypes: Array<ts.Expression | ts.TypeNode> = []; 345 - 346 - if (schema.const && Array.isArray(schema.const)) { 347 - itemTypes = schema.const.map((value) => { 348 - const expression = tsc.valueToExpression({ value }); 349 - return expression ?? tsc.identifier({ text: plugin.config.topType }); 350 - }); 351 - } else if (schema.items) { 352 - for (const item of schema.items) { 353 - const type = schemaToType({ 354 - plugin, 355 - schema: item, 356 - }); 357 - itemTypes.push(type); 358 - } 359 - } 360 - 361 - return tsc.typeTupleNode({ 362 - types: itemTypes, 363 - }); 364 - }; 365 - 366 - const schemaTypeToIdentifier = ({ 367 - plugin, 368 - schema, 369 - }: { 370 - plugin: HeyApiTypeScriptPlugin['Instance']; 371 - schema: IR.SchemaObject; 372 - }): ts.TypeNode => { 373 - const transformersPlugin = plugin.getPlugin('@hey-api/transformers'); 374 - if (transformersPlugin?.config.typeTransformers) { 375 - for (const typeTransformer of transformersPlugin.config.typeTransformers) { 376 - const typeNode = typeTransformer({ schema }); 377 - if (typeNode) { 378 - return typeNode; 379 - } 380 - } 381 - } 382 - 383 - switch (schema.type as Required<IR.SchemaObject>['type']) { 384 - case 'array': 385 - return arrayTypeToIdentifier({ 386 - plugin, 387 - schema: schema as SchemaWithType<'array'>, 388 - }); 389 - case 'boolean': 390 - return booleanTypeToIdentifier({ 391 - schema: schema as SchemaWithType<'boolean'>, 392 - }); 393 - case 'enum': 394 - return enumTypeToIdentifier({ 395 - plugin, 396 - schema: schema as SchemaWithType<'enum'>, 397 - }); 398 - case 'integer': 399 - case 'number': 400 - return numberTypeToIdentifier({ 401 - plugin, 402 - schema: schema as SchemaWithType<'integer' | 'number'>, 403 - }); 404 - case 'never': 405 - return tsc.keywordTypeNode({ 406 - keyword: 'never', 407 - }); 408 - case 'null': 409 - return tsc.literalTypeNode({ 410 - literal: tsc.null(), 411 - }); 412 - case 'object': 413 - return objectTypeToIdentifier({ 414 - plugin, 415 - schema: schema as SchemaWithType<'object'>, 416 - }); 417 - case 'string': 418 - return stringTypeToIdentifier({ 419 - plugin, 420 - schema: schema as SchemaWithType<'string'>, 421 - }); 422 - case 'tuple': 423 - return tupleTypeToIdentifier({ 424 - plugin, 425 - schema: schema as SchemaWithType<'tuple'>, 426 - }); 427 - case 'undefined': 428 - return tsc.keywordTypeNode({ 429 - keyword: 'undefined', 430 - }); 431 - case 'unknown': 432 - return tsc.keywordTypeNode({ 433 - keyword: plugin.config.topType, 434 - }); 435 - case 'void': 436 - return tsc.keywordTypeNode({ 437 - keyword: 'void', 438 - }); 439 - } 440 - }; 441 - 442 - export const schemaToType = ({ 443 - plugin, 444 - schema, 445 - }: { 446 - plugin: HeyApiTypeScriptPlugin['Instance']; 447 - schema: IR.SchemaObject; 448 - }): ts.TypeNode => { 449 - if (schema.$ref) { 450 - const symbol = plugin.referenceSymbol( 451 - plugin.api.selector('ref', schema.$ref), 452 - ); 453 - return tsc.typeReferenceNode({ typeName: symbol.placeholder }); 454 - } 455 - 456 - if (schema.type) { 457 - return schemaTypeToIdentifier({ plugin, schema }); 458 - } 459 - 460 - if (schema.items) { 461 - schema = deduplicateSchema({ detectFormat: false, schema }); 462 - if (schema.items) { 463 - const itemTypes: Array<ts.TypeNode> = []; 464 - 465 - for (const item of schema.items) { 466 - const type = schemaToType({ plugin, schema: item }); 467 - itemTypes.push(type); 468 - } 469 - 470 - return schema.logicalOperator === 'and' 471 - ? tsc.typeIntersectionNode({ types: itemTypes }) 472 - : tsc.typeUnionNode({ types: itemTypes }); 473 - } 474 - 475 - return schemaToType({ plugin, schema }); 476 - } 477 - 478 - // catch-all fallback for failed schemas 479 - return schemaTypeToIdentifier({ 480 - plugin, 481 - schema: { 482 - type: 'unknown', 483 - }, 484 - }); 485 - }; 486 - 487 - const handleComponent = ({ 488 - id, 489 - plugin, 490 - schema, 491 - }: { 492 - id: string; 493 - plugin: HeyApiTypeScriptPlugin['Instance']; 494 - schema: IR.SchemaObject; 495 - }) => { 496 - const type = schemaToType({ plugin, schema }); 497 - 498 - // Don't tag enums as 'type' since they export runtime artifacts (values) 499 - const isEnum = schema.type === 'enum' && plugin.config.enums.enabled; 500 - 501 - const symbol = plugin.registerSymbol({ 502 - exported: true, 503 - meta: { 504 - kind: isEnum ? undefined : 'type', 505 - }, 506 - name: buildName({ 507 - config: plugin.config.definitions, 508 - name: refToName(id), 509 - }), 510 - selector: plugin.api.selector('ref', id), 511 - }); 512 - exportType({ 513 - plugin, 514 - schema, 515 - symbol, 516 - type, 517 - }); 518 - }; 519 - 520 - export const handler: HeyApiTypeScriptPlugin['Handler'] = ({ plugin }) => { 521 - // reserve identifier for ClientOptions 522 - const symbolClientOptions = plugin.registerSymbol({ 523 - exported: true, 524 - meta: { 525 - kind: 'type', 526 - }, 527 - name: buildName({ 528 - config: { 529 - case: plugin.config.case, 530 - }, 531 - name: 'ClientOptions', 532 - }), 533 - selector: plugin.api.selector('ClientOptions'), 534 - }); 535 - // reserve identifier for Webhooks 536 - const symbolWebhooks = plugin.registerSymbol({ 537 - exported: true, 538 - meta: { 539 - kind: 'type', 540 - }, 541 - name: buildName({ 542 - config: { 543 - case: plugin.config.case, 544 - }, 545 - name: 'Webhooks', 546 - }), 547 - selector: plugin.api.selector('Webhooks'), 548 - }); 549 - 550 - const servers: Array<IR.ServerObject> = []; 551 - const webhookNames: Array<string> = []; 552 - 553 - plugin.forEach( 554 - 'operation', 555 - 'parameter', 556 - 'requestBody', 557 - 'schema', 558 - 'server', 559 - 'webhook', 560 - (event) => { 561 - switch (event.type) { 562 - case 'operation': 563 - operationToType({ operation: event.operation, plugin }); 564 - break; 565 - case 'parameter': 566 - handleComponent({ 567 - id: event.$ref, 568 - plugin, 569 - schema: event.parameter.schema, 570 - }); 571 - break; 572 - case 'requestBody': 573 - handleComponent({ 574 - id: event.$ref, 575 - plugin, 576 - schema: event.requestBody.schema, 577 - }); 578 - break; 579 - case 'schema': 580 - handleComponent({ 581 - id: event.$ref, 582 - plugin, 583 - schema: event.schema, 584 - }); 585 - break; 586 - case 'server': 587 - servers.push(event.server); 588 - break; 589 - case 'webhook': 590 - webhookNames.push( 591 - webhookToType({ 592 - operation: event.operation, 593 - plugin, 594 - }), 595 - ); 596 - break; 597 - } 598 - }, 599 - { 600 - order: 'declarations', 601 - }, 602 - ); 603 - 604 - createClientOptions({ plugin, servers, symbolClientOptions }); 605 - createWebhooks({ plugin, symbolWebhooks, webhookNames }); 606 - }; 4 + export const handler: HeyApiTypeScriptPlugin['Handler'] = (args) => 5 + handlerV1(args);
+13
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/types.d.ts
··· 1 + import type { SymbolMeta } from '@hey-api/codegen-core'; 2 + 3 + import type { ToRefs } from '~/plugins'; 4 + 5 + import type { HeyApiTypeScriptPlugin } from '../types'; 6 + 7 + export type IrSchemaToAstOptions = { 8 + plugin: HeyApiTypeScriptPlugin['Instance']; 9 + state: ToRefs<PluginState>; 10 + }; 11 + 12 + export type PluginState = Pick<Required<SymbolMeta>, 'path'> & 13 + Pick<Partial<SymbolMeta>, 'tags'>;
+1 -1
packages/openapi-ts/src/plugins/@hey-api/typescript/types.d.ts
··· 1 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 2 2 import type { StringCase, StringName } from '~/types/case'; 3 3 4 4 import type { IApi } from './api';
+1
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/api.ts
··· 1 + export { irSchemaToAst as irSchemaToAstV1 } from './plugin';
+201
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/plugin.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import { deduplicateSchema } from '~/ir/schema'; 4 + import type { IR } from '~/ir/types'; 5 + import { buildName } from '~/openApi/shared/utils/name'; 6 + import type { SchemaWithType } from '~/plugins'; 7 + import { toRefs } from '~/plugins/shared/utils/refs'; 8 + import { tsc } from '~/tsc'; 9 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 10 + 11 + import { createClientOptions } from '../shared/clientOptions'; 12 + import { exportType } from '../shared/export'; 13 + import { operationToType } from '../shared/operation'; 14 + import type { IrSchemaToAstOptions, PluginState } from '../shared/types'; 15 + import { webhookToType } from '../shared/webhook'; 16 + import { createWebhooks } from '../shared/webhooks'; 17 + import type { HeyApiTypeScriptPlugin } from '../types'; 18 + import { irSchemaWithTypeToAst } from './toAst'; 19 + 20 + export const irSchemaToAst = ({ 21 + plugin, 22 + schema, 23 + state, 24 + }: IrSchemaToAstOptions & { 25 + schema: IR.SchemaObject; 26 + }): ts.TypeNode => { 27 + if (schema.$ref) { 28 + const symbol = plugin.referenceSymbol( 29 + plugin.api.selector('ref', schema.$ref), 30 + ); 31 + return tsc.typeReferenceNode({ typeName: symbol.placeholder }); 32 + } 33 + 34 + if (schema.type) { 35 + return irSchemaWithTypeToAst({ 36 + plugin, 37 + schema: schema as SchemaWithType, 38 + state, 39 + }); 40 + } 41 + 42 + if (schema.items) { 43 + schema = deduplicateSchema({ detectFormat: false, schema }); 44 + if (schema.items) { 45 + const itemTypes: Array<ts.TypeNode> = []; 46 + 47 + for (const item of schema.items) { 48 + const type = irSchemaToAst({ plugin, schema: item, state }); 49 + itemTypes.push(type); 50 + } 51 + 52 + return schema.logicalOperator === 'and' 53 + ? tsc.typeIntersectionNode({ types: itemTypes }) 54 + : tsc.typeUnionNode({ types: itemTypes }); 55 + } 56 + 57 + return irSchemaToAst({ plugin, schema, state }); 58 + } 59 + 60 + // catch-all fallback for failed schemas 61 + return irSchemaWithTypeToAst({ 62 + plugin, 63 + schema: { 64 + type: 'unknown', 65 + }, 66 + state, 67 + }); 68 + }; 69 + 70 + const handleComponent = ({ 71 + plugin, 72 + schema, 73 + state, 74 + }: IrSchemaToAstOptions & { 75 + schema: IR.SchemaObject; 76 + }) => { 77 + const type = irSchemaToAst({ plugin, schema, state }); 78 + 79 + // Don't tag enums as 'type' since they export runtime artifacts (values) 80 + const isEnum = schema.type === 'enum' && plugin.config.enums.enabled; 81 + 82 + const $ref = pathToJsonPointer(state.path.value); 83 + const symbol = plugin.registerSymbol({ 84 + exported: true, 85 + meta: { 86 + kind: isEnum ? undefined : 'type', 87 + path: state.path.value, 88 + tags: state.tags?.value, 89 + }, 90 + name: buildName({ 91 + config: plugin.config.definitions, 92 + name: refToName($ref), 93 + }), 94 + selector: plugin.api.selector('ref', $ref), 95 + }); 96 + exportType({ 97 + plugin, 98 + schema, 99 + symbol, 100 + type, 101 + }); 102 + }; 103 + 104 + export const handlerV1: HeyApiTypeScriptPlugin['Handler'] = ({ plugin }) => { 105 + // reserve identifier for ClientOptions 106 + const symbolClientOptions = plugin.registerSymbol({ 107 + exported: true, 108 + meta: { 109 + kind: 'type', 110 + path: [], 111 + }, 112 + name: buildName({ 113 + config: { 114 + case: plugin.config.case, 115 + }, 116 + name: 'ClientOptions', 117 + }), 118 + selector: plugin.api.selector('ClientOptions'), 119 + }); 120 + // reserve identifier for Webhooks 121 + const symbolWebhooks = plugin.registerSymbol({ 122 + exported: true, 123 + meta: { 124 + kind: 'type', 125 + path: [], 126 + }, 127 + name: buildName({ 128 + config: { 129 + case: plugin.config.case, 130 + }, 131 + name: 'Webhooks', 132 + }), 133 + selector: plugin.api.selector('Webhooks'), 134 + }); 135 + 136 + const servers: Array<IR.ServerObject> = []; 137 + const webhookNames: Array<string> = []; 138 + 139 + plugin.forEach( 140 + 'operation', 141 + 'parameter', 142 + 'requestBody', 143 + 'schema', 144 + 'server', 145 + 'webhook', 146 + (event) => { 147 + const state = toRefs<PluginState>({ 148 + path: event._path, 149 + tags: event.tags, 150 + }); 151 + switch (event.type) { 152 + case 'operation': 153 + operationToType({ 154 + operation: event.operation, 155 + plugin, 156 + state, 157 + }); 158 + break; 159 + case 'parameter': 160 + handleComponent({ 161 + plugin, 162 + schema: event.parameter.schema, 163 + state, 164 + }); 165 + break; 166 + case 'requestBody': 167 + handleComponent({ 168 + plugin, 169 + schema: event.requestBody.schema, 170 + state, 171 + }); 172 + break; 173 + case 'schema': 174 + handleComponent({ 175 + plugin, 176 + schema: event.schema, 177 + state, 178 + }); 179 + break; 180 + case 'server': 181 + servers.push(event.server); 182 + break; 183 + case 'webhook': 184 + webhookNames.push( 185 + webhookToType({ 186 + operation: event.operation, 187 + plugin, 188 + state, 189 + }), 190 + ); 191 + break; 192 + } 193 + }, 194 + { 195 + order: 'declarations', 196 + }, 197 + ); 198 + 199 + createClientOptions({ plugin, servers, symbolClientOptions }); 200 + createWebhooks({ plugin, symbolWebhooks, webhookNames }); 201 + };
+51
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/array.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import { deduplicateSchema } from '~/ir/schema'; 4 + import type { SchemaWithType } from '~/plugins'; 5 + import { toRef } from '~/plugins/shared/utils/refs'; 6 + import { tsc } from '~/tsc'; 7 + 8 + import type { IrSchemaToAstOptions } from '../../shared/types'; 9 + import { irSchemaToAst } from '../plugin'; 10 + 11 + export const arrayToAst = ({ 12 + plugin, 13 + schema, 14 + state, 15 + }: IrSchemaToAstOptions & { 16 + schema: SchemaWithType<'array'>; 17 + }): ts.TypeNode => { 18 + if (!schema.items) { 19 + return tsc.typeArrayNode( 20 + tsc.keywordTypeNode({ keyword: plugin.config.topType }), 21 + ); 22 + } 23 + 24 + schema = deduplicateSchema({ detectFormat: true, schema }); 25 + 26 + const itemTypes: Array<ts.TypeNode> = []; 27 + 28 + if (schema.items) { 29 + schema.items.forEach((item, index) => { 30 + const type = irSchemaToAst({ 31 + plugin, 32 + schema: item, 33 + state: { 34 + ...state, 35 + path: toRef([...state.path.value, 'items', index]), 36 + }, 37 + }); 38 + itemTypes.push(type); 39 + }); 40 + } 41 + 42 + if (itemTypes.length === 1) { 43 + return tsc.typeArrayNode(itemTypes[0]!); 44 + } 45 + 46 + if (schema.logicalOperator === 'and') { 47 + return tsc.typeArrayNode(tsc.typeIntersectionNode({ types: itemTypes })); 48 + } 49 + 50 + return tsc.typeArrayNode(tsc.typeUnionNode({ types: itemTypes })); 51 + };
+22
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/boolean.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + import { tsc } from '~/tsc'; 5 + 6 + import type { IrSchemaToAstOptions } from '../../shared/types'; 7 + 8 + export const booleanToAst = ({ 9 + schema, 10 + }: IrSchemaToAstOptions & { 11 + schema: SchemaWithType<'boolean'>; 12 + }): ts.TypeNode => { 13 + if (schema.const !== undefined) { 14 + return tsc.literalTypeNode({ 15 + literal: tsc.ots.boolean(schema.const as boolean), 16 + }); 17 + } 18 + 19 + return tsc.keywordTypeNode({ 20 + keyword: 'boolean', 21 + }); 22 + };
+24
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/enum.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + 5 + import type { IrSchemaToAstOptions } from '../../shared/types'; 6 + import { irSchemaToAst } from '../plugin'; 7 + 8 + export const enumToAst = ({ 9 + plugin, 10 + schema, 11 + state, 12 + }: IrSchemaToAstOptions & { 13 + schema: SchemaWithType<'enum'>; 14 + }): ts.TypeNode => { 15 + const type = irSchemaToAst({ 16 + plugin, 17 + schema: { 18 + ...schema, 19 + type: undefined, 20 + }, 21 + state, 22 + }); 23 + return type; 24 + };
+98
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/index.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + 5 + import type { IrSchemaToAstOptions } from '../../shared/types'; 6 + import { arrayToAst } from './array'; 7 + import { booleanToAst } from './boolean'; 8 + import { enumToAst } from './enum'; 9 + import { neverToAst } from './never'; 10 + import { nullToAst } from './null'; 11 + import { numberToAst } from './number'; 12 + import { objectToAst } from './object'; 13 + import { stringToAst } from './string'; 14 + import { tupleToAst } from './tuple'; 15 + import { undefinedToAst } from './undefined'; 16 + import { unknownToAst } from './unknown'; 17 + import { voidToAst } from './void'; 18 + 19 + export const irSchemaWithTypeToAst = ({ 20 + schema, 21 + ...args 22 + }: IrSchemaToAstOptions & { 23 + schema: SchemaWithType; 24 + }): ts.TypeNode => { 25 + const transformersPlugin = args.plugin.getPlugin('@hey-api/transformers'); 26 + if (transformersPlugin?.config.typeTransformers) { 27 + for (const typeTransformer of transformersPlugin.config.typeTransformers) { 28 + const typeNode = typeTransformer({ schema }); 29 + if (typeNode) { 30 + return typeNode; 31 + } 32 + } 33 + } 34 + 35 + switch (schema.type) { 36 + case 'array': 37 + return arrayToAst({ 38 + ...args, 39 + schema: schema as SchemaWithType<'array'>, 40 + }); 41 + case 'boolean': 42 + return booleanToAst({ 43 + ...args, 44 + schema: schema as SchemaWithType<'boolean'>, 45 + }); 46 + case 'enum': 47 + return enumToAst({ 48 + ...args, 49 + schema: schema as SchemaWithType<'enum'>, 50 + }); 51 + case 'integer': 52 + case 'number': 53 + return numberToAst({ 54 + ...args, 55 + schema: schema as SchemaWithType<'integer' | 'number'>, 56 + }); 57 + case 'never': 58 + return neverToAst({ 59 + ...args, 60 + schema: schema as SchemaWithType<'never'>, 61 + }); 62 + case 'null': 63 + return nullToAst({ 64 + ...args, 65 + schema: schema as SchemaWithType<'null'>, 66 + }); 67 + case 'object': 68 + return objectToAst({ 69 + ...args, 70 + schema: schema as SchemaWithType<'object'>, 71 + }); 72 + case 'string': 73 + return stringToAst({ 74 + ...args, 75 + schema: schema as SchemaWithType<'string'>, 76 + }); 77 + case 'tuple': 78 + return tupleToAst({ 79 + ...args, 80 + schema: schema as SchemaWithType<'tuple'>, 81 + }); 82 + case 'undefined': 83 + return undefinedToAst({ 84 + ...args, 85 + schema: schema as SchemaWithType<'undefined'>, 86 + }); 87 + case 'unknown': 88 + return unknownToAst({ 89 + ...args, 90 + schema: schema as SchemaWithType<'unknown'>, 91 + }); 92 + case 'void': 93 + return voidToAst({ 94 + ...args, 95 + schema: schema as SchemaWithType<'void'>, 96 + }); 97 + } 98 + };
+18
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/never.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + import { tsc } from '~/tsc'; 5 + 6 + import type { IrSchemaToAstOptions } from '../../shared/types'; 7 + 8 + export const neverToAst = ( 9 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 10 + _args: IrSchemaToAstOptions & { 11 + schema: SchemaWithType<'never'>; 12 + }, 13 + ): ts.TypeNode => { 14 + const node = tsc.keywordTypeNode({ 15 + keyword: 'never', 16 + }); 17 + return node; 18 + };
+18
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/null.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + import { tsc } from '~/tsc'; 5 + 6 + import type { IrSchemaToAstOptions } from '../../shared/types'; 7 + 8 + export const nullToAst = ( 9 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 10 + _args: IrSchemaToAstOptions & { 11 + schema: SchemaWithType<'null'>; 12 + }, 13 + ): ts.TypeNode => { 14 + const node = tsc.literalTypeNode({ 15 + literal: tsc.null(), 16 + }); 17 + return node; 18 + };
+30
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/number.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + import { tsc } from '~/tsc'; 5 + 6 + import type { IrSchemaToAstOptions } from '../../shared/types'; 7 + 8 + export const numberToAst = ({ 9 + plugin, 10 + schema, 11 + }: IrSchemaToAstOptions & { 12 + schema: SchemaWithType<'integer' | 'number'>; 13 + }): ts.TypeNode => { 14 + if (schema.const !== undefined) { 15 + return tsc.literalTypeNode({ 16 + literal: tsc.ots.number(schema.const as number), 17 + }); 18 + } 19 + 20 + if (schema.type === 'integer' && schema.format === 'int64') { 21 + // TODO: parser - add ability to skip type transformers 22 + if (plugin.getPlugin('@hey-api/transformers')?.config.bigInt) { 23 + return tsc.typeReferenceNode({ typeName: 'bigint' }); 24 + } 25 + } 26 + 27 + return tsc.keywordTypeNode({ 28 + keyword: 'number', 29 + }); 30 + };
+131
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/object.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { IR } from '~/ir/types'; 4 + import type { SchemaWithType } from '~/plugins'; 5 + import { fieldName } from '~/plugins/shared/utils/case'; 6 + import { toRef } from '~/plugins/shared/utils/refs'; 7 + import { createSchemaComment } from '~/plugins/shared/utils/schema'; 8 + import type { Property } from '~/tsc'; 9 + import { tsc } from '~/tsc'; 10 + 11 + import type { IrSchemaToAstOptions } from '../../shared/types'; 12 + import { irSchemaToAst } from '../plugin'; 13 + 14 + export const objectToAst = ({ 15 + plugin, 16 + schema, 17 + state, 18 + }: IrSchemaToAstOptions & { 19 + schema: SchemaWithType<'object'>; 20 + }): ts.TypeNode => { 21 + // TODO: parser - handle constants 22 + let indexKey: ts.TypeReferenceNode | undefined; 23 + let indexProperty: Property | undefined; 24 + const schemaProperties: Array<Property> = []; 25 + let indexPropertyItems: Array<IR.SchemaObject> = []; 26 + const required = schema.required ?? []; 27 + let hasOptionalProperties = false; 28 + 29 + for (const name in schema.properties) { 30 + const property = schema.properties[name]!; 31 + const propertyType = irSchemaToAst({ 32 + plugin, 33 + schema: property, 34 + state: { 35 + ...state, 36 + path: toRef([...state.path.value, 'properties', name]), 37 + }, 38 + }); 39 + const isRequired = required.includes(name); 40 + schemaProperties.push({ 41 + comment: createSchemaComment({ schema: property }), 42 + isReadOnly: property.accessScope === 'read', 43 + isRequired, 44 + name: fieldName({ context: plugin.context, name }), 45 + type: propertyType, 46 + }); 47 + indexPropertyItems.push(property); 48 + 49 + if (!isRequired) { 50 + hasOptionalProperties = true; 51 + } 52 + } 53 + 54 + // include pattern value schemas into the index union 55 + if (schema.patternProperties) { 56 + for (const pattern in schema.patternProperties) { 57 + const ir = schema.patternProperties[pattern]!; 58 + indexPropertyItems.unshift(ir); 59 + } 60 + } 61 + 62 + const hasPatterns = 63 + !!schema.patternProperties && 64 + Object.keys(schema.patternProperties).length > 0; 65 + 66 + const addPropsRaw = schema.additionalProperties; 67 + const addPropsObj = 68 + addPropsRaw !== false && addPropsRaw 69 + ? (addPropsRaw as IR.SchemaObject) 70 + : undefined; 71 + const shouldCreateIndex = 72 + hasPatterns || 73 + (!!addPropsObj && 74 + (addPropsObj.type !== 'never' || !indexPropertyItems.length)); 75 + 76 + if (shouldCreateIndex) { 77 + // only inject additionalProperties when it’s not "never" 78 + const addProps = addPropsObj; 79 + if (addProps && addProps.type !== 'never') { 80 + indexPropertyItems.unshift(addProps); 81 + } else if ( 82 + !hasPatterns && 83 + !indexPropertyItems.length && 84 + addProps && 85 + addProps.type === 'never' 86 + ) { 87 + // keep "never" only when there are NO patterns and NO explicit properties 88 + indexPropertyItems = [addProps]; 89 + } 90 + 91 + if (hasOptionalProperties) { 92 + indexPropertyItems.push({ 93 + type: 'undefined', 94 + }); 95 + } 96 + 97 + indexProperty = { 98 + isRequired: !schema.propertyNames, 99 + name: 'key', 100 + type: 101 + indexPropertyItems.length === 1 102 + ? irSchemaToAst({ 103 + plugin, 104 + schema: indexPropertyItems[0]!, 105 + state, 106 + }) 107 + : irSchemaToAst({ 108 + plugin, 109 + schema: { items: indexPropertyItems, logicalOperator: 'or' }, 110 + state, 111 + }), 112 + }; 113 + 114 + if (schema.propertyNames?.$ref) { 115 + indexKey = irSchemaToAst({ 116 + plugin, 117 + schema: { 118 + $ref: schema.propertyNames.$ref, 119 + }, 120 + state, 121 + }) as ts.TypeReferenceNode; 122 + } 123 + } 124 + 125 + return tsc.typeInterfaceNode({ 126 + indexKey, 127 + indexProperty, 128 + properties: schemaProperties, 129 + useLegacyResolution: false, 130 + }); 131 + };
+118
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/string.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + import { tsc } from '~/tsc'; 5 + import { stringCase } from '~/utils/stringCase'; 6 + 7 + import type { IrSchemaToAstOptions } from '../../shared/types'; 8 + 9 + export const stringToAst = ({ 10 + plugin, 11 + schema, 12 + }: IrSchemaToAstOptions & { 13 + schema: SchemaWithType<'string'>; 14 + }): ts.TypeNode => { 15 + if (schema.const !== undefined) { 16 + return tsc.literalTypeNode({ 17 + literal: tsc.stringLiteral({ text: schema.const as string }), 18 + }); 19 + } 20 + 21 + if (schema.format) { 22 + if (schema.format === 'binary') { 23 + return tsc.typeUnionNode({ 24 + types: [ 25 + tsc.typeReferenceNode({ 26 + typeName: 'Blob', 27 + }), 28 + tsc.typeReferenceNode({ 29 + typeName: 'File', 30 + }), 31 + ], 32 + }); 33 + } 34 + 35 + if (schema.format === 'date-time' || schema.format === 'date') { 36 + // TODO: parser - add ability to skip type transformers 37 + if (plugin.getPlugin('@hey-api/transformers')?.config.dates) { 38 + return tsc.typeReferenceNode({ typeName: 'Date' }); 39 + } 40 + } 41 + 42 + if (schema.format === 'typeid' && typeof schema.example === 'string') { 43 + const parts = String(schema.example).split('_'); 44 + parts.pop(); // remove the ID part 45 + const type = parts.join('_'); 46 + 47 + const selector = plugin.api.selector('TypeID', type); 48 + if (!plugin.getSymbol(selector)) { 49 + const selectorTypeId = plugin.api.selector('TypeID'); 50 + 51 + if (!plugin.getSymbol(selectorTypeId)) { 52 + const symbolTypeId = plugin.registerSymbol({ 53 + exported: true, 54 + meta: { 55 + kind: 'type', 56 + path: [], 57 + }, 58 + name: 'TypeID', 59 + selector: selectorTypeId, 60 + }); 61 + const nodeTypeId = tsc.typeAliasDeclaration({ 62 + exportType: symbolTypeId.exported, 63 + name: symbolTypeId.placeholder, 64 + type: tsc.templateLiteralType({ 65 + value: [ 66 + tsc.typeReferenceNode({ typeName: 'T' }), 67 + '_', 68 + tsc.keywordTypeNode({ keyword: 'string' }), 69 + ], 70 + }), 71 + typeParameters: [ 72 + tsc.typeParameterDeclaration({ 73 + constraint: tsc.keywordTypeNode({ 74 + keyword: 'string', 75 + }), 76 + name: 'T', 77 + }), 78 + ], 79 + }); 80 + plugin.setSymbolValue(symbolTypeId, nodeTypeId); 81 + } 82 + 83 + const symbolTypeId = plugin.referenceSymbol(selectorTypeId); 84 + const symbolTypeName = plugin.registerSymbol({ 85 + exported: true, 86 + meta: { 87 + kind: 'type', 88 + path: [], 89 + }, 90 + name: stringCase({ 91 + case: plugin.config.case, 92 + value: `${type}_id`, 93 + }), 94 + selector, 95 + }); 96 + const node = tsc.typeAliasDeclaration({ 97 + exportType: symbolTypeName.exported, 98 + name: symbolTypeName.placeholder, 99 + type: tsc.typeReferenceNode({ 100 + typeArguments: [ 101 + tsc.literalTypeNode({ 102 + literal: tsc.stringLiteral({ text: type }), 103 + }), 104 + ], 105 + typeName: symbolTypeId.placeholder, 106 + }), 107 + }); 108 + plugin.setSymbolValue(symbolTypeName, node); 109 + } 110 + const symbol = plugin.referenceSymbol(selector); 111 + return tsc.typeReferenceNode({ typeName: symbol.placeholder }); 112 + } 113 + } 114 + 115 + return tsc.keywordTypeNode({ 116 + keyword: 'string', 117 + }); 118 + };
+41
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/tuple.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + import { toRef } from '~/plugins/shared/utils/refs'; 5 + import { tsc } from '~/tsc'; 6 + 7 + import type { IrSchemaToAstOptions } from '../../shared/types'; 8 + import { irSchemaToAst } from '../plugin'; 9 + 10 + export const tupleToAst = ({ 11 + plugin, 12 + schema, 13 + state, 14 + }: IrSchemaToAstOptions & { 15 + schema: SchemaWithType<'tuple'>; 16 + }): ts.TypeNode => { 17 + let itemTypes: Array<ts.Expression | ts.TypeNode> = []; 18 + 19 + if (schema.const && Array.isArray(schema.const)) { 20 + itemTypes = schema.const.map((value) => { 21 + const expression = tsc.valueToExpression({ value }); 22 + return expression ?? tsc.identifier({ text: plugin.config.topType }); 23 + }); 24 + } else if (schema.items) { 25 + schema.items.forEach((item, index) => { 26 + const type = irSchemaToAst({ 27 + plugin, 28 + schema: item, 29 + state: { 30 + ...state, 31 + path: toRef([...state.path.value, 'items', index]), 32 + }, 33 + }); 34 + itemTypes.push(type); 35 + }); 36 + } 37 + 38 + return tsc.typeTupleNode({ 39 + types: itemTypes, 40 + }); 41 + };
+18
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/undefined.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + import { tsc } from '~/tsc'; 5 + 6 + import type { IrSchemaToAstOptions } from '../../shared/types'; 7 + 8 + export const undefinedToAst = ( 9 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 10 + _args: IrSchemaToAstOptions & { 11 + schema: SchemaWithType<'undefined'>; 12 + }, 13 + ): ts.TypeNode => { 14 + const node = tsc.keywordTypeNode({ 15 + keyword: 'undefined', 16 + }); 17 + return node; 18 + };
+17
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/unknown.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + import { tsc } from '~/tsc'; 5 + 6 + import type { IrSchemaToAstOptions } from '../../shared/types'; 7 + 8 + export const unknownToAst = ({ 9 + plugin, 10 + }: IrSchemaToAstOptions & { 11 + schema: SchemaWithType<'unknown'>; 12 + }): ts.TypeNode => { 13 + const node = tsc.keywordTypeNode({ 14 + keyword: plugin.config.topType, 15 + }); 16 + return node; 17 + };
+18
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/void.ts
··· 1 + import type ts from 'typescript'; 2 + 3 + import type { SchemaWithType } from '~/plugins'; 4 + import { tsc } from '~/tsc'; 5 + 6 + import type { IrSchemaToAstOptions } from '../../shared/types'; 7 + 8 + export const voidToAst = ( 9 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 10 + _args: IrSchemaToAstOptions & { 11 + schema: SchemaWithType<'void'>; 12 + }, 13 + ): ts.TypeNode => { 14 + const node = tsc.keywordTypeNode({ 15 + keyword: 'void', 16 + }); 17 + return node; 18 + };
+17 -9
packages/openapi-ts/src/plugins/@hey-api/typescript/webhook.ts packages/openapi-ts/src/plugins/@hey-api/typescript/shared/webhook.ts
··· 3 3 import { createSchemaComment } from '~/plugins/shared/utils/schema'; 4 4 import { tsc } from '~/tsc'; 5 5 6 - import { schemaToType } from './plugin'; 7 - import type { HeyApiTypeScriptPlugin } from './types'; 6 + import { irSchemaToAst } from '../v1/plugin'; 7 + import type { IrSchemaToAstOptions } from './types'; 8 8 9 9 const operationToDataType = ({ 10 10 operation, 11 11 plugin, 12 - }: { 12 + state, 13 + }: IrSchemaToAstOptions & { 13 14 operation: IR.OperationObject; 14 - plugin: HeyApiTypeScriptPlugin['Instance']; 15 15 }): string => { 16 16 const data: IR.SchemaObject = { 17 17 type: 'object', ··· 27 27 exported: true, 28 28 meta: { 29 29 kind: 'type', 30 + path: state.path.value, 31 + tags: state.tags?.value, 30 32 }, 31 33 name: buildName({ 32 34 config: { ··· 37 39 }), 38 40 selector: plugin.api.selector('webhook-payload', operation.id), 39 41 }); 40 - const type = schemaToType({ 42 + const type = irSchemaToAst({ 41 43 plugin, 42 44 schema: operation.body.schema, 45 + state, 43 46 }); 44 47 const node = tsc.typeAliasDeclaration({ 45 48 comment: createSchemaComment({ schema: operation.body.schema }), ··· 53 56 exported: true, 54 57 meta: { 55 58 kind: 'type', 59 + path: state.path.value, 60 + tags: state.tags?.value, 56 61 }, 57 62 name: symbolWebhookPayload.name, 58 63 placeholder: symbolWebhookPayload.placeholder, ··· 79 84 exported: true, 80 85 meta: { 81 86 kind: 'type', 87 + path: state.path.value, 88 + tags: state.tags?.value, 82 89 }, 83 90 name: buildName({ 84 91 config: plugin.config.webhooks, ··· 86 93 }), 87 94 selector: plugin.api.selector('webhook-request', operation.id), 88 95 }); 89 - const type = schemaToType({ 96 + const type = irSchemaToAst({ 90 97 plugin, 91 98 schema: data, 99 + state, 92 100 }); 93 101 const node = tsc.typeAliasDeclaration({ 94 102 exportType: symbolWebhookRequest.exported, ··· 103 111 export const webhookToType = ({ 104 112 operation, 105 113 plugin, 106 - }: { 114 + state, 115 + }: IrSchemaToAstOptions & { 107 116 operation: IR.OperationObject; 108 - plugin: HeyApiTypeScriptPlugin['Instance']; 109 117 }): string => { 110 - const name = operationToDataType({ operation, plugin }); 118 + const name = operationToDataType({ operation, plugin, state }); 111 119 return name; 112 120 113 121 // don't handle webhook responses for now, users only need requestBody
+1 -1
packages/openapi-ts/src/plugins/@hey-api/typescript/webhooks.ts packages/openapi-ts/src/plugins/@hey-api/typescript/shared/webhooks.ts
··· 2 2 3 3 import { tsc } from '~/tsc'; 4 4 5 - import type { HeyApiTypeScriptPlugin } from './types'; 5 + import type { HeyApiTypeScriptPlugin } from '../types'; 6 6 7 7 export const createWebhooks = ({ 8 8 plugin,
+1 -1
packages/openapi-ts/src/plugins/@pinia/colada/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 6 6 | '_JSONValue'
+2 -3
packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts
··· 2 2 3 3 import type { IR } from '~/ir/types'; 4 4 import { buildName } from '~/openApi/shared/utils/name'; 5 + import { createOperationComment } from '~/plugins/shared/utils/operation'; 5 6 import { tsc } from '~/tsc'; 6 7 7 8 import { handleMeta } from './meta'; ··· 108 109 }); 109 110 } 110 111 111 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 112 - 113 112 const symbolMutationOptions = plugin.registerSymbol({ 114 113 exported: true, 115 114 name: buildName({ ··· 119 118 }); 120 119 const statement = tsc.constVariable({ 121 120 comment: plugin.config.comments 122 - ? sdkPlugin.api.createOperationComment({ operation }) 121 + ? createOperationComment({ operation }) 123 122 : undefined, 124 123 exportConst: symbolMutationOptions.exported, 125 124 expression: tsc.arrowFunction({
+1 -1
packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts
··· 1 - import { operationClasses } from '~/plugins/@hey-api/sdk/operation'; 1 + import { operationClasses } from '~/plugins/@hey-api/sdk/shared/operation'; 2 2 import { stringCase } from '~/utils/stringCase'; 3 3 4 4 import { createMutationOptions } from './mutationOptions';
+2 -3
packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts
··· 3 3 import type { IR } from '~/ir/types'; 4 4 import { buildName } from '~/openApi/shared/utils/name'; 5 5 import { 6 + createOperationComment, 6 7 hasOperationSse, 7 8 isOperationOptionsRequired, 8 9 } from '~/plugins/shared/utils/operation'; ··· 148 149 }); 149 150 } 150 151 151 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 152 - 153 152 const symbolQueryOptionsFn = plugin.registerSymbol({ 154 153 exported: true, 155 154 name: buildName({ ··· 165 164 }); 166 165 const statement = tsc.constVariable({ 167 166 comment: plugin.config.comments 168 - ? sdkPlugin.api.createOperationComment({ operation }) 167 + ? createOperationComment({ operation }) 169 168 : undefined, 170 169 exportConst: symbolQueryOptionsFn.exported, 171 170 expression: tsc.callExpression({
+1 -1
packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 2 + import type { DefinePlugin, Plugin } from '~/plugins'; 3 3 import type { StringCase, StringName } from '~/types/case'; 4 4 5 5 import type { IApi } from './api';
+1 -1
packages/openapi-ts/src/plugins/@pinia/colada/useType.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 3 - import { operationOptionsType } from '~/plugins/@hey-api/sdk/operation'; 3 + import { operationOptionsType } from '~/plugins/@hey-api/sdk/shared/operation'; 4 4 5 5 import type { PiniaColadaPlugin } from './types'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 6 6 | 'AxiosError'
+1 -1
packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 2 + import type { DefinePlugin, Plugin } from '~/plugins'; 3 3 import type { StringCase, StringName } from '~/types/case'; 4 4 5 5 import type { IApi } from './api';
+10 -4
packages/openapi-ts/src/plugins/@tanstack/query-core/infiniteQueryOptions.ts
··· 3 3 import { operationPagination } from '~/ir/operation'; 4 4 import type { IR } from '~/ir/types'; 5 5 import { buildName } from '~/openApi/shared/utils/name'; 6 - import { isOperationOptionsRequired } from '~/plugins/shared/utils/operation'; 6 + import { 7 + createOperationComment, 8 + isOperationOptionsRequired, 9 + } from '~/plugins/shared/utils/operation'; 7 10 import { tsc } from '~/tsc'; 8 11 import { tsNodeToString } from '~/tsc/utils'; 9 12 ··· 270 273 const type = pluginTypeScript.api.schemaToType({ 271 274 plugin: pluginTypeScript, 272 275 schema: pagination.schema, 276 + state: { 277 + path: { 278 + value: [], 279 + }, 280 + }, 273 281 }); 274 282 const typePageParam = `${tsNodeToString({ 275 283 node: type, ··· 439 447 }); 440 448 } 441 449 442 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 443 - 444 450 const symbolInfiniteQueryOptionsFn = plugin.registerSymbol({ 445 451 exported: true, 446 452 name: buildName({ ··· 450 456 }); 451 457 const statement = tsc.constVariable({ 452 458 comment: plugin.config.comments 453 - ? sdkPlugin.api.createOperationComment({ operation }) 459 + ? createOperationComment({ operation }) 454 460 : undefined, 455 461 exportConst: symbolInfiniteQueryOptionsFn.exported, 456 462 expression: tsc.arrowFunction({
+2 -3
packages/openapi-ts/src/plugins/@tanstack/query-core/mutationOptions.ts
··· 2 2 3 3 import type { IR } from '~/ir/types'; 4 4 import { buildName } from '~/openApi/shared/utils/name'; 5 + import { createOperationComment } from '~/plugins/shared/utils/operation'; 5 6 import { tsc } from '~/tsc'; 6 7 7 8 import { handleMeta } from './meta'; ··· 98 99 }); 99 100 } 100 101 101 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 102 - 103 102 const mutationOptionsFn = 'mutationOptions'; 104 103 const expression = tsc.arrowFunction({ 105 104 parameters: [ ··· 132 131 }); 133 132 const statement = tsc.constVariable({ 134 133 comment: plugin.config.comments 135 - ? sdkPlugin.api.createOperationComment({ operation }) 134 + ? createOperationComment({ operation }) 136 135 : undefined, 137 136 exportConst: symbolMutationOptions.exported, 138 137 expression,
+1 -1
packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts
··· 1 - import { operationClasses } from '~/plugins/@hey-api/sdk/operation'; 1 + import { operationClasses } from '~/plugins/@hey-api/sdk/shared/operation'; 2 2 import { stringCase } from '~/utils/stringCase'; 3 3 4 4 import { createInfiniteQueryOptions } from './infiniteQueryOptions';
+2 -3
packages/openapi-ts/src/plugins/@tanstack/query-core/queryOptions.ts
··· 3 3 import type { IR } from '~/ir/types'; 4 4 import { buildName } from '~/openApi/shared/utils/name'; 5 5 import { 6 + createOperationComment, 6 7 hasOperationSse, 7 8 isOperationOptionsRequired, 8 9 } from '~/plugins/shared/utils/operation'; ··· 153 154 }); 154 155 } 155 156 156 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 157 - 158 157 const symbolQueryOptionsFn = plugin.registerSymbol({ 159 158 exported: plugin.config.queryOptions.exported, 160 159 name: buildName({ ··· 165 164 }); 166 165 const statement = tsc.constVariable({ 167 166 comment: plugin.config.comments 168 - ? sdkPlugin.api.createOperationComment({ operation }) 167 + ? createOperationComment({ operation }) 169 168 : undefined, 170 169 exportConst: symbolQueryOptionsFn.exported, 171 170 expression: tsc.arrowFunction({
+2 -3
packages/openapi-ts/src/plugins/@tanstack/query-core/useQuery.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import { buildName } from '~/openApi/shared/utils/name'; 3 3 import { 4 + createOperationComment, 4 5 hasOperationSse, 5 6 isOperationOptionsRequired, 6 7 } from '~/plugins/shared/utils/operation'; ··· 25 26 if (!('useQuery' in plugin.config)) { 26 27 return; 27 28 } 28 - 29 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 30 29 31 30 const symbolUseQueryFn = plugin.registerSymbol({ 32 31 exported: true, ··· 51 50 ); 52 51 const statement = tsc.constVariable({ 53 52 comment: plugin.config.comments 54 - ? sdkPlugin.api.createOperationComment({ operation }) 53 + ? createOperationComment({ operation }) 55 54 : undefined, 56 55 exportConst: symbolUseQueryFn.exported, 57 56 expression: tsc.arrowFunction({
+1 -1
packages/openapi-ts/src/plugins/@tanstack/query-core/useType.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 3 - import { operationOptionsType } from '~/plugins/@hey-api/sdk/operation'; 3 + import { operationOptionsType } from '~/plugins/@hey-api/sdk/shared/operation'; 4 4 5 5 import type { PluginInstance } from './types'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@tanstack/react-query/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 6 6 | 'AxiosError'
+1 -1
packages/openapi-ts/src/plugins/@tanstack/react-query/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 2 + import type { DefinePlugin, Plugin } from '~/plugins'; 3 3 import type { StringCase, StringName } from '~/types/case'; 4 4 5 5 import type { IApi } from './api';
+1 -1
packages/openapi-ts/src/plugins/@tanstack/solid-query/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 6 6 | 'AxiosError'
+1 -1
packages/openapi-ts/src/plugins/@tanstack/solid-query/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 2 + import type { DefinePlugin, Plugin } from '~/plugins'; 3 3 import type { StringCase, StringName } from '~/types/case'; 4 4 5 5 import type { IApi } from './api';
+1 -1
packages/openapi-ts/src/plugins/@tanstack/svelte-query/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 6 6 | 'AxiosError'
+1 -1
packages/openapi-ts/src/plugins/@tanstack/svelte-query/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 2 + import type { DefinePlugin, Plugin } from '~/plugins'; 3 3 import type { StringCase, StringName } from '~/types/case'; 4 4 5 5 import type { IApi } from './api';
+1 -1
packages/openapi-ts/src/plugins/@tanstack/vue-query/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 6 6 | 'AxiosError'
+1 -1
packages/openapi-ts/src/plugins/@tanstack/vue-query/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 2 + import type { DefinePlugin, Plugin } from '~/plugins'; 3 3 import type { StringCase, StringName } from '~/types/case'; 4 4 5 5 import type { IApi } from './api';
+1 -1
packages/openapi-ts/src/plugins/arktype/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 - import type { Plugin } from '~/plugins/types'; 4 + import type { Plugin } from '~/plugins'; 5 5 6 6 import type { ValidatorArgs } from './shared/types'; 7 7 import { createRequestValidatorV2, createResponseValidatorV2 } from './v2/api';
+6 -8
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'; 4 - import type { ToRefs } from '~/plugins/shared/types/refs'; 5 + import type { ToRefs } from '~/plugins'; 5 6 6 7 import type { ArktypePlugin } from '../types'; 7 8 ··· 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;
+1 -1
packages/openapi-ts/src/plugins/arktype/types.d.ts
··· 1 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 2 2 import type { StringCase, StringName } from '~/types/case'; 3 3 4 4 import type { IApi } from './api';
+13 -23
packages/openapi-ts/src/plugins/arktype/v2/plugin.ts
··· 2 2 import type { IR } from '~/ir/types'; 3 3 import { buildName } from '~/openApi/shared/utils/name'; 4 4 import type { SchemaWithType } from '~/plugins/shared/types/schema'; 5 - import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; 6 5 import { toRefs } from '~/plugins/shared/utils/refs'; 7 6 import { tsc } from '~/tsc'; 8 - import { refToName } from '~/utils/ref'; 7 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 9 8 10 9 import { exportAst } from '../shared/export'; 11 - import type { Ast, IrSchemaToAstOptions } from '../shared/types'; 10 + import type { Ast, IrSchemaToAstOptions, PluginState } from '../shared/types'; 12 11 import type { ArktypePlugin } from '../types'; 13 12 import { irSchemaWithTypeToAst } from './toAst'; 14 13 ··· 237 236 }; 238 237 239 238 const handleComponent = ({ 240 - $ref, 241 239 plugin, 242 240 schema, 243 241 state, 244 242 }: IrSchemaToAstOptions & { 245 - $ref: string; 246 243 schema: IR.SchemaObject; 247 244 }): void => { 245 + const $ref = pathToJsonPointer(state.path.value); 248 246 const ast = irSchemaToAst({ plugin, schema, state }); 249 247 const baseName = refToName($ref); 250 - const resourceType = pathToSymbolResourceType(state._path.value); 251 248 const symbol = plugin.registerSymbol({ 252 249 exported: true, 253 250 meta: { 254 - resourceType, 251 + path: state.path.value, 255 252 }, 256 253 name: buildName({ 257 254 config: plugin.config.definitions, ··· 264 261 exported: true, 265 262 meta: { 266 263 kind: 'type', 267 - resourceType, 264 + path: state.path.value, 268 265 }, 269 266 name: buildName({ 270 267 config: plugin.config.definitions.types.infer, ··· 296 293 'schema', 297 294 'webhook', 298 295 (event) => { 296 + const state = toRefs<PluginState>({ 297 + hasLazyExpression: false, 298 + path: event._path, 299 + tags: event.tags, 300 + }); 299 301 switch (event.type) { 300 302 // case 'operation': 301 303 // operationToZodSchema({ ··· 313 315 // break; 314 316 case 'parameter': 315 317 handleComponent({ 316 - $ref: event.$ref, 317 318 plugin, 318 319 schema: event.parameter.schema, 319 - state: toRefs({ 320 - _path: event._path, 321 - hasLazyExpression: false, 322 - }), 320 + state, 323 321 }); 324 322 break; 325 323 case 'requestBody': 326 324 handleComponent({ 327 - $ref: event.$ref, 328 325 plugin, 329 326 schema: event.requestBody.schema, 330 - state: toRefs({ 331 - _path: event._path, 332 - hasLazyExpression: false, 333 - }), 327 + state, 334 328 }); 335 329 break; 336 330 case 'schema': 337 331 handleComponent({ 338 - $ref: event.$ref, 339 332 plugin, 340 333 schema: event.schema, 341 - state: toRefs({ 342 - _path: event._path, 343 - hasLazyExpression: false, 344 - }), 334 + state, 345 335 }); 346 336 break; 347 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({
+2 -1
packages/openapi-ts/src/plugins/config.ts
··· 1 + import type { Plugin } from '~/plugins'; 1 2 import type { AngularCommonPlugin } from '~/plugins/@angular/common'; 2 3 import { defaultConfig as angularCommon } from '~/plugins/@angular/common'; 3 4 import type { HeyApiClientAngularPlugin } from '~/plugins/@hey-api/client-angular'; ··· 46 47 import { defaultConfig as arktype } from '~/plugins/arktype'; 47 48 import type { FastifyPlugin } from '~/plugins/fastify'; 48 49 import { defaultConfig as fastify } from '~/plugins/fastify'; 49 - import type { Plugin, PluginNames } from '~/plugins/types'; 50 + import type { PluginNames } from '~/plugins/types'; 50 51 import type { ValibotPlugin } from '~/plugins/valibot'; 51 52 import { defaultConfig as valibot } from '~/plugins/valibot'; 52 53 import type { ZodPlugin } from '~/plugins/zod';
+1 -1
packages/openapi-ts/src/plugins/fastify/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 3 - import type { Plugin } from '~/plugins/types'; 3 + import type { Plugin } from '~/plugins'; 4 4 5 5 type SelectorType = 'RouteHandler'; 6 6
+1 -1
packages/openapi-ts/src/plugins/fastify/types.d.ts
··· 1 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 2 2 3 3 import type { IApi } from './api'; 4 4
+3
packages/openapi-ts/src/plugins/index.ts
··· 1 + export type { ToRefs } from './shared/types/refs'; 2 + export type { SchemaWithType } from './shared/types/schema'; 3 + export type { DefinePlugin, Plugin } from './types';
+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,
+3 -3
packages/openapi-ts/src/plugins/shared/types/schema.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 3 - export interface SchemaWithType< 3 + export type SchemaWithType< 4 4 T extends 5 5 Required<IR.SchemaObject>['type'] = Required<IR.SchemaObject>['type'], 6 - > extends Omit<IR.SchemaObject, 'type'> { 6 + > = Omit<IR.SchemaObject, 'type'> & { 7 7 type: Extract<Required<IR.SchemaObject>['type'], T>; 8 - } 8 + };
-46
packages/openapi-ts/src/plugins/shared/utils/__tests__/meta.test.ts
··· 1 - import { describe, expect, it } from 'vitest'; 2 - 3 - import { pathToSymbolResourceType } from '../meta'; 4 - 5 - describe('pathToSymbolResourceType', () => { 6 - it('returns schema for components/schemas', () => { 7 - expect(pathToSymbolResourceType(['components', 'schemas', 'Pet'])).toBe( 8 - 'schema', 9 - ); 10 - }); 11 - 12 - it('returns parameter for components/parameters', () => { 13 - expect( 14 - pathToSymbolResourceType(['components', 'parameters', 'limit']), 15 - ).toBe('parameter'); 16 - }); 17 - 18 - it('returns requestBody for components/requestBodies', () => { 19 - expect( 20 - pathToSymbolResourceType(['components', 'requestBodies', 'Body']), 21 - ).toBe('requestBody'); 22 - }); 23 - 24 - it('returns operation for paths', () => { 25 - expect(pathToSymbolResourceType(['paths', '/pets', 'get'])).toBe( 26 - 'operation', 27 - ); 28 - }); 29 - 30 - it('returns server for servers', () => { 31 - expect(pathToSymbolResourceType(['servers', 0])).toBe('server'); 32 - }); 33 - 34 - it('returns webhook for webhooks', () => { 35 - expect(pathToSymbolResourceType(['webhooks', 'onEvent'])).toBe('webhook'); 36 - }); 37 - 38 - it('returns undefined for unknown paths', () => { 39 - expect( 40 - pathToSymbolResourceType(['components', 'unknown', 'foo']), 41 - ).toBeUndefined(); 42 - expect(pathToSymbolResourceType(['random', 'value'])).toBeUndefined(); 43 - expect(pathToSymbolResourceType(['components'])).toBeUndefined(); 44 - expect(pathToSymbolResourceType([])).toBeUndefined(); 45 - }); 46 - });
+1 -1
packages/openapi-ts/src/plugins/shared/utils/config.ts
··· 1 - import type { Plugin } from '~/plugins/types'; 1 + import type { Plugin } from '~/plugins'; 2 2 3 3 export const definePluginConfig = 4 4 <T extends Plugin.Types>(defaultConfig: Plugin.Config<T>) =>
+15 -13
packages/openapi-ts/src/plugins/shared/utils/instance.ts
··· 20 20 import type { IR } from '~/ir/types'; 21 21 import type { OpenApi } from '~/openApi/types'; 22 22 import type { Hooks } from '~/parser/types/hooks'; 23 + import type { Plugin } from '~/plugins'; 23 24 import type { PluginConfigMap } from '~/plugins/config'; 24 - import type { Plugin } from '~/plugins/types'; 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,
-17
packages/openapi-ts/src/plugins/shared/utils/meta.ts
··· 1 - import type { SymbolMeta } from '@hey-api/codegen-core'; 2 - 3 - export const pathToSymbolResourceType = ( 4 - path: ReadonlyArray<string | number>, 5 - ): SymbolMeta['resourceType'] => { 6 - if (path.length >= 2) { 7 - if (path[0] === 'components') { 8 - if (path[1] === 'schemas') return 'schema'; 9 - if (path[1] === 'parameters') return 'parameter'; 10 - if (path[1] === 'requestBodies') return 'requestBody'; 11 - } 12 - if (path[0] === 'paths') return 'operation'; 13 - if (path[0] === 'servers') return 'server'; 14 - if (path[0] === 'webhooks') return 'webhook'; 15 - } 16 - return; 17 - };
+36
packages/openapi-ts/src/plugins/shared/utils/operation.ts
··· 1 1 import { hasOperationDataRequired } from '~/ir/operation'; 2 2 import type { IR } from '~/ir/types'; 3 3 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 4 + import type { Comments } from '~/tsc'; 5 + import { escapeComment } from '~/utils/escape'; 6 + 7 + export const createOperationComment = ({ 8 + operation, 9 + }: { 10 + operation: IR.OperationObject; 11 + }): Comments | undefined => { 12 + const comments: Array<string> = []; 13 + 14 + if (operation.summary) { 15 + comments.push(escapeComment(operation.summary)); 16 + } 17 + 18 + if (operation.description) { 19 + if (comments.length) { 20 + comments.push(''); // Add an empty line between summary and description 21 + } 22 + 23 + comments.push(escapeComment(operation.description)); 24 + } 25 + 26 + if (operation.deprecated) { 27 + if (comments.length) { 28 + comments.push(''); // Add an empty line before deprecated 29 + } 30 + 31 + comments.push('@deprecated'); 32 + } 33 + 34 + if (!comments.length) { 35 + return; 36 + } 37 + 38 + return comments; 39 + }; 4 40 5 41 export const isOperationOptionsRequired = ({ 6 42 context,
+1 -1
packages/openapi-ts/src/plugins/valibot/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 - import type { Plugin } from '~/plugins/types'; 4 + import type { Plugin } from '~/plugins'; 5 5 6 6 import type { ValidatorArgs } from './shared/types'; 7 7 import { createRequestValidatorV1, createResponseValidatorV1 } from './v1/api';
+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 + };
+14 -11
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 - import type { ToRefs } from '~/plugins/shared/types/refs'; 3 - import type { StringCase, StringName } from '~/types/case'; 5 + import type { ToRefs } from '~/plugins'; 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 - /** 14 - * Path to the schema in the intermediary representation. 15 - */ 16 - _path: ReadonlyArray<string | number>; 17 - hasLazyExpression: boolean; 18 - nameCase: StringCase; 19 - nameTransformer: StringName; 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;
+1 -1
packages/openapi-ts/src/plugins/valibot/types.d.ts
··· 1 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 2 2 import type { StringCase, StringName } from '~/types/case'; 3 3 4 4 import type { IApi } from './api';
+19 -13
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 { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; 5 - import { toRef } from '~/plugins/shared/utils/refs'; 6 4 7 - import type { IrSchemaToAstOptions } from '../shared/types'; 8 - import { irSchemaToAst } from './plugin'; 5 + import { exportAst } from './export'; 6 + import type { Ast, IrSchemaToAstOptions } from './types'; 9 7 10 8 export const irOperationToAst = ({ 9 + getAst, 11 10 operation, 12 11 plugin, 13 12 state, 14 13 }: IrSchemaToAstOptions & { 14 + getAst: ( 15 + schema: IR.SchemaObject, 16 + path: ReadonlyArray<string | number>, 17 + ) => Ast; 15 18 operation: IR.OperationObject; 16 19 }) => { 17 20 if (plugin.config.requests.enabled) { ··· 112 115 113 116 schemaData.required = [...requiredProperties]; 114 117 118 + const ast = getAst(schemaData, state.path.value); 115 119 const symbol = plugin.registerSymbol({ 116 120 exported: true, 117 121 meta: { 118 - resourceType: pathToSymbolResourceType(state._path.value), 122 + path: state.path.value, 123 + tags: state.tags?.value, 119 124 }, 120 125 name: buildName({ 121 126 config: plugin.config.requests, ··· 123 128 }), 124 129 selector: plugin.api.selector('data', operation.id), 125 130 }); 126 - irSchemaToAst({ 131 + exportAst({ 132 + ast, 127 133 plugin, 128 134 schema: schemaData, 129 135 state, ··· 136 142 const { response } = operationResponsesMap(operation); 137 143 138 144 if (response) { 139 - const path = [...state._path.value, 'responses']; 145 + const path = [...state.path.value, 'responses']; 146 + const ast = getAst(response, path); 140 147 const symbol = plugin.registerSymbol({ 141 148 exported: true, 142 149 meta: { 143 - resourceType: pathToSymbolResourceType(path), 150 + path, 151 + tags: state.tags?.value, 144 152 }, 145 153 name: buildName({ 146 154 config: plugin.config.responses, ··· 148 156 }), 149 157 selector: plugin.api.selector('responses', operation.id), 150 158 }); 151 - irSchemaToAst({ 159 + exportAst({ 160 + ast, 152 161 plugin, 153 162 schema: response, 154 - state: { 155 - ...state, 156 - _path: toRef(path), 157 - }, 163 + state, 158 164 symbol, 159 165 }); 160 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,
+86 -91
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'; 5 4 import type { IR } from '~/ir/types'; 6 5 import { buildName } from '~/openApi/shared/utils/name'; 7 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 8 - import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; 6 + import type { SchemaWithType } from '~/plugins'; 9 7 import { toRef, toRefs } from '~/plugins/shared/utils/refs'; 10 - import { createSchemaComment } from '~/plugins/shared/utils/schema'; 11 8 import { tsc } from '~/tsc'; 12 - import { refToName } from '~/utils/ref'; 9 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 13 10 11 + import { exportAst } from '../shared/export'; 14 12 import { numberParameter } from '../shared/numbers'; 15 - 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'; 16 17 import type { ValibotPlugin } from '../types'; 17 18 import { identifiers } from './constants'; 18 - import { irOperationToAst } from './operation'; 19 - import { pipesToAst } from './pipesToAst'; 20 19 import { irSchemaWithTypeToAst } from './toAst'; 21 - import { irWebhookToAst } from './webhook'; 22 20 23 21 export const irSchemaToAst = ({ 24 - $ref, 25 22 optional, 26 23 plugin, 27 24 schema, 28 25 state, 29 - symbol, 30 26 }: IrSchemaToAstOptions & { 31 27 /** 32 - * When $ref is supplied, a node will be emitted to the file. 33 - */ 34 - $ref?: string; 35 - /** 36 28 * Accept `optional` to handle optional object properties. We can't handle 37 29 * this inside the object function because `.optional()` must come before 38 30 * `.default()` which is handled in this function. 39 31 */ 40 32 optional?: boolean; 41 33 schema: IR.SchemaObject; 42 - /** 43 - * When symbol is supplied, the AST node will be set as its value. 44 - */ 45 - symbol?: Symbol; 46 - }): Array<ts.Expression> => { 47 - if ($ref && !symbol) { 48 - const selector = plugin.api.selector('ref', $ref); 49 - symbol = plugin.getSymbol(selector) || plugin.referenceSymbol(selector); 50 - } 34 + }): Ast => { 35 + const ast: Ast = { 36 + pipes: [], 37 + }; 51 38 52 39 const v = plugin.referenceSymbol( 53 40 plugin.api.selector('external', 'valibot.v'), 54 41 ); 55 - let anyType: string | undefined; 56 - let pipes: Array<ts.Expression> = []; 57 42 58 43 if (schema.$ref) { 59 44 const selector = plugin.api.selector('ref', schema.$ref); 60 45 const refSymbol = plugin.referenceSymbol(selector); 61 46 if (plugin.isSymbolRegistered(selector)) { 62 47 const ref = tsc.identifier({ text: refSymbol.placeholder }); 63 - pipes.push(ref); 48 + ast.pipes.push(ref); 64 49 } else { 65 50 const lazyExpression = tsc.callExpression({ 66 51 functionName: tsc.propertyAccessExpression({ ··· 77 62 }), 78 63 ], 79 64 }); 80 - pipes.push(lazyExpression); 65 + ast.pipes.push(lazyExpression); 81 66 state.hasLazyExpression.value = true; 82 67 } 83 68 } else if (schema.type) { 84 - const ast = irSchemaWithTypeToAst({ 69 + const typeAst = irSchemaWithTypeToAst({ 85 70 plugin, 86 71 schema: schema as SchemaWithType, 87 72 state, 88 73 }); 89 - anyType = ast.anyType; 90 - pipes.push(ast.expression); 74 + ast.typeName = typeAst.anyType; 75 + ast.pipes.push(typeAst.expression); 91 76 92 77 if (plugin.config.metadata && schema.description) { 93 78 const expression = tsc.callExpression({ ··· 106 91 }), 107 92 ], 108 93 }); 109 - pipes.push(expression); 94 + ast.pipes.push(expression); 110 95 } 111 96 } else if (schema.items) { 112 97 schema = deduplicateSchema({ schema }); ··· 118 103 schema: item, 119 104 state: { 120 105 ...state, 121 - _path: toRef([...state._path.value, 'items', index]), 106 + path: toRef([...state.path.value, 'items', index]), 122 107 }, 123 108 }); 124 - return pipesToAst({ pipes: itemAst, plugin }); 109 + return pipesToAst({ pipes: itemAst.pipes, plugin }); 125 110 }); 126 111 127 112 if (schema.logicalOperator === 'and') { ··· 136 121 }), 137 122 ], 138 123 }); 139 - pipes.push(intersectExpression); 124 + ast.pipes.push(intersectExpression); 140 125 } else { 141 126 const unionExpression = tsc.callExpression({ 142 127 functionName: tsc.propertyAccessExpression({ ··· 149 134 }), 150 135 ], 151 136 }); 152 - pipes.push(unionExpression); 137 + ast.pipes.push(unionExpression); 153 138 } 154 139 } else { 155 140 const schemaPipes = irSchemaToAst({ plugin, schema, state }); 156 - pipes.push(...schemaPipes); 141 + ast.pipes.push(...schemaPipes.pipes); 157 142 } 158 143 } else { 159 144 // catch-all fallback for failed schemas 160 - const ast = irSchemaWithTypeToAst({ 145 + const typeAst = irSchemaWithTypeToAst({ 161 146 plugin, 162 147 schema: { 163 148 type: 'unknown', 164 149 }, 165 150 state, 166 151 }); 167 - anyType = ast.anyType; 168 - pipes.push(ast.expression); 152 + ast.typeName = typeAst.anyType; 153 + ast.pipes.push(typeAst.expression); 169 154 } 170 155 171 - if (pipes.length) { 156 + if (ast.pipes.length) { 172 157 if (schema.accessScope === 'read') { 173 158 const readonlyExpression = tsc.callExpression({ 174 159 functionName: tsc.propertyAccessExpression({ ··· 176 161 name: identifiers.actions.readonly, 177 162 }), 178 163 }); 179 - pipes.push(readonlyExpression); 164 + ast.pipes.push(readonlyExpression); 180 165 } 181 166 182 167 let callParameter: ts.Expression | undefined; ··· 185 170 const isBigInt = schema.type === 'integer' && schema.format === 'int64'; 186 171 callParameter = numberParameter({ isBigInt, value: schema.default }); 187 172 if (callParameter) { 188 - pipes = [ 173 + ast.pipes = [ 189 174 tsc.callExpression({ 190 175 functionName: tsc.propertyAccessExpression({ 191 176 expression: v.placeholder, 192 177 name: identifiers.schemas.optional, 193 178 }), 194 - parameters: [pipesToAst({ pipes, plugin }), callParameter], 179 + parameters: [ 180 + pipesToAst({ pipes: ast.pipes, plugin }), 181 + callParameter, 182 + ], 195 183 }), 196 184 ]; 197 185 } 198 186 } 199 187 200 188 if (optional && !callParameter) { 201 - pipes = [ 189 + ast.pipes = [ 202 190 tsc.callExpression({ 203 191 functionName: tsc.propertyAccessExpression({ 204 192 expression: v.placeholder, 205 193 name: identifiers.schemas.optional, 206 194 }), 207 - parameters: [pipesToAst({ pipes, plugin })], 195 + parameters: [pipesToAst({ pipes: ast.pipes, plugin })], 208 196 }), 209 197 ]; 210 198 } 211 199 } 212 200 213 - if (symbol) { 214 - if ($ref) { 215 - symbol = plugin.registerSymbol({ 216 - exported: true, 217 - meta: { 218 - resourceType: pathToSymbolResourceType(state._path.value), 219 - }, 220 - name: buildName({ 221 - config: { 222 - case: state.nameCase.value, 223 - name: state.nameTransformer.value, 224 - }, 225 - name: refToName($ref), 226 - }), 227 - selector: plugin.api.selector('ref', $ref), 228 - }); 229 - } 230 - const statement = tsc.constVariable({ 231 - comment: plugin.config.comments 232 - ? createSchemaComment({ schema }) 233 - : undefined, 234 - exportConst: symbol.exported, 235 - expression: pipesToAst({ pipes, plugin }), 236 - name: symbol.placeholder, 237 - typeName: state.hasLazyExpression.value 238 - ? (tsc.propertyAccessExpression({ 239 - expression: v.placeholder, 240 - name: anyType || identifiers.types.GenericSchema.text, 241 - }) as unknown as ts.TypeNode) 242 - : undefined, 243 - }); 244 - plugin.setSymbolValue(symbol, statement); 245 - return []; 246 - } 201 + return ast as Ast; 202 + }; 247 203 248 - 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 + }); 249 233 }; 250 234 251 235 export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { ··· 264 248 'webhook', 265 249 (event) => { 266 250 const state = toRefs<PluginState>({ 267 - _path: event._path, 268 251 hasLazyExpression: false, 269 - nameCase: plugin.config.definitions.case, 270 - nameTransformer: plugin.config.definitions.name, 252 + path: event._path, 253 + tags: event.tags, 271 254 }); 272 - 273 255 switch (event.type) { 274 256 case 'operation': 275 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 + }, 276 266 operation: event.operation, 277 267 plugin, 278 268 state, 279 269 }); 280 270 break; 281 271 case 'parameter': 282 - irSchemaToAst({ 283 - $ref: event.$ref, 272 + handleComponent({ 284 273 plugin, 285 274 schema: event.parameter.schema, 286 275 state, 287 276 }); 288 277 break; 289 278 case 'requestBody': 290 - irSchemaToAst({ 291 - $ref: event.$ref, 279 + handleComponent({ 292 280 plugin, 293 281 schema: event.requestBody.schema, 294 282 state, 295 283 }); 296 284 break; 297 285 case 'schema': 298 - irSchemaToAst({ 299 - $ref: event.$ref, 286 + handleComponent({ 300 287 plugin, 301 288 schema: event.schema, 302 289 state, ··· 304 291 break; 305 292 case 'webhook': 306 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 + }, 307 302 operation: event.operation, 308 303 plugin, 309 304 state,
+21 -18
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 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 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 ); ··· 26 28 name: identifiers.schemas.array, 27 29 }); 28 30 29 - const pipes: Array<ts.CallExpression> = []; 30 - 31 31 if (!schema.items) { 32 32 const expression = tsc.callExpression({ 33 33 functionName, ··· 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: { 54 54 ...state, 55 - _path: toRef([...state._path.value, 'items', index]), 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 };
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import type { IrSchemaToAstOptions } from '../../shared/types';
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { tsc } from '~/tsc'; 5 5 6 6 import type { IrSchemaToAstOptions } from '../../shared/types';
+23 -11
packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 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/never.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import type { IrSchemaToAstOptions } from '../../shared/types';
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import type { IrSchemaToAstOptions } from '../../shared/types';
+2 -2
packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { tsc } from '~/tsc'; 5 5 6 6 import { ··· 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,
+44 -42
packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts
··· 1 1 import ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { toRef } from '~/plugins/shared/utils/refs'; 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, 36 35 state: { 37 36 ...state, 38 - _path: toRef([...state._path.value, 'properties', name]), 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: { 82 84 ...state, 83 - _path: toRef([...state._path.value, 'additionalProperties']), 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 };
+2 -2
packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 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,
+48 -35
packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 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) { ··· 50 54 schema: item, 51 55 state: { 52 56 ...state, 53 - _path: toRef([...state._path.value, 'items', index]), 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, 65 + result.pipes = [ 66 + tsc.callExpression({ 67 + functionName: tsc.propertyAccessExpression({ 68 + expression: v.placeholder, 69 + name: identifiers.schemas.tuple, 70 + }), 71 + parameters: [ 72 + tsc.arrayLiteralExpression({ 73 + elements: tupleElements, 74 + }), 75 + ], 62 76 }), 63 - parameters: [ 64 - tsc.arrayLiteralExpression({ 65 - elements: tupleElements, 66 - }), 67 - ], 68 - }); 69 - return expression; 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 };
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import type { IrSchemaToAstOptions } from '../../shared/types';
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import type { IrSchemaToAstOptions } from '../../shared/types';
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import type { IrSchemaToAstOptions } from '../../shared/types';
+12 -5
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 - import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; 4 3 5 - import type { IrSchemaToAstOptions } from '../shared/types'; 6 - import { irSchemaToAst } from './plugin'; 4 + import { exportAst } from './export'; 5 + import type { Ast, IrSchemaToAstOptions } from './types'; 7 6 8 7 export const irWebhookToAst = ({ 8 + getAst, 9 9 operation, 10 10 plugin, 11 11 state, 12 12 }: IrSchemaToAstOptions & { 13 + getAst: ( 14 + schema: IR.SchemaObject, 15 + path: ReadonlyArray<string | number>, 16 + ) => Ast; 13 17 operation: IR.OperationObject; 14 18 }) => { 15 19 if (plugin.config.webhooks.enabled) { ··· 110 114 111 115 schemaData.required = [...requiredProperties]; 112 116 117 + const ast = getAst(schemaData, state.path.value); 113 118 const symbol = plugin.registerSymbol({ 114 119 exported: true, 115 120 meta: { 116 - resourceType: pathToSymbolResourceType(state._path.value), 121 + path: state.path.value, 122 + tags: state.tags?.value, 117 123 }, 118 124 name: buildName({ 119 125 config: plugin.config.webhooks, ··· 121 127 }), 122 128 selector: plugin.api.selector('webhook-request', operation.id), 123 129 }); 124 - irSchemaToAst({ 130 + exportAst({ 131 + ast, 125 132 plugin, 126 133 schema: schemaData, 127 134 state,
+1 -1
packages/openapi-ts/src/plugins/zod/api.ts
··· 1 1 import type { Selector } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 - import type { Plugin } from '~/plugins/types'; 4 + import type { Plugin } from '~/plugins'; 5 5 6 6 import { 7 7 createRequestValidatorMini,
+22 -32
packages/openapi-ts/src/plugins/zod/mini/plugin.ts
··· 1 1 import { deduplicateSchema } from '~/ir/schema'; 2 2 import type { IR } from '~/ir/types'; 3 3 import { buildName } from '~/openApi/shared/utils/name'; 4 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 5 - import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; 4 + import type { SchemaWithType } from '~/plugins'; 6 5 import { toRef, toRefs } from '~/plugins/shared/utils/refs'; 7 6 import { tsc } from '~/tsc'; 8 - import { refToName } from '~/utils/ref'; 7 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 9 8 10 9 import { identifiers } from '../constants'; 11 10 import { exportAst } from '../shared/export'; ··· 103 102 schema: item, 104 103 state: { 105 104 ...state, 106 - _path: toRef([...state._path.value, 'items', index]), 105 + path: toRef([...state.path.value, 'items', index]), 107 106 }, 108 107 }), 109 108 ); ··· 227 226 }; 228 227 229 228 const handleComponent = ({ 230 - $ref, 231 229 plugin, 232 230 schema, 233 231 state, 234 232 }: IrSchemaToAstOptions & { 235 - $ref: string; 236 233 schema: IR.SchemaObject; 237 234 }): void => { 235 + const $ref = pathToJsonPointer(state.path.value); 238 236 const ast = irSchemaToAst({ plugin, schema, state }); 239 237 const baseName = refToName($ref); 240 - const resourceType = pathToSymbolResourceType(state._path.value); 241 238 const symbol = plugin.registerSymbol({ 242 239 exported: true, 243 240 meta: { 244 - resourceType, 241 + path: state.path.value, 242 + tags: state.tags?.value, 245 243 }, 246 244 name: buildName({ 247 245 config: plugin.config.definitions, ··· 254 252 exported: true, 255 253 meta: { 256 254 kind: 'type', 257 - resourceType, 255 + path: state.path.value, 256 + tags: state.tags?.value, 258 257 }, 259 258 name: buildName({ 260 259 config: plugin.config.definitions.types.infer, ··· 287 286 'schema', 288 287 'webhook', 289 288 (event) => { 289 + const state = toRefs<PluginState>({ 290 + hasLazyExpression: false, 291 + path: event._path, 292 + tags: event.tags, 293 + }); 290 294 switch (event.type) { 291 295 case 'operation': 292 296 irOperationToAst({ 293 297 getAst: (schema, path) => { 294 298 const state = toRefs<PluginState>({ 295 - _path: path, 296 299 hasLazyExpression: false, 300 + path, 301 + tags: event.tags, 297 302 }); 298 303 return irSchemaToAst({ plugin, schema, state }); 299 304 }, 300 305 operation: event.operation, 301 306 plugin, 302 - state: toRefs({ 303 - _path: event._path, 304 - }), 307 + state, 305 308 }); 306 309 break; 307 310 case 'parameter': 308 311 handleComponent({ 309 - $ref: event.$ref, 310 312 plugin, 311 313 schema: event.parameter.schema, 312 - state: toRefs({ 313 - _path: event._path, 314 - hasLazyExpression: false, 315 - }), 314 + state, 316 315 }); 317 316 break; 318 317 case 'requestBody': 319 318 handleComponent({ 320 - $ref: event.$ref, 321 319 plugin, 322 320 schema: event.requestBody.schema, 323 - state: toRefs({ 324 - _path: event._path, 325 - hasLazyExpression: false, 326 - }), 321 + state, 327 322 }); 328 323 break; 329 324 case 'schema': 330 325 handleComponent({ 331 - $ref: event.$ref, 332 326 plugin, 333 327 schema: event.schema, 334 - state: toRefs({ 335 - _path: event._path, 336 - hasLazyExpression: false, 337 - }), 328 + state, 338 329 }); 339 330 break; 340 331 case 'webhook': 341 332 irWebhookToAst({ 342 333 getAst: (schema, path) => { 343 334 const state = toRefs<PluginState>({ 344 - _path: path, 345 335 hasLazyExpression: false, 336 + path, 337 + tags: event.tags, 346 338 }); 347 339 return irSchemaToAst({ plugin, schema, state }); 348 340 }, 349 341 operation: event.operation, 350 342 plugin, 351 - state: toRefs({ 352 - _path: event._path, 353 - }), 343 + state, 354 344 }); 355 345 break; 356 346 }
+2 -2
packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 3 import { deduplicateSchema } from '~/ir/schema'; 4 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 4 + import type { SchemaWithType } from '~/plugins'; 5 5 import { toRef } from '~/plugins/shared/utils/refs'; 6 6 import { tsc } from '~/tsc'; 7 7 ··· 49 49 schema: item, 50 50 state: { 51 51 ...state, 52 - _path: toRef([...state._path.value, 'items', index]), 52 + path: toRef([...state.path.value, 'items', index]), 53 53 }, 54 54 }); 55 55 if (itemAst.hasLazyExpression) {
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { tsc } from '~/tsc'; 5 5 6 6 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/index.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 3 3 import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; 4 4 import { arrayToAst } from './array';
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { tsc } from '~/tsc'; 5 5 6 6 import { identifiers } from '../../constants';
+3 -3
packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts
··· 1 1 import ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { toRef } from '~/plugins/shared/utils/refs'; 5 5 import { tsc } from '~/tsc'; 6 6 import { numberRegExp } from '~/utils/regexp'; ··· 36 36 schema: property, 37 37 state: { 38 38 ...state, 39 - _path: toRef([...state._path.value, 'properties', name]), 39 + path: toRef([...state.path.value, 'properties', name]), 40 40 }, 41 41 }); 42 42 if (propertyAst.hasLazyExpression) { ··· 93 93 schema: schema.additionalProperties, 94 94 state: { 95 95 ...state, 96 - _path: toRef([...state._path.value, 'additionalProperties']), 96 + path: toRef([...state.path.value, 'additionalProperties']), 97 97 }, 98 98 }); 99 99 result.expression = tsc.callExpression({
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { tsc } from '~/tsc'; 5 5 6 6 import { identifiers } from '../../constants';
+2 -2
packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { toRef } from '~/plugins/shared/utils/refs'; 5 5 import { tsc } from '~/tsc'; 6 6 ··· 52 52 schema: item, 53 53 state: { 54 54 ...state, 55 - _path: toRef([...state._path.value, 'items', index]), 55 + path: toRef([...state.path.value, 'items', index]), 56 56 }, 57 57 }); 58 58 tupleElements.push(itemSchema.expression);
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+11 -12
packages/openapi-ts/src/plugins/zod/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 { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; 5 4 6 5 import { exportAst } from './export'; 7 6 import type { Ast, IrSchemaToAstOptions } from './types'; ··· 11 10 operation, 12 11 plugin, 13 12 state, 14 - }: Omit<IrSchemaToAstOptions, 'state'> & { 13 + }: IrSchemaToAstOptions & { 15 14 getAst: ( 16 15 schema: IR.SchemaObject, 17 16 path: ReadonlyArray<string | number>, 18 17 ) => Ast; 19 18 operation: IR.OperationObject; 20 - state: Partial<IrSchemaToAstOptions['state']>; 21 19 }): void => { 22 20 if (plugin.config.requests.enabled) { 23 21 const requiredProperties = new Set<string>(); ··· 117 115 118 116 schemaData.required = [...requiredProperties]; 119 117 120 - const path = state._path?.value || []; 121 - const ast = getAst(schemaData, path); 122 - const resourceType = pathToSymbolResourceType(path); 118 + const ast = getAst(schemaData, state.path.value); 123 119 const symbol = plugin.registerSymbol({ 124 120 exported: true, 125 121 meta: { 126 - resourceType, 122 + path: state.path.value, 123 + tags: state.tags?.value, 127 124 }, 128 125 name: buildName({ 129 126 config: plugin.config.requests, ··· 136 133 exported: true, 137 134 meta: { 138 135 kind: 'type', 139 - resourceType, 136 + path: state.path.value, 137 + tags: state.tags?.value, 140 138 }, 141 139 name: buildName({ 142 140 config: plugin.config.requests.types.infer, ··· 159 157 const { response } = operationResponsesMap(operation); 160 158 161 159 if (response) { 162 - const path = [...(state._path?.value || []), 'responses']; 160 + const path = [...state.path.value, 'responses']; 163 161 const ast = getAst(response, path); 164 - const resourceType = pathToSymbolResourceType(path); 165 162 const symbol = plugin.registerSymbol({ 166 163 exported: true, 167 164 meta: { 168 - resourceType, 165 + path, 166 + tags: state.tags?.value, 169 167 }, 170 168 name: buildName({ 171 169 config: plugin.config.responses, ··· 178 176 exported: true, 179 177 meta: { 180 178 kind: 'type', 181 - resourceType, 179 + path, 180 + tags: state.tags?.value, 182 181 }, 183 182 name: buildName({ 184 183 config: plugin.config.responses.types.infer,
+6 -8
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'; 4 - import type { ToRefs } from '~/plugins/shared/types/refs'; 5 + import type { ToRefs } from '~/plugins'; 5 6 6 7 import type { ZodPlugin } from '../types'; 7 8 ··· 16 17 state: ToRefs<PluginState>; 17 18 }; 18 19 19 - export type PluginState = { 20 - /** 21 - * Path to the schema in the intermediary representation. 22 - */ 23 - _path: ReadonlyArray<string | number>; 24 - hasLazyExpression: boolean; 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 -8
packages/openapi-ts/src/plugins/zod/shared/webhook.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import { buildName } from '~/openApi/shared/utils/name'; 3 - import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; 4 3 5 4 import { exportAst } from './export'; 6 5 import type { Ast, IrSchemaToAstOptions } from './types'; ··· 10 9 operation, 11 10 plugin, 12 11 state, 13 - }: Omit<IrSchemaToAstOptions, 'state'> & { 12 + }: IrSchemaToAstOptions & { 14 13 getAst: ( 15 14 schema: IR.SchemaObject, 16 15 path: ReadonlyArray<string | number>, 17 16 ) => Ast; 18 17 operation: IR.OperationObject; 19 - state: Partial<IrSchemaToAstOptions['state']>; 20 18 }) => { 21 19 if (plugin.config.webhooks.enabled) { 22 20 const requiredProperties = new Set<string>(); ··· 116 114 117 115 schemaData.required = [...requiredProperties]; 118 116 119 - const path = state._path?.value || []; 120 - const ast = getAst(schemaData, path); 121 - const resourceType = pathToSymbolResourceType(path); 117 + const ast = getAst(schemaData, state.path.value); 122 118 const symbol = plugin.registerSymbol({ 123 119 exported: true, 124 120 meta: { 125 - resourceType, 121 + path: state.path.value, 122 + tags: state.tags?.value, 126 123 }, 127 124 name: buildName({ 128 125 config: plugin.config.webhooks, ··· 135 132 exported: true, 136 133 meta: { 137 134 kind: 'type', 138 - resourceType, 135 + path: state.path.value, 136 + tags: state.tags?.value, 139 137 }, 140 138 name: buildName({ 141 139 config: plugin.config.webhooks.types.infer,
+1 -1
packages/openapi-ts/src/plugins/zod/types.d.ts
··· 1 - import type { DefinePlugin, Plugin } from '~/plugins/types'; 1 + import type { DefinePlugin, Plugin } from '~/plugins'; 2 2 import type { StringCase, StringName } from '~/types/case'; 3 3 4 4 import type { IApi } from './api';
+22 -32
packages/openapi-ts/src/plugins/zod/v3/plugin.ts
··· 1 1 import { deduplicateSchema } from '~/ir/schema'; 2 2 import type { IR } from '~/ir/types'; 3 3 import { buildName } from '~/openApi/shared/utils/name'; 4 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 5 - import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; 4 + import type { SchemaWithType } from '~/plugins'; 6 5 import { toRef, toRefs } from '~/plugins/shared/utils/refs'; 7 6 import { tsc } from '~/tsc'; 8 - import { refToName } from '~/utils/ref'; 7 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 9 8 10 9 import { identifiers } from '../constants'; 11 10 import { exportAst } from '../shared/export'; ··· 89 88 schema: item, 90 89 state: { 91 90 ...state, 92 - _path: toRef([...state._path.value, 'items', index]), 91 + path: toRef([...state.path.value, 'items', index]), 93 92 }, 94 93 }); 95 94 return typeAst.expression; ··· 201 200 }; 202 201 203 202 const handleComponent = ({ 204 - $ref, 205 203 plugin, 206 204 schema, 207 205 state, 208 206 }: IrSchemaToAstOptions & { 209 - $ref: string; 210 207 schema: IR.SchemaObject; 211 208 }): void => { 209 + const $ref = pathToJsonPointer(state.path.value); 212 210 const ast = irSchemaToAst({ plugin, schema, state }); 213 211 const baseName = refToName($ref); 214 - const resourceType = pathToSymbolResourceType(state._path.value); 215 212 const symbol = plugin.registerSymbol({ 216 213 exported: true, 217 214 meta: { 218 - resourceType, 215 + path: state.path.value, 216 + tags: state.tags?.value, 219 217 }, 220 218 name: buildName({ 221 219 config: plugin.config.definitions, ··· 228 226 exported: true, 229 227 meta: { 230 228 kind: 'type', 231 - resourceType, 229 + path: state.path.value, 230 + tags: state.tags?.value, 232 231 }, 233 232 name: buildName({ 234 233 config: plugin.config.definitions.types.infer, ··· 260 259 'schema', 261 260 'webhook', 262 261 (event) => { 262 + const state = toRefs<PluginState>({ 263 + hasLazyExpression: false, 264 + path: event._path, 265 + tags: event.tags, 266 + }); 263 267 switch (event.type) { 264 268 case 'operation': 265 269 irOperationToAst({ 266 270 getAst: (schema, path) => { 267 271 const state = toRefs<PluginState>({ 268 - _path: path, 269 272 hasLazyExpression: false, 273 + path, 274 + tags: event.tags, 270 275 }); 271 276 return irSchemaToAst({ plugin, schema, state }); 272 277 }, 273 278 operation: event.operation, 274 279 plugin, 275 - state: toRefs({ 276 - _path: event._path, 277 - }), 280 + state, 278 281 }); 279 282 break; 280 283 case 'parameter': 281 284 handleComponent({ 282 - $ref: event.$ref, 283 285 plugin, 284 286 schema: event.parameter.schema, 285 - state: toRefs({ 286 - _path: event._path, 287 - hasLazyExpression: false, 288 - }), 287 + state, 289 288 }); 290 289 break; 291 290 case 'requestBody': 292 291 handleComponent({ 293 - $ref: event.$ref, 294 292 plugin, 295 293 schema: event.requestBody.schema, 296 - state: toRefs({ 297 - _path: event._path, 298 - hasLazyExpression: false, 299 - }), 294 + state, 300 295 }); 301 296 break; 302 297 case 'schema': 303 298 handleComponent({ 304 - $ref: event.$ref, 305 299 plugin, 306 300 schema: event.schema, 307 - state: toRefs({ 308 - _path: event._path, 309 - hasLazyExpression: false, 310 - }), 301 + state, 311 302 }); 312 303 break; 313 304 case 'webhook': 314 305 irWebhookToAst({ 315 306 getAst: (schema, path) => { 316 307 const state = toRefs<PluginState>({ 317 - _path: path, 318 308 hasLazyExpression: false, 309 + path, 310 + tags: event.tags, 319 311 }); 320 312 return irSchemaToAst({ plugin, schema, state }); 321 313 }, 322 314 operation: event.operation, 323 315 plugin, 324 - state: toRefs({ 325 - _path: event._path, 326 - }), 316 + state, 327 317 }); 328 318 break; 329 319 }
+2 -2
packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 3 import { deduplicateSchema } from '~/ir/schema'; 4 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 4 + import type { SchemaWithType } from '~/plugins'; 5 5 import { toRef } from '~/plugins/shared/utils/refs'; 6 6 import { tsc } from '~/tsc'; 7 7 ··· 52 52 schema: item, 53 53 state: { 54 54 ...state, 55 - _path: toRef([...state._path.value, 'items', index]), 55 + path: toRef([...state.path.value, 'items', index]), 56 56 }, 57 57 }); 58 58 if (itemAst.hasLazyExpression) {
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { tsc } from '~/tsc'; 5 5 6 6 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/index.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 3 3 import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; 4 4 import { arrayToAst } from './array';
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+3 -3
packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts
··· 1 1 import ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { toRef } from '~/plugins/shared/utils/refs'; 5 5 import { tsc } from '~/tsc'; 6 6 import { numberRegExp } from '~/utils/regexp'; ··· 37 37 schema: property, 38 38 state: { 39 39 ...state, 40 - _path: toRef([...state._path.value, 'properties', name]), 40 + path: toRef([...state.path.value, 'properties', name]), 41 41 }, 42 42 }); 43 43 ··· 81 81 schema: schema.additionalProperties, 82 82 state: { 83 83 ...state, 84 - _path: toRef([...state._path.value, 'additionalProperties']), 84 + path: toRef([...state.path.value, 'additionalProperties']), 85 85 }, 86 86 }); 87 87 const expression = tsc.callExpression({
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+2 -2
packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { toRef } from '~/plugins/shared/utils/refs'; 5 5 import { tsc } from '~/tsc'; 6 6 ··· 57 57 schema: item, 58 58 state: { 59 59 ...state, 60 - _path: toRef([...state._path.value, 'items', index]), 60 + path: toRef([...state.path.value, 'items', index]), 61 61 }, 62 62 }); 63 63 tupleElements.push(itemSchema.expression);
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+22 -32
packages/openapi-ts/src/plugins/zod/v4/plugin.ts
··· 1 1 import { deduplicateSchema } from '~/ir/schema'; 2 2 import type { IR } from '~/ir/types'; 3 3 import { buildName } from '~/openApi/shared/utils/name'; 4 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 5 - import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; 4 + import type { SchemaWithType } from '~/plugins'; 6 5 import { toRef, toRefs } from '~/plugins/shared/utils/refs'; 7 6 import { tsc } from '~/tsc'; 8 - import { refToName } from '~/utils/ref'; 7 + import { pathToJsonPointer, refToName } from '~/utils/ref'; 9 8 10 9 import { identifiers } from '../constants'; 11 10 import { exportAst } from '../shared/export'; ··· 103 102 schema: item, 104 103 state: { 105 104 ...state, 106 - _path: toRef([...state._path.value, 'items', index]), 105 + path: toRef([...state.path.value, 'items', index]), 107 106 }, 108 107 }), 109 108 ); ··· 229 228 }; 230 229 231 230 const handleComponent = ({ 232 - $ref, 233 231 plugin, 234 232 schema, 235 233 state, 236 234 }: IrSchemaToAstOptions & { 237 - $ref: string; 238 235 schema: IR.SchemaObject; 239 236 }): void => { 237 + const $ref = pathToJsonPointer(state.path.value); 240 238 const ast = irSchemaToAst({ plugin, schema, state }); 241 239 const baseName = refToName($ref); 242 - const resourceType = pathToSymbolResourceType(state._path.value); 243 240 const symbol = plugin.registerSymbol({ 244 241 exported: true, 245 242 meta: { 246 - resourceType, 243 + path: state.path.value, 244 + tags: state.tags?.value, 247 245 }, 248 246 name: buildName({ 249 247 config: plugin.config.definitions, ··· 256 254 exported: true, 257 255 meta: { 258 256 kind: 'type', 259 - resourceType, 257 + path: state.path.value, 258 + tags: state.tags?.value, 260 259 }, 261 260 name: buildName({ 262 261 config: plugin.config.definitions.types.infer, ··· 288 287 'schema', 289 288 'webhook', 290 289 (event) => { 290 + const state = toRefs<PluginState>({ 291 + hasLazyExpression: false, 292 + path: event._path, 293 + tags: event.tags, 294 + }); 291 295 switch (event.type) { 292 296 case 'operation': 293 297 irOperationToAst({ 294 298 getAst: (schema, path) => { 295 299 const state = toRefs<PluginState>({ 296 - _path: path, 297 300 hasLazyExpression: false, 301 + path, 302 + tags: event.tags, 298 303 }); 299 304 return irSchemaToAst({ plugin, schema, state }); 300 305 }, 301 306 operation: event.operation, 302 307 plugin, 303 - state: toRefs({ 304 - _path: event._path, 305 - }), 308 + state, 306 309 }); 307 310 break; 308 311 case 'parameter': 309 312 handleComponent({ 310 - $ref: event.$ref, 311 313 plugin, 312 314 schema: event.parameter.schema, 313 - state: toRefs({ 314 - _path: event._path, 315 - hasLazyExpression: false, 316 - }), 315 + state, 317 316 }); 318 317 break; 319 318 case 'requestBody': 320 319 handleComponent({ 321 - $ref: event.$ref, 322 320 plugin, 323 321 schema: event.requestBody.schema, 324 - state: toRefs({ 325 - _path: event._path, 326 - hasLazyExpression: false, 327 - }), 322 + state, 328 323 }); 329 324 break; 330 325 case 'schema': 331 326 handleComponent({ 332 - $ref: event.$ref, 333 327 plugin, 334 328 schema: event.schema, 335 - state: toRefs({ 336 - _path: event._path, 337 - hasLazyExpression: false, 338 - }), 329 + state, 339 330 }); 340 331 break; 341 332 case 'webhook': 342 333 irWebhookToAst({ 343 334 getAst: (schema, path) => { 344 335 const state = toRefs<PluginState>({ 345 - _path: path, 346 336 hasLazyExpression: false, 337 + path, 338 + tags: event.tags, 347 339 }); 348 340 return irSchemaToAst({ plugin, schema, state }); 349 341 }, 350 342 operation: event.operation, 351 343 plugin, 352 - state: toRefs({ 353 - _path: event._path, 354 - }), 344 + state, 355 345 }); 356 346 break; 357 347 }
+2 -2
packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 3 import { deduplicateSchema } from '~/ir/schema'; 4 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 4 + import type { SchemaWithType } from '~/plugins'; 5 5 import { toRef } from '~/plugins/shared/utils/refs'; 6 6 import { tsc } from '~/tsc'; 7 7 ··· 49 49 schema: item, 50 50 state: { 51 51 ...state, 52 - _path: toRef([...state._path.value, 'items', index]), 52 + path: toRef([...state.path.value, 'items', index]), 53 53 }, 54 54 }); 55 55 if (itemAst.hasLazyExpression) {
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { tsc } from '~/tsc'; 5 5 6 6 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/index.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 3 3 import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; 4 4 import { arrayToAst } from './array';
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+3 -3
packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts
··· 1 1 import ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { toRef } from '~/plugins/shared/utils/refs'; 5 5 import { tsc } from '~/tsc'; 6 6 import { numberRegExp } from '~/utils/regexp'; ··· 36 36 schema: property, 37 37 state: { 38 38 ...state, 39 - _path: toRef([...state._path.value, 'properties', name]), 39 + path: toRef([...state.path.value, 'properties', name]), 40 40 }, 41 41 }); 42 42 if (propertyAst.hasLazyExpression) { ··· 93 93 schema: schema.additionalProperties, 94 94 state: { 95 95 ...state, 96 - _path: toRef([...state._path.value, 'additionalProperties']), 96 + path: toRef([...state.path.value, 'additionalProperties']), 97 97 }, 98 98 }); 99 99 result.expression = tsc.callExpression({
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+2 -2
packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 3 + import type { SchemaWithType } from '~/plugins'; 4 4 import { toRef } from '~/plugins/shared/utils/refs'; 5 5 import { tsc } from '~/tsc'; 6 6 ··· 52 52 schema: item, 53 53 state: { 54 54 ...state, 55 - _path: toRef([...state._path.value, 'items', index]), 55 + path: toRef([...state.path.value, 'items', index]), 56 56 }, 57 57 }); 58 58 tupleElements.push(itemSchema.expression);
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts
··· 1 - import type { SchemaWithType } from '~/plugins/shared/types/schema'; 1 + import type { SchemaWithType } from '~/plugins'; 2 2 import { tsc } from '~/tsc'; 3 3 4 4 import { identifiers } from '../../constants';
+2 -1
packages/openapi-ts/src/types/config.d.ts
··· 1 + import type { Plugin } from '~/plugins'; 1 2 import type { PluginConfigMap } from '~/plugins/config'; 2 - import type { Plugin, PluginNames } from '~/plugins/types'; 3 + import type { PluginNames } from '~/plugins/types'; 3 4 4 5 import type { Input, UserInput, Watch } from './input'; 5 6 import type { Logs } from './logs';
-2
packages/openapi-ts/src/utils/__tests__/handlebars.test.ts
··· 93 93 }, 94 94 '@hey-api/sdk': { 95 95 api: { 96 - createOperationComment: () => undefined, 97 96 selector: () => [], 98 97 }, 99 98 config: { ··· 224 223 }, 225 224 '@hey-api/sdk': { 226 225 api: { 227 - createOperationComment: () => undefined, 228 226 selector: () => [], 229 227 }, 230 228 config: {
-5
packages/openapi-ts/src/utils/__tests__/parse.test.ts
··· 72 72 plugins: { 73 73 '@hey-api/sdk': { 74 74 api: { 75 - createOperationComment: () => undefined, 76 75 selector: () => [], 77 76 }, 78 77 config: { ··· 103 102 ...optionsCommon.plugins, 104 103 '@hey-api/sdk': { 105 104 api: { 106 - createOperationComment: () => undefined, 107 105 selector: () => [], 108 106 }, 109 107 config: { ··· 124 122 ...optionsCommon.plugins, 125 123 '@hey-api/sdk': { 126 124 api: { 127 - createOperationComment: () => undefined, 128 125 selector: () => [], 129 126 }, 130 127 config: { ··· 157 154 }, 158 155 '@hey-api/sdk': { 159 156 api: { 160 - createOperationComment: () => undefined, 161 157 selector: () => [], 162 158 }, 163 159 config: { ··· 190 186 }, 191 187 '@hey-api/sdk': { 192 188 api: { 193 - createOperationComment: () => undefined, 194 189 selector: () => [], 195 190 }, 196 191 config: {
+24 -23
pnpm-lock.yaml
··· 14 14 .: 15 15 devDependencies: 16 16 '@arethetypeswrong/cli': 17 - specifier: 0.17.4 18 - version: 0.17.4 17 + specifier: 0.18.2 18 + version: 0.18.2 19 19 '@changesets/cli': 20 20 specifier: 2.29.7 21 21 version: 2.29.7(@types/node@22.10.5) ··· 90 90 version: 6.1.1(rollup@4.31.0)(typescript@5.9.3) 91 91 tsdown: 92 92 specifier: 0.15.8 93 - version: 0.15.8(typescript@5.9.3) 93 + version: 0.15.8(@arethetypeswrong/core@0.18.2)(typescript@5.9.3) 94 94 turbo: 95 95 specifier: 2.5.8 96 96 version: 2.5.8 ··· 1314 1314 specifier: workspace:* 1315 1315 version: link:../config-vite-base 1316 1316 '@types/bun': 1317 - specifier: 1.2.23 1318 - version: 1.2.23(@types/react@19.0.1) 1317 + specifier: 1.3.0 1318 + version: 1.3.0(@types/react@19.0.1) 1319 1319 '@types/cross-spawn': 1320 1320 specifier: 6.0.6 1321 1321 version: 6.0.6 ··· 2121 2121 '@antfu/utils@0.7.10': 2122 2122 resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} 2123 2123 2124 - '@arethetypeswrong/cli@0.17.4': 2125 - resolution: {integrity: sha512-AeiKxtf67XD/NdOqXgBOE5TZWH3EOCt+0GkbUpekOzngc+Q/cRZ5azjWyMxISxxfp0EItgm5NoSld9p7BAA5xQ==} 2126 - engines: {node: '>=18'} 2124 + '@arethetypeswrong/cli@0.18.2': 2125 + resolution: {integrity: sha512-PcFM20JNlevEDKBg4Re29Rtv2xvjvQZzg7ENnrWFSS0PHgdP2njibVFw+dRUhNkPgNfac9iUqO0ohAXqQL4hbw==} 2126 + engines: {node: '>=20'} 2127 2127 hasBin: true 2128 2128 2129 - '@arethetypeswrong/core@0.17.4': 2130 - resolution: {integrity: sha512-Izvir8iIoU+X4SKtDAa5kpb+9cpifclzsbA8x/AZY0k0gIfXYQ1fa1B6Epfe6vNA2YfDX8VtrZFgvnXB6aPEoQ==} 2131 - engines: {node: '>=18'} 2129 + '@arethetypeswrong/core@0.18.2': 2130 + resolution: {integrity: sha512-GiwTmBFOU1/+UVNqqCGzFJYfBXEytUkiI+iRZ6Qx7KmUVtLm00sYySkfe203C9QtPG11yOz1ZaMek8dT/xnlgg==} 2131 + engines: {node: '>=20'} 2132 2132 2133 2133 '@ark/regex@0.0.0': 2134 2134 resolution: {integrity: sha512-p4vsWnd/LRGOdGQglbwOguIVhPmCAf5UzquvnDoxqhhPWTP84wWgi1INea8MgJ4SnI2gp37f13oA4Waz9vwNYg==} ··· 6304 6304 '@types/bonjour@3.5.13': 6305 6305 resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} 6306 6306 6307 - '@types/bun@1.2.23': 6308 - resolution: {integrity: sha512-le8ueOY5b6VKYf19xT3McVbXqLqmxzPXHsQT/q9JHgikJ2X22wyTW3g3ohz2ZMnp7dod6aduIiq8A14Xyimm0A==} 6307 + '@types/bun@1.3.0': 6308 + resolution: {integrity: sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA==} 6309 6309 6310 6310 '@types/chai@5.2.2': 6311 6311 resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} ··· 7522 7522 resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} 7523 7523 engines: {node: '>=6'} 7524 7524 7525 - bun-types@1.2.23: 7526 - resolution: {integrity: sha512-R9f0hKAZXgFU3mlrA0YpE/fiDvwV0FT9rORApt2aQVWSuJDzZOyB5QLc0N/4HF57CS8IXJ6+L5E4W1bW6NS2Aw==} 7525 + bun-types@1.3.0: 7526 + resolution: {integrity: sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ==} 7527 7527 peerDependencies: 7528 7528 '@types/react': ^19 7529 7529 ··· 15200 15200 15201 15201 '@antfu/utils@0.7.10': {} 15202 15202 15203 - '@arethetypeswrong/cli@0.17.4': 15203 + '@arethetypeswrong/cli@0.18.2': 15204 15204 dependencies: 15205 - '@arethetypeswrong/core': 0.17.4 15205 + '@arethetypeswrong/core': 0.18.2 15206 15206 chalk: 4.1.2 15207 15207 cli-table3: 0.6.5 15208 15208 commander: 10.0.1 ··· 15210 15210 marked-terminal: 7.3.0(marked@9.1.6) 15211 15211 semver: 7.7.2 15212 15212 15213 - '@arethetypeswrong/core@0.17.4': 15213 + '@arethetypeswrong/core@0.18.2': 15214 15214 dependencies: 15215 15215 '@andrewbranch/untar.js': 1.0.3 15216 15216 '@loaderkit/resolve': 1.0.4 15217 15217 cjs-module-lexer: 1.4.3 15218 15218 fflate: 0.8.2 15219 - lru-cache: 10.4.3 15219 + lru-cache: 11.2.2 15220 15220 semver: 7.7.2 15221 15221 typescript: 5.6.1-rc 15222 15222 validate-npm-package-name: 5.0.1 ··· 20211 20211 dependencies: 20212 20212 '@types/node': 22.10.5 20213 20213 20214 - '@types/bun@1.2.23(@types/react@19.0.1)': 20214 + '@types/bun@1.3.0(@types/react@19.0.1)': 20215 20215 dependencies: 20216 - bun-types: 1.2.23(@types/react@19.0.1) 20216 + bun-types: 1.3.0(@types/react@19.0.1) 20217 20217 transitivePeerDependencies: 20218 20218 - '@types/react' 20219 20219 ··· 21931 21931 21932 21932 builtin-modules@3.3.0: {} 21933 21933 21934 - bun-types@1.2.23(@types/react@19.0.1): 21934 + bun-types@1.3.0(@types/react@19.0.1): 21935 21935 dependencies: 21936 21936 '@types/node': 22.10.5 21937 21937 '@types/react': 19.0.1 ··· 28850 28850 minimist: 1.2.8 28851 28851 strip-bom: 3.0.0 28852 28852 28853 - tsdown@0.15.8(typescript@5.9.3): 28853 + tsdown@0.15.8(@arethetypeswrong/core@0.18.2)(typescript@5.9.3): 28854 28854 dependencies: 28855 28855 ansis: 4.2.0 28856 28856 cac: 6.7.14 ··· 28867 28867 tree-kill: 1.2.2 28868 28868 unconfig: 7.3.3 28869 28869 optionalDependencies: 28870 + '@arethetypeswrong/core': 0.18.2 28870 28871 typescript: 5.9.3 28871 28872 transitivePeerDependencies: 28872 28873 - '@ts-macro/tsc'