From 68c750ab7ad37c71c4f9b267ddc9e1c90c89924a Mon Sep 17 00:00:00 2001 From: Lubos Date: Thu, 12 Mar 2026 05:48:46 +0100 Subject: [PATCH] feat: add getName symbol hook --- .changeset/flat-crews-lie.md | 5 + .changeset/poor-grapes-knock.md | 5 + .changeset/stupid-crabs-joke.md | 5 + .changeset/swift-llamas-notice.md | 5 + .../src/plugins/pydantic/shared/export.ts | 26 ++-- .../typescript/shared/clientOptions.ts | 15 ++- .../@hey-api/typescript/shared/export.ts | 90 ++++++++----- .../@hey-api/typescript/shared/operation.ts | 122 +++++++++++------- .../@hey-api/typescript/shared/webhook.ts | 45 ++++--- .../plugins/@hey-api/typescript/v1/plugin.ts | 15 ++- .../src/plugins/valibot/shared/export.ts | 26 ++-- .../src/plugins/zod/shared/export.ts | 51 +++++--- packages/shared/src/index.ts | 1 + packages/shared/src/parser/hooks.ts | 21 ++- .../src/plugins/shared/utils/instance.ts | 2 +- packages/shared/src/plugins/symbol.ts | 23 ++++ 16 files changed, 296 insertions(+), 161 deletions(-) create mode 100644 .changeset/flat-crews-lie.md create mode 100644 .changeset/poor-grapes-knock.md create mode 100644 .changeset/stupid-crabs-joke.md create mode 100644 .changeset/swift-llamas-notice.md create mode 100644 packages/shared/src/plugins/symbol.ts diff --git a/.changeset/flat-crews-lie.md b/.changeset/flat-crews-lie.md new file mode 100644 index 000000000..61435ecde --- /dev/null +++ b/.changeset/flat-crews-lie.md @@ -0,0 +1,5 @@ +--- +"@hey-api/openapi-ts": patch +--- + +**plugin(@hey-api/typescript)**: implement `getName()` symbol hook diff --git a/.changeset/poor-grapes-knock.md b/.changeset/poor-grapes-knock.md new file mode 100644 index 000000000..0422ddec4 --- /dev/null +++ b/.changeset/poor-grapes-knock.md @@ -0,0 +1,5 @@ +--- +"@hey-api/openapi-ts": patch +--- + +**plugin(zod)**: implement `getName()` symbol hook diff --git a/.changeset/stupid-crabs-joke.md b/.changeset/stupid-crabs-joke.md new file mode 100644 index 000000000..71d4c2d29 --- /dev/null +++ b/.changeset/stupid-crabs-joke.md @@ -0,0 +1,5 @@ +--- +"@hey-api/openapi-ts": patch +--- + +**plugin(valibot)**: implement `getName()` symbol hook diff --git a/.changeset/swift-llamas-notice.md b/.changeset/swift-llamas-notice.md new file mode 100644 index 000000000..56ff558aa --- /dev/null +++ b/.changeset/swift-llamas-notice.md @@ -0,0 +1,5 @@ +--- +"@hey-api/shared": patch +--- + +**parser**: add `getName()` symbol hook diff --git a/packages/openapi-python/src/plugins/pydantic/shared/export.ts b/packages/openapi-python/src/plugins/pydantic/shared/export.ts index fa7cfe0ca..cbf09a8d2 100644 --- a/packages/openapi-python/src/plugins/pydantic/shared/export.ts +++ b/packages/openapi-python/src/plugins/pydantic/shared/export.ts @@ -1,5 +1,5 @@ import type { Symbol } from '@hey-api/codegen-core'; -import { applyNaming, pathToName } from '@hey-api/shared'; +import { applyNaming, buildSymbolIn, pathToName } from '@hey-api/shared'; import { $ } from '../../../py-dsl'; import type { PydanticPlugin } from '../types'; @@ -15,20 +15,26 @@ export function exportAst({ namingAnchor, path, plugin, + schema, tags, }: ProcessorContext & { final: PydanticFinal; }): void { const name = pathToName(path, { anchor: namingAnchor }); - const symbol = plugin.symbol(applyNaming(name, naming), { - meta: { - category: 'schema', - path, - tags, - tool: 'pydantic', - ...meta, - }, - }); + const symbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'schema', + path, + tags, + tool: 'pydantic', + ...meta, + }, + name: applyNaming(name, naming), + plugin, + schema, + }), + ); if (final.enumMembers) { exportEnumClass({ final, plugin, symbol }); diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/clientOptions.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/clientOptions.ts index 16d5e0b95..838fa20a8 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/clientOptions.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/clientOptions.ts @@ -1,5 +1,5 @@ import type { IR } from '@hey-api/shared'; -import { applyNaming, parseUrl } from '@hey-api/shared'; +import { applyNaming, buildSymbolIn, parseUrl } from '@hey-api/shared'; import { getTypedConfig } from '../../../../config/utils'; import { @@ -45,18 +45,19 @@ export const createClientOptions = ({ types.push($.type.and($.type('string'), $.type.object())); } - const symbol = plugin.symbol( - applyNaming('ClientOptions', { - case: plugin.config.case, - }), - { + const symbol = plugin.registerSymbol( + buildSymbolIn({ meta: { category: 'type', resource: 'client', role: 'options', tool: 'typescript', }, - }, + name: applyNaming('ClientOptions', { + case: plugin.config.case, + }), + plugin, + }), ); const node = $.type diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/export.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/export.ts index 45ca6705b..258aa692e 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/export.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/export.ts @@ -1,5 +1,5 @@ import type { IR } from '@hey-api/shared'; -import { applyNaming, pathToName, toCase } from '@hey-api/shared'; +import { applyNaming, buildSymbolIn, pathToName, toCase } from '@hey-api/shared'; import { pathToJsonPointer } from '@hey-api/shared'; import { createSchemaComment } from '../../../../plugins/shared/utils/schema'; @@ -98,14 +98,19 @@ function buildEnumExport({ ? items.filter((item) => item.schema.const !== null) : items; - const symbolObject = plugin.symbol(applyNaming(name, plugin.config.definitions), { - meta: { - category: 'utility', - resource: 'definition', - resourceId, - tool: 'typescript', - }, - }); + const symbolObject = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'utility', + resource: 'definition', + resourceId, + tool: 'typescript', + }, + name: applyNaming(name, plugin.config.definitions), + plugin, + schema, + }), + ); const objectNode = $.const(symbolObject) .export() @@ -126,14 +131,19 @@ function buildEnumExport({ ); plugin.node(objectNode); - const symbol = plugin.symbol(applyNaming(name, plugin.config.definitions), { - meta: { - category: 'type', - resource: 'definition', - resourceId, - tool: 'typescript', - }, - }); + const symbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'type', + resource: 'definition', + resourceId, + tool: 'typescript', + }, + name: applyNaming(name, plugin.config.definitions), + plugin, + schema, + }), + ); const node = $.type .alias(symbol) .export() @@ -149,14 +159,19 @@ function buildEnumExport({ ); if (hasInvalidTypes) return false; - const symbol = plugin.symbol(applyNaming(name, plugin.config.definitions), { - meta: { - category: 'type', - resource: 'definition', - resourceId, - tool: 'typescript', - }, - }); + const symbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'type', + resource: 'definition', + resourceId, + tool: 'typescript', + }, + name: applyNaming(name, plugin.config.definitions), + plugin, + schema, + }), + ); const enumNode = $.enum(symbol) .export() .$if(plugin.config.comments && createSchemaComment(schema), (e, v) => e.doc(v)) @@ -203,16 +218,21 @@ export function exportAst({ return; } - const symbol = plugin.symbol(applyNaming(name, naming), { - meta: { - category: 'type', - path, - resource: 'definition', - resourceId: $ref, - tags, - tool: 'typescript', - }, - }); + const symbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'type', + path, + resource: 'definition', + resourceId: $ref, + tags, + tool: 'typescript', + }, + name: applyNaming(name, naming), + plugin, + schema, + }), + ); const node = $.type .alias(symbol) diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/operation.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/operation.ts index 0e2ac86b7..93a5e4d60 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/operation.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/operation.ts @@ -1,7 +1,10 @@ import type { IR } from '@hey-api/shared'; -import { applyNaming } from '@hey-api/shared'; -import { operationResponsesMap } from '@hey-api/shared'; -import { deduplicateSchema } from '@hey-api/shared'; +import { + applyNaming, + buildSymbolIn, + deduplicateSchema, + operationResponsesMap, +} from '@hey-api/shared'; import { $ } from '../../../../ts-dsl'; import type { HeyApiTypeScriptPlugin } from '../types'; @@ -117,17 +120,22 @@ export const operationToType = ({ schema: data, }); - const dataSymbol = plugin.symbol(applyNaming(operation.id, plugin.config.requests), { - meta: { - category: 'type', - path, - resource: 'operation', - resourceId: operation.id, - role: 'data', - tags, - tool: 'typescript', - }, - }); + const dataSymbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'type', + path, + resource: 'operation', + resourceId: operation.id, + role: 'data', + tags, + tool: 'typescript', + }, + name: applyNaming(operation.id, plugin.config.requests), + operation, + plugin, + }), + ); const dataNode = $.type .alias(dataSymbol) .export() @@ -149,17 +157,22 @@ export const operationToType = ({ schema: errors, }); - const errorsSymbol = plugin.symbol(applyNaming(operation.id, plugin.config.errors), { - meta: { - category: 'type', - path, - resource: 'operation', - resourceId: operation.id, - role: 'errors', - tags, - tool: 'typescript', - }, - }); + const errorsSymbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'type', + path, + resource: 'operation', + resourceId: operation.id, + role: 'errors', + tags, + tool: 'typescript', + }, + name: applyNaming(operation.id, plugin.config.errors), + operation, + plugin, + }), + ); const errorsNode = $.type .alias(errorsSymbol) .export() @@ -167,12 +180,8 @@ export const operationToType = ({ plugin.node(errorsNode); if (error) { - const errorSymbol = plugin.symbol( - applyNaming(operation.id, { - case: plugin.config.errors.case, - name: plugin.config.errors.error, - }), - { + const errorSymbol = plugin.registerSymbol( + buildSymbolIn({ meta: { category: 'type', path, @@ -182,7 +191,13 @@ export const operationToType = ({ tags, tool: 'typescript', }, - }, + name: applyNaming(operation.id, { + case: plugin.config.errors.case, + name: plugin.config.errors.error, + }), + operation, + plugin, + }), ); const errorNode = $.type .alias(errorSymbol) @@ -205,17 +220,22 @@ export const operationToType = ({ schema: responses, }); - const responsesSymbol = plugin.symbol(applyNaming(operation.id, plugin.config.responses), { - meta: { - category: 'type', - path, - resource: 'operation', - resourceId: operation.id, - role: 'responses', - tags, - tool: 'typescript', - }, - }); + const responsesSymbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'type', + path, + resource: 'operation', + resourceId: operation.id, + role: 'responses', + tags, + tool: 'typescript', + }, + name: applyNaming(operation.id, plugin.config.responses), + operation, + plugin, + }), + ); const responsesNode = $.type .alias(responsesSymbol) .export() @@ -223,12 +243,8 @@ export const operationToType = ({ plugin.node(responsesNode); if (response) { - const responseSymbol = plugin.symbol( - applyNaming(operation.id, { - case: plugin.config.responses.case, - name: plugin.config.responses.response, - }), - { + const responseSymbol = plugin.registerSymbol( + buildSymbolIn({ meta: { category: 'type', path, @@ -238,7 +254,13 @@ export const operationToType = ({ tags, tool: 'typescript', }, - }, + name: applyNaming(operation.id, { + case: plugin.config.responses.case, + name: plugin.config.responses.response, + }), + operation, + plugin, + }), ); const responseNode = $.type .alias(responseSymbol) diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/webhook.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/webhook.ts index 79f858a53..3bf859bd3 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/webhook.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/shared/webhook.ts @@ -1,6 +1,6 @@ import type { Symbol } from '@hey-api/codegen-core'; import type { IR } from '@hey-api/shared'; -import { applyNaming } from '@hey-api/shared'; +import { applyNaming, buildSymbolIn } from '@hey-api/shared'; import { createSchemaComment } from '../../../../plugins/shared/utils/schema'; import { $ } from '../../../../ts-dsl'; @@ -22,12 +22,8 @@ export function webhookToType({ let symbolWebhookPayload: Symbol | undefined; if (operation.body) { - symbolWebhookPayload = plugin.symbol( - applyNaming(operation.id, { - case: plugin.config.webhooks.case, - name: plugin.config.webhooks.payload, - }), - { + symbolWebhookPayload = plugin.registerSymbol( + buildSymbolIn({ meta: { category: 'type', path, @@ -37,7 +33,13 @@ export function webhookToType({ tags, tool: 'typescript', }, - }, + name: applyNaming(operation.id, { + case: plugin.config.webhooks.case, + name: plugin.config.webhooks.payload, + }), + operation, + plugin, + }), ); const payloadResult = processor.process({ @@ -71,17 +73,22 @@ export function webhookToType({ .prop('path', (p) => p.required(false).type($.type('never'))) .prop('query', (p) => p.required(false).type($.type('never'))); - const symbol = plugin.symbol(applyNaming(operation.id, plugin.config.webhooks), { - meta: { - category: 'type', - path, - resource: 'webhook', - resourceId: operation.id, - role: 'data', - tags, - tool: 'typescript', - }, - }); + const symbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'type', + path, + resource: 'webhook', + resourceId: operation.id, + role: 'data', + tags, + tool: 'typescript', + }, + name: applyNaming(operation.id, plugin.config.webhooks), + operation, + plugin, + }), + ); const node = $.type.alias(symbol).export().type(requestType); plugin.node(node); diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/v1/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/v1/plugin.ts index 87e555be2..2fad04255 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/v1/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/v1/plugin.ts @@ -1,6 +1,6 @@ import type { Symbol } from '@hey-api/codegen-core'; import type { IR } from '@hey-api/shared'; -import { applyNaming, pathToJsonPointer } from '@hey-api/shared'; +import { applyNaming, buildSymbolIn, pathToJsonPointer } from '@hey-api/shared'; import { $ } from '../../../../ts-dsl'; import { createClientOptions } from '../shared/clientOptions'; @@ -97,18 +97,19 @@ export const handlerV1: HeyApiTypeScriptPlugin['Handler'] = ({ plugin }) => { createClientOptions({ nodeIndex: nodeClientIndex, plugin, servers }); if (webhooks.length > 0) { - const symbol = plugin.symbol( - applyNaming('Webhooks', { - case: plugin.config.case, - }), - { + const symbol = plugin.registerSymbol( + buildSymbolIn({ meta: { category: 'type', resource: 'webhook', tool: 'typescript', variant: 'container', }, - }, + name: applyNaming('Webhooks', { + case: plugin.config.case, + }), + plugin, + }), ); const node = $.type .alias(symbol) diff --git a/packages/openapi-ts/src/plugins/valibot/shared/export.ts b/packages/openapi-ts/src/plugins/valibot/shared/export.ts index f4ac6e17e..7edad3da4 100644 --- a/packages/openapi-ts/src/plugins/valibot/shared/export.ts +++ b/packages/openapi-ts/src/plugins/valibot/shared/export.ts @@ -1,4 +1,4 @@ -import { applyNaming, pathToName } from '@hey-api/shared'; +import { applyNaming, buildSymbolIn, pathToName } from '@hey-api/shared'; import { createSchemaComment } from '../../../plugins/shared/utils/schema'; import { $ } from '../../../ts-dsl'; @@ -21,15 +21,21 @@ export function exportAst({ const v = plugin.external('valibot.v'); const name = pathToName(path, { anchor: namingAnchor }); - const symbol = plugin.symbol(applyNaming(name, naming), { - meta: { - category: 'schema', - path, - tags, - tool: 'valibot', - ...meta, - }, - }); + + const symbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'schema', + path, + tags, + tool: 'valibot', + ...meta, + }, + name: applyNaming(name, naming), + plugin, + schema, + }), + ); const statement = $.const(symbol) .export() diff --git a/packages/openapi-ts/src/plugins/zod/shared/export.ts b/packages/openapi-ts/src/plugins/zod/shared/export.ts index 6b12a621d..f6db83ac4 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/export.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/export.ts @@ -1,4 +1,4 @@ -import { applyNaming, pathToName } from '@hey-api/shared'; +import { applyNaming, buildSymbolIn, pathToName } from '@hey-api/shared'; import { createSchemaComment } from '../../../plugins/shared/utils/schema'; import { $ } from '../../../ts-dsl'; @@ -22,27 +22,38 @@ export function exportAst({ const z = plugin.external('zod.z'); const name = pathToName(path, { anchor: namingAnchor }); - const symbol = plugin.symbol(applyNaming(name, naming), { - meta: { - category: 'schema', - path, - tags, - tool: 'zod', - ...meta, - }, - }); + + const symbol = plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'schema', + path, + tags, + tool: 'zod', + ...meta, + }, + name: applyNaming(name, naming), + plugin, + schema, + }), + ); const typeInferSymbol = naming.types.infer.enabled - ? plugin.symbol(applyNaming(name, naming.types.infer), { - meta: { - category: 'type', - path, - tags, - tool: 'zod', - variant: 'infer', - ...meta, - }, - }) + ? plugin.registerSymbol( + buildSymbolIn({ + meta: { + category: 'type', + path, + tags, + tool: 'zod', + variant: 'infer', + ...meta, + }, + name: applyNaming(name, naming.types.infer), + plugin, + schema, + }), + ) : undefined; const statement = $.const(symbol) diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 5f7b8d597..efb28190a 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -108,6 +108,7 @@ export type { SchemaWithType } from './plugins/shared/types/schema'; export { definePluginConfig, mappers } from './plugins/shared/utils/config'; export type { PluginInstanceTypes } from './plugins/shared/utils/instance'; export { PluginInstance } from './plugins/shared/utils/instance'; +export { buildSymbolIn } from './plugins/symbol'; export type { AnyPluginName, DefinePlugin, diff --git a/packages/shared/src/parser/hooks.ts b/packages/shared/src/parser/hooks.ts index 93c69c041..7d3f92e93 100644 --- a/packages/shared/src/parser/hooks.ts +++ b/packages/shared/src/parser/hooks.ts @@ -1,7 +1,7 @@ -import type { Node, Symbol, SymbolIn } from '@hey-api/codegen-core'; +import type { Node, Symbol, SymbolIn, SymbolMeta } from '@hey-api/codegen-core'; import type { SchemaProcessorContext } from '../ir/schema-processor'; -import type { IROperationObject } from '../ir/types'; +import type { IROperationObject, IRSchemaObject } from '../ir/types'; import type { PluginInstance } from '../plugins/shared/utils/instance'; export type Hooks = { @@ -210,5 +210,22 @@ export type Hooks = { * @returns The file path to output the symbol to, or undefined to fallback to default behavior. */ getFilePath?: (symbol: Symbol) => string | undefined; + /** + * Optional output strategy to override default plugin behavior. + * + * Use this to customize symbol names. + * + * @returns The name to register the symbol with, or undefined to fallback to default behavior. + */ + getName?: (ctx: { + /** Arbitrary metadata about the symbol. */ + meta: SymbolMeta; + /** The proposed name for the symbol. */ + name: string; + /** The operation object associated with the symbol. */ + operation?: IROperationObject; + /** The schema object associated with the symbol. */ + schema?: IRSchemaObject; + }) => string | undefined; }; }; diff --git a/packages/shared/src/plugins/shared/utils/instance.ts b/packages/shared/src/plugins/shared/utils/instance.ts index e92eca4de..88f075db5 100644 --- a/packages/shared/src/plugins/shared/utils/instance.ts +++ b/packages/shared/src/plugins/shared/utils/instance.ts @@ -353,7 +353,7 @@ export class PluginInstance { } /** - * @deprecated use `plugin.symbol()` instead + * Alias for `symbol()` method with single argument. */ registerSymbol(symbol: SymbolIn): Symbol { return this.symbol(symbol.name, symbol) as Symbol; diff --git a/packages/shared/src/plugins/symbol.ts b/packages/shared/src/plugins/symbol.ts new file mode 100644 index 000000000..2981f2b9d --- /dev/null +++ b/packages/shared/src/plugins/symbol.ts @@ -0,0 +1,23 @@ +import type { SymbolIn, SymbolMeta } from '@hey-api/codegen-core'; + +import type { IROperationObject, IRSchemaObject } from '../ir/types'; +import type { PluginInstance } from './shared/utils/instance'; + +/** + * Helper function to build the input for symbol registration, applying naming hooks if provided. + */ +export function buildSymbolIn(ctx: { + meta: SymbolMeta; + name: string; + operation?: IROperationObject; + plugin: { + config: Pick; + }; + schema?: IRSchemaObject; +}): SymbolIn { + const getName = ctx.plugin.config['~hooks']?.symbols?.getName ?? (() => {}); + return { + meta: ctx.meta, + name: getName(ctx) ?? ctx.name, + }; +} -- 2.51.2