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

feat: canonical symbols

Lubos 944f14cd 2b253c05

+1401 -1112
+1 -1
.vscode/launch.json
··· 18 18 "runtimeArgs": ["-r", "ts-node/register/transpile-only"], 19 19 "program": "${workspaceFolder}/packages/openapi-ts/src/run.ts", 20 20 "env": { 21 - "DEBUG": "false" 21 + "DEBUG": "heyapi:*" 22 22 } 23 23 } 24 24 ]
+3 -3
dev/openapi-ts.config.ts
··· 39 39 '3.1.x', 40 40 // 'circular.yaml', 41 41 // 'dutchie.json', 42 - 'enum-names-values.yaml', 42 + // 'enum-names-values.yaml', 43 43 // 'invalid', 44 - // 'full.yaml', 44 + 'full.yaml', 45 45 // 'object-property-names.yaml', 46 46 // 'openai.yaml', 47 47 // 'opencode.yaml', ··· 83 83 // 'https://somefakedomain.com/openapi.yaml', 84 84 ], 85 85 logs: { 86 - // level: 'debug', 86 + level: 'silent', 87 87 path: './logs', 88 88 }, 89 89 // name: 'foo',
+2 -2
docs/index.md
··· 15 15 text: Roadmap 16 16 theme: alt 17 17 image: 18 - alt: Two people looking at the blueprint 19 - src: /images/blueprint-640w.png 18 + alt: Two people looking at the TypeScript logo 19 + src: /images/hero-920w.png 20 20 21 21 features: 22 22 - icon: <svg class="icon-openapi" width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 32"><path d="M8.96 18.397H.515l.005.123.014.238.007.102.022.275.006.061.033.304.003.03.043.327c.098.677.243 1.343.437 1.999l.003.008.1.326.006.018.093.276.025.07.087.24.04.107.078.2.06.149.065.154.086.188.05.114.105.225.035.072.126.256.02.039.154.293.033.057 7.235-4.366a5.754 5.754 0 0 1-.528-1.885ZM.914 22.27l.002.007.273-.085-.275.078ZM11.034 22.275l-5.97 5.967.092.085.255.227.203.172.055.045.232.187.03.024.255.196a.066.066 0 0 1 .01.007l1.113.752.04.024.219.13.134.076.128.072.232.126.032.017.658.32 3.213-7.805a5.719 5.719 0 0 1-.934-.623l.003.001ZM10.415 21.683l-.186-.219-.154-.199-.165-.233-.154-.241-7.22 4.349.371.584.03.044.002.003.388.547.009.011.008.011.176.229.21.261.045.055.173.203.076.087.15.171.084.092.039.042.114.12.046.047.2.204 5.956-5.956-.195-.209-.003-.003ZM18.31 22.272l-.2.154.016.025 4.342 7.209.594-.41c.42-.31.827-.645 1.22-1.007l-5.949-5.947-.023-.024ZM21.92 30.003l.01-.006-.01.006Zm-.005.003ZM21.929 29.994l.057-.028-.001-.002-.056.033v-.003Zm-.01.009-.002.001.002-.001ZM21.916 30.006l-.011-.018.01.018Zm.004-.003.01-.005-.01.005Z" fill="#fff"></path><path d="m21.837 29.719-4.2-6.97-.25.139-.256.128a5.756 5.756 0 0 1-4.106.319l-.27-.095-.27-.095-3.207 7.788.024.009.024.009.007.003.615.235a14.262 14.262 0 0 0 3.007.708l.349.038.056.005.28.023.095.006.245.014.15.006.195.007.348.004c.788 0 1.575-.066 2.352-.196l.04-.006.246-.045.143-.027.145-.03.24-.053.044-.01a14.241 14.241 0 0 0 3.398-1.267l.209-.115.424-.238-.007-.02.01.018.014-.008.056-.034-.15-.25Zm-10.8-16.335.2-.155-.015-.024-4.343-7.206-.595.41c-.42.31-.827.645-1.218 1.006l5.948 5.945.024.024ZM4.654 7.808l-.395.413c-.44.476-.841.971-1.203 1.491l-.052.075-.121.178-.123.188-.045.068a14.135 14.135 0 0 0-2.2 7.035l-.007.286-.005.285h8.424l.013-.285.016-.286a5.716 5.716 0 0 1 1.27-3.068c.058-.073.128-.142.192-.212.065-.07.124-.144.192-.212L4.654 7.808Zm17.38-2.09L22 5.695l-.224-.132-.13-.075-.132-.073-.228-.123-.036-.019a14.74 14.74 0 0 0-1.52-.686l-.04-.015-.342-.124a14.216 14.216 0 0 0-2.839-.673l-.118-.016-.119-.013-.228-.025-.064-.006-.273-.023-.342-.02-.124-.006v8.444c.433.045.862.138 1.279.279l6.216-6.211a13.96 13.96 0 0 0-.703-.461h.002ZM7.363 5.692l.147.244-.147-.244Zm0 0L7.36 5.69l.004.002Z" fill="#fff"></path><path d="m14.388 3.664-.285.005a14.24 14.24 0 0 0-1.78.184l-.04.007-.247.044-.143.027-.145.03-.24.053-.043.01a14.252 14.252 0 0 0-3.4 1.268l-.705.398v.001l4.349 7.219.25-.14a5.727 5.727 0 0 1 2.141-.657l.285-.022s.19-.01.286-.01V3.658c-.095 0-.19.003-.285.005h.002ZM28.827 17.131l-.014-.227-.007-.113-.022-.267-.006-.07-.032-.297-.002-.024-.002-.012-.043-.32-.001-.01a14.164 14.164 0 0 0-.436-1.992l-.003-.013-.094-.304-.013-.04-.091-.272-.026-.074-.086-.235-.043-.111-.075-.194-.063-.153-.063-.15-.083-.191-.049-.108-.107-.228-.033-.069-.128-.259-.018-.035-.149-.286c-.002-.003-.003-.007-.006-.01a14.217 14.217 0 0 0-.806-1.308l-6.217 6.218c.14.415.233.844.278 1.279h8.444l-.004-.125ZM20.42 17.828l-.013.285-.016.286a5.709 5.709 0 0 1-1.27 3.068c-.057.073-.128.142-.192.212s-.123.144-.191.212l5.956 5.956c.067-.068.13-.138.197-.206l.197-.207c.44-.477.843-.977 1.206-1.496l.043-.06.13-.193.113-.173.057-.084a14.13 14.13 0 0 0 2.196-7.03l.007-.285.005-.286H20.42Z" fill="#fff"></path></svg>
docs/public/hero.png

This is a binary file and will not be displayed.

docs/public/images/hero-300w.png

This is a binary file and will not be displayed.

docs/public/images/hero-640w.png

This is a binary file and will not be displayed.

docs/public/images/hero-920w.png

This is a binary file and will not be displayed.

