From 47fde14cf3dd00914b20cef8114d7170458f21f5 Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:09:47 +0800 Subject: [PATCH] Update --- web/plugins/hey-api-orpc/config.ts | 10 ++++ web/plugins/hey-api-orpc/index.ts | 2 + web/plugins/hey-api-orpc/plugin.ts | 84 +++++++++++++++++++----------- web/plugins/hey-api-orpc/types.ts | 23 +++++++- 4 files changed, 88 insertions(+), 31 deletions(-) create mode 100644 web/plugins/hey-api-orpc/index.ts diff --git a/web/plugins/hey-api-orpc/config.ts b/web/plugins/hey-api-orpc/config.ts index 051a3cf863..608522fb33 100644 --- a/web/plugins/hey-api-orpc/config.ts +++ b/web/plugins/hey-api-orpc/config.ts @@ -11,6 +11,16 @@ export const defaultConfig: OrpcPlugin['Config'] = { dependencies: ['@hey-api/typescript', 'zod'], handler, name: 'orpc', + resolveConfig: (plugin) => { + plugin.config.output = plugin.config.output ?? 'orpc' + plugin.config.exportFromIndex = plugin.config.exportFromIndex ?? false + plugin.config.groupBy = plugin.config.groupBy ?? 'tag' + plugin.config.contractNameBuilder = plugin.config.contractNameBuilder + ?? ((id: string) => `${id}Contract`) + }, } +/** + * Type helper for oRPC plugin, returns {@link Plugin.Config} object + */ export const defineConfig = definePluginConfig(defaultConfig) diff --git a/web/plugins/hey-api-orpc/index.ts b/web/plugins/hey-api-orpc/index.ts new file mode 100644 index 0000000000..f2f65bcd22 --- /dev/null +++ b/web/plugins/hey-api-orpc/index.ts @@ -0,0 +1,2 @@ +export { defaultConfig, defineConfig } from './config' +export type { Config, OrpcPlugin, ResolvedConfig } from './types' diff --git a/web/plugins/hey-api-orpc/plugin.ts b/web/plugins/hey-api-orpc/plugin.ts index 6cbc5f7119..32e0c299ce 100644 --- a/web/plugins/hey-api-orpc/plugin.ts +++ b/web/plugins/hey-api-orpc/plugin.ts @@ -30,8 +30,9 @@ function collectOperation(operation: IR.OperationObject): OperationInfo { const hasPathParams = Boolean(operation.parameters?.path && Object.keys(operation.parameters.path).length > 0) const hasQueryParams = Boolean(operation.parameters?.query && Object.keys(operation.parameters.query).length > 0) + const hasHeaderParams = Boolean(operation.parameters?.header && Object.keys(operation.parameters.header).length > 0) const hasBody = Boolean(operation.body) - const hasInput = hasPathParams || hasQueryParams || hasBody + const hasInput = hasPathParams || hasQueryParams || hasHeaderParams || hasBody // Check if operation has a successful response with actual content // Look for 2xx responses that have a schema with mediaType (indicating response body) @@ -61,6 +62,7 @@ function collectOperation(operation: IR.OperationObject): OperationInfo { } export const handler: OrpcPlugin['Handler'] = ({ plugin }) => { + const { contractNameBuilder, groupBy } = plugin.config const operations: OperationInfo[] = [] const zodImports = new Set() @@ -118,10 +120,13 @@ export const handler: OrpcPlugin['Handler'] = ({ plugin }) => { const contractSymbols: Record> = {} for (const op of operations) { - const contractSymbol = plugin.symbol(`${op.id}Contract`, { + const contractName = contractNameBuilder(op.id) + const contractSymbol = plugin.symbol(contractName, { exported: true, meta: { category: 'schema', + resource: 'operation', + resourceId: op.id, }, }) contractSymbols[op.id] = contractSymbol @@ -167,30 +172,6 @@ export const handler: OrpcPlugin['Handler'] = ({ plugin }) => { } // Create contracts object export - // Group operations by tag - const operationsByTag = new Map() - for (const op of operations) { - const tag = op.tags[0] - if (!operationsByTag.has(tag)) { - operationsByTag.set(tag, []) - } - operationsByTag.get(tag)!.push(op) - } - - // Build contracts object - const contractsObject = $.object() - for (const [tag, tagOps] of operationsByTag) { - const tagKey = tag.charAt(0).toLowerCase() + tag.slice(1) - const tagObject = $.object() - for (const op of tagOps) { - const contractSymbol = contractSymbols[op.id] - if (contractSymbol) { - tagObject.prop(`${op.id}Contract`, $(contractSymbol)) - } - } - contractsObject.prop(tagKey, tagObject) - } - const contractsSymbol = plugin.symbol('contracts', { exported: true, meta: { @@ -198,10 +179,53 @@ export const handler: OrpcPlugin['Handler'] = ({ plugin }) => { }, }) - const contractsNode = $.const(contractsSymbol) - .export() - .assign(contractsObject) - plugin.node(contractsNode) + if (groupBy === 'tag') { + // Group operations by tag + const operationsByTag = new Map() + for (const op of operations) { + const tag = op.tags[0] + if (!operationsByTag.has(tag)) { + operationsByTag.set(tag, []) + } + operationsByTag.get(tag)!.push(op) + } + + // Build contracts object grouped by tag + const contractsObject = $.object() + for (const [tag, tagOps] of operationsByTag) { + const tagKey = tag.charAt(0).toLowerCase() + tag.slice(1) + const tagObject = $.object() + for (const op of tagOps) { + const contractSymbol = contractSymbols[op.id] + if (contractSymbol) { + const contractName = contractNameBuilder(op.id) + tagObject.prop(contractName, $(contractSymbol)) + } + } + contractsObject.prop(tagKey, tagObject) + } + + const contractsNode = $.const(contractsSymbol) + .export() + .assign(contractsObject) + plugin.node(contractsNode) + } + else { + // Flat structure without grouping + const contractsObject = $.object() + for (const op of operations) { + const contractSymbol = contractSymbols[op.id] + if (contractSymbol) { + const contractName = contractNameBuilder(op.id) + contractsObject.prop(contractName, $(contractSymbol)) + } + } + + const contractsNode = $.const(contractsSymbol) + .export() + .assign(contractsObject) + plugin.node(contractsNode) + } // Create type export: export type Contracts = typeof contracts const contractsTypeSymbol = plugin.symbol('Contracts', { diff --git a/web/plugins/hey-api-orpc/types.ts b/web/plugins/hey-api-orpc/types.ts index 914c19fc94..75460380c7 100644 --- a/web/plugins/hey-api-orpc/types.ts +++ b/web/plugins/hey-api-orpc/types.ts @@ -6,10 +6,31 @@ export type Config = { name: 'orpc' } & { * @default 'orpc' */ output?: string + /** + * Whether exports should be re-exported in the index file. + * @default false + */ + exportFromIndex?: boolean + /** + * Custom naming function for contract symbols. + * @default (id) => `${id}Contract` + */ + contractNameBuilder?: (operationId: string) => string + /** + * How to group contracts in the exported object. + * - 'tag': Group by OpenAPI tags (default) + * - 'none': Flat structure without grouping + * @default 'tag' + */ + groupBy?: 'tag' | 'none' } -export type ResolvedConfig = Config & { +export type ResolvedConfig = { + name: 'orpc' output: string + exportFromIndex: boolean + contractNameBuilder: (operationId: string) => string + groupBy: 'tag' | 'none' } export type OrpcPlugin = DefinePlugin