fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #3433 from hey-api/refactor/valibot-plugin-impl

refactor: valibot plugin internals

authored by

Lubos and committed by
GitHub
fe8f3b50 3a462639

+781 -817
+192
packages/openapi-ts/src/plugins/valibot/resolvers.ts
··· 1 + import type { Symbol } from '@hey-api/codegen-core'; 2 + import type { IR, Plugin, SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 3 + 4 + import type { MaybeBigInt, ShouldCoerceToBigInt } from '../../plugins/shared/utils/coerce'; 5 + import type { GetIntegerLimit } from '../../plugins/shared/utils/formats'; 6 + import type { $, DollarTsDsl } from '../../ts-dsl'; 7 + import type { Pipe, PipeResult, Pipes, PipesUtils } from './shared/pipes'; 8 + import type { ValibotFinal, ValibotResult } from './shared/types'; 9 + import type { ValibotPlugin } from './types'; 10 + 11 + export type Resolvers = Plugin.Resolvers<{ 12 + /** 13 + * Resolver for enum schemas. 14 + * 15 + * Allows customization of how enum types are rendered. 16 + * 17 + * Returning `undefined` will execute the default resolver logic. 18 + */ 19 + enum?: (ctx: EnumResolverContext) => PipeResult; 20 + /** 21 + * Resolver for number schemas. 22 + * 23 + * Allows customization of how number types are rendered. 24 + * 25 + * Returning `undefined` will execute the default resolver logic. 26 + */ 27 + number?: (ctx: NumberResolverContext) => PipeResult; 28 + /** 29 + * Resolver for object schemas. 30 + * 31 + * Allows customization of how object types are rendered. 32 + * 33 + * Returning `undefined` will execute the default resolver logic. 34 + */ 35 + object?: (ctx: ObjectResolverContext) => PipeResult; 36 + /** 37 + * Resolver for string schemas. 38 + * 39 + * Allows customization of how string types are rendered. 40 + * 41 + * Returning `undefined` will execute the default resolver logic. 42 + */ 43 + string?: (ctx: StringResolverContext) => PipeResult; 44 + /** 45 + * Resolvers for request and response validators. 46 + * 47 + * Allow customization of validator function bodies. 48 + * 49 + * Example path: `~resolvers.validator.request` or `~resolvers.validator.response` 50 + * 51 + * Returning `undefined` will execute the default resolver logic. 52 + */ 53 + validator?: 54 + | ValidatorResolver 55 + | { 56 + /** 57 + * Controls how the request validator function body is generated. 58 + * 59 + * Returning `undefined` will execute the default resolver logic. 60 + */ 61 + request?: ValidatorResolver; 62 + /** 63 + * Controls how the response validator function body is generated. 64 + * 65 + * Returning `undefined` will execute the default resolver logic. 66 + */ 67 + response?: ValidatorResolver; 68 + }; 69 + }>; 70 + 71 + type ValidatorResolver = (ctx: ValidatorResolverContext) => PipeResult | null | undefined; 72 + 73 + interface BaseContext extends DollarTsDsl { 74 + /** 75 + * Functions for working with pipes. 76 + */ 77 + pipes: PipesUtils & { 78 + /** 79 + * The current pipe. 80 + * 81 + * In Valibot, this represents a list of call expressions ("pipes") 82 + * being assembled to form a schema definition. 83 + * 84 + * Each pipe can be extended, modified, or replaced to customize 85 + * the resulting schema. 86 + */ 87 + current: Pipes; 88 + }; 89 + /** The plugin instance. */ 90 + plugin: ValibotPlugin['Instance']; 91 + /** 92 + * Provides access to commonly used symbols within the plugin. 93 + */ 94 + symbols: { 95 + v: Symbol; 96 + }; 97 + } 98 + 99 + export interface EnumResolverContext extends BaseContext { 100 + /** 101 + * Nodes used to build different parts of the schema. 102 + */ 103 + nodes: { 104 + /** 105 + * Returns the base enum expression (v.picklist([...])). 106 + */ 107 + base: (ctx: EnumResolverContext) => PipeResult; 108 + /** 109 + * Returns parsed enum items with metadata about the enum members. 110 + */ 111 + items: (ctx: EnumResolverContext) => { 112 + /** 113 + * String literal values for use with v.picklist([...]). 114 + */ 115 + enumMembers: Array<ReturnType<typeof $.literal>>; 116 + /** 117 + * Whether the enum includes a null value. 118 + */ 119 + isNullable: boolean; 120 + }; 121 + }; 122 + schema: SchemaWithType<'enum'>; 123 + } 124 + 125 + export interface NumberResolverContext extends BaseContext { 126 + /** 127 + * Nodes used to build different parts of the schema. 128 + */ 129 + nodes: { 130 + base: (ctx: NumberResolverContext) => PipeResult; 131 + const: (ctx: NumberResolverContext) => PipeResult; 132 + max: (ctx: NumberResolverContext) => PipeResult; 133 + min: (ctx: NumberResolverContext) => PipeResult; 134 + }; 135 + schema: SchemaWithType<'integer' | 'number'>; 136 + /** 137 + * Utility functions for number schema processing. 138 + */ 139 + utils: { 140 + getIntegerLimit: GetIntegerLimit; 141 + maybeBigInt: MaybeBigInt; 142 + shouldCoerceToBigInt: ShouldCoerceToBigInt; 143 + }; 144 + } 145 + 146 + export interface ObjectResolverContext extends BaseContext { 147 + _childResults: Array<ValibotResult>; 148 + applyModifiers: (result: ValibotResult, opts: { optional?: boolean }) => ValibotFinal; 149 + /** 150 + * Nodes used to build different parts of the schema. 151 + */ 152 + nodes: { 153 + additionalProperties: (ctx: ObjectResolverContext) => Pipe | null | undefined; 154 + base: (ctx: ObjectResolverContext) => Pipes | Pipe; 155 + shape: (ctx: ObjectResolverContext) => ReturnType<typeof $.object>; 156 + }; 157 + schema: SchemaWithType<'object'>; 158 + /** 159 + * Utility functions for object schema processing. 160 + */ 161 + utils: { 162 + ast: Partial<{ hasLazy: boolean }>; 163 + }; 164 + walk: Walker<ValibotResult, ValibotPlugin['Instance']>; 165 + walkerCtx: SchemaVisitorContext<ValibotPlugin['Instance']>; 166 + } 167 + 168 + export interface StringResolverContext extends BaseContext { 169 + /** 170 + * Nodes used to build different parts of the schema. 171 + */ 172 + nodes: { 173 + base: (ctx: StringResolverContext) => PipeResult; 174 + const: (ctx: StringResolverContext) => PipeResult; 175 + format: (ctx: StringResolverContext) => PipeResult; 176 + length: (ctx: StringResolverContext) => PipeResult; 177 + maxLength: (ctx: StringResolverContext) => PipeResult; 178 + minLength: (ctx: StringResolverContext) => PipeResult; 179 + pattern: (ctx: StringResolverContext) => PipeResult; 180 + }; 181 + schema: SchemaWithType<'string'>; 182 + } 183 + 184 + export interface ValidatorResolverContext extends BaseContext { 185 + operation: IR.OperationObject; 186 + /** 187 + * Provides access to commonly used symbols within the plugin. 188 + */ 189 + symbols: BaseContext['symbols'] & { 190 + schema: Symbol; 191 + }; 192 + }
-8
packages/openapi-ts/src/plugins/valibot/resolvers/index.ts
··· 1 - export type { 2 - EnumResolverContext, 3 - NumberResolverContext, 4 - ObjectResolverContext, 5 - Resolvers, 6 - StringResolverContext, 7 - ValidatorResolverContext, 8 - } from './types';
-201
packages/openapi-ts/src/plugins/valibot/resolvers/types.ts
··· 1 - import type { Refs, Symbol } from '@hey-api/codegen-core'; 2 - import type { IR, Plugin, SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 3 - 4 - import type { MaybeBigInt, ShouldCoerceToBigInt } from '../../../plugins/shared/utils/coerce'; 5 - import type { GetIntegerLimit } from '../../../plugins/shared/utils/formats'; 6 - import type { $, DollarTsDsl } from '../../../ts-dsl'; 7 - import type { Pipe, PipeResult, Pipes, PipesUtils } from '../shared/pipes'; 8 - import type { Ast, IrSchemaToAstOptions, PluginState, ValibotSchemaResult } from '../shared/types'; 9 - import type { ValibotPlugin } from '../types'; 10 - 11 - export type Resolvers = Plugin.Resolvers<{ 12 - /** 13 - * Resolver for enum schemas. 14 - * 15 - * Allows customization of how enum types are rendered. 16 - * 17 - * Returning `undefined` will execute the default resolver logic. 18 - */ 19 - enum?: (ctx: EnumResolverContext) => PipeResult | undefined; 20 - /** 21 - * Resolver for number schemas. 22 - * 23 - * Allows customization of how number types are rendered. 24 - * 25 - * Returning `undefined` will execute the default resolver logic. 26 - */ 27 - number?: (ctx: NumberResolverContext) => PipeResult | undefined; 28 - /** 29 - * Resolver for object schemas. 30 - * 31 - * Allows customization of how object types are rendered. 32 - * 33 - * Returning `undefined` will execute the default resolver logic. 34 - */ 35 - object?: (ctx: ObjectResolverContext) => PipeResult | undefined; 36 - /** 37 - * Resolver for string schemas. 38 - * 39 - * Allows customization of how string types are rendered. 40 - * 41 - * Returning `undefined` will execute the default resolver logic. 42 - */ 43 - string?: (ctx: StringResolverContext) => PipeResult | undefined; 44 - /** 45 - * Resolvers for request and response validators. 46 - * 47 - * Allow customization of validator function bodies. 48 - * 49 - * Example path: `~resolvers.validator.request` or `~resolvers.validator.response` 50 - * 51 - * Returning `undefined` will execute the default resolver logic. 52 - */ 53 - validator?: 54 - | ValidatorResolver 55 - | { 56 - /** 57 - * Controls how the request validator function body is generated. 58 - * 59 - * Returning `undefined` will execute the default resolver logic. 60 - */ 61 - request?: ValidatorResolver; 62 - /** 63 - * Controls how the response validator function body is generated. 64 - * 65 - * Returning `undefined` will execute the default resolver logic. 66 - */ 67 - response?: ValidatorResolver; 68 - }; 69 - }>; 70 - 71 - type ValidatorResolver = (ctx: ValidatorResolverContext) => PipeResult | null | undefined; 72 - 73 - type BaseContext = DollarTsDsl & 74 - Pick<IrSchemaToAstOptions, 'plugin'> & { 75 - /** 76 - * Functions for working with pipes. 77 - */ 78 - pipes: PipesUtils & { 79 - /** 80 - * The current pipe. 81 - * 82 - * In Valibot, this represents a list of call expressions ("pipes") 83 - * being assembled to form a schema definition. 84 - * 85 - * Each pipe can be extended, modified, or replaced to customize 86 - * the resulting schema. 87 - */ 88 - current: Pipes; 89 - }; 90 - /** 91 - * Provides access to commonly used symbols within the plugin. 92 - */ 93 - symbols: { 94 - v: Symbol; 95 - }; 96 - }; 97 - 98 - export interface EnumResolverContext extends BaseContext { 99 - /** 100 - * Nodes used to build different parts of the enum schema. 101 - */ 102 - nodes: { 103 - /** 104 - * Returns the base enum expression (v.picklist([...])). 105 - */ 106 - base: (ctx: EnumResolverContext) => PipeResult; 107 - /** 108 - * Returns parsed enum items with metadata about the enum members. 109 - */ 110 - items: (ctx: EnumResolverContext) => { 111 - /** 112 - * String literal values for use with v.picklist([...]). 113 - */ 114 - enumMembers: Array<ReturnType<typeof $.literal>>; 115 - /** 116 - * Whether the enum includes a null value. 117 - */ 118 - isNullable: boolean; 119 - }; 120 - }; 121 - schema: SchemaWithType<'enum'>; 122 - /** 123 - * Utility functions for enum schema processing. 124 - */ 125 - utils: { 126 - state: Refs<PluginState>; 127 - }; 128 - } 129 - 130 - export interface NumberResolverContext extends BaseContext { 131 - /** 132 - * Nodes used to build different parts of the number schema. 133 - */ 134 - nodes: { 135 - base: (ctx: NumberResolverContext) => PipeResult; 136 - const: (ctx: NumberResolverContext) => PipeResult | undefined; 137 - max: (ctx: NumberResolverContext) => PipeResult | undefined; 138 - min: (ctx: NumberResolverContext) => PipeResult | undefined; 139 - }; 140 - schema: SchemaWithType<'integer' | 'number'>; 141 - /** 142 - * Utility functions for number schema processing. 143 - */ 144 - utils: { 145 - getIntegerLimit: GetIntegerLimit; 146 - maybeBigInt: MaybeBigInt; 147 - shouldCoerceToBigInt: ShouldCoerceToBigInt; 148 - }; 149 - } 150 - 151 - export interface ObjectResolverContext extends BaseContext { 152 - applyModifiers: (result: ValibotSchemaResult, opts: { optional?: boolean }) => Ast; 153 - /** 154 - * Nodes used to build different parts of the object schema. 155 - */ 156 - nodes: { 157 - /** 158 - * If `additionalProperties` is `false` or `{ type: 'never' }`, returns `null` 159 - * to indicate no additional properties are allowed. 160 - */ 161 - additionalProperties: (ctx: ObjectResolverContext) => Pipe | null | undefined; 162 - base: (ctx: ObjectResolverContext) => PipeResult; 163 - shape: (ctx: ObjectResolverContext) => ReturnType<typeof $.object>; 164 - }; 165 - schema: SchemaWithType<'object'>; 166 - /** 167 - * Utility functions for object schema processing. 168 - */ 169 - utils: { 170 - ast: Partial<Omit<Ast, 'typeName'>>; 171 - state: Refs<PluginState>; 172 - }; 173 - walk: Walker<ValibotSchemaResult, ValibotPlugin['Instance']>; 174 - walkerCtx: SchemaVisitorContext<ValibotPlugin['Instance']>; 175 - } 176 - 177 - export interface StringResolverContext extends BaseContext { 178 - /** 179 - * Nodes used to build different parts of the string schema. 180 - */ 181 - nodes: { 182 - base: (ctx: StringResolverContext) => PipeResult; 183 - const: (ctx: StringResolverContext) => PipeResult | undefined; 184 - format: (ctx: StringResolverContext) => PipeResult | undefined; 185 - length: (ctx: StringResolverContext) => PipeResult | undefined; 186 - maxLength: (ctx: StringResolverContext) => PipeResult | undefined; 187 - minLength: (ctx: StringResolverContext) => PipeResult | undefined; 188 - pattern: (ctx: StringResolverContext) => PipeResult | undefined; 189 - }; 190 - schema: SchemaWithType<'string'>; 191 - } 192 - 193 - export interface ValidatorResolverContext extends BaseContext { 194 - operation: IR.OperationObject; 195 - /** 196 - * Provides access to commonly used symbols within the plugin. 197 - */ 198 - symbols: BaseContext['symbols'] & { 199 - schema: Symbol; 200 - }; 201 - }
+10 -12
packages/openapi-ts/src/plugins/valibot/shared/export.ts
··· 2 2 3 3 import { createSchemaComment } from '../../../plugins/shared/utils/schema'; 4 4 import { $ } from '../../../ts-dsl'; 5 - import { identifiers } from '../v1/constants'; 5 + import type { ValibotPlugin } from '../types'; 6 6 import { pipesToNode } from './pipes'; 7 7 import type { ProcessorContext } from './processor'; 8 - import type { Ast, IrSchemaToAstOptions } from './types'; 8 + import type { ValibotFinal } from './types'; 9 9 10 10 export function exportAst({ 11 - ast, 11 + final, 12 12 meta, 13 13 naming, 14 14 namingAnchor, 15 15 path, 16 16 plugin, 17 17 schema, 18 - state, 19 18 tags, 20 - }: Pick<IrSchemaToAstOptions, 'state'> & 21 - ProcessorContext & { 22 - ast: Ast; 23 - }): void { 19 + }: ProcessorContext & { 20 + final: ValibotFinal; 21 + plugin: ValibotPlugin['Instance']; 22 + }): void { 24 23 const v = plugin.external('valibot.v'); 25 24 26 25 const name = pathToName(path, { anchor: namingAnchor }); ··· 37 36 const statement = $.const(symbol) 38 37 .export() 39 38 .$if(plugin.config.comments && createSchemaComment(schema), (c, v) => c.doc(v)) 40 - .$if(state.hasLazyExpression['~ref'], (c) => 41 - c.type($.type(v).attr(ast.typeName || identifiers.types.GenericSchema)), 42 - ) 43 - .assign(pipesToNode(ast.pipes, plugin)); 39 + .$if(final.typeName, (c) => c.type($.type(v).attr(final.typeName!))) 40 + .assign(pipesToNode(final.pipes, plugin)); 41 + 44 42 plugin.node(statement); 45 43 }
+59
packages/openapi-ts/src/plugins/valibot/shared/meta.ts
··· 1 + import type { IR } from '@hey-api/shared'; 2 + 3 + import type { ValibotMeta, ValibotResult } from './types'; 4 + 5 + /** 6 + * Creates default metadata from a schema. 7 + */ 8 + export function defaultMeta(schema: IR.SchemaObject): ValibotMeta { 9 + return { 10 + default: schema.default, 11 + format: schema.format, 12 + hasLazy: false, 13 + nullable: false, 14 + readonly: schema.accessScope === 'read', 15 + }; 16 + } 17 + 18 + /** 19 + * Composes metadata from child results. 20 + * 21 + * Automatically propagates hasLazy, nullable, readonly from children. 22 + * 23 + * @param children - Results from walking child schemas 24 + * @param overrides - Explicit overrides (e.g., from parent schema) 25 + */ 26 + export function composeMeta( 27 + children: ReadonlyArray<ValibotResult>, 28 + overrides?: Partial<ValibotMeta>, 29 + ): ValibotMeta { 30 + const hasLazy = overrides?.hasLazy ?? children.some((c) => c.meta.hasLazy); 31 + const nullable = overrides?.nullable ?? children.some((c) => c.meta.nullable); 32 + const readonly = overrides?.readonly ?? children.some((c) => c.meta.readonly); 33 + 34 + return { 35 + default: overrides?.default, 36 + format: overrides?.format, 37 + hasLazy, 38 + nullable, 39 + readonly, 40 + }; 41 + } 42 + 43 + /** 44 + * Merges parent schema metadata with composed child metadata. 45 + * 46 + * @param parent - The parent schema 47 + * @param children - Results from walking child schemas 48 + */ 49 + export function inheritMeta( 50 + parent: IR.SchemaObject, 51 + children: ReadonlyArray<ValibotResult>, 52 + ): ValibotMeta { 53 + return composeMeta(children, { 54 + default: parent.default, 55 + format: parent.format, 56 + nullable: false, 57 + readonly: parent.accessScope === 'read', 58 + }); 59 + }
+4 -6
packages/openapi-ts/src/plugins/valibot/shared/operation.ts
··· 3 3 4 4 import { buildOperationSchema } from './operation-schema'; 5 5 import type { ProcessorContext, ProcessorResult } from './processor'; 6 - import type { IrSchemaToAstOptions } from './types'; 7 6 8 7 export function irOperationToAst({ 9 8 operation, ··· 11 10 plugin, 12 11 processor, 13 12 tags, 14 - }: Pick<IrSchemaToAstOptions, 'plugin'> & 15 - Pick<ProcessorContext, 'path' | 'tags'> & { 16 - operation: IR.OperationObject; 17 - processor: ProcessorResult; 18 - }): void { 13 + }: Pick<ProcessorContext, 'path' | 'plugin' | 'tags'> & { 14 + operation: IR.OperationObject; 15 + processor: ProcessorResult; 16 + }): void { 19 17 if (plugin.config.requests.enabled) { 20 18 const { schema } = buildOperationSchema(operation); 21 19
+46 -28
packages/openapi-ts/src/plugins/valibot/shared/pipes.ts
··· 4 4 5 5 export type Pipe = ReturnType<typeof $.call | typeof $.expr>; 6 6 export type Pipes = Array<Pipe>; 7 - export type PipeResult = Pipes | Pipe; 7 + export type PipeResult = Pipes | Pipe | undefined; 8 8 9 - type PushPipes = (target: Pipes, pipes: PipeResult) => Pipes; 10 - type PipesToNode = (pipes: PipeResult, plugin: ValibotPlugin['Instance']) => Pipe; 9 + export interface PipesUtils { 10 + /** 11 + * Pushes a pipe result onto a pipes array. 12 + * 13 + * Handles single pipes, arrays of pipes, and undefined. 14 + */ 15 + push: (target: Pipes, result: PipeResult) => Pipes; 16 + /** 17 + * Converts a pipes array to a single node expression. 18 + */ 19 + toNode: (pipes: Pipes | Pipe, plugin: ValibotPlugin['Instance']) => Pipe; 20 + } 11 21 12 - export const pipesToNode: PipesToNode = (pipes, plugin) => { 13 - if (!(pipes instanceof Array)) return pipes; 14 - if (pipes.length === 1) return pipes[0]!; 22 + function push(target: Pipes, result: PipeResult): Pipes { 23 + if (result === undefined) { 24 + return target; 25 + } 26 + if (result instanceof Array) { 27 + target.push(...result); 28 + } else { 29 + target.push(result); 30 + } 31 + return target; 32 + } 15 33 34 + function toNode(pipes: Pipes | Pipe, plugin: ValibotPlugin['Instance']): Pipe { 35 + if (!(pipes instanceof Array)) { 36 + return pipes; 37 + } 38 + if (pipes.length === 0) { 39 + const v = plugin.external('valibot.v'); 40 + return $(v).attr(identifiers.schemas.unknown).call(); 41 + } 42 + if (pipes.length === 1) { 43 + return pipes[0]!; 44 + } 16 45 const v = plugin.external('valibot.v'); 17 46 return $(v) 18 47 .attr(identifiers.methods.pipe) 19 48 .call(...pipes); 20 - }; 21 - 22 - export const pushPipes: PushPipes = (target, pipes) => { 23 - if (pipes instanceof Array) { 24 - target.push(...pipes); 25 - } else { 26 - target.push(pipes); 27 - } 28 - return target; 29 - }; 30 - 31 - export interface PipesUtils { 32 - /** 33 - * Push pipes into target array. 34 - */ 35 - push: PushPipes; 36 - /** 37 - * Convert pipes to a single node. 38 - */ 39 - toNode: PipesToNode; 40 49 } 41 50 42 51 /** 43 52 * Functions for working with pipes. 44 53 */ 45 54 export const pipes: PipesUtils = { 46 - push: pushPipes, 47 - toNode: pipesToNode, 55 + push, 56 + toNode, 48 57 }; 58 + 59 + /** 60 + * Convenience function for converting pipes to a node. 61 + * 62 + * Re-exported for backward compatibility. 63 + */ 64 + export function pipesToNode(p: Pipes, plugin: ValibotPlugin['Instance']): Pipe { 65 + return toNode(p, plugin); 66 + }
+7 -6
packages/openapi-ts/src/plugins/valibot/shared/processor.ts
··· 5 5 SchemaProcessorResult, 6 6 } from '@hey-api/shared'; 7 7 8 - import type { IrSchemaToAstOptions } from './types'; 8 + import type { ValibotPlugin } from '../types'; 9 9 10 - export type ProcessorContext = Pick<IrSchemaToAstOptions, 'plugin'> & 11 - SchemaProcessorContext & { 12 - naming: NamingConfig; 13 - schema: IR.SchemaObject; 14 - }; 10 + export type ProcessorContext = SchemaProcessorContext & { 11 + naming: NamingConfig; 12 + /** The plugin instance. */ 13 + plugin: ValibotPlugin['Instance']; 14 + schema: IR.SchemaObject; 15 + }; 15 16 16 17 export type ProcessorResult = SchemaProcessorResult<ProcessorContext>;
+28 -31
packages/openapi-ts/src/plugins/valibot/shared/types.ts
··· 1 - import type { Refs, SymbolMeta } from '@hey-api/codegen-core'; 2 - import type { IR, Walker } from '@hey-api/shared'; 1 + import type { IR } from '@hey-api/shared'; 3 2 import type ts from 'typescript'; 4 3 5 4 import type { ValibotPlugin } from '../types'; 6 5 import type { Pipes } from './pipes'; 7 6 8 - export type Ast = { 9 - hasLazyExpression?: boolean; 10 - pipes: Pipes; 11 - typeName?: string | ts.Identifier; 12 - }; 13 - 14 - export type IrSchemaToAstOptions = { 15 - /** The plugin instance. */ 16 - plugin: ValibotPlugin['Instance']; 17 - /** The plugin state references. */ 18 - state: Refs<PluginState>; 19 - walk: Walker<ValibotSchemaResult, ValibotPlugin['Instance']>; 20 - }; 21 - 22 - export type PluginState = Pick<Required<SymbolMeta>, 'path'> & 23 - Pick<Partial<SymbolMeta>, 'tags'> & { 24 - hasLazyExpression: boolean; 25 - }; 26 - 27 7 export type ValidatorArgs = { 28 8 operation: IR.OperationObject; 9 + /** The plugin instance. */ 29 10 plugin: ValibotPlugin['Instance']; 30 11 }; 31 12 32 13 /** 33 - * The result from schema walking. 14 + * Metadata that flows through schema walking. 34 15 */ 35 - export interface ValibotSchemaResult { 36 - /** Default value from schema, if any. */ 16 + export interface ValibotMeta { 17 + /** Default value from schema. */ 37 18 default?: unknown; 38 - /** The Valibot pipes AST. */ 39 - expression: { pipes: Pipes }; 40 - /** The original schema format (for BigInt coercion). */ 19 + /** Original format (for BigInt coercion). */ 41 20 format?: string; 42 - /** Whether any child contains a lazy expression. */ 43 - hasLazyExpression?: boolean; 21 + /** Whether this or any child contains a lazy reference. */ 22 + hasLazy: boolean; 44 23 /** Does this schema explicitly allow null? */ 45 24 nullable: boolean; 46 25 /** Is this schema read-only? */ ··· 48 27 } 49 28 50 29 /** 51 - * The finalized expression after applyModifiers. 30 + * Result from walking a schema node. 31 + */ 32 + export interface ValibotResult { 33 + meta: ValibotMeta; 34 + pipes: Pipes; 35 + } 36 + 37 + /** 38 + * Finalized result after applyModifiers. 52 39 */ 53 - export interface ValibotAppliedResult { 40 + export interface ValibotFinal { 41 + pipes: Pipes; 42 + /** Type annotation for schemas requiring explicit typing (e.g., lazy). */ 43 + typeName?: string | ts.Identifier; 44 + } 45 + 46 + /** 47 + * Result from composite handlers that walk children. 48 + */ 49 + export interface CompositeHandlerResult { 50 + childResults: Array<ValibotResult>; 54 51 pipes: Pipes; 55 52 }
+4 -6
packages/openapi-ts/src/plugins/valibot/shared/webhook.ts
··· 2 2 3 3 import { buildOperationSchema } from './operation-schema'; 4 4 import type { ProcessorContext, ProcessorResult } from './processor'; 5 - import type { IrSchemaToAstOptions } from './types'; 6 5 7 6 export function irWebhookToAst({ 8 7 operation, ··· 10 9 plugin, 11 10 processor, 12 11 tags, 13 - }: Pick<IrSchemaToAstOptions, 'plugin'> & 14 - Pick<ProcessorContext, 'path' | 'tags'> & { 15 - operation: IR.OperationObject; 16 - processor: ProcessorResult; 17 - }): void { 12 + }: Pick<ProcessorContext, 'path' | 'plugin' | 'tags'> & { 13 + operation: IR.OperationObject; 14 + processor: ProcessorResult; 15 + }): void { 18 16 if (plugin.config.webhooks.enabled) { 19 17 const { schema } = buildOperationSchema(operation); 20 18
-2
packages/openapi-ts/src/plugins/valibot/v1/api.ts
··· 48 48 .do(...(statements instanceof Array ? statements : [statements])); 49 49 } 50 50 } 51 - return; 52 51 }; 53 52 54 53 export const createResponseValidatorV1 = ({ ··· 90 89 .do(...(statements instanceof Array ? statements : [statements])); 91 90 } 92 91 } 93 - return; 94 92 };
+13 -21
packages/openapi-ts/src/plugins/valibot/v1/processor.ts
··· 1 - import { ref, refs } from '@hey-api/codegen-core'; 1 + import { ref } from '@hey-api/codegen-core'; 2 2 import type { IR } from '@hey-api/shared'; 3 3 import { createSchemaProcessor, createSchemaWalker, pathToJsonPointer } from '@hey-api/shared'; 4 4 5 5 import { exportAst } from '../shared/export'; 6 6 import type { ProcessorContext, ProcessorResult } from '../shared/processor'; 7 - import type { PluginState, ValibotAppliedResult } from '../shared/types'; 7 + import type { ValibotFinal } from '../shared/types'; 8 8 import type { ValibotPlugin } from '../types'; 9 9 import { createVisitor } from './walker'; 10 10 ··· 37 37 if (!processor.markEmitted(ctx.path)) return; 38 38 39 39 processor.withContext({ anchor: ctx.namingAnchor, tags: ctx.tags }, () => { 40 - const state = refs<PluginState>({ 41 - hasLazyExpression: false, 42 - path: ctx.path, 43 - tags: ctx.tags, 44 - }); 45 - 46 - const visitor = createVisitor({ 47 - schemaExtractor: extractor, 48 - state, 49 - }); 40 + const visitor = createVisitor({ schemaExtractor: extractor }); 50 41 const walk = createSchemaWalker(visitor); 51 42 52 43 const result = walk(ctx.schema, { 53 44 path: ref(ctx.path), 54 45 plugin, 55 46 }); 56 - const ast = 57 - (visitor.applyModifiers(result, { 58 - path: ref(ctx.path), 59 - plugin, 60 - }) as ValibotAppliedResult) ?? result.expression; 61 - if (result.hasLazyExpression) { 62 - state.hasLazyExpression['~ref'] = true; 63 - } 47 + 48 + const final = visitor.applyModifiers(result, { 49 + path: ref(ctx.path), 50 + plugin, 51 + }) as ValibotFinal; 64 52 65 - exportAst({ ...ctx, ast, plugin, state }); 53 + exportAst({ 54 + ...ctx, 55 + final, 56 + plugin, 57 + }); 66 58 }); 67 59 } 68 60
+36 -86
packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts
··· 1 - import type { SchemaWithType } from '@hey-api/shared'; 1 + import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 2 import { childContext, deduplicateSchema } from '@hey-api/shared'; 3 3 4 4 import { $ } from '../../../../ts-dsl'; 5 + import type { Pipes } from '../../shared/pipes'; 5 6 import { pipesToNode } from '../../shared/pipes'; 6 - import type { 7 - Ast, 8 - IrSchemaToAstOptions, 9 - ValibotAppliedResult, 10 - ValibotSchemaResult, 11 - } from '../../shared/types'; 7 + import type { CompositeHandlerResult, ValibotFinal, ValibotResult } from '../../shared/types'; 8 + import type { ValibotPlugin } from '../../types'; 12 9 import { identifiers } from '../constants'; 13 - import { unknownToAst } from './unknown'; 10 + import { unknownToPipes } from './unknown'; 14 11 15 - export function arrayToAst( 16 - options: IrSchemaToAstOptions & { 17 - applyModifiers: ( 18 - result: ValibotSchemaResult, 19 - opts: { optional?: boolean }, 20 - ) => ValibotAppliedResult; 21 - schema: SchemaWithType<'array'>; 22 - }, 23 - ): Omit<Ast, 'typeName'> { 24 - const { applyModifiers, plugin, walk } = options; 25 - let { schema } = options; 12 + interface ArrayToPipesContext { 13 + applyModifiers: (result: ValibotResult, options?: { optional?: boolean }) => ValibotFinal; 14 + plugin: ValibotPlugin['Instance']; 15 + schema: SchemaWithType<'array'>; 16 + walk: Walker<ValibotResult, ValibotPlugin['Instance']>; 17 + walkerCtx: SchemaVisitorContext<ValibotPlugin['Instance']>; 18 + } 26 19 27 - const result: Omit<Ast, 'typeName'> = { 28 - pipes: [], 29 - }; 20 + export function arrayToPipes(ctx: ArrayToPipesContext): CompositeHandlerResult { 21 + const { plugin, walk, walkerCtx } = ctx; 22 + let { schema } = ctx; 30 23 31 24 const v = plugin.external('valibot.v'); 32 - const functionName = $(v).attr(identifiers.schemas.array); 25 + const arrayFn = $(v).attr(identifiers.schemas.array); 26 + const childResults: Array<ValibotResult> = []; 27 + const resultPipes: Pipes = []; 33 28 34 29 if (!schema.items) { 35 - const expression = functionName.call( 36 - unknownToAst({ 37 - ...options, 38 - schema: { 39 - type: 'unknown', 40 - }, 41 - }), 42 - ); 43 - result.pipes.push(expression); 30 + resultPipes.push(arrayFn.call(unknownToPipes({ plugin }))); 44 31 } else { 45 32 schema = deduplicateSchema({ schema }); 46 33 47 - // at least one item is guaranteed 48 - const itemExpressions = schema.items!.map((item, index) => { 49 - const itemResult = walk( 50 - item, 51 - childContext( 52 - { 53 - path: options.state.path, 54 - plugin: options.plugin, 55 - }, 56 - 'items', 57 - index, 58 - ), 59 - ); 60 - if (itemResult.hasLazyExpression) { 61 - result.hasLazyExpression = true; 62 - } 34 + for (let i = 0; i < schema.items!.length; i++) { 35 + const item = schema.items![i]!; 36 + const result = walk(item, childContext(walkerCtx, 'items', i)); 37 + childResults.push(result); 38 + } 63 39 64 - const finalExpr = applyModifiers(itemResult, { optional: false }); 65 - return pipesToNode(finalExpr.pipes, plugin); 66 - }); 67 - 68 - if (itemExpressions.length === 1) { 69 - const expression = functionName.call(...itemExpressions); 70 - result.pipes.push(expression); 40 + if (childResults.length === 1) { 41 + const itemNode = pipesToNode(ctx.applyModifiers(childResults[0]!).pipes, plugin); 42 + resultPipes.push(arrayFn.call(itemNode)); 71 43 } else { 72 - if (schema.logicalOperator === 'and') { 73 - // TODO: parser - handle intersection 74 - // return tsc.typeArrayNode( 75 - // tsc.typeIntersectionNode({ types: itemExpressions }), 76 - // ); 77 - } 78 - 79 - // TODO: parser - handle union 80 - // return tsc.typeArrayNode(tsc.typeUnionNode({ types: itemExpressions })); 81 - 82 - const expression = functionName.call( 83 - unknownToAst({ 84 - ...options, 85 - schema: { 86 - type: 'unknown', 87 - }, 88 - }), 89 - ); 90 - result.pipes.push(expression); 44 + // TODO: handle intersection/union properly 45 + resultPipes.push(arrayFn.call(unknownToPipes({ plugin }))); 91 46 } 92 47 } 93 48 94 49 if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { 95 - const expression = $(v).attr(identifiers.actions.length).call($.fromValue(schema.minItems)); 96 - result.pipes.push(expression); 50 + resultPipes.push($(v).attr(identifiers.actions.length).call($.fromValue(schema.minItems))); 97 51 } else { 98 52 if (schema.minItems !== undefined) { 99 - const expression = $(v) 100 - .attr(identifiers.actions.minLength) 101 - .call($.fromValue(schema.minItems)); 102 - result.pipes.push(expression); 53 + resultPipes.push($(v).attr(identifiers.actions.minLength).call($.fromValue(schema.minItems))); 103 54 } 104 - 105 55 if (schema.maxItems !== undefined) { 106 - const expression = $(v) 107 - .attr(identifiers.actions.maxLength) 108 - .call($.fromValue(schema.maxItems)); 109 - result.pipes.push(expression); 56 + resultPipes.push($(v).attr(identifiers.actions.maxLength).call($.fromValue(schema.maxItems))); 110 57 } 111 58 } 112 59 113 - return result as Omit<Ast, 'typeName'>; 60 + return { 61 + childResults, 62 + pipes: resultPipes, 63 + }; 114 64 }
+8 -11
packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 - import { pipesToNode } from '../../shared/pipes'; 5 - import type { IrSchemaToAstOptions } from '../../shared/types'; 4 + import type { Pipe } from '../../shared/pipes'; 5 + import type { ValibotPlugin } from '../../types'; 6 6 import { identifiers } from '../constants'; 7 7 8 - export function booleanToAst({ 8 + export function booleanToPipes({ 9 9 plugin, 10 10 schema, 11 - }: Pick<IrSchemaToAstOptions, 'plugin'> & { 11 + }: { 12 + plugin: ValibotPlugin['Instance']; 12 13 schema: SchemaWithType<'boolean'>; 13 - }): ReturnType<typeof $.call | typeof $.expr> { 14 - const pipes: Array<ReturnType<typeof $.call>> = []; 15 - 14 + }): Pipe { 16 15 const v = plugin.external('valibot.v'); 17 16 18 17 if (typeof schema.const === 'boolean') { 19 - pipes.push($(v).attr(identifiers.schemas.literal).call($.literal(schema.const))); 20 - return pipesToNode(pipes, plugin); 18 + return $(v).attr(identifiers.schemas.literal).call($.literal(schema.const)); 21 19 } 22 20 23 - pipes.push($(v).attr(identifiers.schemas.boolean).call()); 24 - return pipesToNode(pipes, plugin); 21 + return $(v).attr(identifiers.schemas.boolean).call(); 25 22 }
+25 -29
packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import type { EnumResolverContext } from '../../resolvers'; 5 - import type { Pipe, PipeResult } from '../../shared/pipes'; 5 + import type { Pipe, PipeResult, Pipes } from '../../shared/pipes'; 6 6 import { pipes } from '../../shared/pipes'; 7 - import type { IrSchemaToAstOptions } from '../../shared/types'; 7 + import type { ValibotPlugin } from '../../types'; 8 8 import { identifiers } from '../constants'; 9 - import { unknownToAst } from './unknown'; 9 + import { unknownToPipes } from './unknown'; 10 10 11 11 function itemsNode(ctx: EnumResolverContext): ReturnType<EnumResolverContext['nodes']['items']> { 12 12 const { schema } = ctx; 13 - 14 13 const enumMembers: Array<ReturnType<typeof $.literal>> = []; 15 - 16 14 let isNullable = false; 17 15 18 16 for (const item of schema.items ?? []) { ··· 23 21 } 24 22 } 25 23 26 - return { 27 - enumMembers, 28 - isNullable, 29 - }; 24 + return { enumMembers, isNullable }; 30 25 } 31 26 32 27 function baseNode(ctx: EnumResolverContext): PipeResult { ··· 38 33 .call($.array(...enumMembers)); 39 34 } 40 35 41 - function enumResolver(ctx: EnumResolverContext): PipeResult { 36 + function enumResolver(ctx: EnumResolverContext): Pipes { 42 37 const { enumMembers } = ctx.nodes.items(ctx); 43 38 44 39 if (!enumMembers.length) { ··· 51 46 return ctx.pipes.current; 52 47 } 53 48 54 - export function enumToAst({ 49 + export interface EnumToPipesResult { 50 + isNullable: boolean; 51 + pipe: Pipe; 52 + } 53 + 54 + export function enumToPipes({ 55 55 plugin, 56 56 schema, 57 - state, 58 - }: Pick<IrSchemaToAstOptions, 'plugin' | 'state'> & { 57 + }: { 58 + plugin: ValibotPlugin['Instance']; 59 59 schema: SchemaWithType<'enum'>; 60 - }): Pipe { 60 + }): EnumToPipesResult { 61 61 const v = plugin.external('valibot.v'); 62 62 63 - const { enumMembers } = itemsNode({ 63 + const { enumMembers, isNullable } = itemsNode({ 64 64 $, 65 65 nodes: { base: baseNode, items: itemsNode }, 66 66 pipes: { ...pipes, current: [] }, 67 67 plugin, 68 68 schema, 69 69 symbols: { v }, 70 - utils: { state }, 71 70 }); 72 71 73 72 if (!enumMembers.length) { 74 - return unknownToAst({ 75 - plugin, 76 - schema: { 77 - type: 'unknown', 78 - }, 79 - }); 73 + return { 74 + isNullable, 75 + pipe: unknownToPipes({ plugin }), 76 + }; 80 77 } 81 78 82 79 const ctx: EnumResolverContext = { ··· 91 88 }, 92 89 plugin, 93 90 schema, 94 - symbols: { 95 - v, 96 - }, 97 - utils: { 98 - state, 99 - }, 91 + symbols: { v }, 100 92 }; 101 93 102 94 const resolver = plugin.config['~resolvers']?.enum; 103 95 const node = resolver?.(ctx) ?? enumResolver(ctx); 104 - return ctx.pipes.toNode(node, plugin); 96 + 97 + return { 98 + isNullable, 99 + pipe: ctx.pipes.toNode(node, plugin), 100 + }; 105 101 }
+7 -6
packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 - import type { IrSchemaToAstOptions } from '../../shared/types'; 4 + import type { Pipe } from '../../shared/pipes'; 5 + import type { ValibotPlugin } from '../../types'; 5 6 import { identifiers } from '../constants'; 6 7 7 - export function neverToAst({ 8 + export function neverToPipes({ 8 9 plugin, 9 - }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 + }: { 11 + plugin: ValibotPlugin['Instance']; 10 12 schema: SchemaWithType<'never'>; 11 - }) { 13 + }): Pipe { 12 14 const v = plugin.external('valibot.v'); 13 - const expression = $(v).attr(identifiers.schemas.never).call(); 14 - return expression; 15 + return $(v).attr(identifiers.schemas.never).call(); 15 16 }
+7 -6
packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 - import type { IrSchemaToAstOptions } from '../../shared/types'; 4 + import type { Pipe } from '../../shared/pipes'; 5 + import type { ValibotPlugin } from '../../types'; 5 6 import { identifiers } from '../constants'; 6 7 7 - export function nullToAst({ 8 + export function nullToPipes({ 8 9 plugin, 9 - }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 + }: { 11 + plugin: ValibotPlugin['Instance']; 10 12 schema: SchemaWithType<'null'>; 11 - }) { 13 + }): Pipe { 12 14 const v = plugin.external('valibot.v'); 13 - const expression = $(v).attr(identifiers.schemas.null).call(); 14 - return expression; 15 + return $(v).attr(identifiers.schemas.null).call(); 15 16 }
+43 -21
packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts
··· 6 6 import type { NumberResolverContext } from '../../resolvers'; 7 7 import type { Pipe, PipeResult, Pipes } from '../../shared/pipes'; 8 8 import { pipes } from '../../shared/pipes'; 9 - import type { IrSchemaToAstOptions } from '../../shared/types'; 9 + import type { ValibotPlugin } from '../../types'; 10 10 import { identifiers } from '../constants'; 11 11 12 12 function baseNode(ctx: NumberResolverContext): PipeResult { 13 13 const { schema, symbols } = ctx; 14 14 const { v } = symbols; 15 + 15 16 if (ctx.utils.shouldCoerceToBigInt(schema.format)) { 16 17 return [ 17 18 $(v) ··· 28 29 .call($.func().param('x').do($('BigInt').call('x').return())), 29 30 ]; 30 31 } 31 - const pipes: Pipes = []; 32 - pipes.push($(v).attr(identifiers.schemas.number).call()); 32 + 33 + const result: Pipes = []; 34 + result.push($(v).attr(identifiers.schemas.number).call()); 35 + 33 36 if (schema.type === 'integer') { 34 - pipes.push($(v).attr(identifiers.actions.integer).call()); 37 + result.push($(v).attr(identifiers.actions.integer).call()); 35 38 } 36 - return pipes; 39 + 40 + return result; 37 41 } 38 42 39 - function constNode(ctx: NumberResolverContext): PipeResult | undefined { 43 + function constNode(ctx: NumberResolverContext): PipeResult { 40 44 const { schema, symbols } = ctx; 41 45 const { v } = symbols; 42 - if (schema.const === undefined) return; 46 + 47 + if (schema.const === undefined) { 48 + return; 49 + } 50 + 43 51 return $(v) 44 52 .attr(identifiers.schemas.literal) 45 53 .call(ctx.utils.maybeBigInt(schema.const, schema.format)); 46 54 } 47 55 48 - function maxNode(ctx: NumberResolverContext): PipeResult | undefined { 56 + function maxNode(ctx: NumberResolverContext): PipeResult { 49 57 const { schema, symbols } = ctx; 50 58 const { v } = symbols; 59 + 51 60 if (schema.exclusiveMaximum !== undefined) { 52 61 return $(v) 53 62 .attr(identifiers.actions.ltValue) 54 63 .call(ctx.utils.maybeBigInt(schema.exclusiveMaximum, schema.format)); 55 64 } 65 + 56 66 if (schema.maximum !== undefined) { 57 67 return $(v) 58 68 .attr(identifiers.actions.maxValue) 59 69 .call(ctx.utils.maybeBigInt(schema.maximum, schema.format)); 60 70 } 71 + 61 72 const limit = ctx.utils.getIntegerLimit(schema.format); 62 73 if (limit) { 63 74 return $(v) 64 75 .attr(identifiers.actions.maxValue) 65 76 .call(ctx.utils.maybeBigInt(limit.maxValue, schema.format), $.literal(limit.maxError)); 66 77 } 67 - return; 68 78 } 69 79 70 - function minNode(ctx: NumberResolverContext): PipeResult | undefined { 80 + function minNode(ctx: NumberResolverContext): PipeResult { 71 81 const { schema, symbols } = ctx; 72 82 const { v } = symbols; 83 + 73 84 if (schema.exclusiveMinimum !== undefined) { 74 85 return $(v) 75 86 .attr(identifiers.actions.gtValue) 76 87 .call(ctx.utils.maybeBigInt(schema.exclusiveMinimum, schema.format)); 77 88 } 89 + 78 90 if (schema.minimum !== undefined) { 79 91 return $(v) 80 92 .attr(identifiers.actions.minValue) 81 93 .call(ctx.utils.maybeBigInt(schema.minimum, schema.format)); 82 94 } 95 + 83 96 const limit = ctx.utils.getIntegerLimit(schema.format); 84 97 if (limit) { 85 98 return $(v) 86 99 .attr(identifiers.actions.minValue) 87 100 .call(ctx.utils.maybeBigInt(limit.minValue, schema.format), $.literal(limit.minError)); 88 101 } 89 - return; 90 102 } 91 103 92 104 function numberResolver(ctx: NumberResolverContext): Pipes { 93 - const constNode = ctx.nodes.const(ctx); 94 - if (constNode) return ctx.pipes.push(ctx.pipes.current, constNode); 105 + const constResult = ctx.nodes.const(ctx); 106 + if (constResult) { 107 + return ctx.pipes.push(ctx.pipes.current, constResult); 108 + } 95 109 96 - const baseNode = ctx.nodes.base(ctx); 97 - if (baseNode) ctx.pipes.push(ctx.pipes.current, baseNode); 110 + const baseResult = ctx.nodes.base(ctx); 111 + if (baseResult) { 112 + ctx.pipes.push(ctx.pipes.current, baseResult); 113 + } 98 114 99 - const minNode = ctx.nodes.min(ctx); 100 - if (minNode) ctx.pipes.push(ctx.pipes.current, minNode); 115 + const minResult = ctx.nodes.min(ctx); 116 + if (minResult) { 117 + ctx.pipes.push(ctx.pipes.current, minResult); 118 + } 101 119 102 - const maxNode = ctx.nodes.max(ctx); 103 - if (maxNode) ctx.pipes.push(ctx.pipes.current, maxNode); 120 + const maxResult = ctx.nodes.max(ctx); 121 + if (maxResult) { 122 + ctx.pipes.push(ctx.pipes.current, maxResult); 123 + } 104 124 105 125 return ctx.pipes.current; 106 126 } 107 127 108 - export function numberToNode({ 128 + export function numberToPipes({ 109 129 plugin, 110 130 schema, 111 - }: Pick<IrSchemaToAstOptions, 'plugin'> & { 131 + }: { 132 + plugin: ValibotPlugin['Instance']; 112 133 schema: SchemaWithType<'integer' | 'number'>; 113 134 }): Pipe { 114 135 const ctx: NumberResolverContext = { ··· 134 155 shouldCoerceToBigInt, 135 156 }, 136 157 }; 158 + 137 159 const resolver = plugin.config['~resolvers']?.number; 138 160 const node = resolver?.(ctx) ?? numberResolver(ctx); 139 161 return ctx.pipes.toNode(node, plugin);
+42 -57
packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts
··· 1 - import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 1 import { childContext } from '@hey-api/shared'; 3 2 4 3 import { $ } from '../../../../ts-dsl'; 5 4 import type { ObjectResolverContext } from '../../resolvers'; 6 - import type { Pipe, PipeResult } from '../../shared/pipes'; 7 - import { pipes } from '../../shared/pipes'; 8 - import type { 9 - Ast, 10 - IrSchemaToAstOptions, 11 - ValibotAppliedResult, 12 - ValibotSchemaResult, 13 - } from '../../shared/types'; 14 - import type { ValibotPlugin } from '../../types'; 5 + import type { Pipe, Pipes } from '../../shared/pipes'; 6 + import { pipes, pipesToNode } from '../../shared/pipes'; 7 + import type { CompositeHandlerResult, ValibotResult } from '../../shared/types'; 15 8 import { identifiers } from '../constants'; 16 9 17 - type WalkerCtx = SchemaVisitorContext<ValibotPlugin['Instance']>; 18 - 19 - interface ObjectToAstOptions extends IrSchemaToAstOptions { 20 - applyModifiers: ( 21 - result: ValibotSchemaResult, 22 - opts: { optional?: boolean }, 23 - ) => ValibotAppliedResult; 24 - schema: SchemaWithType<'object'>; 25 - walk: Walker<ValibotSchemaResult, ValibotPlugin['Instance']>; 26 - walkerCtx: WalkerCtx; 27 - } 28 - 29 - type ExtendedContext = ObjectResolverContext & { 30 - applyModifiers: ObjectToAstOptions['applyModifiers']; 31 - walk: ObjectToAstOptions['walk']; 32 - walkerCtx: ObjectToAstOptions['walkerCtx']; 33 - }; 34 - 35 - function additionalPropertiesNode(ctx: ExtendedContext): Pipe | null | undefined { 36 - const { plugin, schema, walk, walkerCtx } = ctx; 10 + function additionalPropertiesNode(ctx: ObjectResolverContext): Pipe | null | undefined { 11 + const { schema } = ctx; 37 12 38 13 if (!schema.additionalProperties || !schema.additionalProperties.type) return; 39 14 if (schema.additionalProperties.type === 'never') return null; 40 15 41 - const additionalResult = walk( 16 + const additionalResult = ctx.walk( 42 17 schema.additionalProperties, 43 - childContext(walkerCtx, 'additionalProperties'), 18 + childContext(ctx.walkerCtx, 'additionalProperties'), 44 19 ); 45 - if (additionalResult.hasLazyExpression) ctx.utils.ast.hasLazyExpression = true; 46 - return pipes.toNode(additionalResult.expression.pipes, plugin); 20 + ctx._childResults.push(additionalResult); 21 + 22 + return pipesToNode(additionalResult.pipes, ctx.plugin); 47 23 } 48 24 49 - function baseNode(ctx: ExtendedContext): PipeResult { 50 - const { nodes, symbols } = ctx; 51 - const { v } = symbols; 25 + function baseNode(ctx: ObjectResolverContext): Pipes | Pipe { 26 + const { v } = ctx.symbols; 52 27 53 - const additional = nodes.additionalProperties(ctx); 54 - const shape = nodes.shape(ctx); 28 + const additional = ctx.nodes.additionalProperties(ctx); 29 + const shape = ctx.nodes.shape(ctx); 55 30 56 31 if (additional === null) { 57 32 return $(v).attr(identifiers.schemas.strictObject).call(shape); ··· 70 45 return $(v).attr(identifiers.schemas.object).call(shape); 71 46 } 72 47 73 - function objectResolver(ctx: ExtendedContext): PipeResult { 48 + function objectResolver(ctx: ObjectResolverContext): Pipes | Pipe { 74 49 // TODO: parser - handle constants 75 50 return ctx.nodes.base(ctx); 76 51 } 77 52 78 - function shapeNode(ctx: ExtendedContext): ReturnType<typeof $.object> { 79 - const { applyModifiers, plugin, schema, walk, walkerCtx } = ctx; 53 + function shapeNode(ctx: ObjectResolverContext): ReturnType<typeof $.object> { 54 + const { schema } = ctx; 80 55 const shape = $.object().pretty(); 81 56 82 57 for (const name in schema.properties) { 83 58 const property = schema.properties[name]!; 84 59 const isOptional = !schema.required?.includes(name); 85 60 86 - const propertyResult = walk(property, childContext(walkerCtx, 'properties', name)); 87 - if (propertyResult.hasLazyExpression) ctx.utils.ast.hasLazyExpression = true; 61 + const propertyResult = ctx.walk(property, childContext(ctx.walkerCtx, 'properties', name)); 62 + ctx._childResults.push(propertyResult); 88 63 89 - const ast = applyModifiers(propertyResult, { 90 - optional: isOptional, 91 - }); 92 - 93 - shape.prop(name, pipes.toNode(ast.pipes, plugin)); 64 + const finalExpr = ctx.applyModifiers(propertyResult, { optional: isOptional }); 65 + shape.prop(name, pipesToNode(finalExpr.pipes, ctx.plugin)); 94 66 } 95 67 96 68 return shape; 97 69 } 98 70 99 - export function objectToAst(options: ObjectToAstOptions): Omit<Ast, 'typeName'> { 100 - const { plugin } = options; 101 - const ctx: ExtendedContext = { 102 - ...options, 71 + export function objectToPipes( 72 + ctx: Pick<ObjectResolverContext, 'applyModifiers' | 'plugin' | 'schema' | 'walk' | 'walkerCtx'>, 73 + ): CompositeHandlerResult { 74 + const { applyModifiers, plugin, schema, walk, walkerCtx } = ctx; 75 + 76 + const childResults: Array<ValibotResult> = []; 77 + 78 + const extendedCtx: ObjectResolverContext = { 103 79 $, 80 + _childResults: childResults, 81 + applyModifiers, 104 82 nodes: { 105 83 additionalProperties: additionalPropertiesNode, 106 84 base: baseNode, ··· 110 88 ...pipes, 111 89 current: [], 112 90 }, 91 + plugin, 92 + schema, 113 93 symbols: { 114 94 v: plugin.external('valibot.v'), 115 95 }, 116 96 utils: { 117 97 ast: {}, 118 - state: options.state, 119 98 }, 99 + walk, 100 + walkerCtx, 120 101 }; 102 + 121 103 const resolver = plugin.config['~resolvers']?.object; 122 - const node = resolver?.(ctx) ?? objectResolver(ctx); 123 - ctx.utils.ast.pipes = [ctx.pipes.toNode(node, plugin)]; 124 - return ctx.utils.ast as Omit<Ast, 'typeName'>; 104 + const node = resolver?.(extendedCtx) ?? objectResolver(extendedCtx); 105 + 106 + return { 107 + childResults, 108 + pipes: [extendedCtx.pipes.toNode(node, plugin)], 109 + }; 125 110 }
+20 -11
packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 + import { shouldCoerceToBigInt } from '../../../../plugins/shared/utils/coerce'; 3 4 import { $ } from '../../../../ts-dsl'; 4 5 import type { StringResolverContext } from '../../resolvers'; 5 6 import type { Pipe, PipeResult, Pipes } from '../../shared/pipes'; 6 7 import { pipes } from '../../shared/pipes'; 7 - import type { IrSchemaToAstOptions } from '../../shared/types'; 8 + import type { ValibotPlugin } from '../../types'; 8 9 import { identifiers } from '../constants'; 10 + import { numberToPipes } from './number'; 9 11 10 12 function baseNode(ctx: StringResolverContext): PipeResult { 11 13 const { v } = ctx.symbols; 12 14 return $(v).attr(identifiers.schemas.string).call(); 13 15 } 14 16 15 - function constNode(ctx: StringResolverContext): PipeResult | undefined { 17 + function constNode(ctx: StringResolverContext): PipeResult { 16 18 const { schema, symbols } = ctx; 17 19 const { v } = symbols; 18 20 if (typeof schema.const !== 'string') return; 19 21 return $(v).attr(identifiers.schemas.literal).call($.literal(schema.const)); 20 22 } 21 23 22 - function formatNode(ctx: StringResolverContext): PipeResult | undefined { 24 + function formatNode(ctx: StringResolverContext): PipeResult { 23 25 const { schema, symbols } = ctx; 24 26 const { v } = symbols; 25 27 switch (schema.format) { ··· 39 41 case 'uuid': 40 42 return $(v).attr(identifiers.actions.uuid).call(); 41 43 } 42 - 43 - return; 44 44 } 45 45 46 - function lengthNode(ctx: StringResolverContext): PipeResult | undefined { 46 + function lengthNode(ctx: StringResolverContext): PipeResult { 47 47 const { schema, symbols } = ctx; 48 48 const { v } = symbols; 49 49 if (schema.minLength === undefined || schema.minLength !== schema.maxLength) return; 50 50 return $(v).attr(identifiers.actions.length).call($.literal(schema.minLength)); 51 51 } 52 52 53 - function maxLengthNode(ctx: StringResolverContext): PipeResult | undefined { 53 + function maxLengthNode(ctx: StringResolverContext): PipeResult { 54 54 const { schema, symbols } = ctx; 55 55 const { v } = symbols; 56 56 if (schema.maxLength === undefined) return; 57 57 return $(v).attr(identifiers.actions.maxLength).call($.literal(schema.maxLength)); 58 58 } 59 59 60 - function minLengthNode(ctx: StringResolverContext): PipeResult | undefined { 60 + function minLengthNode(ctx: StringResolverContext): PipeResult { 61 61 const { schema, symbols } = ctx; 62 62 const { v } = symbols; 63 63 if (schema.minLength === undefined) return; 64 64 return $(v).attr(identifiers.actions.minLength).call($.literal(schema.minLength)); 65 65 } 66 66 67 - function patternNode(ctx: StringResolverContext): PipeResult | undefined { 67 + function patternNode(ctx: StringResolverContext): PipeResult { 68 68 const { schema, symbols } = ctx; 69 69 const { v } = symbols; 70 70 if (!schema.pattern) return; ··· 98 98 return ctx.pipes.current; 99 99 } 100 100 101 - export function stringToNode({ 101 + export function stringToPipes({ 102 102 plugin, 103 103 schema, 104 - }: Pick<IrSchemaToAstOptions, 'plugin'> & { 104 + }: { 105 + plugin: ValibotPlugin['Instance']; 105 106 schema: SchemaWithType<'string'>; 106 107 }): Pipe { 108 + if (shouldCoerceToBigInt(schema.format)) { 109 + return numberToPipes({ 110 + plugin, 111 + schema: { ...schema, type: 'number' }, 112 + }); 113 + } 114 + 107 115 const ctx: StringResolverContext = { 108 116 $, 109 117 nodes: { ··· 125 133 v: plugin.external('valibot.v'), 126 134 }, 127 135 }; 136 + 128 137 const resolver = plugin.config['~resolvers']?.string; 129 138 const node = resolver?.(ctx) ?? stringResolver(ctx); 130 139 return ctx.pipes.toNode(node, plugin);
+40 -57
packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts
··· 1 - import type { SchemaWithType } from '@hey-api/shared'; 1 + import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 2 import { childContext } from '@hey-api/shared'; 3 3 4 4 import { $ } from '../../../../ts-dsl'; 5 5 import { pipesToNode } from '../../shared/pipes'; 6 - import type { 7 - Ast, 8 - IrSchemaToAstOptions, 9 - ValibotAppliedResult, 10 - ValibotSchemaResult, 11 - } from '../../shared/types'; 6 + import type { CompositeHandlerResult, ValibotFinal, ValibotResult } from '../../shared/types'; 7 + import type { ValibotPlugin } from '../../types'; 12 8 import { identifiers } from '../constants'; 13 - import { unknownToAst } from './unknown'; 9 + import { unknownToPipes } from './unknown'; 14 10 15 - export function tupleToAst( 16 - options: IrSchemaToAstOptions & { 17 - applyModifiers: ( 18 - result: ValibotSchemaResult, 19 - opts: { optional?: boolean }, 20 - ) => ValibotAppliedResult; 21 - schema: SchemaWithType<'tuple'>; 22 - }, 23 - ): Omit<Ast, 'typeName'> { 24 - const { applyModifiers, plugin, schema, walk } = options; 11 + interface TupleToPipesContext { 12 + applyModifiers: (result: ValibotResult, options?: { optional?: boolean }) => ValibotFinal; 13 + plugin: ValibotPlugin['Instance']; 14 + schema: SchemaWithType<'tuple'>; 15 + walk: Walker<ValibotResult, ValibotPlugin['Instance']>; 16 + walkerCtx: SchemaVisitorContext<ValibotPlugin['Instance']>; 17 + } 25 18 26 - const result: Partial<Omit<Ast, 'typeName'>> = {}; 19 + export function tupleToPipes(ctx: TupleToPipesContext): CompositeHandlerResult { 20 + const { plugin, schema, walk, walkerCtx } = ctx; 27 21 28 22 const v = plugin.external('valibot.v'); 23 + const childResults: Array<ValibotResult> = []; 29 24 30 25 if (schema.const && Array.isArray(schema.const)) { 31 26 const tupleElements = schema.const.map((value) => 32 27 $(v).attr(identifiers.schemas.literal).call($.fromValue(value)), 33 28 ); 34 - result.pipes = [ 35 - $(v) 36 - .attr(identifiers.schemas.tuple) 37 - .call($.array(...tupleElements)), 38 - ]; 39 - return result as Omit<Ast, 'typeName'>; 29 + 30 + return { 31 + childResults: [], 32 + pipes: [ 33 + $(v) 34 + .attr(identifiers.schemas.tuple) 35 + .call($.array(...tupleElements)), 36 + ], 37 + }; 40 38 } 41 39 42 40 if (schema.items) { 43 - const tupleElements = schema.items.map((item, index) => { 44 - const itemResult = walk( 45 - item, 46 - childContext( 47 - { 48 - path: options.state.path, 49 - plugin: options.plugin, 50 - }, 51 - 'items', 52 - index, 53 - ), 54 - ); 55 - if (itemResult.hasLazyExpression) { 56 - result.hasLazyExpression = true; 57 - } 41 + for (let i = 0; i < schema.items.length; i++) { 42 + const item = schema.items[i]!; 43 + const result = walk(item, childContext(walkerCtx, 'items', i)); 44 + childResults.push(result); 45 + } 58 46 59 - const finalExpr = applyModifiers(itemResult, { optional: false }); 60 - return pipesToNode(finalExpr.pipes, plugin); 61 - }); 62 - result.pipes = [ 63 - $(v) 64 - .attr(identifiers.schemas.tuple) 65 - .call($.array(...tupleElements)), 66 - ]; 67 - return result as Omit<Ast, 'typeName'>; 47 + const tupleElements = childResults.map((r) => pipesToNode(ctx.applyModifiers(r).pipes, plugin)); 48 + 49 + return { 50 + childResults, 51 + pipes: [ 52 + $(v) 53 + .attr(identifiers.schemas.tuple) 54 + .call($.array(...tupleElements)), 55 + ], 56 + }; 68 57 } 69 58 70 59 return { 71 - pipes: [ 72 - unknownToAst({ 73 - ...options, 74 - schema: { 75 - type: 'unknown', 76 - }, 77 - }), 78 - ], 60 + childResults: [], 61 + pipes: [unknownToPipes({ plugin })], 79 62 }; 80 63 }
+7 -6
packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 - import type { IrSchemaToAstOptions } from '../../shared/types'; 4 + import type { Pipe } from '../../shared/pipes'; 5 + import type { ValibotPlugin } from '../../types'; 5 6 import { identifiers } from '../constants'; 6 7 7 - export function undefinedToAst({ 8 + export function undefinedToPipes({ 8 9 plugin, 9 - }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 + }: { 11 + plugin: ValibotPlugin['Instance']; 10 12 schema: SchemaWithType<'undefined'>; 11 - }) { 13 + }): Pipe { 12 14 const v = plugin.external('valibot.v'); 13 - const expression = $(v).attr(identifiers.schemas.undefined).call(); 14 - return expression; 15 + return $(v).attr(identifiers.schemas.undefined).call(); 15 16 }
+8 -7
packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 - import type { IrSchemaToAstOptions } from '../../shared/types'; 4 + import type { Pipe } from '../../shared/pipes'; 5 + import type { ValibotPlugin } from '../../types'; 5 6 import { identifiers } from '../constants'; 6 7 7 - export function unknownToAst({ 8 + export function unknownToPipes({ 8 9 plugin, 9 - }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 - schema: SchemaWithType<'unknown'>; 11 - }) { 10 + }: { 11 + plugin: ValibotPlugin['Instance']; 12 + schema?: SchemaWithType<'unknown'>; 13 + }): Pipe { 12 14 const v = plugin.external('valibot.v'); 13 - const expression = $(v).attr(identifiers.schemas.unknown).call(); 14 - return expression; 15 + return $(v).attr(identifiers.schemas.unknown).call(); 15 16 }
+7 -6
packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts
··· 1 1 import type { SchemaWithType } from '@hey-api/shared'; 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 - import type { IrSchemaToAstOptions } from '../../shared/types'; 4 + import type { Pipe } from '../../shared/pipes'; 5 + import type { ValibotPlugin } from '../../types'; 5 6 import { identifiers } from '../constants'; 6 7 7 - export function voidToAst({ 8 + export function voidToPipes({ 8 9 plugin, 9 - }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 + }: { 11 + plugin: ValibotPlugin['Instance']; 10 12 schema: SchemaWithType<'void'>; 11 - }) { 13 + }): Pipe { 12 14 const v = plugin.external('valibot.v'); 13 - const expression = $(v).attr(identifiers.schemas.void).call(); 14 - return expression; 15 + return $(v).attr(identifiers.schemas.void).call(); 15 16 }
+167 -193
packages/openapi-ts/src/plugins/valibot/v1/walker.ts
··· 1 - import type { Refs, SymbolMeta } from '@hey-api/codegen-core'; 1 + import type { SymbolMeta } from '@hey-api/codegen-core'; 2 2 import { fromRef } from '@hey-api/codegen-core'; 3 3 import type { SchemaExtractor, SchemaVisitor } from '@hey-api/shared'; 4 4 import { pathToJsonPointer } from '@hey-api/shared'; 5 5 6 6 import { $ } from '../../../ts-dsl'; 7 - import { maybeBigInt, shouldCoerceToBigInt } from '../../shared/utils/coerce'; 7 + import { maybeBigInt } from '../../shared/utils/coerce'; 8 + import { composeMeta, defaultMeta, inheritMeta } from '../shared/meta'; 9 + import type { Pipes } from '../shared/pipes'; 8 10 import { pipesToNode } from '../shared/pipes'; 9 11 import type { ProcessorContext } from '../shared/processor'; 10 - import type { PluginState, ValibotAppliedResult, ValibotSchemaResult } from '../shared/types'; 12 + import type { ValibotFinal, ValibotMeta, ValibotResult } from '../shared/types'; 11 13 import type { ValibotPlugin } from '../types'; 12 14 import { identifiers } from './constants'; 13 - import { arrayToAst } from './toAst/array'; 14 - import { booleanToAst } from './toAst/boolean'; 15 - import { enumToAst } from './toAst/enum'; 16 - import { neverToAst } from './toAst/never'; 17 - import { nullToAst } from './toAst/null'; 18 - import { numberToNode } from './toAst/number'; 19 - import { objectToAst } from './toAst/object'; 20 - import { stringToNode } from './toAst/string'; 21 - import { tupleToAst } from './toAst/tuple'; 22 - import { undefinedToAst } from './toAst/undefined'; 23 - import { unknownToAst } from './toAst/unknown'; 24 - import { voidToAst } from './toAst/void'; 15 + import { arrayToPipes } from './toAst/array'; 16 + import { booleanToPipes } from './toAst/boolean'; 17 + import { enumToPipes } from './toAst/enum'; 18 + import { neverToPipes } from './toAst/never'; 19 + import { nullToPipes } from './toAst/null'; 20 + import { numberToPipes } from './toAst/number'; 21 + import { objectToPipes } from './toAst/object'; 22 + import { stringToPipes } from './toAst/string'; 23 + import { tupleToPipes } from './toAst/tuple'; 24 + import { undefinedToPipes } from './toAst/undefined'; 25 + import { unknownToPipes } from './toAst/unknown'; 26 + import { voidToPipes } from './toAst/void'; 25 27 26 28 export interface VisitorConfig { 27 29 /** Optional schema extractor function. */ 28 30 schemaExtractor?: SchemaExtractor<ProcessorContext>; 29 - /** The plugin state references. */ 30 - state: Refs<PluginState>; 31 31 } 32 32 33 - function getDefaultValue(result: ValibotSchemaResult) { 34 - return result.format ? maybeBigInt(result.default, result.format) : $.fromValue(result.default); 33 + function getDefaultValue(meta: ValibotMeta): ReturnType<typeof $.fromValue> { 34 + return meta.format ? maybeBigInt(meta.default, meta.format) : $.fromValue(meta.default); 35 35 } 36 36 37 37 export function createVisitor( 38 38 config: VisitorConfig, 39 - ): SchemaVisitor<ValibotSchemaResult, ValibotPlugin['Instance']> { 40 - const { schemaExtractor, state } = config; 39 + ): SchemaVisitor<ValibotResult, ValibotPlugin['Instance']> { 40 + const { schemaExtractor } = config; 41 + 41 42 return { 42 - applyModifiers(result, ctx, options = {}): ValibotAppliedResult { 43 + applyModifiers(result, ctx, options = {}): ValibotFinal { 43 44 const { optional } = options; 44 45 const v = ctx.plugin.external('valibot.v'); 45 - const pipes = [...result.expression.pipes]; 46 + const pipes: Pipes = [...result.pipes]; 46 47 47 - if (result.readonly) { 48 + if (result.meta.readonly) { 48 49 pipes.push($(v).attr(identifiers.actions.readonly).call()); 49 50 } 50 51 51 - const hasDefault = result.default !== undefined; 52 + const hasDefault = result.meta.default !== undefined; 52 53 const needsOptional = optional || hasDefault; 53 - const needsNullable = result.nullable; 54 + const needsNullable = result.meta.nullable; 54 55 const innerNode = pipesToNode(pipes, ctx.plugin); 55 56 57 + let finalPipes: Pipes; 58 + 56 59 if (needsOptional && needsNullable) { 57 60 if (hasDefault) { 58 - return { 59 - pipes: [ 60 - $(v).attr(identifiers.schemas.nullish).call(innerNode, getDefaultValue(result)), 61 - ], 62 - }; 61 + finalPipes = [ 62 + $(v).attr(identifiers.schemas.nullish).call(innerNode, getDefaultValue(result.meta)), 63 + ]; 64 + } else { 65 + finalPipes = [$(v).attr(identifiers.schemas.nullish).call(innerNode)]; 63 66 } 64 - return { pipes: [$(v).attr(identifiers.schemas.nullish).call(innerNode)] }; 65 - } 66 - 67 - if (needsOptional) { 67 + } else if (needsOptional) { 68 68 if (hasDefault) { 69 - return { 70 - pipes: [ 71 - $(v).attr(identifiers.schemas.optional).call(innerNode, getDefaultValue(result)), 72 - ], 73 - }; 69 + finalPipes = [ 70 + $(v).attr(identifiers.schemas.optional).call(innerNode, getDefaultValue(result.meta)), 71 + ]; 72 + } else { 73 + finalPipes = [$(v).attr(identifiers.schemas.optional).call(innerNode)]; 74 74 } 75 - return { pipes: [$(v).attr(identifiers.schemas.optional).call(innerNode)] }; 75 + } else if (needsNullable) { 76 + finalPipes = [$(v).attr(identifiers.schemas.nullable).call(innerNode)]; 77 + } else { 78 + finalPipes = pipes; 76 79 } 77 80 78 - if (needsNullable) { 79 - return { pipes: [$(v).attr(identifiers.schemas.nullable).call(innerNode)] }; 80 - } 81 - 82 - return { pipes }; 81 + return { 82 + pipes: finalPipes, 83 + typeName: result.meta.hasLazy ? identifiers.types.GenericSchema : undefined, 84 + }; 83 85 }, 84 86 array(schema, ctx, walk) { 85 - const applyModifiers = (result: ValibotSchemaResult, opts: { optional?: boolean }) => 86 - this.applyModifiers(result, ctx, opts) as ValibotAppliedResult; 87 - const ast = arrayToAst({ 88 - ...ctx, 87 + const applyModifiers = (result: ValibotResult, opts?: { optional?: boolean }) => 88 + this.applyModifiers(result, ctx, opts) as ValibotFinal; 89 + 90 + const { childResults, pipes } = arrayToPipes({ 89 91 applyModifiers, 92 + plugin: ctx.plugin, 90 93 schema, 91 - state, 92 94 walk, 95 + walkerCtx: ctx, 93 96 }); 97 + 94 98 return { 95 - default: schema.default, 96 - expression: ast, 97 - hasLazyExpression: state.hasLazyExpression['~ref'], 98 - nullable: false, 99 - readonly: schema.accessScope === 'read', 99 + meta: inheritMeta(schema, childResults), 100 + pipes, 100 101 }; 101 102 }, 102 103 boolean(schema, ctx) { 103 - const pipe = booleanToAst({ ...ctx, schema }); 104 + const pipe = booleanToPipes({ plugin: ctx.plugin, schema }); 104 105 return { 105 - default: schema.default, 106 - expression: { pipes: [pipe] }, 107 - nullable: false, 108 - readonly: schema.accessScope === 'read', 106 + meta: defaultMeta(schema), 107 + pipes: [pipe], 109 108 }; 110 109 }, 111 110 enum(schema, ctx) { 112 - const pipe = enumToAst({ ...ctx, schema, state }); 113 - const hasNull = 114 - schema.items?.some((item) => item.type === 'null' || item.const === null) ?? false; 111 + const { isNullable, pipe } = enumToPipes({ plugin: ctx.plugin, schema }); 115 112 return { 116 - default: schema.default, 117 - expression: { pipes: [pipe] }, 118 - nullable: hasNull, 119 - readonly: schema.accessScope === 'read', 113 + meta: { 114 + ...defaultMeta(schema), 115 + nullable: isNullable, 116 + }, 117 + pipes: [pipe], 120 118 }; 121 119 }, 122 120 integer(schema, ctx) { 123 - const pipe = numberToNode({ ...ctx, schema }); 121 + const pipe = numberToPipes({ plugin: ctx.plugin, schema }); 124 122 return { 125 - default: schema.default, 126 - expression: { pipes: [pipe] }, 127 - format: schema.format, 128 - nullable: false, 129 - readonly: schema.accessScope === 'read', 123 + meta: defaultMeta(schema), 124 + pipes: [pipe], 130 125 }; 131 126 }, 132 127 intercept(schema, ctx, walk) { ··· 141 136 plugin: ctx.plugin, 142 137 schema, 143 138 }); 139 + 144 140 if (extracted !== schema) { 145 141 return walk(extracted, ctx); 146 142 } ··· 148 144 }, 149 145 intersection(items, schemas, parentSchema, ctx) { 150 146 const v = ctx.plugin.external('valibot.v'); 151 - const hasAnyLazy = items.some((item) => item.hasLazyExpression); 152 - const itemNodes = items.map((item) => pipesToNode(item.expression.pipes, ctx.plugin)); 147 + const itemNodes = items.map((item) => pipesToNode(item.pipes, ctx.plugin)); 153 148 154 149 return { 155 - default: parentSchema.default, 156 - expression: { 157 - pipes: [ 158 - $(v) 159 - .attr(identifiers.schemas.intersect) 160 - .call($.array(...itemNodes)), 161 - ], 162 - }, 163 - hasLazyExpression: hasAnyLazy, 164 - nullable: items.some((i) => i.nullable), 165 - readonly: items.some((i) => i.readonly), 150 + meta: composeMeta(items, { default: parentSchema.default }), 151 + pipes: [ 152 + $(v) 153 + .attr(identifiers.schemas.intersect) 154 + .call($.array(...itemNodes)), 155 + ], 166 156 }; 167 157 }, 168 158 never(schema, ctx) { 169 - const pipe = neverToAst({ ...ctx, schema }); 159 + const pipe = neverToPipes({ plugin: ctx.plugin, schema }); 170 160 return { 171 - default: schema.default, 172 - expression: { pipes: [pipe] }, 173 - nullable: false, 174 - readonly: false, 161 + meta: { 162 + ...defaultMeta(schema), 163 + nullable: false, 164 + readonly: false, 165 + }, 166 + pipes: [pipe], 175 167 }; 176 168 }, 177 169 null(schema, ctx) { 178 - const pipe = nullToAst({ ...ctx, schema }); 170 + const pipe = nullToPipes({ plugin: ctx.plugin, schema }); 179 171 return { 180 - default: schema.default, 181 - expression: { pipes: [pipe] }, 182 - nullable: false, 183 - readonly: false, 172 + meta: { 173 + ...defaultMeta(schema), 174 + nullable: false, 175 + readonly: false, 176 + }, 177 + pipes: [pipe], 184 178 }; 185 179 }, 186 180 number(schema, ctx) { 187 - const pipe = numberToNode({ ...ctx, schema }); 181 + const pipe = numberToPipes({ plugin: ctx.plugin, schema }); 188 182 return { 189 - default: schema.default, 190 - expression: { pipes: [pipe] }, 191 - format: schema.format, 192 - nullable: false, 193 - readonly: schema.accessScope === 'read', 183 + meta: defaultMeta(schema), 184 + pipes: [pipe], 194 185 }; 195 186 }, 196 187 object(schema, ctx, walk) { 197 - const applyModifiers = (result: ValibotSchemaResult, opts: { optional?: boolean }) => 198 - this.applyModifiers(result, ctx, opts) as ValibotAppliedResult; 199 - const ast = objectToAst({ 188 + const applyModifiers = (result: ValibotResult, opts: { optional?: boolean }) => 189 + this.applyModifiers(result, ctx, opts) as ValibotFinal; 190 + 191 + const { childResults, pipes } = objectToPipes({ 200 192 applyModifiers, 201 193 plugin: ctx.plugin, 202 194 schema, 203 - state, 204 195 walk, 205 196 walkerCtx: ctx, 206 197 }); 198 + 207 199 return { 208 - default: schema.default, 209 - expression: ast, 210 - hasLazyExpression: state.hasLazyExpression['~ref'], 211 - nullable: false, 212 - readonly: schema.accessScope === 'read', 200 + meta: inheritMeta(schema, childResults), 201 + pipes, 213 202 }; 214 203 }, 215 204 postProcess(result, schema, ctx) { ··· 218 207 const metadataExpr = $(v) 219 208 .attr(identifiers.actions.metadata) 220 209 .call($.object().prop('description', $.literal(schema.description))); 210 + 221 211 return { 222 - ...result, 223 - expression: { pipes: [...result.expression.pipes, metadataExpr] }, 212 + meta: result.meta, 213 + pipes: [...result.pipes, metadataExpr], 224 214 }; 225 215 } 216 + 226 217 return result; 227 218 }, 228 219 reference($ref, schema, ctx) { ··· 235 226 }; 236 227 237 228 const refSymbol = ctx.plugin.referenceSymbol(query); 229 + const isRegistered = ctx.plugin.isSymbolRegistered(query); 238 230 239 - if (ctx.plugin.isSymbolRegistered(query)) { 231 + if (isRegistered) { 240 232 return { 241 - default: schema.default, 242 - expression: { pipes: [$(refSymbol)] }, 243 - nullable: false, 244 - readonly: schema.accessScope === 'read', 233 + meta: defaultMeta(schema), 234 + pipes: [$(refSymbol)], 245 235 }; 246 236 } 247 237 248 - state.hasLazyExpression['~ref'] = true; 249 238 return { 250 - default: schema.default, 251 - expression: { 252 - pipes: [ 253 - $(v) 254 - .attr(identifiers.schemas.lazy) 255 - .call($.func().do($(refSymbol).return())), 256 - ], 239 + meta: { 240 + ...defaultMeta(schema), 241 + hasLazy: true, 257 242 }, 258 - hasLazyExpression: true, 259 - nullable: false, 260 - readonly: schema.accessScope === 'read', 243 + pipes: [ 244 + $(v) 245 + .attr(identifiers.schemas.lazy) 246 + .call($.func().do($(refSymbol).return())), 247 + ], 261 248 }; 262 249 }, 263 250 string(schema, ctx) { 264 - if (shouldCoerceToBigInt(schema.format)) { 265 - const pipe = numberToNode({ 266 - plugin: ctx.plugin, 267 - schema: { ...schema, type: 'number' }, 268 - }); 269 - return { 270 - default: schema.default, 271 - expression: { pipes: [pipe] }, 272 - nullable: false, 273 - readonly: schema.accessScope === 'read', 274 - }; 275 - } 276 - 277 - const pipe = stringToNode({ ...ctx, schema }); 251 + const pipe = stringToPipes({ plugin: ctx.plugin, schema }); 278 252 return { 279 - default: schema.default, 280 - expression: { pipes: [pipe] }, 281 - nullable: false, 282 - readonly: schema.accessScope === 'read', 253 + meta: defaultMeta(schema), 254 + pipes: [pipe], 283 255 }; 284 256 }, 285 257 tuple(schema, ctx, walk) { 286 - const applyModifiers = (result: ValibotSchemaResult, opts: { optional?: boolean }) => 287 - this.applyModifiers(result, ctx, opts) as ValibotAppliedResult; 288 - const ast = tupleToAst({ 289 - ...ctx, 258 + const applyModifiers = (result: ValibotResult, opts?: { optional?: boolean }) => 259 + this.applyModifiers(result, ctx, opts) as ValibotFinal; 260 + 261 + const { childResults, pipes } = tupleToPipes({ 290 262 applyModifiers, 263 + plugin: ctx.plugin, 291 264 schema, 292 - state, 293 265 walk, 266 + walkerCtx: ctx, 294 267 }); 268 + 295 269 return { 296 - default: schema.default, 297 - expression: ast, 298 - hasLazyExpression: state.hasLazyExpression['~ref'], 299 - nullable: false, 300 - readonly: schema.accessScope === 'read', 270 + meta: inheritMeta(schema, childResults), 271 + pipes, 301 272 }; 302 273 }, 303 274 undefined(schema, ctx) { 304 - const pipe = undefinedToAst({ ...ctx, schema }); 275 + const pipe = undefinedToPipes({ plugin: ctx.plugin, schema }); 305 276 return { 306 - default: schema.default, 307 - expression: { pipes: [pipe] }, 308 - nullable: false, 309 - readonly: false, 277 + meta: { 278 + ...defaultMeta(schema), 279 + nullable: false, 280 + readonly: false, 281 + }, 282 + pipes: [pipe], 310 283 }; 311 284 }, 312 285 union(items, schemas, parentSchema, ctx) { 313 286 const v = ctx.plugin.external('valibot.v'); 314 - const hasAnyLazy = items.some((item) => item.hasLazyExpression); 315 287 316 - const hasNull = schemas.some((s) => s.type === 'null') || items.some((i) => i.nullable); 317 - 318 - const nonNullItems: typeof items = []; 288 + const hasNull = schemas.some((s) => s.type === 'null') || items.some((i) => i.meta.nullable); 319 289 290 + const nonNullItems: Array<ValibotResult> = []; 320 291 items.forEach((item, index) => { 321 292 const schema = schemas[index]!; 322 293 if (schema.type !== 'null') { ··· 324 295 } 325 296 }); 326 297 327 - let expression: ValibotSchemaResult['expression']; 298 + let pipes: Pipes; 299 + 328 300 if (nonNullItems.length === 0) { 329 - expression = { pipes: [$(v).attr(identifiers.schemas.null).call()] }; 301 + pipes = [$(v).attr(identifiers.schemas.null).call()]; 330 302 } else if (nonNullItems.length === 1) { 331 - expression = nonNullItems[0]!.expression; 303 + pipes = nonNullItems[0]!.pipes; 332 304 } else { 333 - const itemNodes = nonNullItems.map((i) => pipesToNode(i.expression.pipes, ctx.plugin)); 334 - expression = { 335 - pipes: [ 336 - $(v) 337 - .attr(identifiers.schemas.union) 338 - .call($.array(...itemNodes)), 339 - ], 340 - }; 305 + const itemNodes = nonNullItems.map((i) => pipesToNode(i.pipes, ctx.plugin)); 306 + pipes = [ 307 + $(v) 308 + .attr(identifiers.schemas.union) 309 + .call($.array(...itemNodes)), 310 + ]; 341 311 } 342 312 343 313 return { 344 - default: parentSchema.default, 345 - expression, 346 - hasLazyExpression: hasAnyLazy, 347 - nullable: hasNull, 348 - readonly: items.some((i) => i.readonly), 314 + meta: composeMeta(items, { 315 + default: parentSchema.default, 316 + nullable: hasNull, 317 + }), 318 + pipes, 349 319 }; 350 320 }, 351 321 unknown(schema, ctx) { 352 - const pipe = unknownToAst({ ...ctx, schema }); 322 + const pipe = unknownToPipes({ plugin: ctx.plugin }); 353 323 return { 354 - default: schema.default, 355 - expression: { pipes: [pipe] }, 356 - nullable: false, 357 - readonly: false, 324 + meta: { 325 + ...defaultMeta(schema), 326 + nullable: false, 327 + readonly: false, 328 + }, 329 + pipes: [pipe], 358 330 }; 359 331 }, 360 332 void(schema, ctx) { 361 - const pipe = voidToAst({ ...ctx, schema }); 333 + const pipe = voidToPipes({ plugin: ctx.plugin, schema }); 362 334 return { 363 - default: schema.default, 364 - expression: { pipes: [pipe] }, 365 - nullable: false, 366 - readonly: false, 335 + meta: { 336 + ...defaultMeta(schema), 337 + nullable: false, 338 + readonly: false, 339 + }, 340 + pipes: [pipe], 367 341 }; 368 342 }, 369 343 };
+1
packages/shared/src/plugins/types.ts
··· 126 126 > = { 127 127 Config: Plugin.Config<Plugin.Types<Config, ResolvedConfig, Api>>; 128 128 Handler: (args: { plugin: PluginInstance<Plugin.Types<Config, ResolvedConfig, Api>> }) => void; 129 + /** The plugin instance. */ 129 130 Instance: PluginInstance<Plugin.Types<Config, ResolvedConfig, Api>>; 130 131 Types: Plugin.Types<Config, ResolvedConfig, Api>; 131 132 };