+17 -13
docs/scripts/optimize-images.js
··· 31 31 ], 32 32 source: 'bricks.png', 33 33 }, 34 - // { 35 - // sizes: [ 36 - // { 37 - // formats: ['png'], 38 - // width: 300, 39 - // }, 40 - // { 41 - // formats: ['png'], 42 - // width: 640, 43 - // }, 44 - // ], 45 - // source: 'hero.png', 46 - // }, 34 + { 35 + sizes: [ 36 + { 37 + formats: ['png'], 38 + width: 300, 39 + }, 40 + { 41 + formats: ['png'], 42 + width: 640, 43 + }, 44 + { 45 + formats: ['png'], 46 + width: 920, 47 + }, 48 + ], 49 + source: 'hero.png', 50 + }, 47 51 { 48 52 sizes: [ 49 53 {
+4
packages/codegen-core/package.json
··· 60 60 "engines": { 61 61 "node": ">=20.19.0" 62 62 }, 63 + "dependencies": { 64 + "ansi-colors": "4.1.3", 65 + "color-support": "1.1.3" 66 + }, 63 67 "peerDependencies": { 64 68 "typescript": ">=5.5.3" 65 69 },
+34
packages/codegen-core/src/debug.ts
··· 1 + import colors from 'ansi-colors'; 2 + // @ts-expect-error 3 + import colorSupport from 'color-support'; 4 + 5 + colors.enabled = colorSupport().hasBasic; 6 + 7 + const DEBUG_GROUPS = { 8 + dsl: colors.cyanBright, 9 + registry: colors.blueBright, 10 + symbol: colors.magentaBright, 11 + } as const; 12 + 13 + export function debug(message: string, group: keyof typeof DEBUG_GROUPS) { 14 + const value = process.env.DEBUG; 15 + if (!value) return; 16 + 17 + const groups = value.split(",").map(x => x.trim().toLowerCase()); 18 + 19 + if ( 20 + !( 21 + groups.includes("*") || 22 + groups.includes("heyapi:*") || 23 + groups.includes(`heyapi:${group}`) || 24 + groups.includes(group) 25 + ) 26 + ) { 27 + return; 28 + } 29 + 30 + const color = DEBUG_GROUPS[group] ?? colors.whiteBright; 31 + const prefix = color(`heyapi:${group}`); 32 + 33 + console.debug(`${prefix} ${message}`); 34 + }
+1
packages/codegen-core/src/index.ts
··· 1 1 export type { IBiMap as BiMap } from './bimap/types'; 2 2 export type { IBinding as Binding } from './bindings/types'; 3 3 export { createBinding, mergeBindings } from './bindings/utils'; 4 + export { debug } from './debug'; 4 5 export type { 5 6 IProjectRenderMeta as ProjectRenderMeta, 6 7 ISymbolMeta as SymbolMeta,
+35
packages/codegen-core/src/symbols/symbol.ts
··· 1 + import { debug } from '../debug'; 1 2 import type { ISymbolMeta } from '../extensions'; 2 3 import type { IFileOut } from '../files/types'; 3 4 import { wrapId } from '../renderer/utils'; ··· 171 172 ); 172 173 } 173 174 175 + /** 176 + * Custom file path resolver, if provided. 177 + */ 174 178 get getFilePath(): ((symbol: Symbol) => string | undefined) | undefined { 175 179 return this.canonical._getFilePath; 176 180 } ··· 196 200 return this.canonical._meta; 197 201 } 198 202 203 + /** 204 + * User-intended name before aliasing or conflict resolution. 205 + */ 199 206 get name(): string { 200 207 return this.canonical._name; 201 208 } ··· 211 218 * Add a direct dependency on another symbol. 212 219 */ 213 220 addDependency(symbol: Symbol): void { 221 + this.assertCanonical(); 214 222 if (symbol !== this) this._dependencies.add(symbol); 215 223 } 216 224 ··· 232 240 * @param exported — Whether the symbol is exported. 233 241 */ 234 242 setExported(exported: boolean): void { 243 + this.assertCanonical(); 235 244 this._exported = exported; 236 245 } 237 246 ··· 241 250 * @param list — Source files re‑exporting this symbol. 242 251 */ 243 252 setExportFrom(list: ReadonlyArray<string>): void { 253 + this.assertCanonical(); 244 254 this._exportFrom = list; 245 255 } 246 256 ··· 250 260 * This may only be set once. 251 261 */ 252 262 setFile(file: IFileOut): void { 263 + this.assertCanonical(); 253 264 if (this._file && this._file !== file) { 254 265 throw new Error('Symbol is already assigned to a different file.'); 255 266 } ··· 262 273 * This may only be set once. 263 274 */ 264 275 setFinalName(name: string): void { 276 + this.assertCanonical(); 265 277 if (this._finalName && this._finalName !== name) { 266 278 throw new Error('Symbol finalName has already been resolved.'); 267 279 } ··· 274 286 * @param kind — The import strategy (named/default/namespace). 275 287 */ 276 288 setImportKind(kind: SymbolImportKind): void { 289 + this.assertCanonical(); 277 290 this._importKind = kind; 278 291 } 279 292 ··· 283 296 * @param kind — The new symbol kind. 284 297 */ 285 298 setKind(kind: SymbolKind): void { 299 + this.assertCanonical(); 286 300 this._kind = kind; 287 301 } 288 302 ··· 292 306 * @param name — The new name. 293 307 */ 294 308 setName(name: string): void { 309 + this.assertCanonical(); 295 310 this._name = name; 296 311 } 297 312 ··· 301 316 * This may only be set once. 302 317 */ 303 318 setRootNode(node: ISyntaxNode): void { 319 + this.assertCanonical(); 304 320 if (this._rootNode && this._rootNode !== node) { 305 321 throw new Error('Symbol is already bound to a different root DSL node.'); 306 322 } ··· 312 328 */ 313 329 toString(): string { 314 330 return `[Symbol ${this.name}#${this.id}]`; 331 + } 332 + 333 + /** 334 + * Ensures this symbol is canonical before allowing mutation. 335 + * 336 + * A symbol that has been marked as a stub (i.e., its `_canonical` points 337 + * to a different symbol) may not be mutated. This guard throws an error 338 + * if any setter attempts to modify a stub, preventing accidental writes 339 + * to non‑canonical instances. 340 + * 341 + * @throws {Error} If the symbol is a stub and is being mutated. 342 + * @private 343 + */ 344 + private assertCanonical(): void { 345 + if (this._canonical && this._canonical !== this) { 346 + const message = `Illegal mutation of stub symbol ${this.toString()} → canonical: ${this._canonical.toString()}`; 347 + debug(message, "symbol"); 348 + throw new Error(message); 349 + } 315 350 } 316 351 }
+1 -1
packages/openapi-ts/README.md
··· 1 1 <div align="center"> 2 - <img alt="Two people looking at the blueprint" height="214" src="https://heyapi.dev/images/blueprint-640w.png" width="320"> 2 + <img alt="Two people looking at the TypeScript logo" height="214" src="https://heyapi.dev/images/hero-920w.png" width="320"> 3 3 <h1><b>OpenAPI TypeScript</b></h1> 4 4 <p><em>“OpenAPI codegen that just works.”</em><br/><sub>— Guillermo Rauch, CEO of Vercel</sub></p> 5 5 </div>
+1 -1
packages/openapi-ts/src/cli.ts
··· 85 85 } 86 86 : {}; 87 87 88 - if (userConfig.debug || stringToBoolean(process.env.DEBUG)) { 88 + if (userConfig.debug) { 89 89 (userConfig.logs as Record<string, unknown>).level = 'debug'; 90 90 delete userConfig.debug; 91 91 } else if (userConfig.silent) {
+65 -13
packages/openapi-ts/src/ts-dsl/base.ts
··· 1 1 // TODO: symbol should be protected, but needs to be public to satisfy types 2 2 import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 3 + import { debug } from '@hey-api/codegen-core'; 3 4 import ts from 'typescript'; 4 5 5 6 export type MaybeArray<T> = T | ReadonlyArray<T>; ··· 9 10 $render(): T; 10 11 } 11 12 13 + // @deprecated 12 14 export type Constructor<T = ITsDsl> = new (...args: ReadonlyArray<any>) => T; 13 15 14 16 export abstract class TsDsl<T extends ts.Node = ts.Node> implements ITsDsl<T> { 17 + /** Render this DSL node into a concrete TypeScript AST node. */ 18 + protected abstract _render(): T; 19 + 15 20 /** Walk this node and its children with a visitor. */ 16 21 abstract traverse(visitor: (node: SyntaxNode) => void): void; 17 - 18 - /** Render this DSL node into a concrete TypeScript AST node. */ 19 - abstract $render(): T; 20 22 21 23 /** Parent DSL node in the constructed syntax tree. */ 22 24 protected parent?: TsDsl<any>; ··· 99 101 return this; 100 102 } 101 103 104 + /** Render this DSL node into a concrete TypeScript AST node. */ 105 + $render(): T { 106 + if (!this.parent) { 107 + this._validate(); 108 + } 109 + return this._render(); 110 + } 111 + 102 112 /** Returns all locally declared names within this node. */ 103 113 getLocalNames(): Iterable<string> { 104 114 return []; ··· 118 128 /** Assigns the parent DSL node, enforcing a single-parent invariant. */ 119 129 setParent(parent: TsDsl<any>): this { 120 130 if (this.parent && this.parent !== parent) { 121 - throw new Error( 122 - `DSL node already has a parent (${this.parent.constructor.name}); cannot reassign to ${parent.constructor.name}.`, 123 - ); 131 + const message = `${this.constructor.name} already had a parent (${this.parent.constructor.name}), new parent attempted: ${parent.constructor.name}`; 132 + debug(message, 'dsl'); 133 + throw new Error(message); 134 + } 135 + 136 + if (this.symbol) { 137 + const message = `${this.constructor.name} has BOTH a symbol and a parent. This violates DSL invariants.`; 138 + debug(message, 'dsl'); 139 + throw new Error(message); 124 140 } 141 + 125 142 this.parent = parent; 126 143 return this; 127 144 } ··· 175 192 } 176 193 177 194 /** Returns the root symbol associated with this DSL subtree. */ 178 - protected getRootSymbol(): Symbol | undefined { 179 - // eslint-disable-next-line @typescript-eslint/no-this-alias 180 - let n: TsDsl<any> | undefined = this; 181 - while (n) { 182 - if (n.symbol) return n.symbol; 183 - n = n.parent; 195 + protected getRootSymbol(): Symbol { 196 + if (!this.parent && !this.symbol) { 197 + const message = `${this.constructor.name} has neither a parent nor a symbol — root symbol resolution failed.`; 198 + debug(message, 'dsl'); 199 + throw new Error(message); 184 200 } 185 - return undefined; 201 + return this.parent ? this.parent.getRootSymbol() : this.symbol!.canonical; 186 202 } 187 203 188 204 /** Unwraps nested DSL nodes into raw TypeScript AST nodes. */ ··· 190 206 return ( 191 207 value instanceof TsDsl ? value.$render() : value 192 208 ) as I extends TsDsl<infer N> ? N : I; 209 + } 210 + 211 + /** Validate DSL invariants. */ 212 + protected _validate(): void { 213 + if (!this.parent && !this.symbol) { 214 + const message = `${this.constructor.name}: top-level DSL node has no symbol`; 215 + debug(message, 'dsl'); 216 + throw new Error(message); 217 + } 218 + 219 + if (this.parent && this.symbol) { 220 + const message = `${this.constructor.name}: non-top-level node must not have a symbol`; 221 + debug(message, 'dsl'); 222 + throw new Error(message); 223 + } 224 + 225 + if (this.parent === undefined && this.symbol === undefined) { 226 + const message = `${this.constructor.name}: non-root DSL node is missing a parent`; 227 + debug(message, 'dsl'); 228 + throw new Error(message); 229 + } 230 + 231 + if (this.symbol && this.symbol.canonical !== this.symbol) { 232 + const message = `${this.constructor.name}: DSL node is holding a non-canonical (stub) symbol`; 233 + debug(message, 'dsl'); 234 + throw new Error(message); 235 + } 236 + 237 + this.traverse((node) => { 238 + const dsl = node as TsDsl<any>; 239 + if (dsl !== this && dsl.parent !== this) { 240 + const message = `${dsl.constructor.name}: child node has incorrect or missing parent`; 241 + debug(message, 'dsl'); 242 + throw new Error(message); 243 + } 244 + }); 193 245 } 194 246 } 195 247
+19 -37
packages/openapi-ts/src/ts-dsl/decl/class.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 6 import { NewlineTsDsl } from '../layout/newline'; 8 - import { mixin } from '../mixins/apply'; 9 7 import { DecoratorMixin } from '../mixins/decorator'; 10 8 import { DocMixin } from '../mixins/doc'; 11 - import { 12 - AbstractMixin, 13 - createModifierAccessor, 14 - DefaultMixin, 15 - ExportMixin, 16 - } from '../mixins/modifiers'; 9 + import { AbstractMixin, DefaultMixin, ExportMixin } from '../mixins/modifiers'; 17 10 import { TypeParamsMixin } from '../mixins/type-params'; 18 11 import { FieldTsDsl } from './field'; 19 12 import { InitTsDsl } from './init'; 20 13 import { MethodTsDsl } from './method'; 21 14 22 - export class ClassTsDsl extends TsDsl<ts.ClassDeclaration> { 15 + const Mixed = AbstractMixin( 16 + DecoratorMixin( 17 + DefaultMixin( 18 + DocMixin(ExportMixin(TypeParamsMixin(TsDsl<ts.ClassDeclaration>))), 19 + ), 20 + ), 21 + ); 22 + 23 + export class ClassTsDsl extends Mixed { 23 24 protected baseClass?: Symbol | string; 24 25 protected body: Array<MaybeTsDsl<ts.ClassElement | NewlineTsDsl>> = []; 25 - protected modifiers = createModifierAccessor(this); 26 - protected name: string; 26 + protected name: Symbol | string; 27 27 28 28 constructor(name: Symbol | string) { 29 29 super(); ··· 31 31 this.name = name; 32 32 return; 33 33 } 34 - this.name = name.finalName; 34 + this.name = name; 35 35 this.symbol = name; 36 36 this.symbol.setKind('class'); 37 37 this.symbol.setRootNode(this); ··· 40 40 /** Adds one or more class members (fields, methods, etc.). */ 41 41 do(...items: ReadonlyArray<MaybeTsDsl<ts.ClassElement | ts.Node>>): this { 42 42 for (const item of items) { 43 - if (item && typeof item === 'object' && 'setParent' in item) { 43 + if (typeof item === 'object' && 'setParent' in item) { 44 44 item.setParent(this); 45 45 } 46 46 // @ts-expect-error --- IGNORE --- ··· 54 54 if (!base) return this; 55 55 this.baseClass = base; 56 56 if (typeof base !== 'string') { 57 - const symbol = this.getRootSymbol(); 58 - if (symbol) symbol.addDependency(base); 57 + this.getRootSymbol().addDependency(base); 59 58 } 60 59 return this; 61 60 } ··· 87 86 return this; 88 87 } 89 88 90 - /** Walk this node and its children with a visitor. */ 91 89 traverse(visitor: (node: SyntaxNode) => void): void { 92 90 console.log(visitor); 93 91 } 94 92 95 - /** Builds the `ClassDeclaration` node. */ 96 - $render(): ts.ClassDeclaration { 93 + protected override _render() { 97 94 const body = this.$node(this.body) as ReadonlyArray<ts.ClassElement>; 95 + const name = 96 + typeof this.name === 'string' ? this.name : this.name.finalName; 98 97 return ts.factory.createClassDeclaration( 99 - [...this.$decorators(), ...this.modifiers.list()], 100 - this.name, 98 + [...this.$decorators(), ...this.modifiers], 99 + name, 101 100 this.$generics(), 102 101 this._renderHeritage(), 103 102 body, ··· 118 117 ]; 119 118 } 120 119 } 121 - 122 - export interface ClassTsDsl 123 - extends AbstractMixin, 124 - DecoratorMixin, 125 - DefaultMixin, 126 - DocMixin, 127 - ExportMixin, 128 - TypeParamsMixin {} 129 - mixin( 130 - ClassTsDsl, 131 - AbstractMixin, 132 - DecoratorMixin, 133 - DefaultMixin, 134 - DocMixin, 135 - ExportMixin, 136 - TypeParamsMixin, 137 - );
+5 -10
packages/openapi-ts/src/ts-dsl/decl/decorator.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { ArgsMixin } from '../mixins/args'; 9 7 10 - export class DecoratorTsDsl extends TsDsl<ts.Decorator> { 8 + const Mixed = ArgsMixin(TsDsl<ts.Decorator>); 9 + 10 + export class DecoratorTsDsl extends Mixed { 11 11 protected name: Symbol | string | MaybeTsDsl<ts.Expression>; 12 12 13 13 constructor( ··· 17 17 super(); 18 18 this.name = name; 19 19 if (typeof name !== 'string' && 'id' in name) { 20 - const symbol = this.getRootSymbol(); 21 - if (symbol) symbol.addDependency(name); 20 + this.getRootSymbol().addDependency(name); 22 21 } 23 22 this.args(...args); 24 23 } 25 24 26 - /** Walk this node and its children with a visitor. */ 27 25 traverse(visitor: (node: SyntaxNode) => void): void { 28 26 console.log(visitor); 29 27 } 30 28 31 - $render(): ts.Decorator { 29 + protected override _render() { 32 30 const target = 33 31 typeof this.name !== 'string' && 'id' in this.name 34 32 ? this.$maybeId(this.name.finalName) ··· 42 40 ); 43 41 } 44 42 } 45 - 46 - export interface DecoratorTsDsl extends ArgsMixin {} 47 - mixin(DecoratorTsDsl, ArgsMixin);
+6 -16
packages/openapi-ts/src/ts-dsl/decl/enum.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { DocMixin } from '../mixins/doc'; 9 - import { 10 - ConstMixin, 11 - createModifierAccessor, 12 - ExportMixin, 13 - } from '../mixins/modifiers'; 7 + import { ConstMixin, ExportMixin } from '../mixins/modifiers'; 14 8 import { EnumMemberTsDsl } from './member'; 15 9 16 10 type Value = string | number | MaybeTsDsl<ts.Expression>; 17 11 type ValueFn = Value | ((m: EnumMemberTsDsl) => void); 18 12 19 - export class EnumTsDsl extends TsDsl<ts.EnumDeclaration> { 13 + const Mixed = ConstMixin(DocMixin(ExportMixin(TsDsl<ts.EnumDeclaration>))); 14 + 15 + export class EnumTsDsl extends Mixed { 20 16 private _members: Array<EnumMemberTsDsl> = []; 21 17 private _name: string | ts.Identifier; 22 - protected modifiers = createModifierAccessor(this); 23 18 24 19 constructor(name: Symbol | string, fn?: (e: EnumTsDsl) => void) { 25 20 super(); ··· 47 42 return this; 48 43 } 49 44 50 - /** Walk this node and its children with a visitor. */ 51 45 traverse(visitor: (node: SyntaxNode) => void): void { 52 46 console.log(visitor); 53 47 } 54 48 55 - /** Renders the enum declaration. */ 56 - $render(): ts.EnumDeclaration { 49 + protected override _render() { 57 50 return ts.factory.createEnumDeclaration( 58 - this.modifiers.list(), 51 + this.modifiers, 59 52 this._name, 60 53 this.$node(this._members), 61 54 ); 62 55 } 63 56 } 64 - 65 - export interface EnumTsDsl extends ConstMixin, DocMixin, ExportMixin {} 66 - mixin(EnumTsDsl, ConstMixin, DocMixin, ExportMixin);
+15 -30
packages/openapi-ts/src/ts-dsl/decl/field.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TsDsl, TypeTsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { DecoratorMixin } from '../mixins/decorator'; 8 6 import { DocMixin } from '../mixins/doc'; 9 7 import { 10 - createModifierAccessor, 11 8 PrivateMixin, 12 9 ProtectedMixin, 13 10 PublicMixin, ··· 17 14 import { ValueMixin } from '../mixins/value'; 18 15 import { TypeExprTsDsl } from '../type/expr'; 19 16 20 - export class FieldTsDsl extends TsDsl<ts.PropertyDeclaration> { 21 - protected modifiers = createModifierAccessor(this); 17 + const Mixed = DecoratorMixin( 18 + DocMixin( 19 + PrivateMixin( 20 + ProtectedMixin( 21 + PublicMixin( 22 + ReadonlyMixin(StaticMixin(ValueMixin(TsDsl<ts.PropertyDeclaration>))), 23 + ), 24 + ), 25 + ), 26 + ), 27 + ); 28 + 29 + export class FieldTsDsl extends Mixed { 22 30 protected name: string; 23 31 protected _type?: TypeTsDsl; 24 32 ··· 34 42 return this; 35 43 } 36 44 37 - /** Walk this node and its children with a visitor. */ 38 45 traverse(visitor: (node: SyntaxNode) => void): void { 39 46 console.log(visitor); 40 47 } 41 48 42 - /** Builds the `PropertyDeclaration` node. */ 43 - $render(): ts.PropertyDeclaration { 49 + protected override _render() { 44 50 return ts.factory.createPropertyDeclaration( 45 - [...this.$decorators(), ...this.modifiers.list()], 51 + [...this.$decorators(), ...this.modifiers], 46 52 this.name, 47 53 undefined, 48 54 this.$type(this._type), ··· 50 56 ); 51 57 } 52 58 } 53 - 54 - export interface FieldTsDsl 55 - extends DecoratorMixin, 56 - DocMixin, 57 - PrivateMixin, 58 - ProtectedMixin, 59 - PublicMixin, 60 - ReadonlyMixin, 61 - StaticMixin, 62 - ValueMixin {} 63 - mixin( 64 - FieldTsDsl, 65 - DecoratorMixin, 66 - DocMixin, 67 - PrivateMixin, 68 - ProtectedMixin, 69 - PublicMixin, 70 - ReadonlyMixin, 71 - StaticMixin, 72 - ValueMixin, 73 - );
+28 -45
packages/openapi-ts/src/ts-dsl/decl/func.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TsDsl, TypeTsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { AsMixin } from '../mixins/as'; 8 6 import { DecoratorMixin } from '../mixins/decorator'; 9 7 import { DoMixin } from '../mixins/do'; ··· 11 9 import { 12 10 AbstractMixin, 13 11 AsyncMixin, 14 - createModifierAccessor, 15 12 PrivateMixin, 16 13 ProtectedMixin, 17 14 PublicMixin, ··· 23 20 24 21 type FuncMode = 'arrow' | 'decl' | 'expr'; 25 22 26 - class ImplFuncTsDsl<M extends FuncMode = 'arrow'> extends TsDsl< 27 - M extends 'decl' 28 - ? ts.FunctionDeclaration 29 - : M extends 'expr' 30 - ? ts.FunctionExpression 31 - : ts.ArrowFunction 32 - > { 23 + const Mixed = AbstractMixin( 24 + AsMixin( 25 + AsyncMixin( 26 + DecoratorMixin( 27 + DoMixin( 28 + DocMixin( 29 + ParamMixin( 30 + PrivateMixin( 31 + ProtectedMixin( 32 + PublicMixin( 33 + StaticMixin(TypeParamsMixin(TsDsl<ts.ArrowFunction>)), 34 + ), 35 + ), 36 + ), 37 + ), 38 + ), 39 + ), 40 + ), 41 + ), 42 + ), 43 + ); 44 + 45 + class ImplFuncTsDsl<M extends FuncMode = 'arrow'> extends Mixed { 33 46 protected mode?: FuncMode; 34 - protected modifiers = createModifierAccessor(this); 35 47 protected name?: string; 36 48 protected _returns?: TypeTsDsl; 37 49 ··· 85 97 return this; 86 98 } 87 99 88 - /** Walk this node and its children with a visitor. */ 89 100 traverse(visitor: (node: SyntaxNode) => void): void { 90 101 console.log(visitor); 91 102 } 92 103 93 - $render(): M extends 'decl' 104 + // @ts-expect-error --- need to fix types --- 105 + protected override _render(): M extends 'decl' 94 106 ? ts.FunctionDeclaration 95 107 : M extends 'expr' 96 108 ? ts.FunctionExpression ··· 98 110 if (this.mode === 'decl') { 99 111 if (!this.name) throw new Error('Function declaration requires a name'); 100 112 return ts.factory.createFunctionDeclaration( 101 - [...this.$decorators(), ...this.modifiers.list()], 113 + [...this.$decorators(), ...this.modifiers], 102 114 undefined, 103 115 this.name, 104 116 this.$generics(), ··· 110 122 111 123 if (this.mode === 'expr') { 112 124 return ts.factory.createFunctionExpression( 113 - this.modifiers.list(), 125 + this.modifiers, 114 126 undefined, 115 127 this.name, 116 128 this.$generics(), ··· 127 139 : ts.factory.createBlock(body, true); 128 140 129 141 return ts.factory.createArrowFunction( 130 - this.modifiers.list(), 142 + this.modifiers, 131 143 this.$generics(), 132 144 this.$params(), 133 145 this.$type(this._returns), ··· 136 148 ) as any; 137 149 } 138 150 } 139 - 140 - interface ImplFuncTsDsl 141 - extends AbstractMixin, 142 - AsMixin, 143 - AsyncMixin, 144 - DecoratorMixin, 145 - DoMixin, 146 - DocMixin, 147 - ParamMixin, 148 - PrivateMixin, 149 - ProtectedMixin, 150 - PublicMixin, 151 - StaticMixin, 152 - TypeParamsMixin {} 153 - mixin( 154 - ImplFuncTsDsl, 155 - AbstractMixin, 156 - AsMixin, 157 - AsyncMixin, 158 - DecoratorMixin, 159 - DoMixin, 160 - DocMixin, 161 - ParamMixin, 162 - PrivateMixin, 163 - ProtectedMixin, 164 - PublicMixin, 165 - StaticMixin, 166 - TypeParamsMixin, 167 - ); 168 151 169 152 export const FuncTsDsl = ImplFuncTsDsl as { 170 153 new (): FuncTsDsl<'arrow'>;
+22 -33
packages/openapi-ts/src/ts-dsl/decl/getter.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { DecoratorMixin } from '../mixins/decorator'; 8 6 import { DoMixin } from '../mixins/do'; 9 7 import { DocMixin } from '../mixins/doc'; 10 - import type { AsyncMixin } from '../mixins/modifiers'; 11 8 import { 12 9 AbstractMixin, 13 - createModifierAccessor, 10 + AsyncMixin, 14 11 PrivateMixin, 15 12 ProtectedMixin, 16 13 PublicMixin, ··· 18 15 } from '../mixins/modifiers'; 19 16 import { ParamMixin } from '../mixins/param'; 20 17 21 - export class GetterTsDsl extends TsDsl<ts.GetAccessorDeclaration> { 22 - protected modifiers = createModifierAccessor(this); 18 + const Mixed = AbstractMixin( 19 + AsyncMixin( 20 + DecoratorMixin( 21 + DoMixin( 22 + DocMixin( 23 + ParamMixin( 24 + PrivateMixin( 25 + ProtectedMixin( 26 + PublicMixin(StaticMixin(TsDsl<ts.GetAccessorDeclaration>)), 27 + ), 28 + ), 29 + ), 30 + ), 31 + ), 32 + ), 33 + ), 34 + ); 35 + 36 + export class GetterTsDsl extends Mixed { 23 37 protected name: string | ts.PropertyName; 24 38 25 39 constructor(name: string | ts.PropertyName, fn?: (g: GetterTsDsl) => void) { ··· 28 42 fn?.(this); 29 43 } 30 44 31 - /** Walk this node and its children with a visitor. */ 32 45 traverse(visitor: (node: SyntaxNode) => void): void { 33 46 console.log(visitor); 34 47 } 35 48 36 - $render(): ts.GetAccessorDeclaration { 49 + protected override _render() { 37 50 return ts.factory.createGetAccessorDeclaration( 38 - [...this.$decorators(), ...this.modifiers.list()], 51 + [...this.$decorators(), ...this.modifiers], 39 52 this.name, 40 53 this.$params(), 41 54 undefined, ··· 43 56 ); 44 57 } 45 58 } 46 - 47 - export interface GetterTsDsl 48 - extends AbstractMixin, 49 - AsyncMixin, 50 - DecoratorMixin, 51 - DoMixin, 52 - DocMixin, 53 - ParamMixin, 54 - PrivateMixin, 55 - ProtectedMixin, 56 - PublicMixin, 57 - StaticMixin {} 58 - mixin( 59 - GetterTsDsl, 60 - AbstractMixin, 61 - DecoratorMixin, 62 - DoMixin, 63 - DocMixin, 64 - ParamMixin, 65 - PrivateMixin, 66 - ProtectedMixin, 67 - PublicMixin, 68 - StaticMixin, 69 - );
+15 -33
packages/openapi-ts/src/ts-dsl/decl/init.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { DecoratorMixin } from '../mixins/decorator'; 8 6 import { DoMixin } from '../mixins/do'; 9 7 import { DocMixin } from '../mixins/doc'; 10 - import { 11 - createModifierAccessor, 12 - PrivateMixin, 13 - ProtectedMixin, 14 - PublicMixin, 15 - } from '../mixins/modifiers'; 8 + import { PrivateMixin, ProtectedMixin, PublicMixin } from '../mixins/modifiers'; 16 9 import { ParamMixin } from '../mixins/param'; 17 10 18 - export class InitTsDsl extends TsDsl<ts.ConstructorDeclaration> { 19 - protected modifiers = createModifierAccessor(this); 11 + const Mixed = DecoratorMixin( 12 + DoMixin( 13 + DocMixin( 14 + ParamMixin( 15 + PrivateMixin( 16 + ProtectedMixin(PublicMixin(TsDsl<ts.ConstructorDeclaration>)), 17 + ), 18 + ), 19 + ), 20 + ), 21 + ); 20 22 23 + export class InitTsDsl extends Mixed { 21 24 constructor(fn?: (i: InitTsDsl) => void) { 22 25 super(); 23 26 fn?.(this); 24 27 } 25 28 26 - /** Walk this node and its children with a visitor. */ 27 29 traverse(visitor: (node: SyntaxNode) => void): void { 28 30 console.log(visitor); 29 31 } 30 32 31 - /** Builds the `ConstructorDeclaration` node. */ 32 - $render(): ts.ConstructorDeclaration { 33 + protected override _render() { 33 34 return ts.factory.createConstructorDeclaration( 34 - [...this.$decorators(), ...this.modifiers.list()], 35 + [...this.$decorators(), ...this.modifiers], 35 36 this.$params(), 36 37 ts.factory.createBlock(this.$do(), true), 37 38 ); 38 39 } 39 40 } 40 - 41 - export interface InitTsDsl 42 - extends DecoratorMixin, 43 - DoMixin, 44 - DocMixin, 45 - ParamMixin, 46 - PrivateMixin, 47 - ProtectedMixin, 48 - PublicMixin {} 49 - mixin( 50 - InitTsDsl, 51 - DecoratorMixin, 52 - DoMixin, 53 - DocMixin, 54 - ParamMixin, 55 - PrivateMixin, 56 - ProtectedMixin, 57 - PublicMixin, 58 - );
+4 -8
packages/openapi-ts/src/ts-dsl/decl/member.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { DocMixin } from '../mixins/doc'; 9 7 import { safeMemberName } from '../utils/prop'; 10 8 11 9 type Value = string | number | MaybeTsDsl<ts.Expression>; 12 10 type ValueFn = Value | ((m: EnumMemberTsDsl) => void); 13 11 14 - export class EnumMemberTsDsl extends TsDsl<ts.EnumMember> { 12 + const Mixed = DocMixin(TsDsl<ts.EnumMember>); 13 + 14 + export class EnumMemberTsDsl extends Mixed { 15 15 private _name: string; 16 16 private _value?: Value; 17 17 ··· 25 25 } 26 26 } 27 27 28 - /** Walk this node and its children with a visitor. */ 29 28 traverse(visitor: (node: SyntaxNode) => void): void { 30 29 console.log(visitor); 31 30 } ··· 36 35 return this; 37 36 } 38 37 39 - $render(): ts.EnumMember { 38 + protected override _render() { 40 39 return ts.factory.createEnumMember( 41 40 safeMemberName(this._name), 42 41 this.$node(this._value), 43 42 ); 44 43 } 45 44 } 46 - 47 - export interface EnumMemberTsDsl extends DocMixin {} 48 - mixin(EnumMemberTsDsl, DocMixin);
+25 -38
packages/openapi-ts/src/ts-dsl/decl/method.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TsDsl, TypeTsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { DecoratorMixin } from '../mixins/decorator'; 8 6 import { DoMixin } from '../mixins/do'; 9 7 import { DocMixin } from '../mixins/doc'; 10 8 import { 11 9 AbstractMixin, 12 10 AsyncMixin, 13 - createModifierAccessor, 14 11 PrivateMixin, 15 12 ProtectedMixin, 16 13 PublicMixin, ··· 22 19 import { TokenTsDsl } from '../token'; 23 20 import { TypeExprTsDsl } from '../type/expr'; 24 21 25 - export class MethodTsDsl extends TsDsl<ts.MethodDeclaration> { 26 - protected modifiers = createModifierAccessor(this); 22 + const Mixed = AbstractMixin( 23 + AsyncMixin( 24 + DecoratorMixin( 25 + DoMixin( 26 + DocMixin( 27 + OptionalMixin( 28 + ParamMixin( 29 + PrivateMixin( 30 + ProtectedMixin( 31 + PublicMixin( 32 + StaticMixin(TypeParamsMixin(TsDsl<ts.MethodDeclaration>)), 33 + ), 34 + ), 35 + ), 36 + ), 37 + ), 38 + ), 39 + ), 40 + ), 41 + ), 42 + ); 43 + 44 + export class MethodTsDsl extends Mixed { 27 45 protected name: string; 28 46 protected _returns?: TypeTsDsl; 29 47 ··· 39 57 return this; 40 58 } 41 59 42 - /** Walk this node and its children with a visitor. */ 43 60 traverse(visitor: (node: SyntaxNode) => void): void { 44 61 console.log(visitor); 45 62 } 46 63 47 - /** Builds the `MethodDeclaration` node. */ 48 - $render(): ts.MethodDeclaration { 64 + protected override _render() { 49 65 return ts.factory.createMethodDeclaration( 50 - [...this.$decorators(), ...this.modifiers.list()], 66 + [...this.$decorators(), ...this.modifiers], 51 67 undefined, 52 68 this.name, 53 69 this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, ··· 58 74 ); 59 75 } 60 76 } 61 - 62 - export interface MethodTsDsl 63 - extends AbstractMixin, 64 - AsyncMixin, 65 - DecoratorMixin, 66 - DoMixin, 67 - DocMixin, 68 - OptionalMixin, 69 - ParamMixin, 70 - PrivateMixin, 71 - ProtectedMixin, 72 - PublicMixin, 73 - StaticMixin, 74 - TypeParamsMixin {} 75 - mixin( 76 - MethodTsDsl, 77 - AbstractMixin, 78 - AsyncMixin, 79 - DecoratorMixin, 80 - DoMixin, 81 - DocMixin, 82 - OptionalMixin, 83 - ParamMixin, 84 - PrivateMixin, 85 - ProtectedMixin, 86 - PublicMixin, 87 - StaticMixin, 88 - TypeParamsMixin, 89 - );
+6 -12
packages/openapi-ts/src/ts-dsl/decl/param.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TsDsl, TypeTsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { DecoratorMixin } from '../mixins/decorator'; 8 6 import { OptionalMixin } from '../mixins/optional'; 9 7 import { PatternMixin } from '../mixins/pattern'; ··· 11 9 import { TokenTsDsl } from '../token'; 12 10 import { TypeExprTsDsl } from '../type/expr'; 13 11 14 - export class ParamTsDsl extends TsDsl<ts.ParameterDeclaration> { 12 + const Mixed = DecoratorMixin( 13 + OptionalMixin(PatternMixin(ValueMixin(TsDsl<ts.ParameterDeclaration>))), 14 + ); 15 + 16 + export class ParamTsDsl extends Mixed { 15 17 protected name?: string; 16 18 protected _type?: TypeTsDsl; 17 19 ··· 28 30 } 29 31 } 30 32 31 - /** Walk this node and its children with a visitor. */ 32 33 traverse(visitor: (node: SyntaxNode) => void): void { 33 34 console.log(visitor); 34 35 } ··· 39 40 return this; 40 41 } 41 42 42 - $render(): ts.ParameterDeclaration { 43 + protected override _render() { 43 44 const name = this.$pattern() ?? this.name; 44 45 if (!name) 45 46 throw new Error( ··· 55 56 ); 56 57 } 57 58 } 58 - 59 - export interface ParamTsDsl 60 - extends DecoratorMixin, 61 - OptionalMixin, 62 - PatternMixin, 63 - ValueMixin {} 64 - mixin(ParamTsDsl, DecoratorMixin, OptionalMixin, PatternMixin, ValueMixin);
+4 -4
packages/openapi-ts/src/ts-dsl/decl/pattern.ts
··· 6 6 import { IdTsDsl } from '../expr/id'; 7 7 import { TokenTsDsl } from '../token'; 8 8 9 + const Mixed = TsDsl<ts.BindingName>; 10 + 9 11 /** 10 12 * Builds binding patterns (e.g. `{ foo, bar }`, `[a, b, ...rest]`). 11 13 */ 12 - export class PatternTsDsl extends TsDsl<ts.BindingName> { 14 + export class PatternTsDsl extends Mixed { 13 15 protected pattern?: 14 16 | { kind: 'array'; values: ReadonlyArray<string> } 15 17 | { kind: 'object'; values: Record<string, string> }; ··· 45 47 return this; 46 48 } 47 49 48 - /** Walk this node and its children with a visitor. */ 49 50 traverse(visitor: (node: SyntaxNode) => void): void { 50 51 console.log(visitor); 51 52 } 52 53 53 - /** Builds and returns a BindingName (ObjectBindingPattern, ArrayBindingPattern, or Identifier). */ 54 - $render(): ts.BindingName { 54 + protected override _render() { 55 55 if (!this.pattern) { 56 56 throw new Error('PatternTsDsl requires object() or array() pattern'); 57 57 }
+22 -33
packages/openapi-ts/src/ts-dsl/decl/setter.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { DecoratorMixin } from '../mixins/decorator'; 8 6 import { DoMixin } from '../mixins/do'; 9 7 import { DocMixin } from '../mixins/doc'; 10 - import type { AsyncMixin } from '../mixins/modifiers'; 11 8 import { 12 9 AbstractMixin, 13 - createModifierAccessor, 10 + AsyncMixin, 14 11 PrivateMixin, 15 12 ProtectedMixin, 16 13 PublicMixin, ··· 18 15 } from '../mixins/modifiers'; 19 16 import { ParamMixin } from '../mixins/param'; 20 17 21 - export class SetterTsDsl extends TsDsl<ts.SetAccessorDeclaration> { 22 - protected modifiers = createModifierAccessor(this); 18 + const Mixed = AbstractMixin( 19 + AsyncMixin( 20 + DecoratorMixin( 21 + DoMixin( 22 + DocMixin( 23 + ParamMixin( 24 + PrivateMixin( 25 + ProtectedMixin( 26 + PublicMixin(StaticMixin(TsDsl<ts.SetAccessorDeclaration>)), 27 + ), 28 + ), 29 + ), 30 + ), 31 + ), 32 + ), 33 + ), 34 + ); 35 + 36 + export class SetterTsDsl extends Mixed { 23 37 protected name: string | ts.PropertyName; 24 38 25 39 constructor(name: string | ts.PropertyName, fn?: (s: SetterTsDsl) => void) { ··· 28 42 fn?.(this); 29 43 } 30 44 31 - /** Walk this node and its children with a visitor. */ 32 45 traverse(visitor: (node: SyntaxNode) => void): void { 33 46 console.log(visitor); 34 47 } 35 48 36 - $render(): ts.SetAccessorDeclaration { 49 + protected override _render() { 37 50 return ts.factory.createSetAccessorDeclaration( 38 - [...this.$decorators(), ...this.modifiers.list()], 51 + [...this.$decorators(), ...this.modifiers], 39 52 this.name, 40 53 this.$params(), 41 54 ts.factory.createBlock(this.$do(), true), 42 55 ); 43 56 } 44 57 } 45 - 46 - export interface SetterTsDsl 47 - extends AbstractMixin, 48 - AsyncMixin, 49 - DecoratorMixin, 50 - DoMixin, 51 - DocMixin, 52 - ParamMixin, 53 - PrivateMixin, 54 - ProtectedMixin, 55 - PublicMixin, 56 - StaticMixin {} 57 - mixin( 58 - SetterTsDsl, 59 - AbstractMixin, 60 - DecoratorMixin, 61 - DoMixin, 62 - DocMixin, 63 - ParamMixin, 64 - PrivateMixin, 65 - ProtectedMixin, 66 - PublicMixin, 67 - StaticMixin, 68 - );
+4 -8
packages/openapi-ts/src/ts-dsl/expr/array.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { AsMixin } from '../mixins/as'; 9 7 import { LayoutMixin } from '../mixins/layout'; 10 8 import { LiteralTsDsl } from './literal'; 11 9 12 - export class ArrayTsDsl extends TsDsl<ts.ArrayLiteralExpression> { 10 + const Mixed = AsMixin(LayoutMixin(TsDsl<ts.ArrayLiteralExpression>)); 11 + 12 + export class ArrayTsDsl extends Mixed { 13 13 protected _elements: Array< 14 14 | { expr: MaybeTsDsl<ts.Expression>; kind: 'element' } 15 15 | { expr: MaybeTsDsl<ts.Expression>; kind: 'spread' } ··· 50 50 return this; 51 51 } 52 52 53 - /** Walk this node and its children with a visitor. */ 54 53 traverse(visitor: (node: SyntaxNode) => void): void { 55 54 console.log(visitor); 56 55 } 57 56 58 - $render(): ts.ArrayLiteralExpression { 57 + protected override _render() { 59 58 const elements = this._elements.map((item) => { 60 59 const node = this.$node(item.expr); 61 60 return item.kind === 'spread' ··· 69 68 ); 70 69 } 71 70 } 72 - 73 - export interface ArrayTsDsl extends AsMixin, LayoutMixin {} 74 - mixin(ArrayTsDsl, AsMixin, LayoutMixin);
+4 -7
packages/openapi-ts/src/ts-dsl/expr/as.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl, TypeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { AsMixin, registerLazyAccessAsFactory } from '../mixins/as'; 9 7 import { ExprMixin } from '../mixins/expr'; 10 8 11 - export class AsTsDsl extends TsDsl<ts.AsExpression> { 9 + const Mixed = AsMixin(ExprMixin(TsDsl<ts.AsExpression>)); 10 + 11 + export class AsTsDsl extends Mixed { 12 12 protected expr: string | MaybeTsDsl<ts.Expression>; 13 13 protected type: string | TypeTsDsl; 14 14 ··· 26 26 console.log(visitor); 27 27 } 28 28 29 - $render() { 29 + protected override _render() { 30 30 return ts.factory.createAsExpression( 31 31 this.$node(this.expr), 32 32 this.$type(this.type), 33 33 ); 34 34 } 35 35 } 36 - 37 - export interface AsTsDsl extends AsMixin, ExprMixin {} 38 - mixin(AsTsDsl, AsMixin, ExprMixin); 39 36 40 37 registerLazyAccessAsFactory((...args) => new AsTsDsl(...args));
+12 -14
packages/openapi-ts/src/ts-dsl/expr/attr.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 ··· 6 5 7 6 import type { MaybeTsDsl } from '../base'; 8 7 import { TsDsl } from '../base'; 9 - import { mixin } from '../mixins/apply'; 10 8 import { AsMixin } from '../mixins/as'; 11 9 import { ExprMixin, registerLazyAccessAttrFactory } from '../mixins/expr'; 12 10 import { OperatorMixin } from '../mixins/operator'; ··· 14 12 import { TokenTsDsl } from '../token'; 15 13 import { LiteralTsDsl } from './literal'; 16 14 17 - export class AttrTsDsl extends TsDsl< 18 - ts.PropertyAccessExpression | ts.ElementAccessExpression 19 - > { 15 + const Mixed = AsMixin( 16 + ExprMixin( 17 + OperatorMixin( 18 + OptionalMixin( 19 + TsDsl<ts.PropertyAccessExpression | ts.ElementAccessExpression>, 20 + ), 21 + ), 22 + ), 23 + ); 24 + 25 + export class AttrTsDsl extends Mixed { 20 26 protected left: string | MaybeTsDsl<ts.Expression>; 21 27 protected right: string | ts.MemberName | number; 22 28 ··· 29 35 this.right = right; 30 36 } 31 37 32 - /** Walk this node and its children with a visitor. */ 33 38 traverse(visitor: (node: SyntaxNode) => void): void { 34 39 console.log(visitor); 35 40 } 36 41 37 - $render(): ts.PropertyAccessExpression | ts.ElementAccessExpression { 42 + protected override _render() { 38 43 const leftNode = this.$node(this.left); 39 44 validTypescriptIdentifierRegExp.lastIndex = 0; 40 45 if ( ··· 67 72 ); 68 73 } 69 74 } 70 - 71 - export interface AttrTsDsl 72 - extends AsMixin, 73 - ExprMixin, 74 - OperatorMixin, 75 - OptionalMixin {} 76 - mixin(AttrTsDsl, AsMixin, ExprMixin, OperatorMixin, OptionalMixin); 77 75 78 76 registerLazyAccessAttrFactory((...args) => new AttrTsDsl(...args));
+4 -8
packages/openapi-ts/src/ts-dsl/expr/await.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { ExprMixin, registerLazyAccessAwaitFactory } from '../mixins/expr'; 9 7 10 - export class AwaitTsDsl extends TsDsl<ts.AwaitExpression> { 8 + const Mixed = ExprMixin(TsDsl<ts.AwaitExpression>); 9 + 10 + export class AwaitTsDsl extends Mixed { 11 11 protected _awaitExpr: string | MaybeTsDsl<ts.Expression>; 12 12 13 13 constructor(expr: string | MaybeTsDsl<ts.Expression>) { ··· 15 15 this._awaitExpr = expr; 16 16 } 17 17 18 - /** Walk this node and its children with a visitor. */ 19 18 traverse(visitor: (node: SyntaxNode) => void): void { 20 19 console.log(visitor); 21 20 } 22 21 23 - $render(): ts.AwaitExpression { 22 + protected override _render() { 24 23 return ts.factory.createAwaitExpression(this.$node(this._awaitExpr)); 25 24 } 26 25 } 27 - 28 - export interface AwaitTsDsl extends ExprMixin {} 29 - mixin(AwaitTsDsl, ExprMixin); 30 26 31 27 registerLazyAccessAwaitFactory((...args) => new AwaitTsDsl(...args));
+4 -8
packages/openapi-ts/src/ts-dsl/expr/binary.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { AsMixin } from '../mixins/as'; 9 7 import { ExprMixin } from '../mixins/expr'; 10 8 ··· 28 26 | '??' 29 27 | '||'; 30 28 31 - export class BinaryTsDsl extends TsDsl<ts.BinaryExpression> { 29 + const Mixed = AsMixin(ExprMixin(TsDsl<ts.BinaryExpression>)); 30 + 31 + export class BinaryTsDsl extends Mixed { 32 32 protected _base: Expr; 33 33 protected _expr?: Expr; 34 34 protected _op?: Op; ··· 120 120 return this.opAndExpr('*', expr); 121 121 } 122 122 123 - /** Walk this node and its children with a visitor. */ 124 123 traverse(visitor: (node: SyntaxNode) => void): void { 125 124 console.log(visitor); 126 125 } 127 126 128 - $render(): ts.BinaryExpression { 127 + protected override _render() { 129 128 if (!this._op) { 130 129 throw new Error('BinaryTsDsl: missing operator'); 131 130 } ··· 172 171 return token; 173 172 } 174 173 } 175 - 176 - export interface BinaryTsDsl extends AsMixin, ExprMixin {} 177 - mixin(BinaryTsDsl, AsMixin, ExprMixin);
+6 -12
packages/openapi-ts/src/ts-dsl/expr/call.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { ArgsMixin } from '../mixins/args'; 9 7 import { AsMixin } from '../mixins/as'; 10 8 import { ExprMixin, registerLazyAccessCallFactory } from '../mixins/expr'; 11 9 import { TypeArgsMixin } from '../mixins/type-args'; 12 10 13 - export class CallTsDsl extends TsDsl<ts.CallExpression> { 11 + const Mixed = ArgsMixin( 12 + AsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.CallExpression>))), 13 + ); 14 + 15 + export class CallTsDsl extends Mixed { 14 16 protected _callee: string | MaybeTsDsl<ts.Expression>; 15 17 16 18 constructor( ··· 24 26 ); 25 27 } 26 28 27 - /** Walk this node and its children with a visitor. */ 28 29 traverse(visitor: (node: SyntaxNode) => void): void { 29 30 console.log(visitor); 30 31 } 31 32 32 - $render(): ts.CallExpression { 33 + protected override _render() { 33 34 return ts.factory.createCallExpression( 34 35 this.$node(this._callee), 35 36 this.$generics(), ··· 37 38 ); 38 39 } 39 40 } 40 - 41 - export interface CallTsDsl 42 - extends ArgsMixin, 43 - AsMixin, 44 - ExprMixin, 45 - TypeArgsMixin {} 46 - mixin(CallTsDsl, ArgsMixin, AsMixin, ExprMixin, TypeArgsMixin); 47 41 48 42 registerLazyAccessCallFactory((expr, args) => new CallTsDsl(expr, ...args));
+6 -12
packages/openapi-ts/src/ts-dsl/expr/expr.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import type ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { AsMixin } from '../mixins/as'; 9 7 import { ExprMixin } from '../mixins/expr'; 10 8 import { OperatorMixin } from '../mixins/operator'; 11 9 import { TypeExprMixin } from '../mixins/type-expr'; 12 10 13 - export class ExprTsDsl extends TsDsl<ts.Expression> { 11 + const Mixed = AsMixin( 12 + ExprMixin(OperatorMixin(TypeExprMixin(TsDsl<ts.Expression>))), 13 + ); 14 + 15 + export class ExprTsDsl extends Mixed { 14 16 protected _exprInput: string | MaybeTsDsl<ts.Expression>; 15 17 16 18 constructor(id: string | MaybeTsDsl<ts.Expression>) { ··· 18 20 this._exprInput = id; 19 21 } 20 22 21 - /** Walk this node and its children with a visitor. */ 22 23 traverse(visitor: (node: SyntaxNode) => void): void { 23 24 console.log(visitor); 24 25 } 25 26 26 - $render(): ts.Expression { 27 + protected override _render() { 27 28 return this.$node(this._exprInput); 28 29 } 29 30 } 30 - 31 - export interface ExprTsDsl 32 - extends AsMixin, 33 - ExprMixin, 34 - OperatorMixin, 35 - TypeExprMixin {} 36 - mixin(ExprTsDsl, AsMixin, ExprMixin, OperatorMixin, TypeExprMixin);
+4 -3
packages/openapi-ts/src/ts-dsl/expr/id.ts
··· 3 3 4 4 import { TsDsl } from '../base'; 5 5 6 - export class IdTsDsl extends TsDsl<ts.Identifier> { 6 + const Mixed = TsDsl<ts.Identifier>; 7 + 8 + export class IdTsDsl extends Mixed { 7 9 protected name: string; 8 10 9 11 constructor(name: string) { ··· 11 13 this.name = name; 12 14 } 13 15 14 - /** Walk this node and its children with a visitor. */ 15 16 traverse(visitor: (node: SyntaxNode) => void): void { 16 17 console.log(visitor); 17 18 } 18 19 19 - $render(): ts.Identifier { 20 + protected override _render() { 20 21 return ts.factory.createIdentifier(this.name); 21 22 } 22 23 }
+4 -8
packages/openapi-ts/src/ts-dsl/expr/literal.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging, @typescript-eslint/no-empty-object-type */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TsDsl } from '../base'; 6 5 import { PrefixTsDsl } from '../expr/prefix'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { AsMixin } from '../mixins/as'; 9 7 10 - export class LiteralTsDsl extends TsDsl<ts.LiteralTypeNode['literal']> { 8 + const Mixed = AsMixin(TsDsl<ts.LiteralTypeNode['literal']>); 9 + 10 + export class LiteralTsDsl extends Mixed { 11 11 protected value: string | number | boolean | null; 12 12 13 13 constructor(value: string | number | boolean | null) { ··· 15 15 this.value = value; 16 16 } 17 17 18 - /** Walk this node and its children with a visitor. */ 19 18 traverse(visitor: (node: SyntaxNode) => void): void { 20 19 console.log(visitor); 21 20 } 22 21 23 - $render(): ts.LiteralTypeNode['literal'] { 22 + protected override _render() { 24 23 if (typeof this.value === 'boolean') { 25 24 return this.value ? ts.factory.createTrue() : ts.factory.createFalse(); 26 25 } ··· 37 36 throw new Error(`Unsupported literal: ${String(this.value)}`); 38 37 } 39 38 } 40 - 41 - export interface LiteralTsDsl extends AsMixin {} 42 - mixin(LiteralTsDsl, AsMixin);
+4 -9
packages/openapi-ts/src/ts-dsl/expr/new.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { ArgsMixin } from '../mixins/args'; 9 7 import { ExprMixin } from '../mixins/expr'; 10 8 import { TypeArgsMixin } from '../mixins/type-args'; 11 9 12 - export class NewTsDsl extends TsDsl<ts.NewExpression> { 10 + const Mixed = ArgsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.NewExpression>))); 11 + 12 + export class NewTsDsl extends Mixed { 13 13 protected classExpr: string | MaybeTsDsl<ts.Expression>; 14 14 15 15 constructor( ··· 21 21 this.args(...args); 22 22 } 23 23 24 - /** Walk this node and its children with a visitor. */ 25 24 traverse(visitor: (node: SyntaxNode) => void): void { 26 25 console.log(visitor); 27 26 } 28 27 29 - /** Builds the `NewExpression` node. */ 30 - $render(): ts.NewExpression { 28 + protected override _render() { 31 29 return ts.factory.createNewExpression( 32 30 this.$node(this.classExpr), 33 31 this.$generics(), ··· 35 33 ); 36 34 } 37 35 } 38 - 39 - export interface NewTsDsl extends ArgsMixin, ExprMixin, TypeArgsMixin {} 40 - mixin(NewTsDsl, ArgsMixin, ExprMixin, TypeArgsMixin);
+6 -13
packages/openapi-ts/src/ts-dsl/expr/object.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { AsMixin } from '../mixins/as'; 9 7 import { ExprMixin } from '../mixins/expr'; 10 8 import { HintMixin } from '../mixins/hint'; ··· 16 14 type ExprFn = Expr | ((p: ObjectPropTsDsl) => void); 17 15 type StmtFn = Stmt | ((p: ObjectPropTsDsl) => void); 18 16 19 - export class ObjectTsDsl extends TsDsl<ts.ObjectLiteralExpression> { 17 + const Mixed = AsMixin( 18 + ExprMixin(HintMixin(LayoutMixin(TsDsl<ts.ObjectLiteralExpression>))), 19 + ); 20 + 21 + export class ObjectTsDsl extends Mixed { 20 22 protected _props: Array<ObjectPropTsDsl> = []; 21 23 22 24 constructor(...props: Array<ObjectPropTsDsl>) { ··· 72 74 return this; 73 75 } 74 76 75 - /** Walk this node and its children with a visitor. */ 76 77 traverse(visitor: (node: SyntaxNode) => void): void { 77 78 console.log(visitor); 78 79 } 79 80 80 - /** Builds and returns the object literal expression. */ 81 - $render(): ts.ObjectLiteralExpression { 81 + protected override _render() { 82 82 return ts.factory.createObjectLiteralExpression( 83 83 this.$node(this._props), 84 84 this.$multiline(this._props.length), 85 85 ); 86 86 } 87 87 } 88 - 89 - export interface ObjectTsDsl 90 - extends AsMixin, 91 - ExprMixin, 92 - HintMixin, 93 - LayoutMixin {} 94 - mixin(ObjectTsDsl, AsMixin, ExprMixin, HintMixin, LayoutMixin);
+4 -4
packages/openapi-ts/src/ts-dsl/expr/prefix.ts
··· 4 4 import type { MaybeTsDsl } from '../base'; 5 5 import { TsDsl } from '../base'; 6 6 7 - export class PrefixTsDsl extends TsDsl<ts.PrefixUnaryExpression> { 7 + const Mixed = TsDsl<ts.PrefixUnaryExpression>; 8 + 9 + export class PrefixTsDsl extends Mixed { 8 10 protected _expr?: string | MaybeTsDsl<ts.Expression>; 9 11 protected _op?: ts.PrefixUnaryOperator; 10 12 ··· 41 43 return this; 42 44 } 43 45 44 - /** Walk this node and its children with a visitor. */ 45 46 traverse(visitor: (node: SyntaxNode) => void): void { 46 47 console.log(visitor); 47 48 } 48 49 49 - /** Renders the prefix unary expression node. */ 50 - $render(): ts.PrefixUnaryExpression { 50 + protected override _render() { 51 51 if (!this._expr) { 52 52 throw new Error('Missing expression for prefix unary expression'); 53 53 }
+4 -8
packages/openapi-ts/src/ts-dsl/expr/prop.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 ··· 6 5 import { TsDsl } from '../base'; 7 6 import { GetterTsDsl } from '../decl/getter'; 8 7 import { SetterTsDsl } from '../decl/setter'; 9 - import { mixin } from '../mixins/apply'; 10 8 import { DocMixin } from '../mixins/doc'; 11 9 import { safePropName } from '../utils/prop'; 12 10 import { IdTsDsl } from './id'; ··· 22 20 | { kind: 'setter'; name: string } 23 21 | { kind: 'spread'; name?: undefined }; 24 22 25 - export class ObjectPropTsDsl extends TsDsl<ts.ObjectLiteralElementLike> { 23 + const Mixed = DocMixin(TsDsl<ts.ObjectLiteralElementLike>); 24 + 25 + export class ObjectPropTsDsl extends Mixed { 26 26 protected _value?: Expr | Stmt; 27 27 protected meta: Meta; 28 28 ··· 36 36 return this.missingRequiredCalls().length === 0; 37 37 } 38 38 39 - /** Walk this node and its children with a visitor. */ 40 39 traverse(visitor: (node: SyntaxNode) => void): void { 41 40 console.log(visitor); 42 41 } ··· 50 49 return this; 51 50 } 52 51 53 - $render(): ts.ObjectLiteralElementLike { 52 + protected override _render() { 54 53 this.$validate(); 55 54 const node = this.$node(this._value); 56 55 if (this.meta.kind === 'spread') { ··· 104 103 return missing; 105 104 } 106 105 } 107 - 108 - export interface ObjectPropTsDsl extends DocMixin {} 109 - mixin(ObjectPropTsDsl, DocMixin);
+4 -4
packages/openapi-ts/src/ts-dsl/expr/regexp.ts
··· 11 11 [K in Avail]: `${K}${RegexFlags<Exclude<Avail, K>>}`; 12 12 }[Avail]; 13 13 14 - export class RegExpTsDsl extends TsDsl<ts.RegularExpressionLiteral> { 14 + const Mixed = TsDsl<ts.RegularExpressionLiteral>; 15 + 16 + export class RegExpTsDsl extends Mixed { 15 17 protected pattern: string; 16 18 protected flags?: RegexFlags; 17 19 ··· 21 23 this.flags = flags; 22 24 } 23 25 24 - /** Walk this node and its children with a visitor. */ 25 26 traverse(visitor: (node: SyntaxNode) => void): void { 26 27 console.log(visitor); 27 28 } 28 29 29 - /** Emits a RegularExpressionLiteral node. */ 30 - $render(): ts.RegularExpressionLiteral { 30 + protected override _render() { 31 31 const patternContent = 32 32 this.pattern.startsWith('/') && this.pattern.endsWith('/') 33 33 ? this.pattern.slice(1, -1)
+4 -5
packages/openapi-ts/src/ts-dsl/expr/template.ts
··· 4 4 import type { MaybeTsDsl } from '../base'; 5 5 import { TsDsl } from '../base'; 6 6 7 - export class TemplateTsDsl extends TsDsl< 8 - ts.TemplateExpression | ts.NoSubstitutionTemplateLiteral 9 - > { 7 + const Mixed = TsDsl<ts.TemplateExpression | ts.NoSubstitutionTemplateLiteral>; 8 + 9 + export class TemplateTsDsl extends Mixed { 10 10 protected parts: Array<string | MaybeTsDsl<ts.Expression>> = []; 11 11 12 12 constructor(value?: string | MaybeTsDsl<ts.Expression>) { ··· 19 19 return this; 20 20 } 21 21 22 - /** Walk this node and its children with a visitor. */ 23 22 traverse(visitor: (node: SyntaxNode) => void): void { 24 23 console.log(visitor); 25 24 } 26 25 27 - $render(): ts.TemplateExpression | ts.NoSubstitutionTemplateLiteral { 26 + protected override _render() { 28 27 const parts = this.$node(this.parts); 29 28 30 29 const normalized: Array<string | ts.Expression> = [];
+4 -3
packages/openapi-ts/src/ts-dsl/expr/ternary.ts
··· 4 4 import type { MaybeTsDsl } from '../base'; 5 5 import { TsDsl } from '../base'; 6 6 7 - export class TernaryTsDsl extends TsDsl<ts.ConditionalExpression> { 7 + const Mixed = TsDsl<ts.ConditionalExpression>; 8 + 9 + export class TernaryTsDsl extends Mixed { 8 10 protected _condition?: string | MaybeTsDsl<ts.Expression>; 9 11 protected _then?: string | MaybeTsDsl<ts.Expression>; 10 12 protected _else?: string | MaybeTsDsl<ts.Expression>; ··· 29 31 return this; 30 32 } 31 33 32 - /** Walk this node and its children with a visitor. */ 33 34 traverse(visitor: (node: SyntaxNode) => void): void { 34 35 console.log(visitor); 35 36 } 36 37 37 - $render(): ts.ConditionalExpression { 38 + protected override _render() { 38 39 if (!this._condition) throw new Error('Missing condition in ternary'); 39 40 if (!this._then) throw new Error('Missing then expression in ternary'); 40 41 if (!this._else) throw new Error('Missing else expression in ternary');
+4 -8
packages/openapi-ts/src/ts-dsl/expr/typeof.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { OperatorMixin } from '../mixins/operator'; 9 7 import { registerLazyAccessTypeOfExprFactory } from '../mixins/type-expr'; 10 8 11 - export class TypeOfExprTsDsl extends TsDsl<ts.TypeOfExpression> { 9 + const Mixed = OperatorMixin(TsDsl<ts.TypeOfExpression>); 10 + 11 + export class TypeOfExprTsDsl extends Mixed { 12 12 protected _expr: string | MaybeTsDsl<ts.Expression>; 13 13 14 14 constructor(expr: string | MaybeTsDsl<ts.Expression>) { ··· 16 16 this._expr = expr; 17 17 } 18 18 19 - /** Walk this node and its children with a visitor. */ 20 19 traverse(visitor: (node: SyntaxNode) => void): void { 21 20 console.log(visitor); 22 21 } 23 22 24 - $render(): ts.TypeOfExpression { 23 + protected override _render() { 25 24 return ts.factory.createTypeOfExpression(this.$node(this._expr)); 26 25 } 27 26 } 28 - 29 - export interface TypeOfExprTsDsl extends OperatorMixin {} 30 - mixin(TypeOfExprTsDsl, OperatorMixin); 31 27 32 28 registerLazyAccessTypeOfExprFactory((...args) => new TypeOfExprTsDsl(...args));
+4 -4
packages/openapi-ts/src/ts-dsl/layout/doc.ts
··· 59 59 return node; 60 60 } 61 61 62 - /** Walk this node and its children with a visitor. */ 63 - traverse(visitor: (node: SyntaxNode) => void): void { 64 - console.log(visitor); 62 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 63 + traverse(_visitor: (node: SyntaxNode) => void): void { 64 + // noop 65 65 } 66 66 67 - $render(): ts.Node { 67 + protected override _render(): ts.Node { 68 68 // this class does not build a standalone node; 69 69 // it modifies other nodes via `apply()`. 70 70 // Return a dummy comment node for compliance.
+4 -4
packages/openapi-ts/src/ts-dsl/layout/hint.ts
··· 41 41 return node; 42 42 } 43 43 44 - /** Walk this node and its children with a visitor. */ 45 - traverse(visitor: (node: SyntaxNode) => void): void { 46 - console.log(visitor); 44 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 45 + traverse(_visitor: (node: SyntaxNode) => void): void { 46 + // noop 47 47 } 48 48 49 - $render(): ts.Node { 49 + protected override _render(): ts.Node { 50 50 // this class does not build a standalone node; 51 51 // it modifies other nodes via `apply()`. 52 52 // Return a dummy comment node for compliance.
+4 -4
packages/openapi-ts/src/ts-dsl/layout/newline.ts
··· 5 5 import { IdTsDsl } from '../expr/id'; 6 6 7 7 export class NewlineTsDsl extends TsDsl<ts.Identifier> { 8 - /** Walk this node and its children with a visitor. */ 9 - traverse(visitor: (node: SyntaxNode) => void): void { 10 - console.log(visitor); 8 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 9 + traverse(_visitor: (node: SyntaxNode) => void): void { 10 + // noop 11 11 } 12 12 13 - $render(): ts.Identifier { 13 + protected override _render(): ts.Identifier { 14 14 return this.$node(new IdTsDsl('\n')); 15 15 } 16 16 }
+4 -4
packages/openapi-ts/src/ts-dsl/layout/note.ts
··· 39 39 return node; 40 40 } 41 41 42 - /** Walk this node and its children with a visitor. */ 43 - traverse(visitor: (node: SyntaxNode) => void): void { 44 - console.log(visitor); 42 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 43 + traverse(_visitor: (node: SyntaxNode) => void): void { 44 + // noop 45 45 } 46 46 47 - $render(): ts.Node { 47 + protected override _render(): ts.Node { 48 48 // this class does not build a standalone node; 49 49 // it modifies other nodes via `apply()`. 50 50 // Return a dummy comment node for compliance.
-25
packages/openapi-ts/src/ts-dsl/mixins/apply.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-function-type */ 2 - export function mixin(target: Function, ...sources: ReadonlyArray<Function>) { 3 - const targetProto = target.prototype; 4 - for (const src of sources) { 5 - let resolvedSource = src; 6 - if (typeof src === 'function') { 7 - try { 8 - const candidate = src(target); 9 - if (candidate?.prototype) { 10 - resolvedSource = candidate; 11 - } 12 - } catch { 13 - // noop 14 - } 15 - } 16 - const sourceProto = resolvedSource.prototype; 17 - if (!sourceProto) continue; 18 - for (const [key, descriptor] of Object.entries( 19 - Object.getOwnPropertyDescriptors(sourceProto), 20 - )) { 21 - if (key === 'constructor') continue; 22 - Object.defineProperty(targetProto, key, descriptor); 23 - } 24 - } 25 - }
+30 -17
packages/openapi-ts/src/ts-dsl/mixins/args.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 3 import type { MaybeTsDsl } from '../base'; 4 - import { TsDsl } from '../base'; 4 + import type { BaseCtor, MixinCtor } from './types'; 5 + 6 + export interface ArgsMethods { 7 + /** Renders the arguments into an array of `Expression`s. */ 8 + $args(): ReadonlyArray<ts.Expression>; 9 + /** Adds a single expression argument. */ 10 + arg(arg: string | MaybeTsDsl<ts.Expression>): this; 11 + /** Adds one or more expression arguments. */ 12 + args(...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>>): this; 13 + } 5 14 6 15 /** 7 16 * Adds `.arg()` and `.args()` for managing expression arguments in call-like nodes. 8 17 */ 9 - export abstract class ArgsMixin extends TsDsl { 10 - protected _args?: Array<string | MaybeTsDsl<ts.Expression>>; 18 + export function ArgsMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 19 + Base: TBase, 20 + ) { 21 + abstract class Args extends Base { 22 + protected _args: Array<string | MaybeTsDsl<ts.Expression>> = []; 11 23 12 - /** Adds a single expression argument. */ 13 - arg(arg: string | MaybeTsDsl<ts.Expression>): this { 14 - (this._args ??= []).push(arg); 15 - return this; 16 - } 24 + protected arg(arg: string | MaybeTsDsl<ts.Expression>): this { 25 + this._args.push(arg); 26 + return this; 27 + } 28 + 29 + protected args( 30 + ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 31 + ): this { 32 + this._args.push(...args); 33 + return this; 34 + } 17 35 18 - /** Adds one or more expression arguments. */ 19 - args(...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>>): this { 20 - (this._args ??= []).push(...args); 21 - return this; 36 + protected $args(): ReadonlyArray<ts.Expression> { 37 + return this.$node(this._args).map((arg) => this.$maybeId(arg)); 38 + } 22 39 } 23 40 24 - /** Renders the arguments into an array of `Expression`s. */ 25 - protected $args(): ReadonlyArray<ts.Expression> { 26 - if (!this._args) return []; 27 - return this.$node(this._args).map((arg) => this.$maybeId(arg)); 28 - } 41 + return Args as unknown as MixinCtor<TBase, ArgsMethods>; 29 42 }
+14 -6
packages/openapi-ts/src/ts-dsl/mixins/as.ts
··· 2 2 3 3 import type { MaybeTsDsl, TypeTsDsl } from '../base'; 4 4 import type { AsTsDsl } from '../expr/as'; 5 + import type { BaseCtor, MixinCtor } from './types'; 5 6 6 7 type AsFactory = ( 7 8 expr: string | MaybeTsDsl<ts.Expression>, ··· 13 14 asFactory = factory; 14 15 } 15 16 16 - export class AsMixin { 17 + export interface AsMethods { 17 18 /** Creates an `as` type assertion expression (e.g. `value as Type`). */ 18 - as( 19 - this: string | MaybeTsDsl<ts.Expression>, 20 - type: string | TypeTsDsl, 21 - ): AsTsDsl { 22 - return asFactory!(this, type); 19 + as(type: string | TypeTsDsl): AsTsDsl; 20 + } 21 + 22 + export function AsMixin<T extends ts.Expression, TBase extends BaseCtor<T>>( 23 + Base: TBase, 24 + ) { 25 + abstract class As extends Base { 26 + protected as(type: string | TypeTsDsl): AsTsDsl { 27 + return asFactory!(this, type); 28 + } 23 29 } 30 + 31 + return As as unknown as MixinCtor<TBase, AsMethods>; 24 32 }
+27 -14
packages/openapi-ts/src/ts-dsl/mixins/decorator.ts
··· 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 5 import { DecoratorTsDsl } from '../decl/decorator'; 6 + import type { BaseCtor, MixinCtor } from './types'; 7 7 8 - export abstract class DecoratorMixin extends TsDsl { 9 - protected decorators?: Array<DecoratorTsDsl>; 10 - 8 + export interface DecoratorMethods { 9 + /** Renders the decorators into an array of `ts.Decorator`s. */ 10 + $decorators(): ReadonlyArray<ts.Decorator>; 11 11 /** Adds a decorator (e.g. `@sealed({ in: 'root' })`). */ 12 12 decorator( 13 13 name: Symbol | string | MaybeTsDsl<ts.Expression>, 14 14 ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 15 - ): this { 16 - (this.decorators ??= []).push( 17 - new DecoratorTsDsl(name, ...args).setParent(this), 18 - ); 19 - return this; 20 - } 15 + ): this; 16 + } 17 + 18 + export function DecoratorMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 19 + Base: TBase, 20 + ) { 21 + abstract class Decorator extends Base { 22 + protected decorators: Array<DecoratorTsDsl> = []; 23 + 24 + protected decorator( 25 + name: Symbol | string | MaybeTsDsl<ts.Expression>, 26 + ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 27 + ): this { 28 + this.decorators.push( 29 + new DecoratorTsDsl(name, ...args).setParent(this as any), 30 + ); 31 + return this; 32 + } 21 33 22 - /** Renders the decorators into an array of `ts.Decorator`s. */ 23 - protected $decorators(): ReadonlyArray<ts.Decorator> { 24 - if (!this.decorators) return []; 25 - return this.$node(this.decorators); 34 + protected $decorators(): ReadonlyArray<ts.Decorator> { 35 + return this.$node(this.decorators); 36 + } 26 37 } 38 + 39 + return Decorator as unknown as MixinCtor<TBase, DecoratorMethods>; 27 40 }
+24 -12
packages/openapi-ts/src/ts-dsl/mixins/do.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 3 import type { MaybeTsDsl } from '../base'; 4 - import { TsDsl } from '../base'; 5 4 import { StmtTsDsl } from '../stmt/stmt'; 5 + import type { BaseCtor, MixinCtor } from './types'; 6 + 7 + export interface DoMethods { 8 + /** Renders the collected `.do()` calls into an array of `Statement` nodes. */ 9 + $do(): ReadonlyArray<ts.Statement>; 10 + /** Adds one or more expressions/statements to the body. */ 11 + do(...items: ReadonlyArray<MaybeTsDsl<ts.Expression | ts.Statement>>): this; 12 + } 6 13 7 14 /** 8 15 * Adds `.do()` for appending statements or expressions to a body. 9 16 */ 10 - export abstract class DoMixin extends TsDsl { 11 - protected _do?: Array<MaybeTsDsl<ts.Expression | ts.Statement>>; 17 + export function DoMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 18 + Base: TBase, 19 + ) { 20 + abstract class Do extends Base { 21 + protected _do: Array<MaybeTsDsl<ts.Expression | ts.Statement>> = []; 12 22 13 - /** Adds one or more expressions/statements to the body. */ 14 - do(...items: ReadonlyArray<MaybeTsDsl<ts.Expression | ts.Statement>>): this { 15 - (this._do ??= []).push(...items); 16 - return this; 17 - } 23 + protected do( 24 + ...items: ReadonlyArray<MaybeTsDsl<ts.Expression | ts.Statement>> 25 + ): this { 26 + this._do.push(...items); 27 + return this; 28 + } 18 29 19 - /** Renders the collected `.do()` calls into an array of `Statement` nodes. */ 20 - protected $do(): ReadonlyArray<ts.Statement> { 21 - if (!this._do) return []; 22 - return this.$node(this._do.map((item) => new StmtTsDsl(item))); 30 + protected $do(): ReadonlyArray<ts.Statement> { 31 + return this.$node(this._do.map((item) => new StmtTsDsl(item))); 32 + } 23 33 } 34 + 35 + return Do as unknown as MixinCtor<TBase, DoMethods>; 24 36 }
+19 -11
packages/openapi-ts/src/ts-dsl/mixins/doc.ts
··· 1 - import type { Constructor, ITsDsl, MaybeArray } from '../base'; 1 + import type ts from 'typescript'; 2 + 3 + import type { MaybeArray } from '../base'; 2 4 import { DocTsDsl } from '../layout/doc'; 5 + import type { BaseCtor, MixinCtor } from './types'; 3 6 4 - export function DocMixin<TBase extends Constructor>(Base: TBase) { 5 - const $renderBase = Base.prototype.$render; 7 + export interface DocMethods { 8 + doc(lines?: MaybeArray<string>, fn?: (d: DocTsDsl) => void): this; 9 + } 6 10 7 - class Mixin extends Base { 8 - _doc?: DocTsDsl; 11 + export function DocMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 12 + Base: TBase, 13 + ) { 14 + abstract class Doc extends Base { 15 + protected _doc?: DocTsDsl; 9 16 10 - doc(lines?: MaybeArray<string>, fn?: (d: DocTsDsl) => void): this { 17 + protected doc( 18 + lines?: MaybeArray<string>, 19 + fn?: (d: DocTsDsl) => void, 20 + ): this { 11 21 this._doc = new DocTsDsl(lines, fn); 12 22 return this; 13 23 } 14 24 15 - override $render(...args: Parameters<ITsDsl['$render']>) { 16 - const node = $renderBase.apply(this, args); 25 + protected override _render() { 26 + const node = this.$render(); 17 27 return this._doc ? this._doc.apply(node) : node; 18 28 } 19 29 } 20 30 21 - return Mixin; 31 + return Doc as unknown as MixinCtor<TBase, DocMethods>; 22 32 } 23 - 24 - export type DocMixin = InstanceType<ReturnType<typeof DocMixin>>;
+35 -23
packages/openapi-ts/src/ts-dsl/mixins/expr.ts
··· 5 5 import type { AwaitTsDsl } from '../expr/await'; 6 6 import type { CallTsDsl } from '../expr/call'; 7 7 import type { ReturnTsDsl } from '../stmt/return'; 8 + import type { BaseCtor, MixinCtor } from './types'; 8 9 9 10 type AttrFactory = ( 10 - expr: string | MaybeTsDsl<ts.Expression>, 11 + expr: MaybeTsDsl<ts.Expression>, 11 12 name: string | ts.MemberName | number, 12 13 ) => AttrTsDsl; 13 14 let attrFactory: AttrFactory | undefined; ··· 16 17 attrFactory = factory; 17 18 } 18 19 19 - type AwaitFactory = (expr: string | MaybeTsDsl<ts.Expression>) => AwaitTsDsl; 20 + type AwaitFactory = (expr: MaybeTsDsl<ts.Expression>) => AwaitTsDsl; 20 21 let awaitFactory: AwaitFactory | undefined; 21 22 /** Registers the Await DSL factory after its module has finished evaluating. */ 22 23 export function registerLazyAccessAwaitFactory(factory: AwaitFactory): void { ··· 24 25 } 25 26 26 27 type CallFactory = ( 27 - expr: string | MaybeTsDsl<ts.Expression>, 28 + expr: MaybeTsDsl<ts.Expression>, 28 29 args: ReadonlyArray<string | MaybeTsDsl<ts.Expression> | undefined>, 29 30 ) => CallTsDsl; 30 31 let callFactory: CallFactory | undefined; ··· 33 34 callFactory = factory; 34 35 } 35 36 36 - type ReturnFactory = (expr: string | MaybeTsDsl<ts.Expression>) => ReturnTsDsl; 37 + type ReturnFactory = (expr: MaybeTsDsl<ts.Expression>) => ReturnTsDsl; 37 38 let returnFactory: ReturnFactory | undefined; 38 39 /** Registers the Return DSL factory after its module has finished evaluating. */ 39 40 export function registerLazyAccessReturnFactory(factory: ReturnFactory): void { 40 41 returnFactory = factory; 41 42 } 42 43 43 - export class ExprMixin { 44 + export interface ExprMethods { 44 45 /** Accesses a property on the current expression (e.g. `this.foo`). */ 45 - attr( 46 - this: string | MaybeTsDsl<ts.Expression>, 47 - name: string | ts.MemberName | number, 48 - ): AttrTsDsl { 49 - return attrFactory!(this, name); 50 - } 51 - 46 + attr(name: string | ts.MemberName | number): AttrTsDsl; 52 47 /** Awaits the current expression (e.g. `await expr`). */ 53 - await(this: string | MaybeTsDsl<ts.Expression>): AwaitTsDsl { 54 - return awaitFactory!(this); 55 - } 56 - 48 + await(): AwaitTsDsl; 57 49 /** Calls the current expression (e.g. `fn(arg1, arg2)`). */ 58 50 call( 59 - this: string | MaybeTsDsl<ts.Expression>, 60 51 ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression> | undefined> 61 - ): CallTsDsl { 62 - return callFactory!(this, args); 63 - } 64 - 52 + ): CallTsDsl; 65 53 /** Produces a `return` statement returning the current expression. */ 66 - return(this: string | MaybeTsDsl<ts.Expression>): ReturnTsDsl { 67 - return returnFactory!(this); 54 + return(): ReturnTsDsl; 55 + } 56 + 57 + export function ExprMixin<T extends ts.Expression, TBase extends BaseCtor<T>>( 58 + Base: TBase, 59 + ) { 60 + abstract class Expr extends Base { 61 + protected attr(name: string | ts.MemberName | number): AttrTsDsl { 62 + return attrFactory!(this, name); 63 + } 64 + 65 + protected await(): AwaitTsDsl { 66 + return awaitFactory!(this); 67 + } 68 + 69 + protected call( 70 + ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression> | undefined> 71 + ): CallTsDsl { 72 + return callFactory!(this, args); 73 + } 74 + 75 + protected return(): ReturnTsDsl { 76 + return returnFactory!(this); 77 + } 68 78 } 79 + 80 + return Expr as unknown as MixinCtor<TBase, ExprMethods>; 69 81 }
+19 -11
packages/openapi-ts/src/ts-dsl/mixins/hint.ts
··· 1 - import type { Constructor, ITsDsl, MaybeArray } from '../base'; 1 + import type ts from 'typescript'; 2 + 3 + import type { MaybeArray } from '../base'; 2 4 import { HintTsDsl } from '../layout/hint'; 5 + import type { BaseCtor, MixinCtor } from './types'; 3 6 4 - export function HintMixin<TBase extends Constructor>(Base: TBase) { 5 - const $renderBase = Base.prototype.$render; 7 + export interface HintMethods { 8 + hint(lines?: MaybeArray<string>, fn?: (h: HintTsDsl) => void): this; 9 + } 6 10 7 - class Mixin extends Base { 8 - _hint?: HintTsDsl; 11 + export function HintMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 12 + Base: TBase, 13 + ) { 14 + abstract class Hint extends Base { 15 + protected _hint?: HintTsDsl; 9 16 10 - hint(lines?: MaybeArray<string>, fn?: (h: HintTsDsl) => void): this { 17 + protected hint( 18 + lines?: MaybeArray<string>, 19 + fn?: (h: HintTsDsl) => void, 20 + ): this { 11 21 this._hint = new HintTsDsl(lines, fn); 12 22 return this; 13 23 } 14 24 15 - override $render(...args: Parameters<ITsDsl['$render']>) { 16 - const node = $renderBase.apply(this, args); 25 + protected override _render() { 26 + const node = this.$render(); 17 27 return this._hint ? this._hint.apply(node) : node; 18 28 } 19 29 } 20 30 21 - return Mixin; 31 + return Hint as unknown as MixinCtor<TBase, HintMethods>; 22 32 } 23 - 24 - export type HintMixin = InstanceType<ReturnType<typeof HintMixin>>;
+41 -22
packages/openapi-ts/src/ts-dsl/mixins/layout.ts
··· 1 - export class LayoutMixin { 2 - protected static readonly DEFAULT_THRESHOLD = 3; 3 - protected layout: boolean | number | undefined; 1 + import type ts from 'typescript'; 4 2 3 + import type { BaseCtor, MixinCtor } from './types'; 4 + 5 + export interface LayoutMethods { 6 + /** Computes whether output should be multiline based on layout setting and element count. */ 7 + $multiline(count: number): boolean; 5 8 /** Sets automatic line output with optional threshold (default: 3). */ 6 - auto(threshold: number = LayoutMixin.DEFAULT_THRESHOLD): this { 7 - this.layout = threshold; 8 - return this; 9 - } 9 + auto(threshold?: number): this; 10 + /** Sets single line output. */ 11 + inline(): this; 12 + /** Sets multi line output. */ 13 + pretty(): this; 14 + } 10 15 11 - /** Sets single line output. */ 12 - inline(): this { 13 - this.layout = false; 14 - return this; 15 - } 16 + export function LayoutMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 17 + Base: TBase, 18 + ) { 19 + abstract class Layout extends Base { 20 + protected static readonly DEFAULT_THRESHOLD = 3; 21 + protected layout: boolean | number | undefined; 22 + 23 + protected auto(threshold: number = Layout.DEFAULT_THRESHOLD): this { 24 + this.layout = threshold; 25 + return this; 26 + } 27 + 28 + protected inline(): this { 29 + this.layout = false; 30 + return this; 31 + } 16 32 17 - /** Sets multi line output. */ 18 - pretty(): this { 19 - this.layout = true; 20 - return this; 21 - } 33 + protected pretty(): this { 34 + this.layout = true; 35 + return this; 36 + } 22 37 23 - /** Computes whether output should be multiline based on layout setting and element count. */ 24 - protected $multiline(count: number): boolean { 25 - if (this.layout === undefined) { 26 - this.layout = LayoutMixin.DEFAULT_THRESHOLD; 38 + protected $multiline(count: number): boolean { 39 + if (this.layout === undefined) { 40 + this.layout = Layout.DEFAULT_THRESHOLD; 41 + } 42 + return typeof this.layout === 'number' 43 + ? count >= this.layout 44 + : this.layout; 27 45 } 28 - return typeof this.layout === 'number' ? count >= this.layout : this.layout; 29 46 } 47 + 48 + return Layout as unknown as MixinCtor<TBase, LayoutMethods>; 30 49 }
+247 -102
packages/openapi-ts/src/ts-dsl/mixins/modifiers.ts
··· 1 - import type { Symbol } from '@hey-api/codegen-core'; 2 1 import ts from 'typescript'; 3 2 4 - import type { TsDsl } from '../base'; 3 + import type { BaseCtor, MixinCtor } from './types'; 5 4 6 - /** 7 - * Creates an accessor for adding TypeScript modifiers to a parent DSL node. 8 - * 9 - * @param parent - The parent DSL node to which modifiers will be added. 10 - * @returns An object with a `list` method that returns the collected modifiers. 11 - */ 12 - export function createModifierAccessor<Parent extends TsDsl>(parent: Parent) { 13 - const modifiers: Array<ts.Modifier> = []; 5 + export type Modifiers = { 6 + modifiers: Array<ts.Modifier>; 7 + }; 14 8 9 + export interface ModifierMethods extends Modifiers { 15 10 /** 16 11 * Adds a modifier of the specified kind to the modifiers list if the condition is true. 17 12 * ··· 19 14 * @param condition - Whether to add the modifier. 20 15 * @returns The parent DSL node for chaining. 21 16 */ 22 - function _m(kind: ts.ModifierSyntaxKind, condition: boolean): Parent { 23 - if (condition) modifiers.push(ts.factory.createModifier(kind)); 24 - return parent; 25 - } 17 + _m(kind: ts.ModifierSyntaxKind, condition: boolean): this; 18 + } 26 19 27 - Object.assign(parent, { _m }); // attaches to parent 20 + function ModifiersMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 21 + Base: TBase, 22 + ) { 23 + abstract class Modifiers extends Base { 24 + protected modifiers: Array<ts.Modifier> = []; 28 25 29 - /** 30 - * Returns the list of collected modifiers. 31 - * 32 - * @returns Array of TypeScript modifiers. 33 - */ 34 - function list() { 35 - return modifiers; 26 + protected _m(kind: ts.ModifierSyntaxKind, condition: boolean): this { 27 + if (condition) this.modifiers.push(ts.factory.createModifier(kind)); 28 + return this; 29 + } 36 30 } 37 31 38 - return { list }; 32 + return Modifiers as unknown as MixinCtor<TBase, ModifierMethods>; 39 33 } 40 34 41 - type Target = object & { 42 - _m?(kind: ts.ModifierSyntaxKind, condition?: boolean): unknown; 43 - symbol?: Symbol; 44 - }; 45 - 46 - /** 47 - * Mixin that adds an `abstract` modifier to a node. 48 - */ 49 - export class AbstractMixin { 35 + export interface AbstractMethods extends Modifiers { 50 36 /** 51 37 * Adds the `abstract` keyword modifier if the condition is true. 52 38 * 53 39 * @param condition - Whether to add the modifier (default: true). 54 40 * @returns The target object for chaining. 55 41 */ 56 - abstract<T extends Target>(this: T, condition?: boolean): T { 57 - const cond = arguments.length === 0 ? true : Boolean(condition); 58 - return this._m!(ts.SyntaxKind.AbstractKeyword, cond) as T; 59 - } 42 + abstract(condition?: boolean): this; 60 43 } 61 44 62 45 /** 63 - * Mixin that adds an `async` modifier to a node. 46 + * Mixin that adds an `abstract` modifier to a node. 64 47 */ 65 - export class AsyncMixin { 48 + export function AbstractMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 49 + Base: TBase, 50 + ) { 51 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 52 + 53 + abstract class Abstract extends Mixed { 54 + protected abstract(condition?: boolean): this { 55 + const cond = arguments.length === 0 ? true : Boolean(condition); 56 + return this._m(ts.SyntaxKind.AbstractKeyword, cond); 57 + } 58 + } 59 + 60 + return Abstract as unknown as MixinCtor<TBase, AbstractMethods>; 61 + } 62 + 63 + export interface AsyncMethods extends Modifiers { 66 64 /** 67 65 * Adds the `async` keyword modifier if the condition is true. 68 66 * 69 67 * @param condition - Whether to add the modifier (default: true). 70 68 * @returns The target object for chaining. 71 69 */ 72 - async<T extends Target>(this: T, condition?: boolean): T { 73 - const cond = arguments.length === 0 ? true : Boolean(condition); 74 - return this._m!(ts.SyntaxKind.AsyncKeyword, cond) as T; 75 - } 70 + async(condition?: boolean): this; 76 71 } 77 72 78 73 /** 79 - * Mixin that adds a `const` modifier to a node. 74 + * Mixin that adds an `async` modifier to a node. 80 75 */ 81 - export class ConstMixin { 76 + export function AsyncMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 77 + Base: TBase, 78 + ) { 79 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 80 + 81 + abstract class Async extends Mixed { 82 + protected async(condition?: boolean): this { 83 + const cond = arguments.length === 0 ? true : Boolean(condition); 84 + return this._m(ts.SyntaxKind.AsyncKeyword, cond); 85 + } 86 + } 87 + 88 + return Async as unknown as MixinCtor<TBase, AsyncMethods>; 89 + } 90 + 91 + export interface ConstMethods extends Modifiers { 82 92 /** 83 93 * Adds the `const` keyword modifier if the condition is true. 84 94 * 85 95 * @param condition - Whether to add the modifier (default: true). 86 96 * @returns The target object for chaining. 87 97 */ 88 - const<T extends Target>(this: T, condition?: boolean): T { 89 - const cond = arguments.length === 0 ? true : Boolean(condition); 90 - return this._m!(ts.SyntaxKind.ConstKeyword, cond) as T; 91 - } 98 + const(condition?: boolean): this; 92 99 } 93 100 94 101 /** 95 - * Mixin that adds a `declare` modifier to a node. 102 + * Mixin that adds a `const` modifier to a node. 96 103 */ 97 - export class DeclareMixin { 104 + export function ConstMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 105 + Base: TBase, 106 + ) { 107 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 108 + 109 + abstract class Const extends Mixed { 110 + protected const(condition?: boolean): this { 111 + const cond = arguments.length === 0 ? true : Boolean(condition); 112 + return this._m(ts.SyntaxKind.ConstKeyword, cond); 113 + } 114 + } 115 + 116 + return Const as unknown as MixinCtor<TBase, ConstMethods>; 117 + } 118 + 119 + export interface DeclareMethods extends Modifiers { 98 120 /** 99 121 * Adds the `declare` keyword modifier if the condition is true. 100 122 * 101 123 * @param condition - Whether to add the modifier (default: true). 102 124 * @returns The target object for chaining. 103 125 */ 104 - declare<T extends Target>(this: T, condition?: boolean): T { 105 - const cond = arguments.length === 0 ? true : Boolean(condition); 106 - return this._m!(ts.SyntaxKind.DeclareKeyword, cond) as T; 107 - } 126 + declare(condition?: boolean): this; 108 127 } 109 128 110 129 /** 111 - * Mixin that adds a `default` modifier to a node. 130 + * Mixin that adds a `declare` modifier to a node. 112 131 */ 113 - export class DefaultMixin { 132 + export function DeclareMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 133 + Base: TBase, 134 + ) { 135 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 136 + 137 + abstract class Declare extends Mixed { 138 + protected declare(condition?: boolean): this { 139 + const cond = arguments.length === 0 ? true : Boolean(condition); 140 + return this._m(ts.SyntaxKind.DeclareKeyword, cond); 141 + } 142 + } 143 + 144 + return Declare as unknown as MixinCtor<TBase, DeclareMethods>; 145 + } 146 + 147 + export interface DefaultMethods extends Modifiers { 114 148 /** 115 149 * Adds the `default` keyword modifier if the condition is true. 116 150 * 117 151 * @param condition - Whether to add the modifier (default: true). 118 152 * @returns The target object for chaining. 119 153 */ 120 - default<T extends Target>(this: T, condition?: boolean): T { 121 - const cond = arguments.length === 0 ? true : Boolean(condition); 122 - return this._m!(ts.SyntaxKind.DefaultKeyword, cond) as T; 123 - } 154 + default(condition?: boolean): this; 124 155 } 125 156 126 157 /** 127 - * Mixin that adds an `export` modifier to a node. 158 + * Mixin that adds a `default` modifier to a node. 128 159 */ 129 - export class ExportMixin { 160 + export function DefaultMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 161 + Base: TBase, 162 + ) { 163 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 164 + 165 + abstract class Default extends Mixed { 166 + /** 167 + * Adds the `default` keyword modifier if the condition is true. 168 + * 169 + * @param condition - Whether to add the modifier (default: true). 170 + * @returns The target object for chaining. 171 + */ 172 + protected default(condition?: boolean): this { 173 + const cond = arguments.length === 0 ? true : Boolean(condition); 174 + return this._m(ts.SyntaxKind.DefaultKeyword, cond); 175 + } 176 + } 177 + 178 + return Default as unknown as MixinCtor<TBase, DefaultMethods>; 179 + } 180 + 181 + export interface ExportMethods extends Modifiers { 130 182 /** 131 183 * Adds the `export` keyword modifier if the condition is true. 132 184 * 133 185 * @param condition - Whether to add the modifier (default: true). 134 186 * @returns The target object for chaining. 135 187 */ 136 - export<T extends Target>(this: T, condition?: boolean): T { 137 - const cond = arguments.length === 0 ? true : Boolean(condition); 138 - if (this.symbol) this.symbol.setExported(cond); 139 - return this._m!(ts.SyntaxKind.ExportKeyword, cond) as T; 140 - } 188 + export(condition?: boolean): this; 141 189 } 142 190 143 191 /** 144 - * Mixin that adds an `override` modifier to a node. 192 + * Mixin that adds an `export` modifier to a node. 145 193 */ 146 - export class OverrideMixin { 194 + export function ExportMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 195 + Base: TBase, 196 + ) { 197 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 198 + 199 + abstract class Export extends Mixed { 200 + /** 201 + * Adds the `export` keyword modifier if the condition is true. 202 + * 203 + * @param condition - Whether to add the modifier (default: true). 204 + * @returns The target object for chaining. 205 + */ 206 + protected export(condition?: boolean): this { 207 + const cond = arguments.length === 0 ? true : Boolean(condition); 208 + if (this.symbol) this.symbol.setExported(cond); 209 + return this._m(ts.SyntaxKind.ExportKeyword, cond); 210 + } 211 + } 212 + 213 + return Export as unknown as MixinCtor<TBase, ExportMethods>; 214 + } 215 + 216 + export interface OverrideMethods extends Modifiers { 147 217 /** 148 218 * Adds the `override` keyword modifier if the condition is true. 149 219 * 150 220 * @param condition - Whether to add the modifier (default: true). 151 221 * @returns The target object for chaining. 152 222 */ 153 - override<T extends Target>(this: T, condition?: boolean): T { 154 - const cond = arguments.length === 0 ? true : Boolean(condition); 155 - return this._m!(ts.SyntaxKind.OverrideKeyword, cond) as T; 156 - } 223 + override(condition?: boolean): this; 157 224 } 158 225 159 226 /** 160 - * Mixin that adds a `private` modifier to a node. 227 + * Mixin that adds an `override` modifier to a node. 161 228 */ 162 - export class PrivateMixin { 229 + export function OverrideMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 230 + Base: TBase, 231 + ) { 232 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 233 + 234 + abstract class Override extends Mixed { 235 + protected override(condition?: boolean): this { 236 + const cond = arguments.length === 0 ? true : Boolean(condition); 237 + return this._m(ts.SyntaxKind.OverrideKeyword, cond); 238 + } 239 + } 240 + 241 + return Override as unknown as MixinCtor<TBase, OverrideMethods>; 242 + } 243 + 244 + export interface PrivateMethods extends Modifiers { 163 245 /** 164 246 * Adds the `private` keyword modifier if the condition is true. 165 247 * 166 248 * @param condition - Whether to add the modifier (default: true). 167 249 * @returns The target object for chaining. 168 250 */ 169 - private<T extends Target>(this: T, condition?: boolean): T { 170 - const cond = arguments.length === 0 ? true : Boolean(condition); 171 - return this._m!(ts.SyntaxKind.PrivateKeyword, cond) as T; 172 - } 251 + private(condition?: boolean): this; 173 252 } 174 253 175 254 /** 176 - * Mixin that adds a `protected` modifier to a node. 255 + * Mixin that adds a `private` modifier to a node. 177 256 */ 178 - export class ProtectedMixin { 257 + export function PrivateMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 258 + Base: TBase, 259 + ) { 260 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 261 + 262 + abstract class Private extends Mixed { 263 + protected private(condition?: boolean): this { 264 + const cond = arguments.length === 0 ? true : Boolean(condition); 265 + return this._m(ts.SyntaxKind.PrivateKeyword, cond); 266 + } 267 + } 268 + 269 + return Private as unknown as MixinCtor<TBase, PrivateMethods>; 270 + } 271 + 272 + export interface ProtectedMethods extends Modifiers { 179 273 /** 180 274 * Adds the `protected` keyword modifier if the condition is true. 181 275 * 182 276 * @param condition - Whether to add the modifier (default: true). 183 277 * @returns The target object for chaining. 184 278 */ 185 - protected<T extends Target>(this: T, condition?: boolean): T { 186 - const cond = arguments.length === 0 ? true : Boolean(condition); 187 - return this._m!(ts.SyntaxKind.ProtectedKeyword, cond) as T; 188 - } 279 + protected(condition?: boolean): this; 189 280 } 190 281 191 282 /** 192 - * Mixin that adds a `public` modifier to a node. 283 + * Mixin that adds a `protected` modifier to a node. 193 284 */ 194 - export class PublicMixin { 285 + export function ProtectedMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 286 + Base: TBase, 287 + ) { 288 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 289 + 290 + abstract class Protected extends Mixed { 291 + protected protected(condition?: boolean): this { 292 + const cond = arguments.length === 0 ? true : Boolean(condition); 293 + return this._m(ts.SyntaxKind.ProtectedKeyword, cond); 294 + } 295 + } 296 + 297 + return Protected as unknown as MixinCtor<TBase, ProtectedMethods>; 298 + } 299 + 300 + export interface PublicMethods extends Modifiers { 195 301 /** 196 302 * Adds the `public` keyword modifier if the condition is true. 197 303 * 198 304 * @param condition - Whether to add the modifier (default: true). 199 305 * @returns The target object for chaining. 200 306 */ 201 - public<T extends Target>(this: T, condition?: boolean): T { 202 - const cond = arguments.length === 0 ? true : Boolean(condition); 203 - return this._m!(ts.SyntaxKind.PublicKeyword, cond) as T; 204 - } 307 + public(condition?: boolean): this; 205 308 } 206 309 207 310 /** 208 - * Mixin that adds a `readonly` modifier to a node. 311 + * Mixin that adds a `public` modifier to a node. 209 312 */ 210 - export class ReadonlyMixin { 313 + export function PublicMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 314 + Base: TBase, 315 + ) { 316 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 317 + 318 + abstract class Public extends Mixed { 319 + protected public(condition?: boolean): this { 320 + const cond = arguments.length === 0 ? true : Boolean(condition); 321 + return this._m(ts.SyntaxKind.PublicKeyword, cond); 322 + } 323 + } 324 + 325 + return Public as unknown as MixinCtor<TBase, PublicMethods>; 326 + } 327 + 328 + export interface ReadonlyMethods extends Modifiers { 211 329 /** 212 330 * Adds the `readonly` keyword modifier if the condition is true. 213 331 * 214 332 * @param condition - Whether to add the modifier (default: true). 215 333 * @returns The target object for chaining. 216 334 */ 217 - readonly<T extends Target>(this: T, condition?: boolean): T { 218 - const cond = arguments.length === 0 ? true : Boolean(condition); 219 - return this._m!(ts.SyntaxKind.ReadonlyKeyword, cond) as T; 220 - } 335 + readonly(condition?: boolean): this; 221 336 } 222 337 223 338 /** 224 - * Mixin that adds a `static` modifier to a node. 339 + * Mixin that adds a `readonly` modifier to a node. 225 340 */ 226 - export class StaticMixin { 341 + export function ReadonlyMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 342 + Base: TBase, 343 + ) { 344 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 345 + 346 + abstract class Readonly extends Mixed { 347 + protected readonly(condition?: boolean): this { 348 + const cond = arguments.length === 0 ? true : Boolean(condition); 349 + return this._m(ts.SyntaxKind.ReadonlyKeyword, cond); 350 + } 351 + } 352 + 353 + return Readonly as unknown as MixinCtor<TBase, ReadonlyMethods>; 354 + } 355 + 356 + export interface StaticMethods extends Modifiers { 227 357 /** 228 358 * Adds the `static` keyword modifier if the condition is true. 229 359 * 230 360 * @param condition - Whether to add the modifier (default: true). 231 361 * @returns The target object for chaining. 232 362 */ 233 - static<T extends Target>(this: T, condition?: boolean): T { 234 - const cond = arguments.length === 0 ? true : Boolean(condition); 235 - return this._m!(ts.SyntaxKind.StaticKeyword, cond) as T; 363 + static(condition?: boolean): this; 364 + } 365 + 366 + /** 367 + * Mixin that adds a `static` modifier to a node. 368 + */ 369 + export function StaticMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 370 + Base: TBase, 371 + ) { 372 + const Mixed = ModifiersMixin(Base as BaseCtor<T>); 373 + 374 + abstract class Static extends Mixed { 375 + protected static(condition?: boolean): this { 376 + const cond = arguments.length === 0 ? true : Boolean(condition); 377 + return this._m(ts.SyntaxKind.StaticKeyword, cond); 378 + } 236 379 } 380 + 381 + return Static as unknown as MixinCtor<TBase, StaticMethods>; 237 382 }
+19 -11
packages/openapi-ts/src/ts-dsl/mixins/note.ts
··· 1 - import type { Constructor, ITsDsl, MaybeArray } from '../base'; 1 + import type ts from 'typescript'; 2 + 3 + import type { MaybeArray } from '../base'; 2 4 import { NoteTsDsl } from '../layout/note'; 5 + import type { BaseCtor, MixinCtor } from './types'; 3 6 4 - export function NoteMixin<TBase extends Constructor>(Base: TBase) { 5 - const $renderBase = Base.prototype.$render; 7 + export interface NoteMethods { 8 + note(lines?: MaybeArray<string>, fn?: (h: NoteTsDsl) => void): this; 9 + } 6 10 7 - class Mixin extends Base { 8 - _note?: NoteTsDsl; 11 + export function NoteMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 12 + Base: TBase, 13 + ) { 14 + abstract class Note extends Base { 15 + protected _note?: NoteTsDsl; 9 16 10 - note(lines?: MaybeArray<string>, fn?: (h: NoteTsDsl) => void): this { 17 + protected note( 18 + lines?: MaybeArray<string>, 19 + fn?: (h: NoteTsDsl) => void, 20 + ): this { 11 21 this._note = new NoteTsDsl(lines, fn); 12 22 return this; 13 23 } 14 24 15 - override $render(...args: Parameters<ITsDsl['$render']>) { 16 - const node = $renderBase.apply(this, args); 25 + protected override _render() { 26 + const node = this.$render(); 17 27 return this._note ? this._note.apply(node) : node; 18 28 } 19 29 } 20 30 21 - return Mixin; 31 + return Note as unknown as MixinCtor<TBase, NoteMethods>; 22 32 } 23 - 24 - export type NoteMixin = InstanceType<ReturnType<typeof NoteMixin>>;
+90 -64
packages/openapi-ts/src/ts-dsl/mixins/operator.ts
··· 2 2 3 3 import type { MaybeTsDsl } from '../base'; 4 4 import { BinaryTsDsl } from '../expr/binary'; 5 + import type { BaseCtor, MixinCtor } from './types'; 5 6 6 - type This = string | MaybeTsDsl<ts.Expression>; 7 7 type Expr = string | MaybeTsDsl<ts.Expression>; 8 8 9 - export class OperatorMixin { 9 + export interface OperatorMethods { 10 10 /** Logical AND — `this && expr` */ 11 - and(this: This, expr: Expr): BinaryTsDsl { 12 - return new BinaryTsDsl(this).and(expr); 13 - } 14 - 11 + and(expr: Expr): BinaryTsDsl; 15 12 /** Creates an assignment expression (e.g. `this = expr`). */ 16 - assign(this: This, expr: Expr): BinaryTsDsl { 17 - return new BinaryTsDsl(this, '=', expr); 18 - } 19 - 13 + assign(expr: Expr): BinaryTsDsl; 20 14 /** Nullish coalescing — `this ?? expr` */ 21 - coalesce(this: This, expr: Expr): BinaryTsDsl { 22 - return new BinaryTsDsl(this).coalesce(expr); 23 - } 24 - 15 + coalesce(expr: Expr): BinaryTsDsl; 25 16 /** Division — `this / expr` */ 26 - div(this: This, expr: Expr): BinaryTsDsl { 27 - return new BinaryTsDsl(this).div(expr); 28 - } 29 - 17 + div(expr: Expr): BinaryTsDsl; 30 18 /** Strict equality — `this === expr` */ 31 - eq(this: This, expr: Expr): BinaryTsDsl { 32 - return new BinaryTsDsl(this).eq(expr); 33 - } 34 - 19 + eq(expr: Expr): BinaryTsDsl; 35 20 /** Greater than — `this > expr` */ 36 - gt(this: This, expr: Expr): BinaryTsDsl { 37 - return new BinaryTsDsl(this).gt(expr); 38 - } 39 - 21 + gt(expr: Expr): BinaryTsDsl; 40 22 /** Greater than or equal — `this >= expr` */ 41 - gte(this: This, expr: Expr): BinaryTsDsl { 42 - return new BinaryTsDsl(this).gte(expr); 43 - } 44 - 23 + gte(expr: Expr): BinaryTsDsl; 45 24 /** Loose equality — `this == expr` */ 46 - looseEq(this: This, expr: Expr): BinaryTsDsl { 47 - return new BinaryTsDsl(this).looseEq(expr); 48 - } 49 - 25 + looseEq(expr: Expr): BinaryTsDsl; 50 26 /** Loose inequality — `this != expr` */ 51 - looseNeq(this: This, expr: Expr): BinaryTsDsl { 52 - return new BinaryTsDsl(this).looseNeq(expr); 53 - } 54 - 27 + looseNeq(expr: Expr): BinaryTsDsl; 55 28 /** Less than — `this < expr` */ 56 - lt(this: This, expr: Expr): BinaryTsDsl { 57 - return new BinaryTsDsl(this).lt(expr); 58 - } 59 - 29 + lt(expr: Expr): BinaryTsDsl; 60 30 /** Less than or equal — `this <= expr` */ 61 - lte(this: This, expr: Expr): BinaryTsDsl { 62 - return new BinaryTsDsl(this).lte(expr); 63 - } 64 - 31 + lte(expr: Expr): BinaryTsDsl; 65 32 /** Subtraction — `this - expr` */ 66 - minus(this: This, expr: Expr): BinaryTsDsl { 67 - return new BinaryTsDsl(this).minus(expr); 68 - } 69 - 33 + minus(expr: Expr): BinaryTsDsl; 70 34 /** Strict inequality — `this !== expr` */ 71 - neq(this: This, expr: Expr): BinaryTsDsl { 72 - return new BinaryTsDsl(this).neq(expr); 73 - } 74 - 35 + neq(expr: Expr): BinaryTsDsl; 75 36 /** Logical OR — `this || expr` */ 76 - or(this: This, expr: Expr): BinaryTsDsl { 77 - return new BinaryTsDsl(this).or(expr); 78 - } 79 - 37 + or(expr: Expr): BinaryTsDsl; 80 38 /** Addition — `this + expr` */ 81 - plus(this: This, expr: Expr): BinaryTsDsl { 82 - return new BinaryTsDsl(this).plus(expr); 83 - } 39 + plus(expr: Expr): BinaryTsDsl; 40 + /** Multiplication — `this * expr` */ 41 + times(expr: Expr): BinaryTsDsl; 42 + } 84 43 85 - /** Multiplication — `this * expr` */ 86 - times(this: This, expr: Expr): BinaryTsDsl { 87 - return new BinaryTsDsl(this).times(expr); 44 + export function OperatorMixin< 45 + T extends ts.Expression, 46 + TBase extends BaseCtor<T>, 47 + >(Base: TBase) { 48 + abstract class Operator extends Base { 49 + protected and(expr: Expr): BinaryTsDsl { 50 + return new BinaryTsDsl(this).and(expr); 51 + } 52 + 53 + protected assign(expr: Expr): BinaryTsDsl { 54 + return new BinaryTsDsl(this, '=', expr); 55 + } 56 + 57 + protected coalesce(expr: Expr): BinaryTsDsl { 58 + return new BinaryTsDsl(this).coalesce(expr); 59 + } 60 + 61 + protected div(expr: Expr): BinaryTsDsl { 62 + return new BinaryTsDsl(this).div(expr); 63 + } 64 + 65 + protected eq(expr: Expr): BinaryTsDsl { 66 + return new BinaryTsDsl(this).eq(expr); 67 + } 68 + 69 + protected gt(expr: Expr): BinaryTsDsl { 70 + return new BinaryTsDsl(this).gt(expr); 71 + } 72 + 73 + protected gte(expr: Expr): BinaryTsDsl { 74 + return new BinaryTsDsl(this).gte(expr); 75 + } 76 + 77 + protected looseEq(expr: Expr): BinaryTsDsl { 78 + return new BinaryTsDsl(this).looseEq(expr); 79 + } 80 + 81 + protected looseNeq(expr: Expr): BinaryTsDsl { 82 + return new BinaryTsDsl(this).looseNeq(expr); 83 + } 84 + 85 + protected lt(expr: Expr): BinaryTsDsl { 86 + return new BinaryTsDsl(this).lt(expr); 87 + } 88 + 89 + protected lte(expr: Expr): BinaryTsDsl { 90 + return new BinaryTsDsl(this).lte(expr); 91 + } 92 + 93 + protected minus(expr: Expr): BinaryTsDsl { 94 + return new BinaryTsDsl(this).minus(expr); 95 + } 96 + 97 + protected neq(expr: Expr): BinaryTsDsl { 98 + return new BinaryTsDsl(this).neq(expr); 99 + } 100 + 101 + protected or(expr: Expr): BinaryTsDsl { 102 + return new BinaryTsDsl(this).or(expr); 103 + } 104 + 105 + protected plus(expr: Expr): BinaryTsDsl { 106 + return new BinaryTsDsl(this).plus(expr); 107 + } 108 + 109 + protected times(expr: Expr): BinaryTsDsl { 110 + return new BinaryTsDsl(this).times(expr); 111 + } 88 112 } 113 + 114 + return Operator as unknown as MixinCtor<TBase, OperatorMethods>; 89 115 }
+26 -10
packages/openapi-ts/src/ts-dsl/mixins/optional.ts
··· 1 - export class OptionalMixin { 2 - protected _optional?: boolean; 1 + import type ts from 'typescript'; 3 2 4 - /** Marks the node as optional when the condition is true. */ 5 - optional<T extends this>(this: T, condition?: boolean): T { 6 - this._optional = arguments.length === 0 ? true : Boolean(condition); 7 - return this; 8 - } 3 + import type { BaseCtor, MixinCtor } from './types'; 9 4 5 + export interface OptionalMethods { 6 + _optional?: boolean; 7 + /** Marks the node as optional when the condition is true. */ 8 + optional(condition?: boolean): this; 10 9 /** Marks the node as required when the condition is true. */ 11 - required<T extends this>(this: T, condition?: boolean): T { 12 - this._optional = arguments.length === 0 ? false : !condition; 13 - return this; 10 + required(condition?: boolean): this; 11 + } 12 + 13 + export function OptionalMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 14 + Base: TBase, 15 + ) { 16 + abstract class Optional extends Base { 17 + protected _optional?: boolean; 18 + 19 + protected optional(condition?: boolean): this { 20 + this._optional = arguments.length === 0 ? true : Boolean(condition); 21 + return this; 22 + } 23 + 24 + protected required(condition?: boolean): this { 25 + this._optional = arguments.length === 0 ? false : !condition; 26 + return this; 27 + } 14 28 } 29 + 30 + return Optional as unknown as MixinCtor<TBase, OptionalMethods>; 15 31 }
+35 -18
packages/openapi-ts/src/ts-dsl/mixins/param.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import { type MaybeTsDsl, TsDsl } from '../base'; 3 + import type { MaybeTsDsl } from '../base'; 4 4 import { ParamTsDsl } from '../decl/param'; 5 + import type { BaseCtor, MixinCtor } from './types'; 5 6 6 - export abstract class ParamMixin extends TsDsl { 7 - protected _params?: Array<MaybeTsDsl<ts.ParameterDeclaration>>; 8 - 7 + export interface ParamMethods { 8 + /** Renders the parameters into an array of `ParameterDeclaration`s. */ 9 + $params(): ReadonlyArray<ts.ParameterDeclaration>; 9 10 /** Adds a parameter. */ 10 11 param( 11 12 name: string | ((p: ParamTsDsl) => void), 12 13 fn?: (p: ParamTsDsl) => void, 13 - ): this { 14 - const p = new ParamTsDsl(name, fn); 15 - (this._params ??= []).push(p); 16 - return this; 17 - } 14 + ): this; 15 + /** Adds multiple parameters. */ 16 + params(...params: ReadonlyArray<MaybeTsDsl<ts.ParameterDeclaration>>): this; 17 + } 18 + 19 + export function ParamMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 20 + Base: TBase, 21 + ) { 22 + abstract class Param extends Base { 23 + protected _params: Array<MaybeTsDsl<ts.ParameterDeclaration>> = []; 24 + 25 + protected param( 26 + name: string | ((p: ParamTsDsl) => void), 27 + fn?: (p: ParamTsDsl) => void, 28 + ): this { 29 + const p = new ParamTsDsl(name, fn); 30 + this._params.push(p); 31 + return this; 32 + } 18 33 19 - /** Adds multiple parameters. */ 20 - params(...params: ReadonlyArray<MaybeTsDsl<ts.ParameterDeclaration>>): this { 21 - (this._params ??= []).push(...params); 22 - return this; 23 - } 34 + protected params( 35 + ...params: ReadonlyArray<MaybeTsDsl<ts.ParameterDeclaration>> 36 + ): this { 37 + this._params.push(...params); 38 + return this; 39 + } 24 40 25 - /** Renders the parameters into an array of `ParameterDeclaration`s. */ 26 - protected $params(): ReadonlyArray<ts.ParameterDeclaration> { 27 - if (!this._params) return []; 28 - return this.$node(this._params); 41 + protected $params(): ReadonlyArray<ts.ParameterDeclaration> { 42 + return this.$node(this._params); 43 + } 29 44 } 45 + 46 + return Param as unknown as MixinCtor<TBase, ParamMethods>; 30 47 }
+42 -23
packages/openapi-ts/src/ts-dsl/mixins/pattern.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 3 import type { MaybeArray } from '../base'; 4 - import { TsDsl } from '../base'; 5 4 import { PatternTsDsl } from '../decl/pattern'; 5 + import type { BaseCtor, MixinCtor } from './types'; 6 + 7 + export interface PatternMethods { 8 + /** Renders the pattern into a `BindingName`. */ 9 + $pattern(): ts.BindingName | undefined; 10 + /** Defines an array binding pattern. */ 11 + array(...props: ReadonlyArray<string> | [ReadonlyArray<string>]): this; 12 + /** Defines an object binding pattern. */ 13 + object( 14 + ...props: ReadonlyArray<MaybeArray<string> | Record<string, string>> 15 + ): this; 16 + /** Adds a spread element (e.g. `...args`, `...options`) to the pattern. */ 17 + spread(name: string): this; 18 + } 6 19 7 20 /** 8 21 * Mixin providing `.array()`, `.object()`, and `.spread()` methods for defining destructuring patterns. 9 22 */ 10 - export abstract class PatternMixin extends TsDsl { 11 - protected pattern?: PatternTsDsl; 23 + export function PatternMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 24 + Base: TBase, 25 + ) { 26 + abstract class Pattern extends Base { 27 + protected pattern: PatternTsDsl = new PatternTsDsl(); 12 28 13 - /** Defines an array binding pattern. */ 14 - array(...props: ReadonlyArray<string> | [ReadonlyArray<string>]): this { 15 - (this.pattern ??= new PatternTsDsl()).array(...props); 16 - return this; 17 - } 29 + protected array( 30 + ...props: ReadonlyArray<string> | [ReadonlyArray<string>] 31 + ): this { 32 + this.pattern.array(...props); 33 + return this; 34 + } 35 + 36 + protected object( 37 + ...props: ReadonlyArray<MaybeArray<string> | Record<string, string>> 38 + ): this { 39 + this.pattern.object(...props); 40 + return this; 41 + } 18 42 19 - /** Defines an object binding pattern. */ 20 - object( 21 - ...props: ReadonlyArray<MaybeArray<string> | Record<string, string>> 22 - ): this { 23 - (this.pattern ??= new PatternTsDsl()).object(...props); 24 - return this; 25 - } 43 + /** Adds a spread element (e.g. `...args`, `...options`) to the pattern. */ 44 + protected spread(name: string): this { 45 + this.pattern.spread(name); 46 + return this; 47 + } 26 48 27 - /** Adds a spread element (e.g. `...args`, `...options`) to the pattern. */ 28 - spread(name: string): this { 29 - (this.pattern ??= new PatternTsDsl()).spread(name); 30 - return this; 49 + /** Renders the pattern into a `BindingName`. */ 50 + protected $pattern(): ts.BindingName | undefined { 51 + return this.$node(this.pattern); 52 + } 31 53 } 32 54 33 - /** Renders the pattern into a `BindingName`. */ 34 - protected $pattern(): ts.BindingName | undefined { 35 - return this.$node(this.pattern); 36 - } 55 + return Pattern as unknown as MixinCtor<TBase, PatternMethods>; 37 56 }
+30 -16
packages/openapi-ts/src/ts-dsl/mixins/type-args.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 3 import type { MaybeTsDsl, TypeTsDsl } from '../base'; 4 - import { TsDsl } from '../base'; 5 - 6 - export abstract class TypeArgsMixin extends TsDsl { 7 - protected _generics?: Array<string | MaybeTsDsl<TypeTsDsl>>; 4 + import type { BaseCtor, MixinCtor } from './types'; 8 5 6 + export interface TypeArgsMethods { 7 + /** Returns the type arguments as an array of ts.TypeNode nodes. */ 8 + $generics(): ReadonlyArray<ts.TypeNode> | undefined; 9 9 /** Adds a single type argument (e.g. `string` in `Foo<string>`). */ 10 - generic(arg: string | MaybeTsDsl<TypeTsDsl>): this { 11 - (this._generics ??= []).push(arg); 12 - return this; 13 - } 14 - 10 + generic(arg: string | MaybeTsDsl<TypeTsDsl>): this; 15 11 /** Adds type arguments (e.g. `Map<string, number>`). */ 16 - generics(...args: ReadonlyArray<string | MaybeTsDsl<TypeTsDsl>>): this { 17 - (this._generics ??= []).push(...args); 18 - return this; 19 - } 12 + generics(...args: ReadonlyArray<string | MaybeTsDsl<TypeTsDsl>>): this; 13 + } 20 14 21 - /** Returns the type arguments as an array of ts.TypeNode nodes. */ 22 - protected $generics(): ReadonlyArray<ts.TypeNode> | undefined { 23 - return this.$type(this._generics); 15 + export function TypeArgsMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 16 + Base: TBase, 17 + ) { 18 + abstract class TypeArgs extends Base { 19 + protected _generics?: Array<string | MaybeTsDsl<TypeTsDsl>>; 20 + 21 + protected generic(arg: string | MaybeTsDsl<TypeTsDsl>): this { 22 + (this._generics ??= []).push(arg); 23 + return this; 24 + } 25 + 26 + protected generics( 27 + ...args: ReadonlyArray<string | MaybeTsDsl<TypeTsDsl>> 28 + ): this { 29 + (this._generics ??= []).push(...args); 30 + return this; 31 + } 32 + 33 + protected $generics(): ReadonlyArray<ts.TypeNode> | undefined { 34 + return this.$type(this._generics); 35 + } 24 36 } 37 + 38 + return TypeArgs as unknown as MixinCtor<TBase, TypeArgsMethods>; 25 39 }
+69 -40
packages/openapi-ts/src/ts-dsl/mixins/type-expr.ts
··· 7 7 import type { TypeIdxTsDsl } from '../type/idx'; 8 8 import type { TypeOperatorTsDsl } from '../type/operator'; 9 9 import type { TypeQueryTsDsl } from '../type/query'; 10 + import type { BaseCtor, MixinCtor } from './types'; 10 11 11 12 type TypeExprFactory = ( 12 13 nameOrFn?: string | ((t: TypeExprTsDsl) => void), ··· 63 64 typeQueryFactory = factory; 64 65 } 65 66 66 - export class TypeExprMixin { 67 + export interface TypeExprMethods { 67 68 /** Creates an indexed-access type (e.g. `Foo<T>[K]`). */ 68 69 idx( 69 70 this: MaybeTsDsl<TypeTsDsl>, 70 71 index: string | number | MaybeTsDsl<ts.TypeNode>, 71 - ): TypeIdxTsDsl { 72 - return typeIdxFactory!(this, index); 73 - } 74 - 72 + ): TypeIdxTsDsl; 75 73 /** Shorthand: builds `keyof T`. */ 76 - keyof(this: MaybeTsDsl<TypeTsDsl>): TypeOperatorTsDsl { 77 - return typeOperatorFactory!().keyof(this); 78 - } 79 - 74 + keyof(this: MaybeTsDsl<TypeTsDsl>): TypeOperatorTsDsl; 80 75 /** Shorthand: builds `readonly T`. */ 81 - readonly(this: MaybeTsDsl<TypeTsDsl>): TypeOperatorTsDsl { 82 - return typeOperatorFactory!().readonly(this); 83 - } 84 - 76 + readonly(this: MaybeTsDsl<TypeTsDsl>): TypeOperatorTsDsl; 85 77 /** Create a TypeExpr DSL node representing ReturnType<this>. */ 86 - returnType(this: MaybeTsDsl<ts.Expression>): TypeExprTsDsl { 87 - return typeExprFactory!('ReturnType').generic(typeQueryFactory!(this)); 88 - } 89 - 90 - /** Create a TypeOfExpr DSL node representing typeof this. */ 91 - typeofExpr(this: MaybeTsDsl<ts.Expression>): TypeOfExprTsDsl { 92 - return typeOfExprFactory!(this); 93 - } 94 - 95 - /** Create a TypeQuery DSL node representing typeof this. */ 96 - typeofType(this: MaybeTsDsl<TypeTsDsl | ts.Expression>): TypeQueryTsDsl { 97 - return typeQueryFactory!(this); 98 - } 99 - 78 + returnType(this: MaybeTsDsl<ts.Expression>): TypeExprTsDsl; 100 79 /** 101 80 * Create a `typeof` operator that narrows its return type based on the receiver. 102 81 * ··· 111 90 ? TypeOfExprTsDsl 112 91 : T extends MaybeTsDsl<TypeTsDsl> 113 92 ? TypeQueryTsDsl 114 - : TypeQueryTsDsl | TypeOfExprTsDsl { 115 - if (this instanceof TsDsl) { 116 - const node = this.$render(); 117 - return ts.isExpression(node) 118 - ? (typeOfExprFactory!(this) as any) 119 - : (typeQueryFactory!(this) as any); 93 + : TypeQueryTsDsl | TypeOfExprTsDsl; 94 + /** Create a TypeOfExpr DSL node representing typeof this. */ 95 + typeofExpr(this: MaybeTsDsl<ts.Expression>): TypeOfExprTsDsl; 96 + /** Create a TypeQuery DSL node representing typeof this. */ 97 + typeofType(this: MaybeTsDsl<TypeTsDsl | ts.Expression>): TypeQueryTsDsl; 98 + /** Shorthand: builds `unique T`. */ 99 + unique(this: MaybeTsDsl<TypeTsDsl>): TypeOperatorTsDsl; 100 + } 101 + 102 + export function TypeExprMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 103 + Base: TBase, 104 + ) { 105 + abstract class TypeExpr extends Base { 106 + protected idx( 107 + this: MaybeTsDsl<TypeTsDsl>, 108 + index: string | number | MaybeTsDsl<ts.TypeNode>, 109 + ): TypeIdxTsDsl { 110 + return typeIdxFactory!(this, index); 111 + } 112 + 113 + protected keyof(this: MaybeTsDsl<TypeTsDsl>): TypeOperatorTsDsl { 114 + return typeOperatorFactory!().keyof(this); 120 115 } 121 116 122 - if (ts.isExpression(this as any)) { 123 - return typeOfExprFactory!(this as ts.Expression) as any; 117 + protected readonly(this: MaybeTsDsl<TypeTsDsl>): TypeOperatorTsDsl { 118 + return typeOperatorFactory!().readonly(this); 119 + } 120 + 121 + protected returnType(this: MaybeTsDsl<ts.Expression>): TypeExprTsDsl { 122 + return typeExprFactory!('ReturnType').generic(typeQueryFactory!(this)); 124 123 } 125 124 126 - return typeQueryFactory!(this) as any; 127 - } 125 + protected typeofExpr(this: MaybeTsDsl<ts.Expression>): TypeOfExprTsDsl { 126 + return typeOfExprFactory!(this); 127 + } 128 128 129 - /** Shorthand: builds `unique T`. */ 130 - unique(this: MaybeTsDsl<TypeTsDsl>): TypeOperatorTsDsl { 131 - return typeOperatorFactory!().unique(this); 129 + protected typeofType( 130 + this: MaybeTsDsl<TypeTsDsl | ts.Expression>, 131 + ): TypeQueryTsDsl { 132 + return typeQueryFactory!(this); 133 + } 134 + 135 + protected typeof<T extends MaybeTsDsl<TypeTsDsl | ts.Expression>>( 136 + this: T, 137 + ): T extends MaybeTsDsl<ts.Expression> 138 + ? TypeOfExprTsDsl 139 + : T extends MaybeTsDsl<TypeTsDsl> 140 + ? TypeQueryTsDsl 141 + : TypeQueryTsDsl | TypeOfExprTsDsl { 142 + if (this instanceof TsDsl) { 143 + const node = this.$render(); 144 + return ts.isExpression(node) 145 + ? (typeOfExprFactory!(this) as any) 146 + : (typeQueryFactory!(this) as any); 147 + } 148 + 149 + if (ts.isExpression(this as any)) { 150 + return typeOfExprFactory!(this as ts.Expression) as any; 151 + } 152 + 153 + return typeQueryFactory!(this) as any; 154 + } 155 + 156 + protected unique(this: MaybeTsDsl<TypeTsDsl>): TypeOperatorTsDsl { 157 + return typeOperatorFactory!().unique(this); 158 + } 132 159 } 160 + 161 + return TypeExpr as unknown as MixinCtor<TBase, TypeExprMethods>; 133 162 }
+46 -22
packages/openapi-ts/src/ts-dsl/mixins/type-params.ts
··· 1 + import type { Symbol } from '@hey-api/codegen-core'; 1 2 import type ts from 'typescript'; 2 3 3 4 import type { MaybeTsDsl } from '../base'; 4 - import { TsDsl } from '../base'; 5 5 import { TypeParamTsDsl } from '../type/param'; 6 + import type { BaseCtor, MixinCtor } from './types'; 6 7 7 - export abstract class TypeParamsMixin extends TsDsl { 8 - protected _generics?: Array<string | MaybeTsDsl<TypeParamTsDsl>>; 9 - 8 + export interface TypeParamsMethods { 9 + /** Returns the type parameters as an array of ts.TypeParameterDeclaration nodes. */ 10 + $generics(): ReadonlyArray<ts.TypeParameterDeclaration> | undefined; 10 11 /** Adds a single type parameter (e.g. `T` in `Array<T>`). */ 11 - generic(...args: ConstructorParameters<typeof TypeParamTsDsl>): this { 12 - const g = new TypeParamTsDsl(...args); 13 - (this._generics ??= []).push(g); 14 - return this; 15 - } 16 - 12 + generic(...args: ConstructorParameters<typeof TypeParamTsDsl>): this; 17 13 /** Adds type parameters (e.g. `Map<string, T>`). */ 18 - generics(...args: ReadonlyArray<string | MaybeTsDsl<TypeParamTsDsl>>): this { 19 - (this._generics ??= []).push(...args); 20 - return this; 21 - } 14 + generics( 15 + ...args: ReadonlyArray<Symbol | string | MaybeTsDsl<TypeParamTsDsl>> 16 + ): this; 17 + } 22 18 23 - /** Returns the type parameters as an array of ts.TypeParameterDeclaration nodes. */ 24 - protected $generics(): 25 - | ReadonlyArray<ts.TypeParameterDeclaration> 26 - | undefined { 27 - return this._generics?.map((g) => { 28 - const node = typeof g === 'string' ? new TypeParamTsDsl(g) : g; 29 - return this.$node(node); 30 - }); 19 + export function TypeParamsMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 20 + Base: TBase, 21 + ) { 22 + abstract class TypeParams extends Base { 23 + protected _generics?: Array<MaybeTsDsl<TypeParamTsDsl>>; 24 + 25 + protected generic( 26 + ...args: ConstructorParameters<typeof TypeParamTsDsl> 27 + ): this { 28 + const g = new TypeParamTsDsl(...args).setParent(this); 29 + (this._generics ??= []).push(g); 30 + return this; 31 + } 32 + 33 + protected generics( 34 + ...args: ReadonlyArray<Symbol | string | MaybeTsDsl<TypeParamTsDsl>> 35 + ): this { 36 + for (let arg of args) { 37 + if (typeof arg !== 'object' || 'id' in arg) { 38 + arg = new TypeParamTsDsl(arg); 39 + } 40 + if (typeof arg === 'object' && 'setParent' in arg) { 41 + arg.setParent(this); 42 + } 43 + (this._generics ??= []).push(arg); 44 + } 45 + return this; 46 + } 47 + 48 + protected $generics(): 49 + | ReadonlyArray<ts.TypeParameterDeclaration> 50 + | undefined { 51 + return this.$node(this._generics); 52 + } 31 53 } 54 + 55 + return TypeParams as unknown as MixinCtor<TBase, TypeParamsMethods>; 32 56 }
+6
packages/openapi-ts/src/ts-dsl/mixins/types.d.ts
··· 1 + import type { TsDsl } from '../base'; 2 + 3 + export type BaseCtor<T> = abstract new (...args: any[]) => TsDsl<T>; 4 + export type MixinCtor<T extends BaseCtor<any>, K> = abstract new ( 5 + ...args: any[] 6 + ) => InstanceType<T> & K;
+21 -10
packages/openapi-ts/src/ts-dsl/mixins/value.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 3 import type { MaybeTsDsl } from '../base'; 4 - import { TsDsl } from '../base'; 4 + import type { BaseCtor, MixinCtor } from './types'; 5 + 6 + export interface ValueMethods { 7 + $value(): ts.Expression | undefined; 8 + /** Sets the initializer expression (e.g. `= expr`). */ 9 + assign(expr: string | MaybeTsDsl<ts.Expression>): this; 10 + } 5 11 6 - export abstract class ValueMixin extends TsDsl { 7 - protected value?: string | MaybeTsDsl<ts.Expression>; 12 + export function ValueMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 13 + Base: TBase, 14 + ) { 15 + abstract class Value extends Base { 16 + protected value?: string | MaybeTsDsl<ts.Expression>; 17 + 18 + protected assign(expr: string | MaybeTsDsl<ts.Expression>): this { 19 + this.value = expr; 20 + return this; 21 + } 8 22 9 - /** Sets the initializer expression (e.g. `= expr`). */ 10 - assign<T extends this>(this: T, expr: string | MaybeTsDsl<ts.Expression>): T { 11 - this.value = expr; 12 - return this; 23 + protected $value(): ts.Expression | undefined { 24 + return this.$node(this.value); 25 + } 13 26 } 14 27 15 - protected $value(): ts.Expression | undefined { 16 - return this.$node(this.value); 17 - } 28 + return Value as unknown as MixinCtor<TBase, ValueMethods>; 18 29 }
+4 -8
packages/openapi-ts/src/ts-dsl/stmt/if.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { DoMixin } from '../mixins/do'; 9 7 10 - export class IfTsDsl extends TsDsl<ts.IfStatement> { 8 + const Mixed = DoMixin(TsDsl<ts.IfStatement>); 9 + 10 + export class IfTsDsl extends Mixed { 11 11 protected _condition?: string | MaybeTsDsl<ts.Expression>; 12 12 protected _else?: ReadonlyArray<MaybeTsDsl<ts.Statement>>; 13 13 ··· 26 26 return this; 27 27 } 28 28 29 - /** Walk this node and its children with a visitor. */ 30 29 traverse(visitor: (node: SyntaxNode) => void): void { 31 30 console.log(visitor); 32 31 } 33 32 34 - $render(): ts.IfStatement { 33 + protected override _render() { 35 34 if (!this._condition) throw new Error('Missing condition in if'); 36 35 37 36 const thenStmts = this.$do(); ··· 61 60 return ts.factory.createIfStatement(condition, thenNode, elseNode); 62 61 } 63 62 } 64 - 65 - export interface IfTsDsl extends DoMixin {} 66 - mixin(IfTsDsl, DoMixin);
+5 -9
packages/openapi-ts/src/ts-dsl/stmt/return.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 - import { ExprMixin, registerLazyAccessReturnFactory } from '../mixins/expr'; 6 + import { registerLazyAccessReturnFactory } from '../mixins/expr'; 9 7 10 - export class ReturnTsDsl extends TsDsl<ts.ReturnStatement> { 8 + const Mixed = TsDsl<ts.ReturnStatement>; 9 + 10 + export class ReturnTsDsl extends Mixed { 11 11 protected _returnExpr?: string | MaybeTsDsl<ts.Expression>; 12 12 13 13 constructor(expr?: string | MaybeTsDsl<ts.Expression>) { ··· 15 15 this._returnExpr = expr; 16 16 } 17 17 18 - /** Walk this node and its children with a visitor. */ 19 18 traverse(visitor: (node: SyntaxNode) => void): void { 20 19 console.log(visitor); 21 20 } 22 21 23 - $render(): ts.ReturnStatement { 22 + protected override _render() { 24 23 return ts.factory.createReturnStatement(this.$node(this._returnExpr)); 25 24 } 26 25 } 27 - 28 - export interface ReturnTsDsl extends ExprMixin {} 29 - mixin(ReturnTsDsl, ExprMixin); 30 26 31 27 registerLazyAccessReturnFactory((...args) => new ReturnTsDsl(...args));
+4 -3
packages/openapi-ts/src/ts-dsl/stmt/stmt.ts
··· 3 3 4 4 import { TsDsl } from '../base'; 5 5 6 - export class StmtTsDsl extends TsDsl<ts.Statement> { 6 + const Mixed = TsDsl<ts.Statement>; 7 + 8 + export class StmtTsDsl extends Mixed { 7 9 protected _inner: ts.Expression | ts.Statement | TsDsl<any>; 8 10 9 11 constructor(inner: ts.Expression | ts.Statement | TsDsl<any>) { ··· 11 13 this._inner = inner; 12 14 } 13 15 14 - /** Walk this node and its children with a visitor. */ 15 16 traverse(visitor: (node: SyntaxNode) => void): void { 16 17 console.log(visitor); 17 18 } 18 19 19 - $render(): ts.Statement { 20 + protected override _render() { 20 21 const node = this.$node(this._inner); 21 22 return ts.isStatement(node) 22 23 ? node
+4 -3
packages/openapi-ts/src/ts-dsl/stmt/throw.ts
··· 5 5 import { TsDsl } from '../base'; 6 6 import { LiteralTsDsl } from '../expr/literal'; 7 7 8 - export class ThrowTsDsl extends TsDsl<ts.ThrowStatement> { 8 + const Mixed = TsDsl<ts.ThrowStatement>; 9 + 10 + export class ThrowTsDsl extends Mixed { 9 11 protected error: string | MaybeTsDsl<ts.Expression>; 10 12 protected msg?: string | MaybeTsDsl<ts.Expression>; 11 13 protected useNew: boolean; ··· 21 23 return this; 22 24 } 23 25 24 - /** Walk this node and its children with a visitor. */ 25 26 traverse(visitor: (node: SyntaxNode) => void): void { 26 27 console.log(visitor); 27 28 } 28 29 29 - $render(): ts.ThrowStatement { 30 + protected override _render() { 30 31 const errorNode = this.$node(this.error); 31 32 const messageNode = this.$node(this.msg ? [this.msg] : []).map((expr) => 32 33 typeof expr === 'string' ? new LiteralTsDsl(expr).$render() : expr,
+12 -29
packages/openapi-ts/src/ts-dsl/stmt/var.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TsDsl, TypeTsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { DocMixin } from '../mixins/doc'; 8 6 import { HintMixin } from '../mixins/hint'; 9 - import { 10 - createModifierAccessor, 11 - DefaultMixin, 12 - ExportMixin, 13 - } from '../mixins/modifiers'; 7 + import { DefaultMixin, ExportMixin } from '../mixins/modifiers'; 14 8 import { PatternMixin } from '../mixins/pattern'; 15 9 import { ValueMixin } from '../mixins/value'; 16 10 import { TypeExprTsDsl } from '../type/expr'; 17 11 18 - export class VarTsDsl extends TsDsl<ts.VariableStatement> { 12 + const Mixed = DefaultMixin( 13 + DocMixin( 14 + ExportMixin( 15 + HintMixin(PatternMixin(ValueMixin(TsDsl<ts.VariableStatement>))), 16 + ), 17 + ), 18 + ); 19 + 20 + export class VarTsDsl extends Mixed { 19 21 protected kind: ts.NodeFlags = ts.NodeFlags.None; 20 - protected modifiers = createModifierAccessor(this); 21 22 protected name?: string; 22 23 protected _type?: TypeTsDsl; 23 24 ··· 45 46 return this; 46 47 } 47 48 48 - /** Walk this node and its children with a visitor. */ 49 49 traverse(visitor: (node: SyntaxNode) => void): void { 50 50 console.log(visitor); 51 51 } ··· 61 61 return this; 62 62 } 63 63 64 - $render(): ts.VariableStatement { 64 + protected override _render() { 65 65 const name = this.$pattern() ?? this.name; 66 66 if (!name) 67 67 throw new Error('Var must have either a name or a destructuring pattern'); 68 68 return ts.factory.createVariableStatement( 69 - this.modifiers.list(), 69 + this.modifiers, 70 70 ts.factory.createVariableDeclarationList( 71 71 [ 72 72 ts.factory.createVariableDeclaration( ··· 81 81 ); 82 82 } 83 83 } 84 - 85 - export interface VarTsDsl 86 - extends DefaultMixin, 87 - DocMixin, 88 - ExportMixin, 89 - HintMixin, 90 - PatternMixin, 91 - ValueMixin {} 92 - mixin( 93 - VarTsDsl, 94 - DefaultMixin, 95 - DocMixin, 96 - ExportMixin, 97 - HintMixin, 98 - PatternMixin, 99 - ValueMixin, 100 - );
+4 -5
packages/openapi-ts/src/ts-dsl/token.ts
··· 56 56 ); 57 57 } 58 58 59 - /** Walk this node and its children with a visitor. */ 60 - traverse(visitor: (node: SyntaxNode) => void): void { 61 - console.log(visitor); 59 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 60 + traverse(_visitor: (node: SyntaxNode) => void): void { 61 + // noop 62 62 } 63 63 64 - /** Renders the final token */ 65 - $render(): ts.Token<K> { 64 + protected override _render(): ts.Token<K> { 66 65 if (!this._kind) { 67 66 throw new Error(`Token missing \`.kind(kind)\``); 68 67 }
+8 -15
packages/openapi-ts/src/ts-dsl/type/alias.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { DocMixin } from '../mixins/doc'; 9 - import { createModifierAccessor, ExportMixin } from '../mixins/modifiers'; 7 + import { ExportMixin } from '../mixins/modifiers'; 10 8 import { TypeParamsMixin } from '../mixins/type-params'; 11 9 12 - export class TypeAliasTsDsl extends TsDsl<ts.TypeAliasDeclaration> { 13 - protected modifiers = createModifierAccessor(this); 10 + const Mixed = DocMixin( 11 + ExportMixin(TypeParamsMixin(TsDsl<ts.TypeAliasDeclaration>)), 12 + ); 13 + 14 + export class TypeAliasTsDsl extends Mixed { 14 15 protected name: string; 15 16 protected value?: MaybeTsDsl<ts.TypeNode>; 16 17 ··· 27 28 fn?.(this); 28 29 } 29 30 30 - /** Walk this node and its children with a visitor. */ 31 31 traverse(visitor: (node: SyntaxNode) => void): void { 32 32 console.log(visitor); 33 33 } ··· 38 38 return this; 39 39 } 40 40 41 - /** Renders a `TypeAliasDeclaration` node. */ 42 - $render(): ts.TypeAliasDeclaration { 41 + protected override _render() { 43 42 if (!this.value) 44 43 throw new Error(`Type alias '${this.name}' is missing a type definition`); 45 44 return ts.factory.createTypeAliasDeclaration( 46 - this.modifiers.list(), 45 + this.modifiers, 47 46 this.name, 48 47 this.$generics(), 49 48 this.$type(this.value), 50 49 ); 51 50 } 52 51 } 53 - 54 - export interface TypeAliasTsDsl 55 - extends DocMixin, 56 - ExportMixin, 57 - TypeParamsMixin {} 58 - mixin(TypeAliasTsDsl, DocMixin, ExportMixin, TypeParamsMixin);
+4 -3
packages/openapi-ts/src/ts-dsl/type/and.ts
··· 3 3 4 4 import { TypeTsDsl } from '../base'; 5 5 6 - export class TypeAndTsDsl extends TypeTsDsl<ts.IntersectionTypeNode> { 6 + const Mixed = TypeTsDsl<ts.IntersectionTypeNode>; 7 + 8 + export class TypeAndTsDsl extends Mixed { 7 9 protected _types: Array<string | ts.TypeNode | TypeTsDsl> = []; 8 10 9 11 constructor(...nodes: Array<string | ts.TypeNode | TypeTsDsl>) { ··· 11 13 this.types(...nodes); 12 14 } 13 15 14 - /** Walk this node and its children with a visitor. */ 15 16 traverse(visitor: (node: SyntaxNode) => void): void { 16 17 console.log(visitor); 17 18 } ··· 21 22 return this; 22 23 } 23 24 24 - $render(): ts.IntersectionTypeNode { 25 + protected override _render() { 25 26 const flat: Array<ts.TypeNode> = []; 26 27 27 28 for (const n of this._types) {
+4 -8
packages/openapi-ts/src/ts-dsl/type/attr.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TypeTsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { TypeExprMixin } from '../mixins/type-expr'; 9 7 10 - export class TypeAttrTsDsl extends TypeTsDsl<ts.QualifiedName> { 8 + const Mixed = TypeExprMixin(TypeTsDsl<ts.QualifiedName>); 9 + 10 + export class TypeAttrTsDsl extends Mixed { 11 11 protected _base?: Symbol | string | MaybeTsDsl<ts.EntityName>; 12 12 protected right: Symbol | string | ts.Identifier; 13 13 ··· 35 35 return this; 36 36 } 37 37 38 - /** Walk this node and its children with a visitor. */ 39 38 traverse(visitor: (node: SyntaxNode) => void): void { 40 39 console.log(visitor); 41 40 } 42 41 43 - $render(): ts.QualifiedName { 42 + protected override _render() { 44 43 if (!this._base) { 45 44 throw new Error('TypeAttrTsDsl: missing base for qualified name'); 46 45 } ··· 58 57 return ts.factory.createQualifiedName(left, right); 59 58 } 60 59 } 61 - 62 - export interface TypeAttrTsDsl extends TypeExprMixin {} 63 - mixin(TypeAttrTsDsl, TypeExprMixin);
+5 -10
packages/openapi-ts/src/ts-dsl/type/expr.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TypeTsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { TypeArgsMixin } from '../mixins/type-args'; 8 6 import { 9 7 registerLazyAccessTypeExprFactory, ··· 11 9 } from '../mixins/type-expr'; 12 10 import { TypeAttrTsDsl } from './attr'; 13 11 14 - export class TypeExprTsDsl extends TypeTsDsl<ts.TypeReferenceNode> { 12 + const Mixed = TypeArgsMixin(TypeExprMixin(TypeTsDsl<ts.TypeReferenceNode>)); 13 + 14 + export class TypeExprTsDsl extends Mixed { 15 15 protected _exprInput?: Symbol | string | TypeAttrTsDsl; 16 16 17 17 constructor(); ··· 29 29 } else { 30 30 this._exprInput = name; 31 31 if (typeof name !== 'string') { 32 - const symbol = this.getRootSymbol(); 33 - if (symbol) symbol.addDependency(name); 32 + this.getRootSymbol().addDependency(name); 34 33 } 35 34 fn?.(this); 36 35 } ··· 47 46 return this; 48 47 } 49 48 50 - /** Walk this node and its children with a visitor. */ 51 49 traverse(visitor: (node: SyntaxNode) => void): void { 52 50 console.log(visitor); 53 51 } 54 52 55 - $render(): ts.TypeReferenceNode { 53 + protected override _render() { 56 54 if (!this._exprInput) throw new Error('TypeExpr must have an expression'); 57 55 const typeName = 58 56 typeof this._exprInput === 'string' || ··· 66 64 ); 67 65 } 68 66 } 69 - 70 - export interface TypeExprTsDsl extends TypeArgsMixin, TypeExprMixin {} 71 - mixin(TypeExprTsDsl, TypeArgsMixin, TypeExprMixin); 72 67 73 68 registerLazyAccessTypeExprFactory( 74 69 (...args) =>
+6 -8
packages/openapi-ts/src/ts-dsl/type/func.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import { TypeTsDsl } from '../base'; 6 - import { mixin } from '../mixins/apply'; 7 5 import { DocMixin } from '../mixins/doc'; 8 6 import { ParamMixin } from '../mixins/param'; 9 7 import { TypeParamsMixin } from '../mixins/type-params'; 10 8 import { TypeExprTsDsl } from './expr'; 11 9 12 - export class TypeFuncTsDsl extends TypeTsDsl<ts.FunctionTypeNode> { 10 + const Mixed = DocMixin( 11 + ParamMixin(TypeParamsMixin(TypeTsDsl<ts.FunctionTypeNode>)), 12 + ); 13 + 14 + export class TypeFuncTsDsl extends Mixed { 13 15 protected _returns?: TypeTsDsl; 14 16 15 17 /** Sets the return type. */ ··· 18 20 return this; 19 21 } 20 22 21 - /** Walk this node and its children with a visitor. */ 22 23 traverse(visitor: (node: SyntaxNode) => void): void { 23 24 console.log(visitor); 24 25 } 25 26 26 - $render(): ts.FunctionTypeNode { 27 + protected override _render() { 27 28 if (this._returns === undefined) { 28 29 throw new Error('Missing return type in function type DSL'); 29 30 } ··· 34 35 ); 35 36 } 36 37 } 37 - 38 - export interface TypeFuncTsDsl extends DocMixin, ParamMixin, TypeParamsMixin {} 39 - mixin(TypeFuncTsDsl, DocMixin, ParamMixin, TypeParamsMixin);
+6 -11
packages/openapi-ts/src/ts-dsl/type/idx-sig.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TypeTsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { DocMixin } from '../mixins/doc'; 9 - import { createModifierAccessor, ReadonlyMixin } from '../mixins/modifiers'; 7 + import { ReadonlyMixin } from '../mixins/modifiers'; 10 8 11 9 type Type = string | MaybeTsDsl<ts.TypeNode>; 12 10 13 - export class TypeIdxSigTsDsl extends TypeTsDsl<ts.IndexSignatureDeclaration> { 14 - protected modifiers = createModifierAccessor(this); 11 + const Mixed = DocMixin(ReadonlyMixin(TypeTsDsl<ts.IndexSignatureDeclaration>)); 12 + 13 + export class TypeIdxSigTsDsl extends Mixed { 15 14 protected _key?: Type; 16 15 protected _name: string; 17 16 protected _type?: Type; ··· 33 32 return this; 34 33 } 35 34 36 - /** Walk this node and its children with a visitor. */ 37 35 traverse(visitor: (node: SyntaxNode) => void): void { 38 36 console.log(visitor); 39 37 } ··· 44 42 return this; 45 43 } 46 44 47 - $render(): ts.IndexSignatureDeclaration { 45 + protected override _render() { 48 46 this.$validate(); 49 47 return ts.factory.createIndexSignature( 50 - this.modifiers.list(), 48 + this.modifiers, 51 49 [ 52 50 ts.factory.createParameterDeclaration( 53 51 undefined, ··· 81 79 return missing; 82 80 } 83 81 } 84 - 85 - export interface TypeIdxSigTsDsl extends DocMixin, ReadonlyMixin {} 86 - mixin(TypeIdxSigTsDsl, DocMixin, ReadonlyMixin);
+4 -8
packages/openapi-ts/src/ts-dsl/type/idx.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TypeTsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { 9 7 registerLazyAccessTypeIdxFactory, 10 8 TypeExprMixin, 11 9 } from '../mixins/type-expr'; 12 10 13 - export class TypeIdxTsDsl extends TypeTsDsl<ts.IndexedAccessTypeNode> { 11 + const Mixed = TypeExprMixin(TypeTsDsl<ts.IndexedAccessTypeNode>); 12 + 13 + export class TypeIdxTsDsl extends Mixed { 14 14 protected _base: string | MaybeTsDsl<ts.TypeNode>; 15 15 protected _index: string | MaybeTsDsl<ts.TypeNode> | number; 16 16 ··· 33 33 return this; 34 34 } 35 35 36 - /** Walk this node and its children with a visitor. */ 37 36 traverse(visitor: (node: SyntaxNode) => void): void { 38 37 console.log(visitor); 39 38 } 40 39 41 - $render(): ts.IndexedAccessTypeNode { 40 + protected override _render() { 42 41 return ts.factory.createIndexedAccessTypeNode( 43 42 this.$type(this._base), 44 43 this.$type(this._index), 45 44 ); 46 45 } 47 46 } 48 - 49 - export interface TypeIdxTsDsl extends TypeExprMixin {} 50 - mixin(TypeIdxTsDsl, TypeExprMixin); 51 47 52 48 registerLazyAccessTypeIdxFactory((...args) => new TypeIdxTsDsl(...args));
+4 -3
packages/openapi-ts/src/ts-dsl/type/literal.ts
··· 4 4 import { TypeTsDsl } from '../base'; 5 5 import { LiteralTsDsl } from '../expr/literal'; 6 6 7 - export class TypeLiteralTsDsl extends TypeTsDsl<ts.LiteralTypeNode> { 7 + const Mixed = TypeTsDsl<ts.LiteralTypeNode>; 8 + 9 + export class TypeLiteralTsDsl extends Mixed { 8 10 protected value: string | number | boolean | null; 9 11 10 12 constructor(value: string | number | boolean | null) { ··· 12 14 this.value = value; 13 15 } 14 16 15 - /** Walk this node and its children with a visitor. */ 16 17 traverse(visitor: (node: SyntaxNode) => void): void { 17 18 console.log(visitor); 18 19 } 19 20 20 - $render(): ts.LiteralTypeNode { 21 + protected override _render() { 21 22 return ts.factory.createLiteralTypeNode( 22 23 this.$node(new LiteralTsDsl(this.value)), 23 24 );
+4 -3
packages/openapi-ts/src/ts-dsl/type/mapped.ts
··· 5 5 import { TypeTsDsl } from '../base'; 6 6 import { TokenTsDsl } from '../token'; 7 7 8 - export class TypeMappedTsDsl extends TypeTsDsl<ts.MappedTypeNode> { 8 + const Mixed = TypeTsDsl<ts.MappedTypeNode>; 9 + 10 + export class TypeMappedTsDsl extends Mixed { 9 11 protected questionToken?: TokenTsDsl< 10 12 | ts.SyntaxKind.QuestionToken 11 13 | ts.SyntaxKind.PlusToken ··· 66 68 return this; 67 69 } 68 70 69 - /** Walk this node and its children with a visitor. */ 70 71 traverse(visitor: (node: SyntaxNode) => void): void { 71 72 console.log(visitor); 72 73 } ··· 77 78 return this; 78 79 } 79 80 80 - $render(): ts.MappedTypeNode { 81 + protected override _render() { 81 82 this.$validate(); 82 83 return ts.factory.createMappedTypeNode( 83 84 this.$node(this.readonlyToken),
+4 -3
packages/openapi-ts/src/ts-dsl/type/object.ts
··· 5 5 import { TypeIdxSigTsDsl } from './idx-sig'; 6 6 import { TypePropTsDsl } from './prop'; 7 7 8 - export class TypeObjectTsDsl extends TypeTsDsl<ts.TypeNode> { 8 + const Mixed = TypeTsDsl<ts.TypeNode>; 9 + 10 + export class TypeObjectTsDsl extends Mixed { 9 11 protected props: Array<TypePropTsDsl | TypeIdxSigTsDsl> = []; 10 12 11 13 /** Returns true if object has at least one property or spread. */ ··· 32 34 return this; 33 35 } 34 36 35 - /** Walk this node and its children with a visitor. */ 36 37 traverse(visitor: (node: SyntaxNode) => void): void { 37 38 console.log(visitor); 38 39 } 39 40 40 - $render(): ts.TypeNode { 41 + protected override _render() { 41 42 return ts.factory.createTypeLiteralNode(this.$node(this.props)); 42 43 } 43 44 }
+4 -3
packages/openapi-ts/src/ts-dsl/type/operator.ts
··· 11 11 | ts.SyntaxKind.UniqueKeyword; 12 12 type Type = string | MaybeTsDsl<ts.TypeNode>; 13 13 14 + const Mixed = TypeTsDsl<ts.TypeOperatorNode>; 15 + 14 16 /** 15 17 * Builds a TypeScript `TypeOperatorNode`, such as: 16 18 * ··· 23 25 * 24 26 * The node will throw during render if required fields are missing. 25 27 */ 26 - export class TypeOperatorTsDsl extends TypeTsDsl<ts.TypeOperatorNode> { 28 + export class TypeOperatorTsDsl extends Mixed { 27 29 protected _op?: Op; 28 30 protected _type?: Type; 29 31 ··· 47 49 return this; 48 50 } 49 51 50 - /** Walk this node and its children with a visitor. */ 51 52 traverse(visitor: (node: SyntaxNode) => void): void { 52 53 console.log(visitor); 53 54 } ··· 65 66 return this; 66 67 } 67 68 68 - $render(): ts.TypeOperatorNode { 69 + protected override _render() { 69 70 this.$validate(); 70 71 return ts.factory.createTypeOperatorNode(this._op, this.$type(this._type)); 71 72 }
+4 -3
packages/openapi-ts/src/ts-dsl/type/or.ts
··· 3 3 4 4 import { TypeTsDsl } from '../base'; 5 5 6 - export class TypeOrTsDsl extends TypeTsDsl<ts.UnionTypeNode> { 6 + const Mixed = TypeTsDsl<ts.UnionTypeNode>; 7 + 8 + export class TypeOrTsDsl extends Mixed { 7 9 protected _types: Array<string | ts.TypeNode | TypeTsDsl> = []; 8 10 9 11 constructor(...nodes: Array<string | ts.TypeNode | TypeTsDsl>) { ··· 11 13 this.types(...nodes); 12 14 } 13 15 14 - /** Walk this node and its children with a visitor. */ 15 16 traverse(visitor: (node: SyntaxNode) => void): void { 16 17 console.log(visitor); 17 18 } ··· 21 22 return this; 22 23 } 23 24 24 - $render(): ts.UnionTypeNode { 25 + protected override _render() { 25 26 const flat: Array<ts.TypeNode> = []; 26 27 27 28 for (const n of this._types) {
+13 -10
packages/openapi-ts/src/ts-dsl/type/param.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; 5 5 import { TypeTsDsl } from '../base'; 6 6 7 - export class TypeParamTsDsl extends TypeTsDsl<ts.TypeParameterDeclaration> { 8 - protected name?: string | ts.Identifier; 7 + const Mixed = TypeTsDsl<ts.TypeParameterDeclaration>; 8 + 9 + export class TypeParamTsDsl extends Mixed { 10 + protected name?: Symbol | string; 9 11 protected constraint?: string | MaybeTsDsl<TypeTsDsl> | boolean; 10 12 protected defaultValue?: string | MaybeTsDsl<TypeTsDsl> | boolean; 11 13 12 - constructor( 13 - name?: string | ts.Identifier, 14 - fn?: (name: TypeParamTsDsl) => void, 15 - ) { 14 + constructor(name?: Symbol | string, fn?: (name: TypeParamTsDsl) => void) { 16 15 super(); 17 16 this.name = name; 17 + if (name && typeof name !== 'string') { 18 + this.getRootSymbol().addDependency(name); 19 + } 18 20 fn?.(this); 19 21 } 20 22 ··· 28 30 return this; 29 31 } 30 32 31 - /** Walk this node and its children with a visitor. */ 32 33 traverse(visitor: (node: SyntaxNode) => void): void { 33 34 console.log(visitor); 34 35 } 35 36 36 - $render(): ts.TypeParameterDeclaration { 37 + protected override _render() { 37 38 if (!this.name) throw new Error('Missing type name'); 39 + const name = 40 + typeof this.name === 'string' ? this.name : this.name.finalName; 38 41 return ts.factory.createTypeParameterDeclaration( 39 42 undefined, 40 - this.$maybeId(this.name), 43 + this.$maybeId(name), 41 44 this.$type(this.constraint), 42 45 this.$type(this.defaultValue), 43 46 );
+6 -12
packages/openapi-ts/src/ts-dsl/type/prop.ts
··· 1 - /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TypeTsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { DocMixin } from '../mixins/doc'; 9 - import { createModifierAccessor, ReadonlyMixin } from '../mixins/modifiers'; 7 + import { ReadonlyMixin } from '../mixins/modifiers'; 10 8 import { OptionalMixin } from '../mixins/optional'; 11 9 import { TokenTsDsl } from '../token'; 12 10 import { safePropName } from '../utils/prop'; 13 11 14 - export class TypePropTsDsl extends TypeTsDsl<ts.TypeElement> { 15 - protected modifiers = createModifierAccessor(this); 12 + const Mixed = DocMixin(OptionalMixin(ReadonlyMixin(TypeTsDsl<ts.TypeElement>))); 13 + 14 + export class TypePropTsDsl extends Mixed { 16 15 protected name: string; 17 16 protected _type?: string | MaybeTsDsl<ts.TypeNode>; 18 17 ··· 22 21 fn(this); 23 22 } 24 23 25 - /** Walk this node and its children with a visitor. */ 26 24 traverse(visitor: (node: SyntaxNode) => void): void { 27 25 console.log(visitor); 28 26 } ··· 33 31 return this; 34 32 } 35 33 36 - /** Builds and returns the property signature. */ 37 - $render(): ts.TypeElement { 34 + protected override _render() { 38 35 if (!this._type) { 39 36 throw new Error(`Type not specified for property '${this.name}'`); 40 37 } 41 38 return ts.factory.createPropertySignature( 42 - this.modifiers.list(), 39 + this.modifiers, 43 40 safePropName(this.name), 44 41 this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, 45 42 this.$type(this._type), 46 43 ); 47 44 } 48 45 } 49 - 50 - export interface TypePropTsDsl extends DocMixin, OptionalMixin, ReadonlyMixin {} 51 - mixin(TypePropTsDsl, DocMixin, OptionalMixin, ReadonlyMixin);
+4 -8
packages/openapi-ts/src/ts-dsl/type/query.ts
··· 1 - /* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unsafe-declaration-merging */ 2 1 import type { SyntaxNode } from '@hey-api/codegen-core'; 3 2 import ts from 'typescript'; 4 3 5 4 import type { MaybeTsDsl } from '../base'; 6 5 import { TypeTsDsl } from '../base'; 7 - import { mixin } from '../mixins/apply'; 8 6 import { 9 7 registerLazyAccessTypeQueryFactory, 10 8 TypeExprMixin, 11 9 } from '../mixins/type-expr'; 12 10 13 - export class TypeQueryTsDsl extends TypeTsDsl<ts.TypeQueryNode> { 11 + const Mixed = TypeExprMixin(TypeTsDsl<ts.TypeQueryNode>); 12 + 13 + export class TypeQueryTsDsl extends Mixed { 14 14 protected _expr: string | MaybeTsDsl<TypeTsDsl | ts.Expression>; 15 15 16 16 constructor(expr: string | MaybeTsDsl<TypeTsDsl | ts.Expression>) { ··· 18 18 this._expr = expr; 19 19 } 20 20 21 - /** Walk this node and its children with a visitor. */ 22 21 traverse(visitor: (node: SyntaxNode) => void): void { 23 22 console.log(visitor); 24 23 } 25 24 26 - $render(): ts.TypeQueryNode { 25 + protected override _render() { 27 26 const expr = this.$node(this._expr); 28 27 return ts.factory.createTypeQueryNode(expr as unknown as ts.EntityName); 29 28 } 30 29 } 31 - 32 - export interface TypeQueryTsDsl extends TypeExprMixin {} 33 - mixin(TypeQueryTsDsl, TypeExprMixin); 34 30 35 31 registerLazyAccessTypeQueryFactory((...args) => new TypeQueryTsDsl(...args));
+4 -4
packages/openapi-ts/src/ts-dsl/type/template.ts
··· 4 4 import type { MaybeTsDsl } from '../base'; 5 5 import { TypeTsDsl } from '../base'; 6 6 7 - export class TypeTemplateTsDsl extends TypeTsDsl<ts.TemplateLiteralTypeNode> { 7 + const Mixed = TypeTsDsl<ts.TemplateLiteralTypeNode>; 8 + 9 + export class TypeTemplateTsDsl extends Mixed { 8 10 protected parts: Array<string | MaybeTsDsl<ts.TypeNode>> = []; 9 11 10 12 constructor(value?: string | MaybeTsDsl<ts.TypeNode>) { ··· 18 20 return this; 19 21 } 20 22 21 - /** Walk this node and its children with a visitor. */ 22 23 traverse(visitor: (node: SyntaxNode) => void): void { 23 24 console.log(visitor); 24 25 } 25 26 26 - /** Renders a TemplateLiteralTypeNode. */ 27 - $render(): ts.TemplateLiteralTypeNode { 27 + protected override _render() { 28 28 const parts = this.$node(this.parts); 29 29 30 30 const normalized: Array<string | ts.TypeNode> = [];
+4 -3
packages/openapi-ts/src/ts-dsl/type/tuple.ts
··· 3 3 4 4 import { TypeTsDsl } from '../base'; 5 5 6 - export class TypeTupleTsDsl extends TypeTsDsl<ts.TupleTypeNode> { 6 + const Mixed = TypeTsDsl<ts.TupleTypeNode>; 7 + 8 + export class TypeTupleTsDsl extends Mixed { 7 9 protected _elements: Array<string | ts.TypeNode | TypeTsDsl> = []; 8 10 9 11 constructor(...nodes: Array<string | ts.TypeNode | TypeTsDsl>) { ··· 16 18 return this; 17 19 } 18 20 19 - /** Walk this node and its children with a visitor. */ 20 21 traverse(visitor: (node: SyntaxNode) => void): void { 21 22 console.log(visitor); 22 23 } 23 24 24 - $render(): ts.TupleTypeNode { 25 + protected override _render() { 25 26 return ts.factory.createTupleTypeNode( 26 27 this._elements.map((t) => this.$type(t)), 27 28 );
+7
pnpm-lock.yaml
··· 1228 1228 version: 3.0.8(typescript@5.8.3) 1229 1229 1230 1230 packages/codegen-core: 1231 + dependencies: 1232 + ansi-colors: 1233 + specifier: 4.1.3 1234 + version: 4.1.3 1235 + color-support: 1236 + specifier: 1.1.3 1237 + version: 1.1.3 1231 1238 devDependencies: 1232 1239 '@config/vite-base': 1233 1240 specifier: workspace:*