fork of hey-api/openapi-ts because I need some additional things
at feat/skip-token 270 lines 7.8 kB view raw
1// TODO: symbol should be protected, but needs to be public to satisfy types 2import type { 3 AnalysisContext, 4 File, 5 FromRef, 6 Language, 7 Node, 8 NodeName, 9 NodeNameSanitizer, 10 NodeRelationship, 11 NodeScope, 12 Ref, 13 Symbol, 14} from '@hey-api/codegen-core'; 15import { fromRef, isNode, isRef, isSymbol, nodeBrand, ref } from '@hey-api/codegen-core'; 16import type { AnyString } from '@hey-api/types'; 17 18import { py } from '../ts-python'; 19import type { AccessOptions } from './utils/context'; 20 21export abstract class PyDsl<T extends py.Node = py.Node> implements Node<T> { 22 // eslint-disable-next-line @typescript-eslint/no-unused-vars 23 analyze(_: AnalysisContext): void {} 24 clone(): this { 25 const cloned = Object.create(Object.getPrototypeOf(this)); 26 Object.assign(cloned, this); 27 return cloned; 28 } 29 exported?: boolean; 30 file?: File; 31 get name(): Node['name'] { 32 return { 33 ...this._name, 34 set: (value) => { 35 this._name = ref(value); 36 if (isSymbol(value)) { 37 value.setNode(this); 38 } 39 }, 40 toString: () => (this._name ? this.$name(this._name) : ''), 41 } as Node['name']; 42 } 43 readonly nameSanitizer?: NodeNameSanitizer; 44 language: Language = 'python'; 45 parent?: Node; 46 root: boolean = false; 47 scope?: NodeScope = 'value'; 48 structuralChildren?: Map<PyDsl, NodeRelationship>; 49 structuralParents?: Map<PyDsl, NodeRelationship>; 50 symbol?: Symbol; 51 toAst(): T { 52 return undefined as unknown as T; 53 } 54 readonly '~brand' = nodeBrand; 55 56 /** Branding property to identify the DSL class at runtime. */ 57 abstract readonly '~dsl': AnyString; 58 59 /** Conditionally applies a callback to this builder. */ 60 $if<T extends PyDsl, V, R extends PyDsl = T>( 61 this: T, 62 value: V, 63 ifTrue: (self: T, v: Exclude<V, false | null | undefined>) => R | void, 64 ifFalse?: (self: T, v: Extract<V, false | null | undefined>) => R | void, 65 ): R | T; 66 $if<T extends PyDsl, V, R extends PyDsl = T>( 67 this: T, 68 value: V, 69 ifTrue: (v: Exclude<V, false | null | undefined>) => R | void, 70 ifFalse?: (v: Extract<V, false | null | undefined>) => R | void, 71 ): R | T; 72 $if<T extends PyDsl, V, R extends PyDsl = T>( 73 this: T, 74 value: V, 75 ifTrue: () => R | void, 76 ifFalse?: () => R | void, 77 ): R | T; 78 $if<T extends PyDsl, V, R extends PyDsl = T>( 79 this: T, 80 value: V, 81 ifTrue: any, 82 ifFalse?: any, 83 ): R | T { 84 if (value) { 85 // Try calling with (self, value), then (value), then () 86 let result: R | void | undefined; 87 try { 88 result = ifTrue?.(this, value as Exclude<V, false | null | undefined>); 89 } catch { 90 // ignore and try other signatures 91 } 92 if (result === undefined) { 93 try { 94 result = ifTrue?.(value as Exclude<V, false | null | undefined>); 95 } catch { 96 // ignore and try zero-arg 97 } 98 } 99 if (result === undefined) { 100 try { 101 result = ifTrue?.(); 102 } catch { 103 // swallow 104 } 105 } 106 return (result ?? this) as R | T; 107 } 108 if (ifFalse) { 109 let result: R | void | undefined; 110 try { 111 result = ifFalse?.(this, value as Extract<V, false | null | undefined>); 112 } catch { 113 // ignore 114 } 115 if (result === undefined) { 116 try { 117 result = ifFalse?.(value as Extract<V, false | null | undefined>); 118 } catch { 119 // ignore 120 } 121 } 122 if (result === undefined) { 123 try { 124 result = ifFalse?.(); 125 } catch { 126 // ignore 127 } 128 } 129 return (result ?? this) as R | T; 130 } 131 return this; 132 } 133 134 /** Access patterns for this node. */ 135 toAccessNode?( 136 node: this, 137 options: AccessOptions, 138 ctx: { 139 /** The full chain. */ 140 chain: ReadonlyArray<PyDsl>; 141 /** Position in the chain (0 = root). */ 142 index: number; 143 /** Is this the leaf node? */ 144 isLeaf: boolean; 145 /** Is this the root node? */ 146 isRoot: boolean; 147 /** Total length of the chain. */ 148 length: number; 149 }, 150 ): PyDsl | undefined; 151 152 protected $maybeId<T extends string | py.Expression>( 153 expr: T, 154 ): T extends string ? py.Identifier : T { 155 return (typeof expr === 'string' ? py.factory.createIdentifier(expr) : expr) as T extends string 156 ? py.Identifier 157 : T; 158 } 159 160 protected $name(name: Ref<NodeName>): string { 161 const value = fromRef(name); 162 if (isSymbol(value)) { 163 try { 164 return value.finalName; 165 } catch { 166 return value.name; 167 } 168 } 169 return String(value); 170 } 171 172 protected $node<I>(value: I): NodeOfMaybe<I> { 173 if (value === undefined) { 174 return undefined as NodeOfMaybe<I>; 175 } 176 // @ts-expect-error 177 if (isRef(value)) value = fromRef(value); 178 if (isSymbol(value)) { 179 return this.$maybeId(value.finalName) as NodeOfMaybe<I>; 180 } 181 if (typeof value === 'string') { 182 return this.$maybeId(value) as NodeOfMaybe<I>; 183 } 184 if (value instanceof Array) { 185 return value.map((item) => { 186 if (isRef(item)) item = fromRef(item); 187 return this.unwrap(item); 188 }) as NodeOfMaybe<I>; 189 } 190 return this.unwrap(value as any) as NodeOfMaybe<I>; 191 } 192 193 // protected $type<I>(value: I, args?: ReadonlyArray<ts.TypeNode>): TypeOfMaybe<I> { 194 // if (value === undefined) { 195 // return undefined as TypeOfMaybe<I>; 196 // } 197 // // @ts-expect-error 198 // if (isRef(value)) value = fromRef(value); 199 // if (isSymbol(value)) { 200 // return ts.factory.createTypeReferenceNode(value.finalName, args) as TypeOfMaybe<I>; 201 // } 202 // if (typeof value === 'string') { 203 // return ts.factory.createTypeReferenceNode(value, args) as TypeOfMaybe<I>; 204 // } 205 // if (typeof value === 'boolean') { 206 // const literal = value ? ts.factory.createTrue() : ts.factory.createFalse(); 207 // return ts.factory.createLiteralTypeNode(literal) as TypeOfMaybe<I>; 208 // } 209 // if (typeof value === 'number') { 210 // return ts.factory.createLiteralTypeNode( 211 // ts.factory.createNumericLiteral(value), 212 // ) as TypeOfMaybe<I>; 213 // } 214 // if (value instanceof Array) { 215 // return value.map((item) => this.$type(item, args)) as TypeOfMaybe<I>; 216 // } 217 // return this.unwrap(value as any) as TypeOfMaybe<I>; 218 // } 219 220 private _name?: Ref<NodeName>; 221 222 /** Unwraps nested nodes into raw Python AST. */ 223 private unwrap<I>(value: I): I extends PyDsl<infer N> ? N : I { 224 return (isNode(value) ? value.toAst() : value) as I extends PyDsl<infer N> ? N : I; 225 } 226} 227 228type NodeOfMaybe<I> = undefined extends I 229 ? NodeOf<NonNullable<FromRef<I>>> | undefined 230 : NodeOf<FromRef<I>>; 231 232type NodeOf<I> = 233 I extends ReadonlyArray<infer U> 234 ? ReadonlyArray<U extends PyDsl<infer N> ? N : U> 235 : I extends string 236 ? py.Expression 237 : I extends PyDsl<infer N> 238 ? N 239 : I extends py.Node 240 ? I 241 : never; 242 243export type MaybePyDsl<T> = 244 T extends PyDsl<infer U> ? U | PyDsl<U> : T extends py.Node ? T | PyDsl<T> : never; 245 246// export abstract class TypePyDsl< 247// T extends 248// | ts.LiteralTypeNode 249// | ts.QualifiedName 250// | ts.TypeElement 251// | ts.TypeNode 252// | ts.TypeParameterDeclaration = ts.TypeNode, 253// > extends PyDsl<T> {} 254 255// type TypeOfMaybe<I> = undefined extends I 256// ? TypeOf<NonNullable<FromRef<I>>> | undefined 257// : TypeOf<FromRef<I>>; 258 259// type TypeOf<I> = 260// I extends ReadonlyArray<infer U> 261// ? ReadonlyArray<TypeOf<U>> 262// : I extends string 263// ? ts.TypeNode 264// : I extends boolean 265// ? ts.LiteralTypeNode 266// : I extends PyDsl<infer N> 267// ? N 268// : I extends ts.TypeNode 269// ? I 270// : never;