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

Configure Feed

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

refactor: clean up ast context

Lubos bed9a558 b7330971

+951 -916
+6 -7
dev/openapi-ts.config.ts
··· 43 43 // 'circular.yaml', 44 44 // 'dutchie.json', 45 45 // 'enum-names-values.yaml', 46 - 'full.yaml', 46 + // 'full.yaml', 47 47 // 'integer-formats.yaml', 48 48 // 'invalid', 49 49 // 'object-property-names.yaml', ··· 54 54 // 'sdk-instance.yaml', 55 55 // 'sdk-method-class-conflict.yaml', 56 56 // 'sdk-nested-classes.yaml', 57 - // 'sdk-nested-conflict.yaml', 57 + 'sdk-nested-conflict.yaml', 58 58 // 'string-with-format.yaml', 59 59 // 'transformers.json', 60 60 // 'transformers-recursive.json', ··· 274 274 // }, 275 275 }, 276 276 { 277 - // asClass: true, 277 + asClass: true, 278 278 // auth: false, 279 - // classNameBuilder: '{{name}}', 280 - classNameBuilder: '{{name}}Service', 279 + // classNameBuilder: '{{name}}',', 281 280 // classStructure: 'off', 282 281 // client: false, 283 282 // getSignature: ({ fields, signature, operation }) => { ··· 285 284 // fields.unwrap('path') 286 285 // }, 287 286 // include... 288 - instance: 'Root', 289 - methodNameBuilder: '{{name}}', 287 + instance: 'OpencodeClient', 288 + // methodNameBuilder: '{{name}}', 290 289 name: '@hey-api/sdk', 291 290 // operationId: false, 292 291 // paramsStructure: 'flat',
+2 -1
dev/package.json
··· 1 1 { 2 - "name": "@test/openapi-ts-playground", 2 + "name": "@test/playground", 3 3 "version": "0.0.0", 4 4 "private": true, 5 5 "type": "module", ··· 12 12 "devDependencies": { 13 13 "@hey-api/codegen-core": "workspace:*", 14 14 "@hey-api/openapi-ts": "workspace:*", 15 + "@opencode-ai/sdk": "1.0.170", 15 16 "@pinia/colada": "0.19.1", 16 17 "@tanstack/angular-query-experimental": "5.73.3", 17 18 "@tanstack/react-query": "5.73.3",
+7 -13
dev/playground.ts
··· 1 1 import type { DefinePlugin, IR } from '@hey-api/openapi-ts'; 2 - 3 - import { authSet } from './.gen/index.ts'; 2 + import { createOpencode } from '@opencode-ai/sdk'; 4 3 5 4 type MyPluginConfig = { readonly name: 'myplugin' }; 6 5 type MyPlugin = DefinePlugin<MyPluginConfig>; ··· 15 14 }); 16 15 }; 17 16 18 - console.log( 19 - authSet({ 20 - auth: { 21 - access: '', 22 - expires: 1, 23 - refresh: '', 24 - type: 'oauth', 25 - }, 26 - id: '123', 27 - }), 28 - ); 17 + async function run() { 18 + const { client, server } = await createOpencode(); 19 + console.log(client, server); 20 + } 21 + 22 + run();
+5 -2
packages/codegen-core/src/__tests__/exports.test.ts
··· 27 27 // Type-level test: will fail to compile if any type export is missing or renamed 28 28 export type _TypeExports = [ 29 29 index.AnalysisContext, 30 - index.AstContext, 31 30 index.BindingKind, 32 31 index.ExportMember, 33 32 index.ExportModule, ··· 36 35 index.FileIn, 37 36 index.FromRef<any>, 38 37 index.FromRefs<any>, 39 - index.IProject, 40 38 index.ImportMember, 41 39 index.ImportModule, 40 + index.IProject, 42 41 index.Language, 43 42 index.NameConflictResolver, 44 43 index.NameConflictResolvers, 45 44 index.Node, 45 + index.NodeName, 46 + index.NodeNameSanitizer, 47 + index.NodeRelationship, 48 + index.NodeScope, 46 49 index.Output, 47 50 index.Project, 48 51 index.ProjectRenderMeta,
+9 -10
packages/codegen-core/src/bindings.d.ts
··· 32 32 export interface ImportMember { 33 33 /** Whether this import is type-only. */ 34 34 isTypeOnly: boolean; 35 - /** Import flavor. */ 36 - kind: BindingKind; 37 35 /** 38 36 * The name this symbol will have locally in this file. 39 37 * This is where aliasing is applied: ··· 47 45 sourceName: string; 48 46 } 49 47 50 - export type ImportModule = Pick<ImportMember, 'isTypeOnly'> & { 51 - /** Source file. */ 52 - from: File; 53 - /** List of symbols imported from this module. */ 54 - imports: Array<ImportMember>; 55 - /** Namespace import: `import * as name from 'module'`. Mutually exclusive with `imports`. */ 56 - namespaceImport?: string; 57 - }; 48 + export type ImportModule = Pick<ImportMember, 'isTypeOnly'> & 49 + Pick<Partial<ImportMember>, 'localName'> & { 50 + /** Source file. */ 51 + from: File; 52 + /** List of symbols imported from this module. */ 53 + imports: Array<ImportMember>; 54 + /** Import flavor. */ 55 + kind: BindingKind; 56 + };
+1 -5
packages/codegen-core/src/index.ts
··· 20 20 Language, 21 21 NameConflictResolvers, 22 22 } from './languages/types'; 23 - export type { AstContext } from './nodes/context'; 24 23 export type { 25 - AccessPatternContext, 26 - AccessPatternOptions, 27 24 INode as Node, 28 25 NodeName, 29 26 NodeNameSanitizer, 30 - NodeRole, 27 + NodeRelationship, 31 28 NodeScope, 32 - StructuralRelationship, 33 29 } from './nodes/node'; 34 30 export type { IOutput as Output } from './output'; 35 31 export {
-15
packages/codegen-core/src/nodes/context.d.ts
··· 1 - import type { Symbol } from '../symbols/symbol'; 2 - import type { AccessPatternOptions, INode } from './node'; 3 - 4 - /** 5 - * Context passed to `.toAst()` methods. 6 - */ 7 - export interface AstContext { 8 - /** 9 - * Returns the canonical node for accessing the provided node. 10 - */ 11 - getAccess<T = unknown>( 12 - node: INode | Symbol, 13 - options?: AccessPatternOptions, 14 - ): T; 15 - }
+6 -40
packages/codegen-core/src/nodes/node.d.ts
··· 3 3 import type { IAnalysisContext } from '../planner/types'; 4 4 import type { Ref } from '../refs/types'; 5 5 import type { Symbol } from '../symbols/symbol'; 6 - import type { AstContext } from './context'; 7 - 8 - export type AccessContext = 'docs' | 'runtime'; 9 - 10 - export interface AccessPatternContext<Node extends INode = INode> { 11 - /** The full chain. */ 12 - chain: ReadonlyArray<Node>; 13 - /** Position in the chain (0 = root). */ 14 - index: number; 15 - /** Is this the leaf node? */ 16 - isLeaf: boolean; 17 - /** Is this the root node? */ 18 - isRoot: boolean; 19 - /** Total length of the chain. */ 20 - length: number; 21 - } 22 - 23 - export interface AccessPatternOptions { 24 - /** The access context. */ 25 - context: AccessContext; 26 - } 27 6 28 7 export type MaybeRef<T> = T | Ref<T>; 29 8 ··· 31 10 32 11 export type NodeNameSanitizer = (name: string) => string; 33 12 34 - export type NodeRole = 35 - | 'accessor' 36 - | 'container' 37 - | 'control-flow' 38 - | 'expression' 39 - | 'literal'; 13 + export type NodeRelationship = 'container' | 'reference'; 40 14 41 15 export type NodeScope = 'type' | 'value'; 42 16 43 - export type StructuralRelationship = 'container' | 'reference'; 44 - 45 17 export interface INode<T = unknown> { 46 - /** Custom access pattern for this node. */ 47 - accessPattern?( 48 - node: this, 49 - options: AccessPatternOptions, 50 - ctx: AccessPatternContext<this>, 51 - ): unknown | undefined; 52 18 /** Perform semantic analysis. */ 53 19 analyze(ctx: IAnalysisContext): void; 20 + /** Create a shallow copy of this node. */ 21 + clone(): this; 54 22 /** Whether this node is exported from its file. */ 55 23 exported?: boolean; 56 24 /** The file this node belongs to. */ ··· 64 32 }; 65 33 /** Optional function to sanitize the node name. */ 66 34 readonly nameSanitizer?: NodeNameSanitizer; 67 - /** The role of this node within the structure. */ 68 - role?: NodeRole; 69 35 /** Whether this node is a root node in the file. */ 70 36 root?: boolean; 71 37 /** The scope of this node. */ 72 38 scope?: NodeScope; 73 39 /** Semantic children in the structure hierarchy. */ 74 - structuralChildren?: Map<INode, StructuralRelationship>; 40 + structuralChildren?: Map<INode, NodeRelationship>; 75 41 /** Semantic parents in the structure hierarchy. */ 76 - structuralParents?: Map<INode, StructuralRelationship>; 42 + structuralParents?: Map<INode, NodeRelationship>; 77 43 /** The symbol associated with this node. */ 78 44 symbol?: Symbol; 79 45 /** Convert this node into AST representation. */ 80 - toAst(ctx: AstContext): T; 46 + toAst(): T; 81 47 /** Brand used for renderer dispatch. */ 82 48 readonly '~brand': string; 83 49 }
+2 -5
packages/codegen-core/src/planner/analyzer.ts
··· 1 1 import { isNodeRef, isSymbolRef } from '../guards'; 2 - import type { INode, StructuralRelationship } from '../nodes/node'; 2 + import type { INode, NodeRelationship } from '../nodes/node'; 3 3 import { fromRef, isRef, ref } from '../refs/refs'; 4 4 import type { Ref } from '../refs/types'; 5 5 import type { Symbol } from '../symbols/symbol'; ··· 35 35 /** 36 36 * Register a child node under the current parent. 37 37 */ 38 - addChild( 39 - child: INode, 40 - relationship: StructuralRelationship = 'container', 41 - ): void { 38 + addChild(child: INode, relationship: NodeRelationship = 'container'): void { 42 39 const parent = this.currentParent; 43 40 if (!parent) return; 44 41
+6 -2
packages/codegen-core/src/planner/planner.ts
··· 300 300 from: source, 301 301 imports: [], 302 302 isTypeOnly: true, 303 + kind: 'named', 303 304 }; 304 305 } 305 306 const isTypeOnly = [...entry.kinds].every((kind) => ··· 307 308 ); 308 309 if (entry.symbol.importKind === 'namespace') { 309 310 imp.imports = []; 310 - imp.namespaceImport = entry.symbol.finalName; 311 + imp.kind = 'namespace'; 312 + imp.localName = entry.symbol.finalName; 313 + } else if (entry.symbol.importKind === 'default') { 314 + imp.kind = 'default'; 315 + imp.localName = entry.symbol.finalName; 311 316 } else { 312 317 imp.imports.push({ 313 318 isTypeOnly, 314 - kind: entry.symbol.importKind, 315 319 localName: entry.symbol.finalName, 316 320 sourceName: entry.dep.finalName, 317 321 });
+18 -7
packages/openapi-ts/src/plugins/@hey-api/sdk/model/resource.ts
··· 134 134 node.do( 135 135 this.implementFn({ 136 136 node: $.method(this.createFnSymbol(plugin, event), (m) => 137 - m 137 + this.attachComment({ 138 + node: m, 139 + operation, 140 + }) 138 141 .public() 139 - .static(!isAngularClient && !plugin.config.instance) 140 - .$if(createOperationComment(operation), (m, v) => m.doc(v)), 142 + .static(!isAngularClient && !plugin.config.instance), 141 143 ), 142 144 operation, 143 145 plugin, ··· 154 156 } else { 155 157 this.operations.forEach((event) => { 156 158 const { operation } = event; 157 - const node = $.const(this.createFnSymbol(plugin, event)) 159 + let node = $.const(this.createFnSymbol(plugin, event)) 158 160 .export() 159 - .$if(createOperationComment(operation), (c, v) => c.doc(v)) 160 161 .assign( 161 162 this.implementFn({ 162 163 node: $.func(), ··· 164 165 plugin, 165 166 }), 166 167 ); 168 + node = this.attachComment({ node, operation }); 167 169 nodes.push(node); 168 170 }); 169 171 } ··· 181 183 yield* child.walk(); 182 184 } 183 185 yield this; 186 + } 187 + 188 + private attachComment< 189 + T extends ReturnType<typeof $.var | typeof $.method>, 190 + >(args: { node: T; operation: IR.OperationObject }): T { 191 + const { node, operation } = args; 192 + return node.$if(createOperationComment(operation), (m, v) => m.doc(v)) as T; 184 193 } 185 194 186 195 private createFnSymbol( ··· 325 334 const isClientRequired = 326 335 !plugin.config.client || !plugin.getSymbol({ category: 'client' }); 327 336 const registry = plugin.symbol('__registry'); 328 - node.accessPattern = (node) => 329 - $(node.name).attr(registry).attr('get').call(); 337 + node.toAccessNode = (node, options) => { 338 + if (options.context) return; 339 + return $(node.name).attr(registry).attr('get').call(); 340 + }; 330 341 node.do( 331 342 $.field(registry, (f) => 332 343 f
+1 -1
packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts
··· 31 31 32 32 const awaitSdkFn = $.lazy((ctx) => 33 33 ctx 34 - .getAccess<ReturnType<typeof $>>( 34 + .access( 35 35 plugin.referenceSymbol({ 36 36 category: 'sdk', 37 37 resource: 'operation',
+1 -1
packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts
··· 88 88 const typeData = getPublicTypeData({ isNuxtClient, operation, plugin }); 89 89 const awaitSdkFn = $.lazy((ctx) => 90 90 ctx 91 - .getAccess<ReturnType<typeof $>>( 91 + .access( 92 92 plugin.referenceSymbol({ 93 93 category: 'sdk', 94 94 resource: 'operation',
+4 -4
packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts
··· 64 64 * @returns A meta object with any properties you want to include 65 65 * 66 66 * @example 67 - * ```typescript 67 + * ```ts 68 68 * meta: (operation) => ({ 69 69 * customField: operation.id, 70 70 * isDeprecated: operation.deprecated, ··· 166 166 * @returns A meta object with any properties you want to include 167 167 * 168 168 * @example 169 - * ```typescript 169 + * ```ts 170 170 * meta: (operation) => ({ 171 171 * customField: operation.id, 172 172 * isDeprecated: operation.deprecated, ··· 235 235 * @returns A meta object with any properties you want to include 236 236 * 237 237 * @example 238 - * ```typescript 238 + * ```ts 239 239 * meta: (operation) => ({ 240 240 * customField: operation.id, 241 241 * isDeprecated: operation.deprecated, ··· 319 319 * @returns A meta object with any properties you want to include 320 320 * 321 321 * @example 322 - * ```typescript 322 + * ```ts 323 323 * meta: (operation) => ({ 324 324 * customField: operation.id, 325 325 * isDeprecated: operation.deprecated,
+6 -6
packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/types.d.ts
··· 106 106 * @returns A meta object with any properties you want to include 107 107 * 108 108 * @example 109 - * ```typescript 109 + * ```ts 110 110 * meta: (operation) => ({ 111 111 * customField: operation.id, 112 112 * isDeprecated: operation.deprecated, ··· 165 165 * @returns A meta object with any properties you want to include 166 166 * 167 167 * @example 168 - * ```typescript 168 + * ```ts 169 169 * meta: (operation) => ({ 170 170 * customField: operation.id, 171 171 * isDeprecated: operation.deprecated, ··· 274 274 * @returns A meta object with any properties you want to include 275 275 * 276 276 * @example 277 - * ```typescript 277 + * ```ts 278 278 * meta: (operation) => ({ 279 279 * customField: operation.id, 280 280 * isDeprecated: operation.deprecated, ··· 379 379 * @returns A meta object with any properties you want to include 380 380 * 381 381 * @example 382 - * ```typescript 382 + * ```ts 383 383 * meta: (operation) => ({ 384 384 * customField: operation.id, 385 385 * isDeprecated: operation.deprecated, ··· 428 428 * @returns A meta object with any properties you want to include 429 429 * 430 430 * @example 431 - * ```typescript 431 + * ```ts 432 432 * meta: (operation) => ({ 433 433 * customField: operation.id, 434 434 * isDeprecated: operation.deprecated, ··· 517 517 * @returns A meta object with any properties you want to include 518 518 * 519 519 * @example 520 - * ```typescript 520 + * ```ts 521 521 * meta: (operation) => ({ 522 522 * customField: operation.id, 523 523 * isDeprecated: operation.deprecated,
+1 -1
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/infiniteQueryOptions.ts
··· 198 198 199 199 const awaitSdkFn = $.lazy((ctx) => 200 200 ctx 201 - .getAccess<ReturnType<typeof $>>( 201 + .access( 202 202 plugin.referenceSymbol({ 203 203 category: 'sdk', 204 204 resource: 'operation',
+4 -4
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/mutationOptions.ts
··· 30 30 31 31 const awaitSdkFn = $.lazy((ctx) => 32 32 ctx 33 - .getAccess<ReturnType<typeof $>>( 33 + .access( 34 34 plugin.referenceSymbol({ 35 35 category: 'sdk', 36 36 resource: 'operation', ··· 57 57 } 58 58 59 59 const mutationOptionsFn = 'mutationOptions'; 60 - const symbolMutationOptions = plugin.registerSymbol({ 61 - name: buildName({ 60 + const symbolMutationOptions = plugin.symbol( 61 + buildName({ 62 62 config: plugin.config.mutationOptions, 63 63 name: operation.id, 64 64 }), 65 - }); 65 + ); 66 66 const statement = $.const(symbolMutationOptions) 67 67 .export() 68 68 .$if(plugin.config.comments && createOperationComment(operation), (c, v) =>
+1 -1
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/queryOptions.ts
··· 69 69 70 70 const awaitSdkFn = $.lazy((ctx) => 71 71 ctx 72 - .getAccess<ReturnType<typeof $>>( 72 + .access( 73 73 plugin.referenceSymbol({ 74 74 category: 'sdk', 75 75 resource: 'operation',
+6 -6
packages/openapi-ts/src/plugins/@tanstack/react-query/types.d.ts
··· 109 109 * @returns A meta object with any properties you want to include 110 110 * 111 111 * @example 112 - * ```typescript 112 + * ```ts 113 113 * meta: (operation) => ({ 114 114 * customField: operation.id, 115 115 * isDeprecated: operation.deprecated, ··· 170 170 * @returns A meta object with any properties you want to include 171 171 * 172 172 * @example 173 - * ```typescript 173 + * ```ts 174 174 * meta: (operation) => ({ 175 175 * customField: operation.id, 176 176 * isDeprecated: operation.deprecated, ··· 282 282 * @returns A meta object with any properties you want to include 283 283 * 284 284 * @example 285 - * ```typescript 285 + * ```ts 286 286 * meta: (operation) => ({ 287 287 * customField: operation.id, 288 288 * isDeprecated: operation.deprecated, ··· 427 427 * @returns A meta object with any properties you want to include 428 428 * 429 429 * @example 430 - * ```typescript 430 + * ```ts 431 431 * meta: (operation) => ({ 432 432 * customField: operation.id, 433 433 * isDeprecated: operation.deprecated, ··· 477 477 * @returns A meta object with any properties you want to include 478 478 * 479 479 * @example 480 - * ```typescript 480 + * ```ts 481 481 * meta: (operation) => ({ 482 482 * customField: operation.id, 483 483 * isDeprecated: operation.deprecated, ··· 567 567 * @returns A meta object with any properties you want to include 568 568 * 569 569 * @example 570 - * ```typescript 570 + * ```ts 571 571 * meta: (operation) => ({ 572 572 * customField: operation.id, 573 573 * isDeprecated: operation.deprecated,
+6 -6
packages/openapi-ts/src/plugins/@tanstack/solid-query/types.d.ts
··· 107 107 * @returns A meta object with any properties you want to include 108 108 * 109 109 * @example 110 - * ```typescript 110 + * ```ts 111 111 * meta: (operation) => ({ 112 112 * customField: operation.id, 113 113 * isDeprecated: operation.deprecated, ··· 166 166 * @returns A meta object with any properties you want to include 167 167 * 168 168 * @example 169 - * ```typescript 169 + * ```ts 170 170 * meta: (operation) => ({ 171 171 * customField: operation.id, 172 172 * isDeprecated: operation.deprecated, ··· 275 275 * @returns A meta object with any properties you want to include 276 276 * 277 277 * @example 278 - * ```typescript 278 + * ```ts 279 279 * meta: (operation) => ({ 280 280 * customField: operation.id, 281 281 * isDeprecated: operation.deprecated, ··· 380 380 * @returns A meta object with any properties you want to include 381 381 * 382 382 * @example 383 - * ```typescript 383 + * ```ts 384 384 * meta: (operation) => ({ 385 385 * customField: operation.id, 386 386 * isDeprecated: operation.deprecated, ··· 429 429 * @returns A meta object with any properties you want to include 430 430 * 431 431 * @example 432 - * ```typescript 432 + * ```ts 433 433 * meta: (operation) => ({ 434 434 * customField: operation.id, 435 435 * isDeprecated: operation.deprecated, ··· 518 518 * @returns A meta object with any properties you want to include 519 519 * 520 520 * @example 521 - * ```typescript 521 + * ```ts 522 522 * meta: (operation) => ({ 523 523 * customField: operation.id, 524 524 * isDeprecated: operation.deprecated,
+6 -6
packages/openapi-ts/src/plugins/@tanstack/svelte-query/types.d.ts
··· 106 106 * @returns A meta object with any properties you want to include 107 107 * 108 108 * @example 109 - * ```typescript 109 + * ```ts 110 110 * meta: (operation) => ({ 111 111 * customField: operation.id, 112 112 * isDeprecated: operation.deprecated, ··· 165 165 * @returns A meta object with any properties you want to include 166 166 * 167 167 * @example 168 - * ```typescript 168 + * ```ts 169 169 * meta: (operation) => ({ 170 170 * customField: operation.id, 171 171 * isDeprecated: operation.deprecated, ··· 274 274 * @returns A meta object with any properties you want to include 275 275 * 276 276 * @example 277 - * ```typescript 277 + * ```ts 278 278 * meta: (operation) => ({ 279 279 * customField: operation.id, 280 280 * isDeprecated: operation.deprecated, ··· 379 379 * @returns A meta object with any properties you want to include 380 380 * 381 381 * @example 382 - * ```typescript 382 + * ```ts 383 383 * meta: (operation) => ({ 384 384 * customField: operation.id, 385 385 * isDeprecated: operation.deprecated, ··· 428 428 * @returns A meta object with any properties you want to include 429 429 * 430 430 * @example 431 - * ```typescript 431 + * ```ts 432 432 * meta: (operation) => ({ 433 433 * customField: operation.id, 434 434 * isDeprecated: operation.deprecated, ··· 517 517 * @returns A meta object with any properties you want to include 518 518 * 519 519 * @example 520 - * ```typescript 520 + * ```ts 521 521 * meta: (operation) => ({ 522 522 * customField: operation.id, 523 523 * isDeprecated: operation.deprecated,
+6 -6
packages/openapi-ts/src/plugins/@tanstack/vue-query/types.d.ts
··· 107 107 * @returns A meta object with any properties you want to include 108 108 * 109 109 * @example 110 - * ```typescript 110 + * ```ts 111 111 * meta: (operation) => ({ 112 112 * customField: operation.id, 113 113 * isDeprecated: operation.deprecated, ··· 167 167 * @returns A meta object with any properties you want to include 168 168 * 169 169 * @example 170 - * ```typescript 170 + * ```ts 171 171 * meta: (operation) => ({ 172 172 * customField: operation.id, 173 173 * isDeprecated: operation.deprecated, ··· 277 277 * @returns A meta object with any properties you want to include 278 278 * 279 279 * @example 280 - * ```typescript 280 + * ```ts 281 281 * meta: (operation) => ({ 282 282 * customField: operation.id, 283 283 * isDeprecated: operation.deprecated, ··· 383 383 * @returns A meta object with any properties you want to include 384 384 * 385 385 * @example 386 - * ```typescript 386 + * ```ts 387 387 * meta: (operation) => ({ 388 388 * customField: operation.id, 389 389 * isDeprecated: operation.deprecated, ··· 433 433 * @returns A meta object with any properties you want to include 434 434 * 435 435 * @example 436 - * ```typescript 436 + * ```ts 437 437 * meta: (operation) => ({ 438 438 * customField: operation.id, 439 439 * isDeprecated: operation.deprecated, ··· 523 523 * @returns A meta object with any properties you want to include 524 524 * 525 525 * @example 526 - * ```typescript 526 + * ```ts 527 527 * meta: (operation) => ({ 528 528 * customField: operation.id, 529 529 * isDeprecated: operation.deprecated,
+6 -6
packages/openapi-ts/src/plugins/swr/types.d.ts
··· 109 109 * @returns A meta object with any properties you want to include 110 110 * 111 111 * @example 112 - * ```typescript 112 + * ```ts 113 113 * meta: (operation) => ({ 114 114 * customField: operation.id, 115 115 * isDeprecated: operation.deprecated, ··· 170 170 * @returns A meta object with any properties you want to include 171 171 * 172 172 * @example 173 - * ```typescript 173 + * ```ts 174 174 * meta: (operation) => ({ 175 175 * customField: operation.id, 176 176 * isDeprecated: operation.deprecated, ··· 282 282 * @returns A meta object with any properties you want to include 283 283 * 284 284 * @example 285 - * ```typescript 285 + * ```ts 286 286 * meta: (operation) => ({ 287 287 * customField: operation.id, 288 288 * isDeprecated: operation.deprecated, ··· 427 427 * @returns A meta object with any properties you want to include 428 428 * 429 429 * @example 430 - * ```typescript 430 + * ```ts 431 431 * meta: (operation) => ({ 432 432 * customField: operation.id, 433 433 * isDeprecated: operation.deprecated, ··· 477 477 * @returns A meta object with any properties you want to include 478 478 * 479 479 * @example 480 - * ```typescript 480 + * ```ts 481 481 * meta: (operation) => ({ 482 482 * customField: operation.id, 483 483 * isDeprecated: operation.deprecated, ··· 567 567 * @returns A meta object with any properties you want to include 568 568 * 569 569 * @example 570 - * ```typescript 570 + * ```ts 571 571 * meta: (operation) => ({ 572 572 * customField: operation.id, 573 573 * isDeprecated: operation.deprecated,
+1 -1
packages/openapi-ts/src/plugins/swr/v2/useSwr.ts
··· 33 33 34 34 const awaitSdkFn = $.lazy((ctx) => 35 35 ctx 36 - .getAccess<ReturnType<typeof $>>( 36 + .access( 37 37 plugin.referenceSymbol({ 38 38 category: 'sdk', 39 39 resource: 'operation',
+35 -28
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 { 3 - AccessPatternContext, 4 - AccessPatternOptions, 5 3 AnalysisContext, 6 - AstContext, 7 4 File, 8 5 FromRef, 9 6 Language, 10 7 Node, 11 8 NodeName, 12 9 NodeNameSanitizer, 13 - NodeRole, 10 + NodeRelationship, 14 11 NodeScope, 15 12 Ref, 16 - StructuralRelationship, 17 13 Symbol, 18 14 } from '@hey-api/codegen-core'; 19 15 import { ··· 25 21 ref, 26 22 } from '@hey-api/codegen-core'; 27 23 import ts from 'typescript'; 24 + 25 + import type { AccessOptions } from './utils/context'; 28 26 29 27 export type MaybeArray<T> = T | ReadonlyArray<T>; 30 28 31 29 export abstract class TsDsl<T extends ts.Node = ts.Node> implements Node<T> { 32 - accessPattern?( 33 - node: this, 34 - options: AccessPatternOptions, 35 - ctx: AccessPatternContext<this>, 36 - ): unknown | undefined; 37 30 // eslint-disable-next-line @typescript-eslint/no-unused-vars 38 31 analyze(_: AnalysisContext): void {} 32 + clone(): this { 33 + const cloned = Object.create(Object.getPrototypeOf(this)); 34 + Object.assign(cloned, this); 35 + return cloned; 36 + } 39 37 exported?: boolean; 40 38 file?: File; 41 39 get name(): Node['name'] { ··· 50 48 readonly nameSanitizer?: NodeNameSanitizer; 51 49 language: Language = 'typescript'; 52 50 parent?: Node; 53 - role?: NodeRole; 54 51 root: boolean = false; 55 52 scope?: NodeScope = 'value'; 56 - structuralChildren?: Map<TsDsl, StructuralRelationship>; 57 - structuralParents?: Map<TsDsl, StructuralRelationship>; 53 + structuralChildren?: Map<TsDsl, NodeRelationship>; 54 + structuralParents?: Map<TsDsl, NodeRelationship>; 58 55 symbol?: Symbol; 59 - // eslint-disable-next-line @typescript-eslint/no-unused-vars 60 - toAst(_: AstContext): T { 56 + toAst(): T { 61 57 return undefined as unknown as T; 62 58 } 63 59 readonly '~brand' = nodeBrand; 64 60 61 + /** Access patterns for this node. */ 62 + toAccessNode?( 63 + node: this, 64 + options: AccessOptions, 65 + ctx: { 66 + /** The full chain. */ 67 + chain: ReadonlyArray<TsDsl>; 68 + /** Position in the chain (0 = root). */ 69 + index: number; 70 + /** Is this the leaf node? */ 71 + isLeaf: boolean; 72 + /** Is this the root node? */ 73 + isRoot: boolean; 74 + /** Total length of the chain. */ 75 + length: number; 76 + }, 77 + ): TsDsl | undefined; 65 78 /** Branding property to identify the DSL class at runtime. */ 66 79 abstract readonly '~dsl': string & {}; 67 80 ··· 160 173 return String(value); 161 174 } 162 175 163 - protected $node<I>(ctx: AstContext, value: I): NodeOfMaybe<I> { 176 + protected $node<I>(value: I): NodeOfMaybe<I> { 164 177 if (value === undefined) { 165 178 return undefined as NodeOfMaybe<I>; 166 179 } ··· 175 188 if (value instanceof Array) { 176 189 return value.map((item) => { 177 190 if (isRef(item)) item = fromRef(item); 178 - return this.unwrap(ctx, item); 191 + return this.unwrap(item); 179 192 }) as NodeOfMaybe<I>; 180 193 } 181 - return this.unwrap(ctx, value as any) as NodeOfMaybe<I>; 194 + return this.unwrap(value as any) as NodeOfMaybe<I>; 182 195 } 183 196 184 197 protected $type<I>( 185 - ctx: AstContext, 186 198 value: I, 187 199 args?: ReadonlyArray<ts.TypeNode>, 188 200 ): TypeOfMaybe<I> { ··· 212 224 ) as TypeOfMaybe<I>; 213 225 } 214 226 if (value instanceof Array) { 215 - return value.map((item) => this.$type(ctx, item, args)) as TypeOfMaybe<I>; 227 + return value.map((item) => this.$type(item, args)) as TypeOfMaybe<I>; 216 228 } 217 - return this.unwrap(ctx, value as any) as TypeOfMaybe<I>; 229 + return this.unwrap(value as any) as TypeOfMaybe<I>; 218 230 } 219 231 220 232 private _name?: Ref<NodeName>; 221 233 222 234 /** Unwraps nested nodes into raw TypeScript AST. */ 223 - private unwrap<I>( 224 - ctx: AstContext, 225 - value: I, 226 - ): I extends TsDsl<infer N> ? N : I { 227 - return (isNode(value) ? value.toAst(ctx) : value) as I extends TsDsl< 228 - infer N 229 - > 235 + private unwrap<I>(value: I): I extends TsDsl<infer N> ? N : I { 236 + return (isNode(value) ? value.toAst() : value) as I extends TsDsl<infer N> 230 237 ? N 231 238 : I; 232 239 }
+10 -17
packages/openapi-ts/src/ts-dsl/decl/class.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - NodeRole, 6 - Ref, 7 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 8 2 import { isSymbol, ref } from '@hey-api/codegen-core'; 9 3 import ts from 'typescript'; 10 4 ··· 33 27 export class ClassTsDsl extends Mixed { 34 28 readonly '~dsl' = 'ClassTsDsl'; 35 29 override readonly nameSanitizer = safeRuntimeName; 36 - override role?: NodeRole = 'container'; 37 30 38 31 protected baseClass?: Ref<NodeName>; 39 32 protected body: Body = []; ··· 106 99 return this; 107 100 } 108 101 109 - override toAst(ctx: AstContext) { 110 - const body = this.$node(ctx, this.body) as ReadonlyArray<ts.ClassElement>; 102 + override toAst() { 103 + const body = this.$node(this.body) as ReadonlyArray<ts.ClassElement>; 111 104 const node = ts.factory.createClassDeclaration( 112 - [...this.$decorators(ctx), ...this.modifiers], 113 - this.$node(ctx, this.name) as ts.Identifier, 114 - this.$generics(ctx), 115 - this._heritage(ctx), 105 + [...this.$decorators(), ...this.modifiers], 106 + this.$node(this.name) as ts.Identifier, 107 + this.$generics(), 108 + this._heritage(), 116 109 body, 117 110 ); 118 - return this.$docs(ctx, node); 111 + return this.$docs(node); 119 112 } 120 113 121 114 /** Builds heritage clauses (extends). */ 122 - private _heritage(ctx: AstContext): ReadonlyArray<ts.HeritageClause> { 123 - const node = this.$node(ctx, this.baseClass); 115 + private _heritage(): ReadonlyArray<ts.HeritageClause> { 116 + const node = this.$node(this.baseClass); 124 117 if (!node) return []; 125 118 return [ 126 119 ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [
+4 -8
packages/openapi-ts/src/ts-dsl/decl/decorator.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { MaybeTsDsl } from '../base'; ··· 30 26 ctx.analyze(this.name); 31 27 } 32 28 33 - override toAst(ctx: AstContext) { 34 - const target = this.$node(ctx, this.name); 35 - const args = this.$args(ctx); 29 + override toAst() { 30 + const target = this.$node(this.name); 31 + const args = this.$args(); 36 32 return ts.factory.createDecorator( 37 33 args.length 38 34 ? ts.factory.createCallExpression(target, undefined, args)
+5 -9
packages/openapi-ts/src/ts-dsl/decl/enum.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import { isSymbol } from '@hey-api/codegen-core'; 7 3 import ts from 'typescript'; 8 4 ··· 60 56 return this; 61 57 } 62 58 63 - override toAst(ctx: AstContext) { 59 + override toAst() { 64 60 const node = ts.factory.createEnumDeclaration( 65 61 this.modifiers, 66 - this.$node(ctx, this.name) as ts.Identifier, 67 - this.$node(ctx, this._members) as ReadonlyArray<ts.EnumMember>, 62 + this.$node(this.name) as ts.Identifier, 63 + this.$node(this._members) as ReadonlyArray<ts.EnumMember>, 68 64 ); 69 - return this.$docs(ctx, node); 65 + return this.$docs(node); 70 66 } 71 67 }
+8 -14
packages/openapi-ts/src/ts-dsl/decl/field.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - NodeRole, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 7 2 import ts from 'typescript'; 8 3 9 4 import { TsDsl, TypeTsDsl } from '../base'; ··· 41 36 42 37 export class FieldTsDsl extends Mixed { 43 38 readonly '~dsl' = 'FieldTsDsl'; 44 - override role?: NodeRole = 'accessor'; 45 39 46 40 protected _type?: TypeTsDsl; 47 41 ··· 63 57 return this; 64 58 } 65 59 66 - override toAst(ctx: AstContext) { 60 + override toAst() { 67 61 const node = ts.factory.createPropertyDeclaration( 68 - [...this.$decorators(ctx), ...this.modifiers], 69 - this.$node(ctx, this.name) as ts.PropertyName, 70 - this._optional ? this.$node(ctx, new TokenTsDsl().optional()) : undefined, 71 - this.$type(ctx, this._type), 72 - this.$value(ctx), 62 + [...this.$decorators(), ...this.modifiers], 63 + this.$node(this.name) as ts.PropertyName, 64 + this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, 65 + this.$type(this._type), 66 + this.$value(), 73 67 ); 74 - return this.$docs(ctx, node); 68 + return this.$docs(node); 75 69 } 76 70 }
+18 -24
packages/openapi-ts/src/ts-dsl/decl/func.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import { isSymbol } from '@hey-api/codegen-core'; 7 3 import ts from 'typescript'; 8 4 ··· 111 107 } 112 108 113 109 // @ts-expect-error --- need to fix types --- 114 - override toAst( 115 - ctx: AstContext, 116 - ): M extends 'decl' 110 + override toAst(): M extends 'decl' 117 111 ? ts.FunctionDeclaration 118 112 : M extends 'expr' 119 113 ? ts.FunctionExpression 120 114 : ts.ArrowFunction { 121 - const body = this.$node(ctx, new BlockTsDsl(...this._do).pretty()); 115 + const body = this.$node(new BlockTsDsl(...this._do).pretty()); 122 116 123 117 if (this.mode === 'decl') { 124 118 const name = this.name.toString(); 125 119 if (!name) throw new Error('Function declaration requires a name'); 126 120 const node = ts.factory.createFunctionDeclaration( 127 - [...this.$decorators(ctx), ...this.modifiers], 121 + [...this.$decorators(), ...this.modifiers], 128 122 undefined, 129 - this.$node(ctx, this.name) as ts.Identifier, 130 - this.$generics(ctx), 131 - this.$params(ctx), 132 - this.$returns(ctx), 123 + this.$node(this.name) as ts.Identifier, 124 + this.$generics(), 125 + this.$params(), 126 + this.$returns(), 133 127 body, 134 128 ) as any; 135 - return this.$docs(ctx, node); 129 + return this.$docs(node); 136 130 } 137 131 138 132 if (this.mode === 'expr') { 139 133 const node = ts.factory.createFunctionExpression( 140 134 this.modifiers, 141 135 undefined, 142 - this.$node(ctx, this.name) as ts.Identifier, 143 - this.$generics(ctx), 144 - this.$params(ctx), 145 - this.$returns(ctx), 136 + this.$node(this.name) as ts.Identifier, 137 + this.$generics(), 138 + this.$params(), 139 + this.$returns(), 146 140 body, 147 141 ) as any; 148 - return this.$docs(ctx, node); 142 + return this.$docs(node); 149 143 } 150 144 151 145 const node = ts.factory.createArrowFunction( 152 146 this.modifiers, 153 - this.$generics(ctx), 154 - this.$params(ctx), 155 - this.$returns(ctx), 147 + this.$generics(), 148 + this.$params(), 149 + this.$returns(), 156 150 undefined, 157 151 body.statements.length === 1 && 158 152 ts.isReturnStatement(body.statements[0]!) && ··· 160 154 ? body.statements[0].expression 161 155 : body, 162 156 ) as any; 163 - return this.$docs(ctx, node); 157 + return this.$docs(node); 164 158 } 165 159 } 166 160
+8 -14
packages/openapi-ts/src/ts-dsl/decl/getter.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - NodeRole, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 7 2 import ts from 'typescript'; 8 3 9 4 import { TsDsl } from '../base'; ··· 48 43 export class GetterTsDsl extends Mixed { 49 44 readonly '~dsl' = 'GetterTsDsl'; 50 45 override readonly nameSanitizer = safeAccessorName; 51 - override role?: NodeRole = 'accessor'; 52 46 53 47 constructor(name: NodeName, fn?: (g: GetterTsDsl) => void) { 54 48 super(); ··· 67 61 } 68 62 } 69 63 70 - override toAst(ctx: AstContext) { 64 + override toAst() { 71 65 const node = ts.factory.createGetAccessorDeclaration( 72 - [...this.$decorators(ctx), ...this.modifiers], 73 - this.$node(ctx, this.name) as ts.PropertyName, 74 - this.$params(ctx), 75 - this.$returns(ctx), 76 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 66 + [...this.$decorators(), ...this.modifiers], 67 + this.$node(this.name) as ts.PropertyName, 68 + this.$params(), 69 + this.$returns(), 70 + this.$node(new BlockTsDsl(...this._do).pretty()), 77 71 ); 78 - return this.$docs(ctx, node); 72 + return this.$docs(node); 79 73 } 80 74 }
+6 -6
packages/openapi-ts/src/ts-dsl/decl/init.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 38 38 } 39 39 } 40 40 41 - override toAst(ctx: AstContext) { 41 + override toAst() { 42 42 const node = ts.factory.createConstructorDeclaration( 43 - [...this.$decorators(ctx), ...this.modifiers], 44 - this.$params(ctx), 45 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 43 + [...this.$decorators(), ...this.modifiers], 44 + this.$params(), 45 + this.$node(new BlockTsDsl(...this._do).pretty()), 46 46 ); 47 - return this.$docs(ctx, node); 47 + return this.$docs(node); 48 48 } 49 49 }
+5 -9
packages/openapi-ts/src/ts-dsl/decl/member.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { MaybeTsDsl } from '../base'; ··· 41 37 return this; 42 38 } 43 39 44 - override toAst(ctx: AstContext) { 40 + override toAst() { 45 41 const node = ts.factory.createEnumMember( 46 - this.$node(ctx, safeMemberName(this.name.toString())), 47 - this.$node(ctx, this._value), 42 + this.$node(safeMemberName(this.name.toString())), 43 + this.$node(this._value), 48 44 ); 49 - return this.$docs(ctx, node); 45 + return this.$docs(node); 50 46 } 51 47 }
+10 -16
packages/openapi-ts/src/ts-dsl/decl/method.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - NodeRole, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 7 2 import { isSymbol } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 56 51 export class MethodTsDsl extends Mixed { 57 52 readonly '~dsl' = 'MethodTsDsl'; 58 53 override readonly nameSanitizer = safeAccessorName; 59 - override role?: NodeRole = 'accessor'; 60 54 61 55 constructor(name: NodeName, fn?: (m: MethodTsDsl) => void) { 62 56 super(); ··· 78 72 } 79 73 } 80 74 81 - override toAst(ctx: AstContext) { 75 + override toAst() { 82 76 const node = ts.factory.createMethodDeclaration( 83 - [...this.$decorators(ctx), ...this.modifiers], 77 + [...this.$decorators(), ...this.modifiers], 84 78 undefined, 85 - this.$node(ctx, this.name) as ts.PropertyName, 86 - this._optional ? this.$node(ctx, new TokenTsDsl().optional()) : undefined, 87 - this.$generics(ctx), 88 - this.$params(ctx), 89 - this.$returns(ctx), 90 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 79 + this.$node(this.name) as ts.PropertyName, 80 + this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, 81 + this.$generics(), 82 + this.$params(), 83 + this.$returns(), 84 + this.$node(new BlockTsDsl(...this._do).pretty()), 91 85 ); 92 - return this.$docs(ctx, node); 86 + return this.$docs(node); 93 87 } 94 88 }
+7 -11
packages/openapi-ts/src/ts-dsl/decl/param.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import { TsDsl, TypeTsDsl } from '../base'; ··· 52 48 return this; 53 49 } 54 50 55 - override toAst(ctx: AstContext) { 56 - const name = this.$pattern(ctx) || this.name.toString(); 51 + override toAst() { 52 + const name = this.$pattern() || this.name.toString(); 57 53 if (!name) { 58 54 throw new Error( 59 55 'Param must have either a name or a destructuring pattern', 60 56 ); 61 57 } 62 58 return ts.factory.createParameterDeclaration( 63 - this.$decorators(ctx), 59 + this.$decorators(), 64 60 undefined, 65 61 name, 66 - this._optional ? this.$node(ctx, new TokenTsDsl().optional()) : undefined, 67 - this.$type(ctx, this._type), 68 - this.$value(ctx), 62 + this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, 63 + this.$type(this._type), 64 + this.$value(), 69 65 ); 70 66 } 71 67 }
+7 -7
packages/openapi-ts/src/ts-dsl/decl/pattern.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; ··· 53 53 return this; 54 54 } 55 55 56 - override toAst(ctx: AstContext) { 56 + override toAst() { 57 57 if (!this.pattern) { 58 58 throw new Error('PatternTsDsl requires object() or array() pattern'); 59 59 } ··· 70 70 ) 71 71 : ts.factory.createBindingElement(undefined, key, alias, undefined), 72 72 ); 73 - const spread = this.createSpread(ctx); 73 + const spread = this.createSpread(); 74 74 if (spread) elements.push(spread); 75 75 return ts.factory.createObjectBindingPattern(elements); 76 76 } ··· 79 79 const elements = this.pattern.values.map((p) => 80 80 ts.factory.createBindingElement(undefined, undefined, p, undefined), 81 81 ); 82 - const spread = this.createSpread(ctx); 82 + const spread = this.createSpread(); 83 83 if (spread) elements.push(spread); 84 84 return ts.factory.createArrayBindingPattern(elements); 85 85 } ··· 87 87 throw new Error('PatternTsDsl requires object() or array() pattern'); 88 88 } 89 89 90 - private createSpread(ctx: AstContext): ts.BindingElement | undefined { 90 + private createSpread(): ts.BindingElement | undefined { 91 91 return this._spread 92 92 ? ts.factory.createBindingElement( 93 - this.$node(ctx, new TokenTsDsl().spread()), 93 + this.$node(new TokenTsDsl().spread()), 94 94 undefined, 95 - this.$node(ctx, new IdTsDsl(this._spread)), 95 + this.$node(new IdTsDsl(this._spread)), 96 96 ) 97 97 : undefined; 98 98 }
+7 -11
packages/openapi-ts/src/ts-dsl/decl/setter.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import { TsDsl } from '../base'; ··· 60 56 } 61 57 } 62 58 63 - override toAst(ctx: AstContext) { 59 + override toAst() { 64 60 const node = ts.factory.createSetAccessorDeclaration( 65 - [...this.$decorators(ctx), ...this.modifiers], 66 - this.$node(ctx, this.name) as ts.PropertyName, 67 - this.$params(ctx), 68 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 61 + [...this.$decorators(), ...this.modifiers], 62 + this.$node(this.name) as ts.PropertyName, 63 + this.$params(), 64 + this.$node(new BlockTsDsl(...this._do).pretty()), 69 65 ); 70 - return this.$docs(ctx, node); 66 + return this.$docs(node); 71 67 } 72 68 }
+3 -3
packages/openapi-ts/src/ts-dsl/expr/array.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 59 59 return this; 60 60 } 61 61 62 - override toAst(ctx: AstContext) { 62 + override toAst() { 63 63 const elements = this._elements.map((item) => { 64 - const node = this.$node(ctx, item.expr); 64 + const node = this.$node(item.expr); 65 65 return item.kind === 'spread' 66 66 ? ts.factory.createSpreadElement(node) 67 67 : node;
+4 -9
packages/openapi-ts/src/ts-dsl/expr/as.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - Ref, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 37 32 ctx.analyze(this.type); 38 33 } 39 34 40 - override toAst(ctx: AstContext) { 35 + override toAst() { 41 36 return ts.factory.createAsExpression( 42 - this.$node(ctx, this.expr), 43 - this.$type(ctx, this.type), 37 + this.$node(this.expr), 38 + this.$type(this.type), 44 39 ); 45 40 } 46 41 }
+9 -14
packages/openapi-ts/src/ts-dsl/expr/attr.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - Ref, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { fromRef, isSymbol, ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 48 43 ctx.analyze(this.name); 49 44 } 50 45 51 - override toAst(ctx: AstContext) { 52 - const leftNode = this.$node(ctx, this.left); 46 + override toAst() { 47 + const leftNode = this.$node(this.left); 53 48 regexp.typeScriptIdentifier.lastIndex = 0; 54 49 const right = fromRef(this.name); 55 50 if (!regexp.typeScriptIdentifier.test(this.name.toString())) { ··· 65 60 if (this._optional) { 66 61 return ts.factory.createElementAccessChain( 67 62 leftNode, 68 - this.$node(ctx, new TokenTsDsl().questionDot()), 69 - this.$node(ctx, new LiteralTsDsl(value)), 63 + this.$node(new TokenTsDsl().questionDot()), 64 + this.$node(new LiteralTsDsl(value)), 70 65 ); 71 66 } 72 67 return ts.factory.createElementAccessExpression( 73 68 leftNode, 74 - this.$node(ctx, new LiteralTsDsl(value)), 69 + this.$node(new LiteralTsDsl(value)), 75 70 ); 76 71 } 77 72 if (this._optional) { 78 73 return ts.factory.createPropertyAccessChain( 79 74 leftNode, 80 - this.$node(ctx, new TokenTsDsl().questionDot()), 81 - this.$node(ctx, this.name) as ts.MemberName, 75 + this.$node(new TokenTsDsl().questionDot()), 76 + this.$node(this.name) as ts.MemberName, 82 77 ); 83 78 } 84 79 return ts.factory.createPropertyAccessExpression( 85 80 leftNode, 86 - this.$node(ctx, this.name) as ts.MemberName, 81 + this.$node(this.name) as ts.MemberName, 87 82 ); 88 83 } 89 84 }
+3 -8
packages/openapi-ts/src/ts-dsl/expr/await.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - Ref, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 32 27 ctx.analyze(this._awaitExpr); 33 28 } 34 29 35 - override toAst(ctx: AstContext) { 36 - return ts.factory.createAwaitExpression(this.$node(ctx, this._awaitExpr)); 30 + override toAst() { 31 + return ts.factory.createAwaitExpression(this.$node(this._awaitExpr)); 37 32 } 38 33 } 39 34
+4 -9
packages/openapi-ts/src/ts-dsl/expr/binary.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - Ref, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 140 135 return this.opAndExpr('*', expr); 141 136 } 142 137 143 - override toAst(ctx: AstContext) { 138 + override toAst() { 144 139 if (!this._op) { 145 140 throw new Error('BinaryTsDsl: missing operator'); 146 141 } 147 - const expr = this.$node(ctx, this._expr); 142 + const expr = this.$node(this._expr); 148 143 if (!expr) { 149 144 throw new Error('BinaryTsDsl: missing right-hand expression'); 150 145 } 151 - const base = this.$node(ctx, this._base); 146 + const base = this.$node(this._base); 152 147 const operator = 153 148 typeof this._op === 'string' ? this.opToToken(this._op) : this._op; 154 149 return ts.factory.createBinaryExpression(base, operator, expr);
+13 -17
packages/openapi-ts/src/ts-dsl/expr/call.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 2 + import { ref } from '@hey-api/codegen-core'; 6 3 import ts from 'typescript'; 7 4 8 5 import type { MaybeTsDsl } from '../base'; ··· 13 10 import { TypeArgsMixin } from '../mixins/type-args'; 14 11 import { f } from '../utils/factories'; 15 12 16 - export type CallCallee = string | MaybeTsDsl<ts.Expression>; 17 - export type CallArg = NodeName | MaybeTsDsl<ts.Expression>; 18 - export type CallArgs = ReadonlyArray<CallArg | undefined>; 19 - export type CallCtor = (callee: CallCallee, ...args: CallArgs) => CallTsDsl; 13 + export type CallArgs = ReadonlyArray<CallExpr | undefined>; 14 + export type CallExpr = NodeName | MaybeTsDsl<ts.Expression>; 15 + export type CallCtor = (expr: CallExpr, ...args: CallArgs) => CallTsDsl; 20 16 21 17 const Mixed = ArgsMixin( 22 18 AsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.CallExpression>))), ··· 25 21 export class CallTsDsl extends Mixed { 26 22 readonly '~dsl' = 'CallTsDsl'; 27 23 28 - protected _callee: CallCallee; 24 + protected _callExpr: Ref<CallExpr>; 29 25 30 - constructor(callee: CallCallee, ...args: CallArgs) { 26 + constructor(expr: CallExpr, ...args: CallArgs) { 31 27 super(); 32 - this._callee = callee; 28 + this._callExpr = ref(expr); 33 29 this.args(...args); 34 30 } 35 31 36 32 override analyze(ctx: AnalysisContext): void { 37 33 super.analyze(ctx); 38 - ctx.analyze(this._callee); 34 + ctx.analyze(this._callExpr); 39 35 } 40 36 41 - override toAst(ctx: AstContext) { 37 + override toAst() { 42 38 return ts.factory.createCallExpression( 43 - this.$node(ctx, this._callee), 44 - this.$generics(ctx), 45 - this.$args(ctx), 39 + this.$node(this._callExpr), 40 + this.$generics(), 41 + this.$args(), 46 42 ); 47 43 } 48 44 }
+9 -9
packages/openapi-ts/src/ts-dsl/expr/expr.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - Ref, 6 - } from '@hey-api/codegen-core'; 7 - import { ref } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 2 + import { isNode, isSymbol, ref } from '@hey-api/codegen-core'; 8 3 import type ts from 'typescript'; 9 4 10 5 import type { MaybeTsDsl } from '../base'; ··· 28 23 constructor(id: Id) { 29 24 super(); 30 25 this._exprInput = ref(id); 26 + if (typeof id === 'string' || isSymbol(id)) { 27 + this.name.set(id); 28 + } else if (isNode(id)) { 29 + this.name.set(id.name); 30 + } 31 31 } 32 32 33 33 override analyze(ctx: AnalysisContext): void { ··· 35 35 ctx.analyze(this._exprInput); 36 36 } 37 37 38 - override toAst(ctx: AstContext) { 39 - return this.$node(ctx, this._exprInput); 38 + override toAst() { 39 + return this.$node(this._exprInput); 40 40 } 41 41 }
+3 -5
packages/openapi-ts/src/ts-dsl/expr/literal.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 32 32 super.analyze(ctx); 33 33 } 34 34 35 - override toAst(ctx: AstContext) { 35 + override toAst() { 36 36 if (typeof this.value === 'boolean') { 37 37 return this.value ? ts.factory.createTrue() : ts.factory.createFalse(); 38 38 } 39 39 if (typeof this.value === 'number') { 40 40 const expr = ts.factory.createNumericLiteral(Math.abs(this.value)); 41 - return this.value < 0 42 - ? this.$node(ctx, new PrefixTsDsl(expr).neg()) 43 - : expr; 41 + return this.value < 0 ? this.$node(new PrefixTsDsl(expr).neg()) : expr; 44 42 } 45 43 if (typeof this.value === 'string') { 46 44 return ts.factory.createStringLiteral(this.value, true);
+18 -15
packages/openapi-ts/src/ts-dsl/expr/new.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - Ref, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 10 5 import type { MaybeTsDsl } from '../base'; 11 6 import { TsDsl } from '../base'; 12 7 import { ArgsMixin } from '../mixins/args'; 8 + import { AsMixin } from '../mixins/as'; 13 9 import { ExprMixin } from '../mixins/expr'; 14 10 import { TypeArgsMixin } from '../mixins/type-args'; 11 + import { f } from '../utils/factories'; 15 12 13 + export type NewArgs = ReadonlyArray<NewExpr | undefined>; 16 14 export type NewExpr = NodeName | MaybeTsDsl<ts.Expression>; 15 + export type NewCtor = (expr: NewExpr, ...args: NewArgs) => NewTsDsl; 17 16 18 - const Mixed = ArgsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.NewExpression>))); 17 + const Mixed = ArgsMixin( 18 + AsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.NewExpression>))), 19 + ); 19 20 20 21 export class NewTsDsl extends Mixed { 21 22 readonly '~dsl' = 'NewTsDsl'; 22 23 23 - protected classExpr: Ref<NewExpr>; 24 + protected _newExpr: Ref<NewExpr>; 24 25 25 - constructor(classExpr: NewExpr, ...args: ReadonlyArray<NewExpr>) { 26 + constructor(expr: NewExpr, ...args: NewArgs) { 26 27 super(); 27 - this.classExpr = ref(classExpr); 28 + this._newExpr = ref(expr); 28 29 this.args(...args); 29 30 } 30 31 31 32 override analyze(ctx: AnalysisContext): void { 32 33 super.analyze(ctx); 33 - ctx.analyze(this.classExpr); 34 + ctx.analyze(this._newExpr); 34 35 } 35 36 36 - override toAst(ctx: AstContext) { 37 + override toAst() { 37 38 return ts.factory.createNewExpression( 38 - this.$node(ctx, this.classExpr), 39 - this.$generics(ctx), 40 - this.$args(ctx), 39 + this.$node(this._newExpr), 40 + this.$generics(), 41 + this.$args(), 41 42 ); 42 43 } 43 44 } 45 + 46 + f.new.set((...args) => new NewTsDsl(...args));
+4 -8
packages/openapi-ts/src/ts-dsl/expr/object.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { MaybeTsDsl } from '../base'; ··· 87 83 return this; 88 84 } 89 85 90 - override toAst(ctx: AstContext) { 86 + override toAst() { 91 87 const node = ts.factory.createObjectLiteralExpression( 92 - this.$node(ctx, this._props), 88 + this.$node(this._props), 93 89 this.$multiline(this._props.length), 94 90 ); 95 - return this.$hint(ctx, node); 91 + return this.$hint(node); 96 92 } 97 93 }
+3 -3
packages/openapi-ts/src/ts-dsl/expr/prefix.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 50 50 return this; 51 51 } 52 52 53 - override toAst(ctx: AstContext) { 53 + override toAst() { 54 54 if (!this._expr) { 55 55 throw new Error('Missing expression for prefix unary expression'); 56 56 } ··· 59 59 } 60 60 return ts.factory.createPrefixUnaryExpression( 61 61 this._op, 62 - this.$node(ctx, this._expr), 62 + this.$node(this._expr), 63 63 ); 64 64 } 65 65 }
+12 -17
packages/openapi-ts/src/ts-dsl/expr/prop.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - Ref, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 58 53 return this; 59 54 } 60 55 61 - override toAst(ctx: AstContext) { 56 + override toAst() { 62 57 this.$validate(); 63 - const node = this.$node(ctx, this._value); 58 + const node = this.$node(this._value); 64 59 if (this.meta.kind === 'spread') { 65 60 if (ts.isStatement(node)) { 66 61 throw new Error( ··· 68 63 ); 69 64 } 70 65 const result = ts.factory.createSpreadAssignment(node); 71 - return this.$docs(ctx, result); 66 + return this.$docs(result); 72 67 } 73 68 if (this.meta.kind === 'getter') { 74 69 const getter = new GetterTsDsl(this.meta.name).do(node); 75 - const result = this.$node(ctx, getter); 76 - return this.$docs(ctx, result); 70 + const result = this.$node(getter); 71 + return this.$docs(result); 77 72 } 78 73 if (this.meta.kind === 'setter') { 79 74 const setter = new SetterTsDsl(this.meta.name).do(node); 80 - const result = this.$node(ctx, setter); 81 - return this.$docs(ctx, result); 75 + const result = this.$node(setter); 76 + return this.$docs(result); 82 77 } 83 78 if (ts.isIdentifier(node) && node.text === this.meta.name) { 84 79 const result = ts.factory.createShorthandPropertyAssignment( 85 80 this.meta.name, 86 81 ); 87 - return this.$docs(ctx, result); 82 + return this.$docs(result); 88 83 } 89 84 if (ts.isStatement(node)) { 90 85 throw new Error( ··· 94 89 const result = ts.factory.createPropertyAssignment( 95 90 this.meta.kind === 'computed' 96 91 ? ts.factory.createComputedPropertyName( 97 - this.$node(ctx, new IdTsDsl(this.meta.name)), 92 + this.$node(new IdTsDsl(this.meta.name)), 98 93 ) 99 - : this.$node(ctx, safePropName(this.meta.name)), 94 + : this.$node(safePropName(this.meta.name)), 100 95 node, 101 96 ); 102 - return this.$docs(ctx, result); 97 + return this.$docs(result); 103 98 } 104 99 105 100 $validate(): asserts this is this & {
+2 -10
packages/openapi-ts/src/ts-dsl/expr/template.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - NodeRole, 6 - Ref, 7 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 8 2 import { fromRef, isSymbol, ref } from '@hey-api/codegen-core'; 9 3 import ts from 'typescript'; 10 4 ··· 17 11 18 12 export class TemplateTsDsl extends Mixed { 19 13 readonly '~dsl' = 'TemplateTsDsl'; 20 - override role?: NodeRole = 'literal'; 21 14 22 15 protected parts: Array<Ref<TemplatePart>> = []; 23 16 ··· 38 31 return this; 39 32 } 40 33 41 - override toAst(ctx: AstContext) { 34 + override toAst() { 42 35 const parts = this.$node( 43 - ctx, 44 36 this.parts.map((p) => { 45 37 const part = fromRef(p); 46 38 return isSymbol(part) ? part.finalName : part;
+5 -5
packages/openapi-ts/src/ts-dsl/expr/ternary.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 40 40 return this; 41 41 } 42 42 43 - override toAst(ctx: AstContext) { 43 + override toAst() { 44 44 if (!this._condition) throw new Error('Missing condition in ternary'); 45 45 if (!this._then) throw new Error('Missing then expression in ternary'); 46 46 if (!this._else) throw new Error('Missing else expression in ternary'); 47 47 48 48 return ts.factory.createConditionalExpression( 49 - this.$node(ctx, this._condition), 49 + this.$node(this._condition), 50 50 undefined, 51 - this.$node(ctx, this._then), 51 + this.$node(this._then), 52 52 undefined, 53 - this.$node(ctx, this._else), 53 + this.$node(this._else), 54 54 ); 55 55 } 56 56 }
+3 -3
packages/openapi-ts/src/ts-dsl/expr/typeof.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 26 26 ctx.analyze(this._expr); 27 27 } 28 28 29 - override toAst(ctx: AstContext) { 30 - return ts.factory.createTypeOfExpression(this.$node(ctx, this._expr)); 29 + override toAst() { 30 + return ts.factory.createTypeOfExpression(this.$node(this._expr)); 31 31 } 32 32 } 33 33
+2 -2
packages/openapi-ts/src/ts-dsl/index.ts
··· 357 357 358 358 export type { MaybeTsDsl, TypeTsDsl } from './base'; 359 359 export { TsDsl } from './base'; 360 - export { TypeScriptRenderer } from './render/typescript'; 361 - export { astContext } from './utils/context'; 360 + export { TsDslContext } from './utils/context'; 362 361 export { keywords } from './utils/keywords'; 363 362 export { regexp } from './utils/regexp'; 363 + export { TypeScriptRenderer } from './utils/render'; 364 364 export { reserved } from './utils/reserved';
+7 -5
packages/openapi-ts/src/ts-dsl/layout/doc.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; 5 5 import { TsDsl } from '../base'; 6 6 import { IdTsDsl } from '../expr/id'; 7 + import { TsDslContext } from '../utils/context'; 7 8 8 - type DocMaybeLazy<T> = ((ctx: AstContext) => T) | T; 9 + type DocMaybeLazy<T> = ((ctx: TsDslContext) => T) | T; 9 10 export type DocFn = (d: DocTsDsl) => void; 10 11 export type DocLines = DocMaybeLazy<MaybeArray<string>>; 11 12 ··· 29 30 return this; 30 31 } 31 32 32 - apply<T extends ts.Node>(ctx: AstContext, node: T): T { 33 + apply<T extends ts.Node>(node: T): T { 34 + const ctx = new TsDslContext(); 33 35 const lines = this._lines.reduce((lines: Array<string>, line: DocLines) => { 34 36 if (typeof line === 'function') line = line(ctx); 35 37 for (const l of typeof line === 'string' ? [line] : line) { ··· 69 71 return node; 70 72 } 71 73 72 - override toAst(ctx: AstContext): ts.Node { 74 + override toAst(): ts.Node { 73 75 // this class does not build a standalone node; 74 76 // it modifies other nodes via `apply()`. 75 77 // Return a dummy comment node for compliance. 76 - return this.$node(ctx, new IdTsDsl('')); 78 + return this.$node(new IdTsDsl('')); 77 79 } 78 80 }
+7 -5
packages/openapi-ts/src/ts-dsl/layout/hint.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; 5 5 import { TsDsl } from '../base'; 6 6 import { IdTsDsl } from '../expr/id'; 7 + import { TsDslContext } from '../utils/context'; 7 8 8 - type HintMaybeLazy<T> = ((ctx: AstContext) => T) | T; 9 + type HintMaybeLazy<T> = ((ctx: TsDslContext) => T) | T; 9 10 export type HintFn = (d: HintTsDsl) => void; 10 11 export type HintLines = HintMaybeLazy<MaybeArray<string>>; 11 12 ··· 29 30 return this; 30 31 } 31 32 32 - apply<T extends ts.Node>(ctx: AstContext, node: T): T { 33 + apply<T extends ts.Node>(node: T): T { 34 + const ctx = new TsDslContext(); 33 35 const lines = this._lines.reduce( 34 36 (lines: Array<string>, line: HintLines) => { 35 37 if (typeof line === 'function') line = line(ctx); ··· 54 56 return node; 55 57 } 56 58 57 - override toAst(ctx: AstContext): ts.Node { 59 + override toAst(): ts.Node { 58 60 // this class does not build a standalone node; 59 61 // it modifies other nodes via `apply()`. 60 62 // Return a dummy comment node for compliance. 61 - return this.$node(ctx, new IdTsDsl('')); 63 + return this.$node(new IdTsDsl('')); 62 64 } 63 65 }
+3 -3
packages/openapi-ts/src/ts-dsl/layout/newline.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 11 11 super.analyze(ctx); 12 12 } 13 13 14 - override toAst(ctx: AstContext): ts.Identifier { 15 - return this.$node(ctx, new IdTsDsl('\n')); 14 + override toAst(): ts.Identifier { 15 + return this.$node(new IdTsDsl('\n')); 16 16 } 17 17 }
+7 -5
packages/openapi-ts/src/ts-dsl/layout/note.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; 5 5 import { TsDsl } from '../base'; 6 6 import { IdTsDsl } from '../expr/id'; 7 + import { TsDslContext } from '../utils/context'; 7 8 8 - type NoteMaybeLazy<T> = ((ctx: AstContext) => T) | T; 9 + type NoteMaybeLazy<T> = ((ctx: TsDslContext) => T) | T; 9 10 export type NoteFn = (d: NoteTsDsl) => void; 10 11 export type NoteLines = NoteMaybeLazy<MaybeArray<string>>; 11 12 ··· 29 30 return this; 30 31 } 31 32 32 - apply<T extends ts.Node>(ctx: AstContext, node: T): T { 33 + apply<T extends ts.Node>(node: T): T { 34 + const ctx = new TsDslContext(); 33 35 const lines = this._lines.reduce( 34 36 (lines: Array<string>, line: NoteLines) => { 35 37 if (typeof line === 'function') line = line(ctx); ··· 52 54 return node; 53 55 } 54 56 55 - override toAst(ctx: AstContext): ts.Node { 57 + override toAst(): ts.Node { 56 58 // this class does not build a standalone node; 57 59 // it modifies other nodes via `apply()`. 58 60 // Return a dummy comment node for compliance. 59 - return this.$node(ctx, new IdTsDsl('')); 61 + return this.$node(new IdTsDsl('')); 60 62 } 61 63 }
+3 -4
packages/openapi-ts/src/ts-dsl/mixins/args.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 Node, 5 4 NodeName, 6 5 Ref, ··· 15 14 16 15 export interface ArgsMethods extends Node { 17 16 /** Renders the arguments into an array of `Expression`s. */ 18 - $args(ctx: AstContext): ReadonlyArray<ts.Expression>; 17 + $args(): ReadonlyArray<ts.Expression>; 19 18 /** Adds a single expression argument. */ 20 19 arg(arg: Arg | undefined): this; 21 20 /** Adds one or more expression arguments. */ ··· 52 51 return this; 53 52 } 54 53 55 - protected $args(ctx: AstContext): ReadonlyArray<ts.Expression> { 56 - return this.$node(ctx, this._args).map((arg) => this.$node(ctx, arg)); 54 + protected $args(): ReadonlyArray<ts.Expression> { 55 + return this.$node(this._args).map((arg) => this.$node(arg)); 57 56 } 58 57 } 59 58
+4 -9
packages/openapi-ts/src/ts-dsl/mixins/decorator.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Node, 5 - NodeName, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 7 2 import type ts from 'typescript'; 8 3 9 4 import type { MaybeTsDsl } from '../base'; ··· 12 7 13 8 export interface DecoratorMethods extends Node { 14 9 /** Renders the decorators into an array of `ts.Decorator`s. */ 15 - $decorators(ctx: AstContext): ReadonlyArray<ts.Decorator>; 10 + $decorators(): ReadonlyArray<ts.Decorator>; 16 11 /** Adds a decorator (e.g. `@sealed({ in: 'root' })`). */ 17 12 decorator( 18 13 name: NodeName | MaybeTsDsl<ts.Expression>, ··· 41 36 return this; 42 37 } 43 38 44 - protected $decorators(ctx: AstContext): ReadonlyArray<ts.Decorator> { 45 - return this.$node(ctx, this.decorators); 39 + protected $decorators(): ReadonlyArray<ts.Decorator> { 40 + return this.$node(this.decorators); 46 41 } 47 42 } 48 43
+4 -7
packages/openapi-ts/src/ts-dsl/mixins/do.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 9 9 10 10 export interface DoMethods extends Node { 11 11 /** Renders the collected `.do()` calls into an array of `Statement` nodes. */ 12 - $do(ctx: AstContext): ReadonlyArray<ts.Statement>; 12 + $do(): ReadonlyArray<ts.Statement>; 13 13 _do: Array<DoExpr>; 14 14 /** Adds one or more expressions/statements to the body. */ 15 15 do(...items: ReadonlyArray<DoExpr>): this; ··· 41 41 return this; 42 42 } 43 43 44 - protected $do(ctx: AstContext): ReadonlyArray<ts.Statement> { 45 - return this.$node( 46 - ctx, 47 - this._do.map((item) => new StmtTsDsl(item)), 48 - ); 44 + protected $do(): ReadonlyArray<ts.Statement> { 45 + return this.$node(this._do.map((item) => new StmtTsDsl(item))); 49 46 } 50 47 } 51 48
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/doc.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { DocFn, DocLines } from '../layout/doc'; ··· 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 8 export interface DocMethods extends Node { 9 - $docs<T extends ts.Node>(ctx: AstContext, node: T): T; 9 + $docs<T extends ts.Node>(node: T): T; 10 10 doc(lines?: DocLines, fn?: DocFn): this; 11 11 } 12 12 ··· 25 25 return this; 26 26 } 27 27 28 - protected $docs<T extends ts.Node>(ctx: AstContext, node: T): T { 29 - return this._doc ? this._doc.apply(ctx, node) : node; 28 + protected $docs<T extends ts.Node>(node: T): T { 29 + return this._doc ? this._doc.apply(node) : node; 30 30 } 31 31 } 32 32
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/hint.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { HintFn, HintLines } from '../layout/hint'; ··· 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 8 export interface HintMethods extends Node { 9 - $hint<T extends ts.Node>(ctx: AstContext, node: T): T; 9 + $hint<T extends ts.Node>(node: T): T; 10 10 hint(lines?: HintLines, fn?: HintFn): this; 11 11 } 12 12 ··· 25 25 return this; 26 26 } 27 27 28 - protected $hint<T extends ts.Node>(ctx: AstContext, node: T): T { 29 - return this._hint ? this._hint.apply(ctx, node) : node; 28 + protected $hint<T extends ts.Node>(node: T): T { 29 + return this._hint ? this._hint.apply(node) : node; 30 30 } 31 31 } 32 32
+74 -16
packages/openapi-ts/src/ts-dsl/mixins/modifiers.ts
··· 4 4 import type { BaseCtor, MixinCtor } from './types'; 5 5 6 6 export type Modifiers = { 7 + /** 8 + * Checks if the specified modifier is present. 9 + * 10 + * @param modifier - The modifier to check. 11 + * @returns True if the modifier is present, false otherwise. 12 + */ 13 + hasModifier(modifier: Modifier): boolean; 7 14 modifiers: Array<ts.Modifier>; 8 15 }; 9 16 17 + type Modifier = 18 + | 'abstract' 19 + | 'async' 20 + | 'const' 21 + | 'declare' 22 + | 'default' 23 + | 'export' 24 + | 'override' 25 + | 'private' 26 + | 'protected' 27 + | 'public' 28 + | 'readonly' 29 + | 'static'; 30 + 10 31 export interface ModifierMethods extends Modifiers { 11 32 /** 12 33 * Adds a modifier of the specified kind to the modifiers list if the condition is true. 13 34 * 14 - * @param kind - The syntax kind of the modifier to add. 35 + * @param modifier - The modifier to add. 15 36 * @param condition - Whether to add the modifier. 16 37 * @returns The parent node for chaining. 17 38 */ 18 - _m(kind: ts.ModifierSyntaxKind, condition: boolean): this; 39 + _m(modifier: Modifier, condition: boolean): this; 40 + } 41 + 42 + function modifierToKind(modifier: Modifier): ts.ModifierSyntaxKind { 43 + switch (modifier) { 44 + case 'abstract': 45 + return ts.SyntaxKind.AbstractKeyword; 46 + case 'async': 47 + return ts.SyntaxKind.AsyncKeyword; 48 + case 'const': 49 + return ts.SyntaxKind.ConstKeyword; 50 + case 'declare': 51 + return ts.SyntaxKind.DeclareKeyword; 52 + case 'default': 53 + return ts.SyntaxKind.DefaultKeyword; 54 + case 'export': 55 + return ts.SyntaxKind.ExportKeyword; 56 + case 'override': 57 + return ts.SyntaxKind.OverrideKeyword; 58 + case 'private': 59 + return ts.SyntaxKind.PrivateKeyword; 60 + case 'protected': 61 + return ts.SyntaxKind.ProtectedKeyword; 62 + case 'public': 63 + return ts.SyntaxKind.PublicKeyword; 64 + case 'readonly': 65 + return ts.SyntaxKind.ReadonlyKeyword; 66 + case 'static': 67 + return ts.SyntaxKind.StaticKeyword; 68 + } 19 69 } 20 70 21 71 function ModifiersMixin<T extends ts.Node, TBase extends BaseCtor<T>>( ··· 28 78 super.analyze(ctx); 29 79 } 30 80 31 - protected _m(kind: ts.ModifierSyntaxKind, condition: boolean): this { 32 - if (condition) this.modifiers.push(ts.factory.createModifier(kind)); 81 + protected hasModifier(modifier: Modifier): boolean { 82 + const kind = modifierToKind(modifier); 83 + return Boolean(this.modifiers.find((mod) => mod.kind === kind)); 84 + } 85 + 86 + protected _m(modifier: Modifier, condition: boolean): this { 87 + if (condition) { 88 + const kind = modifierToKind(modifier); 89 + this.modifiers.push(ts.factory.createModifier(kind)); 90 + } 33 91 return this; 34 92 } 35 93 } ··· 58 116 abstract class Abstract extends Mixed { 59 117 protected abstract(condition?: boolean): this { 60 118 const cond = arguments.length === 0 ? true : Boolean(condition); 61 - return this._m(ts.SyntaxKind.AbstractKeyword, cond); 119 + return this._m('abstract', cond); 62 120 } 63 121 } 64 122 ··· 86 144 abstract class Async extends Mixed { 87 145 protected async(condition?: boolean): this { 88 146 const cond = arguments.length === 0 ? true : Boolean(condition); 89 - return this._m(ts.SyntaxKind.AsyncKeyword, cond); 147 + return this._m('async', cond); 90 148 } 91 149 } 92 150 ··· 114 172 abstract class Const extends Mixed { 115 173 protected const(condition?: boolean): this { 116 174 const cond = arguments.length === 0 ? true : Boolean(condition); 117 - return this._m(ts.SyntaxKind.ConstKeyword, cond); 175 + return this._m('const', cond); 118 176 } 119 177 } 120 178 ··· 142 200 abstract class Declare extends Mixed { 143 201 protected declare(condition?: boolean): this { 144 202 const cond = arguments.length === 0 ? true : Boolean(condition); 145 - return this._m(ts.SyntaxKind.DeclareKeyword, cond); 203 + return this._m('declare', cond); 146 204 } 147 205 } 148 206 ··· 176 234 */ 177 235 protected default(condition?: boolean): this { 178 236 const cond = arguments.length === 0 ? true : Boolean(condition); 179 - return this._m(ts.SyntaxKind.DefaultKeyword, cond); 237 + return this._m('default', cond); 180 238 } 181 239 } 182 240 ··· 213 271 this.exported = cond; 214 272 // TODO: remove this side-effect once planner handles exported flag 215 273 if (this.symbol) this.symbol.setExported(cond); 216 - return this._m(ts.SyntaxKind.ExportKeyword, cond); 274 + return this._m('export', cond); 217 275 } 218 276 } 219 277 ··· 241 299 abstract class Override extends Mixed { 242 300 protected override(condition?: boolean): this { 243 301 const cond = arguments.length === 0 ? true : Boolean(condition); 244 - return this._m(ts.SyntaxKind.OverrideKeyword, cond); 302 + return this._m('override', cond); 245 303 } 246 304 } 247 305 ··· 269 327 abstract class Private extends Mixed { 270 328 protected private(condition?: boolean): this { 271 329 const cond = arguments.length === 0 ? true : Boolean(condition); 272 - return this._m(ts.SyntaxKind.PrivateKeyword, cond); 330 + return this._m('private', cond); 273 331 } 274 332 } 275 333 ··· 297 355 abstract class Protected extends Mixed { 298 356 protected protected(condition?: boolean): this { 299 357 const cond = arguments.length === 0 ? true : Boolean(condition); 300 - return this._m(ts.SyntaxKind.ProtectedKeyword, cond); 358 + return this._m('protected', cond); 301 359 } 302 360 } 303 361 ··· 325 383 abstract class Public extends Mixed { 326 384 protected public(condition?: boolean): this { 327 385 const cond = arguments.length === 0 ? true : Boolean(condition); 328 - return this._m(ts.SyntaxKind.PublicKeyword, cond); 386 + return this._m('public', cond); 329 387 } 330 388 } 331 389 ··· 353 411 abstract class Readonly extends Mixed { 354 412 protected readonly(condition?: boolean): this { 355 413 const cond = arguments.length === 0 ? true : Boolean(condition); 356 - return this._m(ts.SyntaxKind.ReadonlyKeyword, cond); 414 + return this._m('readonly', cond); 357 415 } 358 416 } 359 417 ··· 381 439 abstract class Static extends Mixed { 382 440 protected static(condition?: boolean): this { 383 441 const cond = arguments.length === 0 ? true : Boolean(condition); 384 - return this._m(ts.SyntaxKind.StaticKeyword, cond); 442 + return this._m('static', cond); 385 443 } 386 444 } 387 445
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/note.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { NoteFn, NoteLines } from '../layout/note'; ··· 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 8 export interface NoteMethods extends Node { 9 - $note<T extends ts.Node>(ctx: AstContext, node: T): T; 9 + $note<T extends ts.Node>(node: T): T; 10 10 note(lines?: NoteLines, fn?: NoteFn): this; 11 11 } 12 12 ··· 25 25 return this; 26 26 } 27 27 28 - protected $note<T extends ts.Node>(ctx: AstContext, node: T): T { 29 - return this._note ? this._note.apply(ctx, node) : node; 28 + protected $note<T extends ts.Node>(node: T): T { 29 + return this._note ? this._note.apply(node) : node; 30 30 } 31 31 } 32 32
+4 -9
packages/openapi-ts/src/ts-dsl/mixins/param.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Node, 5 - NodeName, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 7 2 import type ts from 'typescript'; 8 3 9 4 import type { MaybeTsDsl } from '../base'; ··· 13 8 14 9 export interface ParamMethods extends Node { 15 10 /** Renders the parameters into an array of `ParameterDeclaration`s. */ 16 - $params(ast: AstContext): ReadonlyArray<ts.ParameterDeclaration>; 11 + $params(): ReadonlyArray<ts.ParameterDeclaration>; 17 12 /** Adds a parameter. */ 18 13 param(...args: Parameters<ParamCtor>): this; 19 14 /** Adds multiple parameters. */ ··· 49 44 return this; 50 45 } 51 46 52 - protected $params(ctx: AstContext): ReadonlyArray<ts.ParameterDeclaration> { 53 - return this.$node(ctx, this._params); 47 + protected $params(): ReadonlyArray<ts.ParameterDeclaration> { 48 + return this.$node(this._params); 54 49 } 55 50 } 56 51
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/pattern.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; ··· 7 7 8 8 export interface PatternMethods extends Node { 9 9 /** Renders the pattern into a `BindingName`. */ 10 - $pattern(ctx: AstContext): ts.BindingName | undefined; 10 + $pattern(): ts.BindingName | undefined; 11 11 /** Defines an array binding pattern. */ 12 12 array(...props: ReadonlyArray<string> | [ReadonlyArray<string>]): this; 13 13 /** Defines an object binding pattern. */ ··· 53 53 } 54 54 55 55 /** Renders the pattern into a `BindingName`. */ 56 - protected $pattern(ctx: AstContext): ts.BindingName | undefined { 56 + protected $pattern(): ts.BindingName | undefined { 57 57 if (!this.pattern) return; 58 - return this.$node(ctx, this.pattern); 58 + return this.$node(this.pattern); 59 59 } 60 60 } 61 61
+3 -6
packages/openapi-ts/src/ts-dsl/mixins/type-args.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 Node, 5 4 NodeName, 6 5 Ref, ··· 15 14 16 15 export interface TypeArgsMethods extends Node { 17 16 /** Returns the type arguments as an array of ts.TypeNode nodes. */ 18 - $generics(ctx: AstContext): ReadonlyArray<ts.TypeNode> | undefined; 17 + $generics(): ReadonlyArray<ts.TypeNode> | undefined; 19 18 /** Adds a single type argument (e.g. `string` in `Foo<string>`). */ 20 19 generic(arg: Arg): this; 21 20 /** Adds type arguments (e.g. `Map<string, number>`). */ ··· 45 44 return this; 46 45 } 47 46 48 - protected $generics( 49 - ctx: AstContext, 50 - ): ReadonlyArray<ts.TypeNode> | undefined { 51 - return this.$type(ctx, this._generics); 47 + protected $generics(): ReadonlyArray<ts.TypeNode> | undefined { 48 + return this.$type(this._generics); 52 49 } 53 50 } 54 51
+6 -13
packages/openapi-ts/src/ts-dsl/mixins/type-params.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Node, 5 - NodeName, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 7 2 import { isRef, isSymbol } from '@hey-api/codegen-core'; 8 3 import type ts from 'typescript'; 9 4 ··· 13 8 14 9 export interface TypeParamsMethods extends Node { 15 10 /** Returns the type parameters as an array of ts.TypeParameterDeclaration nodes. */ 16 - $generics( 17 - ast: AstContext, 18 - ): ReadonlyArray<ts.TypeParameterDeclaration> | undefined; 11 + $generics(): ReadonlyArray<ts.TypeParameterDeclaration> | undefined; 19 12 /** Adds a single type parameter (e.g. `T` in `Array<T>`). */ 20 13 generic(...args: ConstructorParameters<typeof TypeParamTsDsl>): this; 21 14 /** Adds type parameters (e.g. `Map<string, T>`). */ ··· 60 53 return this; 61 54 } 62 55 63 - protected $generics( 64 - ctx: AstContext, 65 - ): ReadonlyArray<ts.TypeParameterDeclaration> | undefined { 66 - return this.$node(ctx, this._generics); 56 + protected $generics(): 57 + | ReadonlyArray<ts.TypeParameterDeclaration> 58 + | undefined { 59 + return this.$node(this._generics); 67 60 } 68 61 } 69 62
+4 -9
packages/openapi-ts/src/ts-dsl/mixins/type-returns.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Node, 5 - NodeName, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 7 2 import type ts from 'typescript'; 8 3 9 4 import { TypeTsDsl } from '../base'; ··· 12 7 13 8 export interface TypeReturnsMethods extends Node { 14 9 /** Returns the return type node. */ 15 - $returns(ctx: AstContext): ts.TypeNode | undefined; 10 + $returns(): ts.TypeNode | undefined; 16 11 /** Sets the return type. */ 17 12 returns(type: NodeName | TypeTsDsl): this; 18 13 } ··· 34 29 return this; 35 30 } 36 31 37 - protected $returns(ctx: AstContext): ts.TypeNode | undefined { 38 - return this.$type(ctx, this._returns); 32 + protected $returns(): ts.TypeNode | undefined { 33 + return this.$type(this._returns); 39 34 } 40 35 } 41 36
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/value.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 7 7 export type ValueExpr = string | MaybeTsDsl<ts.Expression>; 8 8 9 9 export interface ValueMethods extends Node { 10 - $value(ctx: AstContext): ts.Expression | undefined; 10 + $value(): ts.Expression | undefined; 11 11 /** Sets the initializer expression (e.g. `= expr`). */ 12 12 assign(expr: ValueExpr): this; 13 13 } ··· 28 28 return this; 29 29 } 30 30 31 - protected $value(ctx: AstContext): ts.Expression | undefined { 32 - return this.$node(ctx, this.value); 31 + protected $value(): ts.Expression | undefined { 32 + return this.$node(this.value); 33 33 } 34 34 } 35 35
+5 -5
packages/openapi-ts/src/ts-dsl/render/__tests__/typescript.test.ts packages/openapi-ts/src/ts-dsl/utils/__tests__/render.test.ts
··· 3 3 import ts from 'typescript'; 4 4 import { describe, expect, it } from 'vitest'; 5 5 6 - import { astContext, type TsDsl } from '~/ts-dsl'; 6 + import type { TsDsl } from '~/ts-dsl'; 7 7 8 - import { TypeScriptRenderer } from '../typescript'; 9 - import type { ModuleExport, ModuleImport } from '../utils'; 8 + import { TypeScriptRenderer } from '../render'; 9 + import type { ModuleExport, ModuleImport } from '../render-utils'; 10 10 11 11 describe('TypeScriptRenderer', () => { 12 12 const renderer = new TypeScriptRenderer(); ··· 57 57 modulePath: 'foo', 58 58 namespaceImport: undefined, 59 59 }; 60 - const node = renderer['renderImport'](astContext, group); 60 + const node = renderer['renderImport'](group); 61 61 expect(ts.isImportDeclaration(node)).toBe(true); 62 62 }); 63 63 ··· 76 76 modulePath: 'bar', 77 77 namespaceExport: undefined, 78 78 }; 79 - const node = renderer['renderExport'](astContext, group); 79 + const node = renderer['renderExport'](group); 80 80 expect(ts.isExportDeclaration(node)).toBe(true); 81 81 }); 82 82 });
+114 -82
packages/openapi-ts/src/ts-dsl/render/typescript.ts packages/openapi-ts/src/ts-dsl/utils/render.ts
··· 1 - import type { 2 - AstContext, 3 - RenderContext, 4 - Renderer, 5 - } from '@hey-api/codegen-core'; 1 + import type { RenderContext, Renderer } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { TsDsl } from '~/ts-dsl'; 9 - import { $, astContext } from '~/ts-dsl'; 5 + import { $ } from '~/ts-dsl'; 10 6 11 7 import type { 12 8 ModuleExport, ··· 14 10 SortGroup, 15 11 SortKey, 16 12 SortModule, 17 - } from './utils'; 18 - import { moduleSortKey, nodeToString } from './utils'; 13 + } from './render-utils'; 14 + import { astToString, moduleSortKey } from './render-utils'; 15 + 16 + type Exports = ReadonlyArray<ReadonlyArray<ModuleExport>>; 17 + type ExportsOptions = { 18 + preferExportAll?: boolean; 19 + }; 20 + type Imports = ReadonlyArray<ReadonlyArray<ModuleImport>>; 19 21 20 22 export class TypeScriptRenderer implements Renderer { 21 23 /** ··· 50 52 } 51 53 52 54 render(ctx: RenderContext<TsDsl>): string { 53 - let text = ''; 55 + // TODO: move header customization to options 54 56 const header = '// This file is auto-generated by @hey-api/openapi-ts'; 55 - text += `${header}\n`; 57 + return TypeScriptRenderer.astToString({ 58 + exports: this.getExports(ctx), 59 + exportsOptions: { 60 + preferExportAll: this.preferExportAll, 61 + }, 62 + headers: [header], 63 + imports: this.getImports(ctx), 64 + nodes: ctx.file.nodes, 65 + }); 66 + } 67 + 68 + supports(ctx: RenderContext): boolean { 69 + return ctx.file.language === 'typescript'; 70 + } 71 + 72 + static astToString(args: { 73 + exports?: Exports; 74 + exportsOptions?: ExportsOptions; 75 + headers?: ReadonlyArray<string>; 76 + imports?: Imports; 77 + nodes?: ReadonlyArray<TsDsl>; 78 + /** 79 + * Whether to include a trailing newline at the end of the file. 80 + * 81 + * @default true 82 + */ 83 + trailingNewline?: boolean; 84 + }): string { 85 + let text = ''; 86 + for (const header of args.headers ?? []) { 87 + text += `${header}\n`; 88 + } 56 89 57 90 let imports = ''; 58 - for (const group of this.getImports(ctx)) { 91 + for (const group of args.imports ?? []) { 59 92 if (imports) imports += '\n'; 60 93 for (const imp of group) { 61 - imports += `${nodeToString(this.renderImport(astContext, imp))}\n`; 94 + imports += `${astToString(TypeScriptRenderer.toImportAst(imp))}\n`; 62 95 } 63 96 } 64 97 text = `${text}${text && imports ? '\n' : ''}${imports}`; 65 98 66 99 let nodes = ''; 67 - for (const node of ctx.file.nodes) { 100 + for (const node of args.nodes ?? []) { 68 101 if (nodes) nodes += '\n'; 69 - nodes += `${nodeToString(node.toAst(astContext))}\n`; 102 + nodes += `${astToString(node.toAst())}\n`; 70 103 } 71 104 text = `${text}${text && nodes ? '\n' : ''}${nodes}`; 72 105 73 106 let exports = ''; 74 - for (const group of this.getExports(ctx)) { 107 + for (const group of args.exports ?? []) { 75 108 if ((!exports && nodes) || exports) exports += '\n'; 76 109 for (const exp of group) { 77 - exports += `${nodeToString(this.renderExport(astContext, exp))}\n`; 110 + exports += `${astToString(TypeScriptRenderer.toExportAst(exp, args.exportsOptions))}\n`; 78 111 } 79 112 } 80 113 text = `${text}${text && exports ? '\n' : ''}${exports}`; 81 114 115 + if (args.trailingNewline === false && text.endsWith('\n')) { 116 + text = text.slice(0, -1); 117 + } 118 + 82 119 return text; 83 120 } 84 121 85 - supports(ctx: RenderContext): boolean { 86 - return ctx.file.language === 'typescript'; 122 + static toExportAst( 123 + group: ModuleExport, 124 + options?: ExportsOptions, 125 + ): ts.ExportDeclaration { 126 + const specifiers = group.exports.map((exp) => { 127 + const specifier = ts.factory.createExportSpecifier( 128 + exp.isTypeOnly, 129 + exp.sourceName !== exp.exportedName 130 + ? $.id(exp.sourceName).toAst() 131 + : undefined, 132 + $.id(exp.exportedName).toAst(), 133 + ); 134 + return specifier; 135 + }); 136 + const exportClause = group.namespaceExport 137 + ? ts.factory.createNamespaceExport($.id(group.namespaceExport).toAst()) 138 + : (!group.canExportAll || !options?.preferExportAll) && specifiers.length 139 + ? ts.factory.createNamedExports(specifiers) 140 + : undefined; 141 + return ts.factory.createExportDeclaration( 142 + undefined, 143 + group.isTypeOnly, 144 + exportClause, 145 + $.literal(group.modulePath).toAst(), 146 + ); 87 147 } 88 148 89 - private getExports( 90 - ctx: RenderContext, 91 - ): ReadonlyArray<ReadonlyArray<ModuleExport>> { 149 + static toImportAst(group: ModuleImport): ts.ImportDeclaration { 150 + const specifiers = group.imports.map((imp) => { 151 + const specifier = ts.factory.createImportSpecifier( 152 + imp.isTypeOnly, 153 + imp.sourceName !== imp.localName 154 + ? $.id(imp.sourceName).toAst() 155 + : undefined, 156 + $.id(imp.localName).toAst(), 157 + ); 158 + return specifier; 159 + }); 160 + const importClause = ts.factory.createImportClause( 161 + group.isTypeOnly, 162 + group.kind === 'default' 163 + ? $.id(group.localName ?? '').toAst() 164 + : undefined, 165 + group.kind === 'namespace' 166 + ? ts.factory.createNamespaceImport($.id(group.localName ?? '').toAst()) 167 + : specifiers.length > 0 168 + ? ts.factory.createNamedImports(specifiers) 169 + : undefined, 170 + ); 171 + return ts.factory.createImportDeclaration( 172 + undefined, 173 + importClause, 174 + $.literal(group.modulePath).toAst(), 175 + ); 176 + } 177 + 178 + private getExports(ctx: RenderContext): Exports { 92 179 type ModuleEntry = { 93 180 group: ModuleExport; 94 181 sortKey: SortKey; ··· 158 245 return exports; 159 246 } 160 247 161 - private getImports( 162 - ctx: RenderContext, 163 - ): ReadonlyArray<ReadonlyArray<ModuleImport>> { 248 + private getImports(ctx: RenderContext): Imports { 164 249 type ModuleEntry = { 165 250 group: ModuleImport; 166 251 sortKey: SortKey; ··· 186 271 group: { 187 272 imports: [], 188 273 isTypeOnly: false, 274 + kind: imp.kind, 189 275 modulePath, 190 276 }, 191 277 sortKey, ··· 195 281 const entry = moduleMap.get(modulePath)!; 196 282 const group = entry.group; 197 283 198 - if (imp.namespaceImport) { 284 + if (imp.kind !== 'named') { 199 285 group.isTypeOnly = imp.isTypeOnly; 200 - group.namespaceImport = imp.namespaceImport; 286 + group.kind = imp.kind; 287 + group.localName = imp.localName; 201 288 } else { 202 289 group.imports.push(...imp.imports); 203 290 } ··· 217 304 218 305 return entries.map((e) => { 219 306 const group = e.group; 220 - if (group.namespaceImport) { 307 + if (group.kind === 'namespace') { 221 308 group.imports = []; 222 309 } else { 223 310 const isTypeOnly = !group.imports.find((imp) => !imp.isTypeOnly); ··· 236 323 }); 237 324 238 325 return imports; 239 - } 240 - 241 - private renderExport( 242 - ctx: AstContext, 243 - group: ModuleExport, 244 - ): ts.ExportDeclaration { 245 - const specifiers = group.exports.map((exp) => { 246 - const specifier = ts.factory.createExportSpecifier( 247 - exp.isTypeOnly, 248 - exp.sourceName !== exp.exportedName 249 - ? $.id(exp.sourceName).toAst() 250 - : undefined, 251 - $.id(exp.exportedName).toAst(), 252 - ); 253 - return specifier; 254 - }); 255 - const exportClause = group.namespaceExport 256 - ? ts.factory.createNamespaceExport($.id(group.namespaceExport).toAst()) 257 - : (!group.canExportAll || !this.preferExportAll) && specifiers.length 258 - ? ts.factory.createNamedExports(specifiers) 259 - : undefined; 260 - return ts.factory.createExportDeclaration( 261 - undefined, 262 - group.isTypeOnly, 263 - exportClause, 264 - $.literal(group.modulePath).toAst(ctx), 265 - ); 266 - } 267 - 268 - private renderImport( 269 - ctx: AstContext, 270 - group: ModuleImport, 271 - ): ts.ImportDeclaration { 272 - const specifiers = group.imports.map((imp) => { 273 - const specifier = ts.factory.createImportSpecifier( 274 - imp.isTypeOnly, 275 - imp.sourceName !== imp.localName 276 - ? $.id(imp.sourceName).toAst() 277 - : undefined, 278 - $.id(imp.localName).toAst(), 279 - ); 280 - return specifier; 281 - }); 282 - const importClause = ts.factory.createImportClause( 283 - group.isTypeOnly, 284 - undefined, // TODO: default imports 285 - group.namespaceImport 286 - ? ts.factory.createNamespaceImport($.id(group.namespaceImport).toAst()) 287 - : ts.factory.createNamedImports(specifiers), 288 - ); 289 - return ts.factory.createImportDeclaration( 290 - undefined, 291 - importClause, 292 - $.literal(group.modulePath).toAst(ctx), 293 - ); 294 326 } 295 327 }
+1 -1
packages/openapi-ts/src/ts-dsl/render/utils.ts packages/openapi-ts/src/ts-dsl/utils/render-utils.ts
··· 17 17 ); 18 18 19 19 /** Print a TypeScript node to a string. */ 20 - export function nodeToString(node: ts.Node): string { 20 + export function astToString(node: ts.Node): string { 21 21 const result = printer.printNode(ts.EmitHint.Unspecified, node, blankFile); 22 22 23 23 try {
+3 -3
packages/openapi-ts/src/ts-dsl/stmt/block.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 20 20 super.analyze(ctx); 21 21 } 22 22 23 - override toAst(ctx: AstContext) { 24 - const statements = this.$do(ctx); 23 + override toAst() { 24 + const statements = this.$do(); 25 25 return ts.factory.createBlock( 26 26 statements, 27 27 this.$multiline(statements.length),
+5 -5
packages/openapi-ts/src/ts-dsl/stmt/if.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 47 47 return this; 48 48 } 49 49 50 - override toAst(ctx: AstContext) { 50 + override toAst() { 51 51 if (!this._condition) throw new Error('Missing condition in if'); 52 52 if (!this._do) throw new Error('Missing then block in if'); 53 53 54 54 return ts.factory.createIfStatement( 55 - this.$node(ctx, this._condition), 56 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 55 + this.$node(this._condition), 56 + this.$node(new BlockTsDsl(...this._do).pretty()), 57 57 this._else 58 - ? this.$node(ctx, new BlockTsDsl(...this._else).pretty()) 58 + ? this.$node(new BlockTsDsl(...this._else).pretty()) 59 59 : undefined, 60 60 ); 61 61 }
+3 -8
packages/openapi-ts/src/ts-dsl/stmt/return.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - Ref, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 31 26 ctx.analyze(this._returnExpr); 32 27 } 33 28 34 - override toAst(ctx: AstContext) { 35 - return ts.factory.createReturnStatement(this.$node(ctx, this._returnExpr)); 29 + override toAst() { 30 + return ts.factory.createReturnStatement(this.$node(this._returnExpr)); 36 31 } 37 32 } 38 33
+3 -3
packages/openapi-ts/src/ts-dsl/stmt/stmt.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 20 20 ctx.analyze(this._inner); 21 21 } 22 22 23 - override toAst(ctx: AstContext) { 24 - const node = this.$node(ctx, this._inner); 23 + override toAst() { 24 + const node = this.$node(this._inner); 25 25 return ts.isStatement(node) 26 26 ? node 27 27 : ts.factory.createExpressionStatement(node);
+5 -8
packages/openapi-ts/src/ts-dsl/stmt/throw.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 31 31 return this; 32 32 } 33 33 34 - override toAst(ctx: AstContext) { 35 - const errorNode = this.$node(ctx, this.error); 36 - const messageNode = this.$node(ctx, this.msg ? [this.msg] : []).map( 37 - (expr) => 38 - typeof expr === 'string' 39 - ? this.$node(ctx, new LiteralTsDsl(expr)) 40 - : expr, 34 + override toAst() { 35 + const errorNode = this.$node(this.error); 36 + const messageNode = this.$node(this.msg ? [this.msg] : []).map((expr) => 37 + typeof expr === 'string' ? this.$node(new LiteralTsDsl(expr)) : expr, 41 38 ); 42 39 if (this.useNew) { 43 40 return ts.factory.createThrowStatement(
+6 -10
packages/openapi-ts/src/ts-dsl/stmt/try.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import { TsDsl } from '../base'; ··· 78 74 return this; 79 75 } 80 76 81 - override toAst(ctx: AstContext) { 77 + override toAst() { 82 78 if (!this._try?.length) throw new Error('Missing try block'); 83 79 84 80 const catchParam = this._catchArg 85 - ? (this.$node(ctx, this._catchArg) as ts.BindingName) 81 + ? (this.$node(this._catchArg) as ts.BindingName) 86 82 : undefined; 87 83 88 84 return ts.factory.createTryStatement( 89 - this.$node(ctx, new BlockTsDsl(...this._try).pretty()), 85 + this.$node(new BlockTsDsl(...this._try).pretty()), 90 86 ts.factory.createCatchClause( 91 87 catchParam 92 88 ? ts.factory.createVariableDeclaration(catchParam) 93 89 : undefined, 94 - this.$node(ctx, new BlockTsDsl(...(this._catch ?? [])).pretty()), 90 + this.$node(new BlockTsDsl(...(this._catch ?? [])).pretty()), 95 91 ), 96 92 this._finally 97 - ? this.$node(ctx, new BlockTsDsl(...this._finally).pretty()) 93 + ? this.$node(new BlockTsDsl(...this._finally).pretty()) 98 94 : undefined, 99 95 ); 100 96 }
+6 -10
packages/openapi-ts/src/ts-dsl/stmt/var.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeName, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import { isSymbol } from '@hey-api/codegen-core'; 7 3 import ts from 'typescript'; 8 4 ··· 66 62 return this; 67 63 } 68 64 69 - override toAst(ctx: AstContext) { 70 - const name = this.$pattern(ctx) ?? this.$node(ctx, this.name); 65 + override toAst() { 66 + const name = this.$pattern() ?? this.$node(this.name); 71 67 if (!name) 72 68 throw new Error('Var must have either a name or a destructuring pattern'); 73 69 const node = ts.factory.createVariableStatement( ··· 77 73 ts.factory.createVariableDeclaration( 78 74 name as ts.BindingName, 79 75 undefined, 80 - this.$type(ctx, this._type), 81 - this.$value(ctx), 76 + this.$type(this._type), 77 + this.$value(), 82 78 ), 83 79 ], 84 80 this.kind, 85 81 ), 86 82 ); 87 - return this.$docs(ctx, this.$hint(ctx, node)); 83 + return this.$docs(this.$hint(node)); 88 84 } 89 85 }
+5 -6
packages/openapi-ts/src/ts-dsl/type/alias.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 NodeName, 5 4 NodeScope, 6 5 } from '@hey-api/codegen-core'; ··· 49 48 return this; 50 49 } 51 50 52 - override toAst(ctx: AstContext) { 51 + override toAst() { 53 52 if (!this.value) 54 53 throw new Error( 55 54 `Type alias '${this.name.toString()}' is missing a type definition`, 56 55 ); 57 56 const node = ts.factory.createTypeAliasDeclaration( 58 57 this.modifiers, 59 - this.$node(ctx, this.name) as ts.Identifier, 60 - this.$generics(ctx), 61 - this.$type(ctx, this.value), 58 + this.$node(this.name) as ts.Identifier, 59 + this.$generics(), 60 + this.$type(this.value), 62 61 ); 63 - return this.$docs(ctx, node); 62 + return this.$docs(node); 64 63 } 65 64 }
+2 -3
packages/openapi-ts/src/ts-dsl/type/and.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 NodeName, 5 4 NodeScope, 6 5 Ref, ··· 38 37 return this; 39 38 } 40 39 41 - override toAst(ctx: AstContext) { 40 + override toAst() { 42 41 const flat: Array<ts.TypeNode> = []; 43 42 44 43 for (const node of this._types) { 45 - const type = this.$type(ctx, node); 44 + const type = this.$type(node); 46 45 if (ts.isIntersectionTypeNode(type)) { 47 46 flat.push(...type.types); 48 47 } else {
+3 -4
packages/openapi-ts/src/ts-dsl/type/attr.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 NodeName, 5 4 NodeScope, 6 5 Ref, ··· 57 56 return this; 58 57 } 59 58 60 - override toAst(ctx: AstContext) { 59 + override toAst() { 61 60 if (!this._base) { 62 61 throw new Error('TypeAttrTsDsl: missing base for qualified name'); 63 62 } 64 - const left = this.$node(ctx, this._base); 63 + const left = this.$node(this._base); 65 64 if (!ts.isEntityName(left)) { 66 65 throw new Error('TypeAttrTsDsl: base must be an EntityName'); 67 66 } 68 67 return ts.factory.createQualifiedName( 69 68 left, 70 - this.$node(ctx, this._right) as ts.Identifier, 69 + this.$node(this._right) as ts.Identifier, 71 70 ); 72 71 } 73 72 }
+3 -4
packages/openapi-ts/src/ts-dsl/type/expr.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 NodeName, 5 4 NodeScope, 6 5 Ref, ··· 56 55 return this; 57 56 } 58 57 59 - override toAst(ctx: AstContext) { 58 + override toAst() { 60 59 if (!this._exprInput) throw new Error('TypeExpr must have an expression'); 61 60 return ts.factory.createTypeReferenceNode( 62 - this.$type(ctx, this._exprInput) as ts.EntityName, 63 - this.$generics(ctx), 61 + this.$type(this._exprInput) as ts.EntityName, 62 + this.$generics(), 64 63 ); 65 64 } 66 65 }
+6 -10
packages/openapi-ts/src/ts-dsl/type/func.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeScope, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import { TsDsl } from '../base'; ··· 23 19 super.analyze(ctx); 24 20 } 25 21 26 - override toAst(ctx: AstContext) { 27 - const returns = this.$returns(ctx); 22 + override toAst() { 23 + const returns = this.$returns(); 28 24 if (returns === undefined) { 29 25 throw new Error('Missing return type in function type DSL'); 30 26 } 31 27 const node = ts.factory.createFunctionTypeNode( 32 - this.$generics(ctx), 33 - this.$params(ctx), 28 + this.$generics(), 29 + this.$params(), 34 30 returns, 35 31 ); 36 - return this.$docs(ctx, node); 32 + return this.$docs(node); 37 33 } 38 34 }
+5 -6
packages/openapi-ts/src/ts-dsl/type/idx-sig.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 NodeName, 5 4 NodeScope, 6 5 } from '@hey-api/codegen-core'; ··· 51 50 return this; 52 51 } 53 52 54 - override toAst(ctx: AstContext) { 53 + override toAst() { 55 54 this.$validate(); 56 55 const node = ts.factory.createIndexSignature( 57 56 this.modifiers, ··· 59 58 ts.factory.createParameterDeclaration( 60 59 undefined, 61 60 undefined, 62 - this.$node(ctx, this.name) as ts.BindingName, 61 + this.$node(this.name) as ts.BindingName, 63 62 undefined, 64 - this.$type(ctx, this._key), 63 + this.$type(this._key), 65 64 ), 66 65 ], 67 - this.$type(ctx, this._type), 66 + this.$type(this._type), 68 67 ); 69 - return this.$docs(ctx, node); 68 + return this.$docs(node); 70 69 } 71 70 72 71 $validate(): asserts this is this & {
+4 -8
packages/openapi-ts/src/ts-dsl/type/idx.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeScope, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { MaybeTsDsl } from '../base'; ··· 45 41 return this; 46 42 } 47 43 48 - override toAst(ctx: AstContext) { 44 + override toAst() { 49 45 return ts.factory.createIndexedAccessTypeNode( 50 - this.$type(ctx, this._base), 51 - this.$type(ctx, this._index), 46 + this.$type(this._base), 47 + this.$type(this._index), 52 48 ); 53 49 } 54 50 }
+3 -7
packages/openapi-ts/src/ts-dsl/type/literal.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeScope, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import { TsDsl } from '../base'; ··· 25 21 super.analyze(ctx); 26 22 } 27 23 28 - override toAst(ctx: AstContext) { 24 + override toAst() { 29 25 return ts.factory.createLiteralTypeNode( 30 - this.$node(ctx, new LiteralTsDsl(this.value)), 26 + this.$node(new LiteralTsDsl(this.value)), 31 27 ); 32 28 } 33 29 }
+6 -7
packages/openapi-ts/src/ts-dsl/type/mapped.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 NodeName, 5 4 NodeScope, 6 5 } from '@hey-api/codegen-core'; ··· 83 82 return this; 84 83 } 85 84 86 - override toAst(ctx: AstContext) { 85 + override toAst() { 87 86 this.$validate(); 88 87 return ts.factory.createMappedTypeNode( 89 - this.$node(ctx, this.readonlyToken), 88 + this.$node(this.readonlyToken), 90 89 ts.factory.createTypeParameterDeclaration( 91 90 undefined, 92 - this.$node(ctx, this.name) as ts.Identifier, 93 - this.$type(ctx, this._key), 91 + this.$node(this.name) as ts.Identifier, 92 + this.$type(this._key), 94 93 undefined, 95 94 ), 96 95 undefined, 97 - this.$node(ctx, this.questionToken), 98 - this.$type(ctx, this._type), 96 + this.$node(this.questionToken), 97 + this.$type(this._type), 99 98 undefined, 100 99 ); 101 100 }
+3 -7
packages/openapi-ts/src/ts-dsl/type/object.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeScope, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import { TsDsl } from '../base'; ··· 48 44 return this; 49 45 } 50 46 51 - override toAst(ctx: AstContext) { 52 - return ts.factory.createTypeLiteralNode(this.$node(ctx, this.props)); 47 + override toAst() { 48 + return ts.factory.createTypeLiteralNode(this.$node(this.props)); 53 49 } 54 50 }
+3 -10
packages/openapi-ts/src/ts-dsl/type/operator.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeScope, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { MaybeTsDsl } from '../base'; ··· 75 71 return this; 76 72 } 77 73 78 - override toAst(ctx: AstContext) { 74 + override toAst() { 79 75 this.$validate(); 80 - return ts.factory.createTypeOperatorNode( 81 - this._op, 82 - this.$type(ctx, this._type), 83 - ); 76 + return ts.factory.createTypeOperatorNode(this._op, this.$type(this._type)); 84 77 } 85 78 86 79 /** Throws if required fields are not set. */
+2 -3
packages/openapi-ts/src/ts-dsl/type/or.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 NodeName, 5 4 NodeScope, 6 5 Ref, ··· 38 37 return this; 39 38 } 40 39 41 - override toAst(ctx: AstContext) { 40 + override toAst() { 42 41 const flat: Array<ts.TypeNode> = []; 43 42 44 43 for (const node of this._types) { 45 - const type = this.$type(ctx, node); 44 + const type = this.$type(node); 46 45 if (ts.isUnionTypeNode(type)) { 47 46 flat.push(...type.types); 48 47 } else {
+4 -5
packages/openapi-ts/src/ts-dsl/type/param.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 NodeName, 5 4 NodeScope, 6 5 Ref, ··· 45 44 return this; 46 45 } 47 46 48 - override toAst(ctx: AstContext) { 47 + override toAst() { 49 48 const name = this.name.toString(); 50 49 if (!name) throw new Error('Missing type name'); 51 50 return ts.factory.createTypeParameterDeclaration( 52 51 undefined, 53 - this.$node(ctx, this.name) as ts.Identifier, 54 - this.$type(ctx, this.constraint), 55 - this.$type(ctx, this.defaultValue), 52 + this.$node(this.name) as ts.Identifier, 53 + this.$type(this.constraint), 54 + this.$type(this.defaultValue), 56 55 ); 57 56 } 58 57 }
+5 -6
packages/openapi-ts/src/ts-dsl/type/prop.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 NodeName, 5 4 NodeScope, 6 5 Ref, ··· 43 42 return this; 44 43 } 45 44 46 - override toAst(ctx: AstContext) { 45 + override toAst() { 47 46 const name = this.name.toString(); 48 47 if (!this._type || !name) { 49 48 throw new Error(`Type not specified for property '${name}'`); 50 49 } 51 50 const node = ts.factory.createPropertySignature( 52 51 this.modifiers, 53 - this.$node(ctx, safePropName(name)), 54 - this._optional ? this.$node(ctx, new TokenTsDsl().optional()) : undefined, 55 - this.$type(ctx, this._type), 52 + this.$node(safePropName(name)), 53 + this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, 54 + this.$type(this._type), 56 55 ); 57 - return this.$docs(ctx, node); 56 + return this.$docs(node); 58 57 } 59 58 }
+3 -7
packages/openapi-ts/src/ts-dsl/type/query.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeScope, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { MaybeTsDsl, TypeTsDsl } from '../base'; ··· 31 27 ctx.analyze(this._expr); 32 28 } 33 29 34 - override toAst(ctx: AstContext) { 35 - const expr = this.$node(ctx, this._expr); 30 + override toAst() { 31 + const expr = this.$node(this._expr); 36 32 return ts.factory.createTypeQueryNode(expr as unknown as ts.EntityName); 37 33 } 38 34 }
+3 -7
packages/openapi-ts/src/ts-dsl/type/template.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeScope, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { MaybeTsDsl } from '../base'; ··· 34 30 return this; 35 31 } 36 32 37 - override toAst(ctx: AstContext) { 38 - const parts = this.$node(ctx, this.parts); 33 + override toAst() { 34 + const parts = this.$node(this.parts); 39 35 40 36 const normalized: Array<string | ts.TypeNode> = []; 41 37 // merge consecutive string parts
+3 -7
packages/openapi-ts/src/ts-dsl/type/tuple.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - NodeScope, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { TypeTsDsl } from '../base'; ··· 33 29 return this; 34 30 } 35 31 36 - override toAst(ctx: AstContext) { 32 + override toAst() { 37 33 return ts.factory.createTupleTypeNode( 38 - this._elements.map((t) => this.$type(ctx, t)), 34 + this._elements.map((t) => this.$type(t)), 39 35 ); 40 36 } 41 37 }
+224 -83
packages/openapi-ts/src/ts-dsl/utils/context.ts
··· 1 - import type { 2 - AccessPatternOptions, 3 - AstContext, 4 - NodeScope, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { BindingKind, NodeScope, Symbol } from '@hey-api/codegen-core'; 7 2 import { isSymbol } from '@hey-api/codegen-core'; 3 + import type ts from 'typescript'; 8 4 9 - import { $ } from '~/ts-dsl'; 5 + import { $, TypeScriptRenderer } from '~/ts-dsl'; 10 6 11 7 import type { TsDsl } from '../base'; 8 + import type { CallArgs } from '../expr/call'; 12 9 13 - const getScope = (node: TsDsl): NodeScope => node.scope ?? 'value'; 10 + export type NodeChain = ReadonlyArray<TsDsl>; 14 11 15 - function traverseStructuralParent(node: TsDsl): boolean { 16 - if (node.role === 'literal') { 17 - return false; 18 - } 19 - return true; 12 + export interface AccessOptions { 13 + /** The access context. */ 14 + context?: 'example'; 20 15 } 21 16 22 - function foldAccessChain<Node extends TsDsl = TsDsl>( 23 - chain: ReadonlyArray<Node>, 24 - ): ReadonlyArray<Node> { 25 - const folded: Array<Node> = []; 17 + export type AccessResult = ReturnType< 18 + typeof $.expr | typeof $.attr | typeof $.call | typeof $.new 19 + >; 20 + 21 + export interface ExampleOptions { 22 + /** Import kind for the root node. */ 23 + importKind?: BindingKind; 24 + /** Import name for the root node. */ 25 + importName?: string; 26 + /** Setup to run before calling the example. */ 27 + importSetup?: 28 + | TsDsl<ts.Expression> 29 + | ((imp: TsDsl<ts.Expression>) => TsDsl<ts.Expression>); 30 + /** Module to import from. */ 31 + moduleName: string; 32 + /** Example request payload. */ 33 + payload?: CallArgs | CallArgs[number]; 34 + /** Variable name for setup node. */ 35 + setupName?: string; 36 + } 26 37 27 - for (const node of chain) { 28 - if (folded.length === 0) { 29 - if (node.role === 'container') { 30 - folded.push(node); 31 - } 32 - } else if (node.role === 'accessor') { 33 - folded.push(node); 38 + function accessChainToNode<T = AccessResult>(accessChain: NodeChain): T { 39 + let result!: AccessResult; 40 + accessChain.forEach((node, index) => { 41 + if (index === 0) { 42 + // assume correct node 43 + result = node as typeof result; 44 + } else { 45 + result = result.attr(node.name); 34 46 } 47 + }); 48 + return result as T; 49 + } 50 + 51 + function getAccessChainForNode(node: TsDsl): NodeChain { 52 + const structuralChain = [...getStructuralChainForNode(node, new Set())]; 53 + const accessChain = structuralToAccessChain(structuralChain); 54 + if (accessChain.length === 0) { 55 + throw new Error( 56 + `Cannot build access chain for node ${node['~dsl']} (${node.name.toString()})`, 57 + ); 35 58 } 59 + return accessChain.map((node) => node.clone()); 60 + } 36 61 37 - return folded; 62 + function getScope(node: TsDsl): NodeScope { 63 + return node.scope ?? 'value'; 38 64 } 39 65 40 - function getAccessChain<Node extends TsDsl = TsDsl>( 41 - node: Node, 42 - ): ReadonlyArray<Node> { 43 - const chain: Array<TsDsl> = []; 44 - const scope: NodeScope = getScope(node); 45 - const visited = new Set<TsDsl>(); 66 + function getStructuralChainForNode( 67 + node: TsDsl, 68 + visited: Set<TsDsl>, 69 + ): NodeChain { 70 + if (visited.has(node)) return []; 71 + visited.add(node); 46 72 47 - let current: TsDsl | undefined = node; 48 - while (current) { 49 - if (visited.has(current)) break; 50 - visited.add(current); 73 + if (node['~dsl'] === 'TemplateTsDsl' || node['~dsl'] === 'FuncTsDsl') { 74 + return []; 75 + } 51 76 52 - chain.unshift(current); 77 + if (node.structuralParents) { 78 + for (const [parent] of node.structuralParents) { 79 + if (getScope(parent) !== getScope(node)) continue; 53 80 54 - let foundParent = false; 55 - for (const [parent] of current.structuralParents || []) { 56 - if (getScope(parent) === scope && traverseStructuralParent(parent)) { 57 - current = parent; 58 - foundParent = true; 59 - break; 60 - } 81 + const chain = getStructuralChainForNode(parent, visited); 82 + if (chain.length > 0) return [...chain, node]; 61 83 } 84 + } 62 85 63 - if (!foundParent) break; 64 - } 86 + if (!node.root) return []; 87 + 88 + return [node]; 89 + } 65 90 66 - // trim any unreachable nodes before root 67 - const rootIndex = chain.findIndex((node) => node.root); 68 - if (rootIndex !== -1) { 69 - chain.splice(0, rootIndex); 70 - } 91 + /** 92 + * Fold a structural chain to an access chain by removing 93 + * non-accessor nodes. 94 + */ 95 + function structuralToAccessChain(structuralChain: NodeChain): NodeChain { 96 + const accessChain: Array<TsDsl> = []; 97 + structuralChain.forEach((node, index) => { 98 + // assume first node is always included 99 + if (index === 0) { 100 + accessChain.push(node); 101 + } else if ( 102 + node['~dsl'] === 'FieldTsDsl' || 103 + node['~dsl'] === 'GetterTsDsl' || 104 + node['~dsl'] === 'MethodTsDsl' 105 + ) { 106 + accessChain.push(node); 107 + } 108 + }); 109 + return accessChain; 110 + } 71 111 72 - return foldAccessChain(chain) as ReadonlyArray<Node>; 112 + function transformAccessChain( 113 + accessChain: NodeChain, 114 + options: AccessOptions = {}, 115 + ): NodeChain { 116 + return accessChain.map((node, index) => { 117 + const accessNode = node.toAccessNode?.(node, options, { 118 + chain: accessChain, 119 + index, 120 + isLeaf: index === accessChain.length - 1, 121 + isRoot: index === 0, 122 + length: accessChain.length, 123 + }); 124 + if (accessNode) return accessNode; 125 + if (index === 0) { 126 + if (node['~dsl'] === 'ClassTsDsl') { 127 + const nextNode = accessChain[index + 1]; 128 + if (nextNode?.['~dsl'] === 'FieldTsDsl') { 129 + if ((nextNode as ReturnType<typeof $.field>).hasModifier('static')) { 130 + return $(node.name); 131 + } 132 + } 133 + return $.new(node.name).args(); 134 + } 135 + return $(node.name); 136 + } 137 + return node; 138 + }); 73 139 } 74 140 75 - export const astContext: AstContext = { 76 - getAccess<T = unknown>( 77 - to: TsDsl | Symbol<TsDsl>, 78 - options?: AccessPatternOptions, 141 + export class TsDslContext { 142 + /** 143 + * Build an expression for accessing the node. 144 + * 145 + * @param node - The node or symbol to build access for 146 + * @param options - Access options 147 + * @returns Expression for accessing the node 148 + * 149 + * @example 150 + * ```ts 151 + * ctx.access(node); // → Expression for accessing the node 152 + * ``` 153 + */ 154 + access<T = AccessResult>( 155 + node: TsDsl | Symbol<TsDsl>, 156 + options?: AccessOptions, 79 157 ): T { 80 - const node = isSymbol(to) ? to.node! : to; 81 - const chain = getAccessChain(node); 82 - if (chain.length === 0) return node as T; 158 + const n = isSymbol(node) ? node.node : node; 159 + if (!n) { 160 + throw new Error(`Symbol ${node.name} is not resolved to a node.`); 161 + } 162 + const accessChain = getAccessChainForNode(n); 163 + const finalChain = transformAccessChain(accessChain, options); 164 + return accessChainToNode<T>(finalChain); 165 + } 83 166 84 - let result!: ReturnType<typeof $.expr | typeof $.attr>; 167 + /** 168 + * Build an example. 169 + * 170 + * @param node - The node to generate an example for 171 + * @param options - Example options 172 + * @returns Full example string 173 + * 174 + * @example 175 + * ```ts 176 + * ctx.example(node, { moduleName: 'my-sdk' }); // → Full example string 177 + * ``` 178 + */ 179 + example( 180 + node: TsDsl, 181 + options: ExampleOptions | undefined, 182 + astOptions?: Parameters<typeof TypeScriptRenderer.astToString>[0], 183 + ): string { 184 + if (astOptions) { 185 + return TypeScriptRenderer.astToString(astOptions); 186 + } 85 187 86 - for (let index = 0; index < chain.length; index++) { 87 - const currentNode = chain[index]!; 188 + if (!options) { 189 + throw new Error('Example options are required.'); 190 + } 191 + 192 + const accessChain = getAccessChainForNode(node); 193 + if (options.importName) { 194 + accessChain[0]!.name.set(options.importName); 195 + } 196 + const importNode = $(accessChain[0]!.name.toString()); // must store name before transform 197 + const finalChain = transformAccessChain(accessChain, { 198 + context: 'example', 199 + }); 88 200 89 - const transformed = currentNode.accessPattern?.( 90 - currentNode, 91 - { 92 - context: 'runtime', 93 - ...options, 94 - }, 95 - { 96 - chain, 97 - index, 98 - isLeaf: index === chain.length - 1, 99 - isRoot: index === 0, 100 - length: chain.length, 101 - }, 102 - ) as typeof result | undefined; 201 + const setupNode = options.importSetup 202 + ? typeof options.importSetup === 'function' 203 + ? options.importSetup(importNode) 204 + : options.importSetup 205 + : (finalChain[0]! as TsDsl<ts.Expression>); 206 + const setupName = options.setupName; 207 + const payload = 208 + options.payload instanceof Array 209 + ? options.payload 210 + : options.payload 211 + ? [options.payload] 212 + : []; 103 213 104 - if (index === 0) { 105 - result = transformed || $(currentNode.name); 106 - } else { 107 - result = result.attr(transformed?.name || currentNode.name); 108 - } 214 + let nodes: Array<TsDsl> = []; 215 + if (setupName) { 216 + nodes = [ 217 + $.const(setupName).assign(setupNode), 218 + accessChainToNode([$(setupName), ...finalChain.slice(1)]).call( 219 + ...payload, 220 + ), 221 + ]; 222 + } else { 223 + nodes = [ 224 + accessChainToNode([setupNode, ...finalChain.slice(1)]).call(...payload), 225 + ]; 109 226 } 110 227 111 - return result as T; 112 - }, 113 - }; 228 + const localName = importNode.name.toString(); 229 + return TypeScriptRenderer.astToString({ 230 + imports: [ 231 + [ 232 + { 233 + imports: 234 + !options.importKind || options.importKind === 'named' 235 + ? [ 236 + { 237 + isTypeOnly: false, 238 + localName, 239 + sourceName: localName, 240 + }, 241 + ] 242 + : [], 243 + isTypeOnly: false, 244 + kind: options.importKind ?? 'named', 245 + localName: options.importKind !== 'named' ? localName : undefined, 246 + modulePath: options.moduleName, 247 + }, 248 + ], 249 + ], 250 + nodes, 251 + trailingNewline: false, 252 + }); 253 + } 254 + }
+4
packages/openapi-ts/src/ts-dsl/utils/factories.ts
··· 2 2 import type { AttrCtor } from '../expr/attr'; 3 3 import type { AwaitCtor } from '../expr/await'; 4 4 import type { CallCtor } from '../expr/call'; 5 + import type { NewCtor } from '../expr/new'; 5 6 import type { TypeOfExprCtor } from '../expr/typeof'; 6 7 import type { ReturnCtor } from '../stmt/return'; 7 8 import type { TypeExprCtor } from '../type/expr'; ··· 44 45 45 46 /** Factory for creating function or method call expressions (e.g. `fn(arg)`). */ 46 47 call: createFactory<CallCtor>('call'), 48 + 49 + /** Factory for creating new expressions (e.g. `new ClassName()`). */ 50 + new: createFactory<NewCtor>('new'), 47 51 48 52 /** Factory for creating return statements. */ 49 53 return: createFactory<ReturnCtor>('return'),
+10 -6
packages/openapi-ts/src/ts-dsl/utils/lazy.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; 5 - import { astContext } from './context'; 5 + import { TsDslContext } from './context'; 6 6 7 - export type LazyThunk<T extends ts.Node> = (ctx: AstContext) => TsDsl<T>; 7 + export type LazyThunk<T extends ts.Node> = (ctx: TsDslContext) => TsDsl<T>; 8 8 9 9 export class LazyTsDsl<T extends ts.Node = ts.Node> extends TsDsl<T> { 10 10 readonly '~dsl' = 'LazyTsDsl'; ··· 18 18 19 19 override analyze(ctx: AnalysisContext): void { 20 20 super.analyze(ctx); 21 - ctx.analyze(this._thunk(astContext)); 21 + ctx.analyze(this.toResult()); 22 22 } 23 23 24 - override toAst(ctx: AstContext): T { 25 - return this._thunk(ctx).toAst(ctx); 24 + toResult(): TsDsl<T> { 25 + return this._thunk(new TsDslContext()); 26 + } 27 + 28 + override toAst(): T { 29 + return this.toResult().toAst(); 26 30 } 27 31 }
+15 -7
pnpm-lock.yaml
··· 118 118 '@hey-api/openapi-ts': 119 119 specifier: workspace:* 120 120 version: link:../packages/openapi-ts 121 + '@opencode-ai/sdk': 122 + specifier: 1.0.170 123 + version: 1.0.170 121 124 '@pinia/colada': 122 125 specifier: 0.19.1 123 126 version: 0.19.1(pinia@3.0.3(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)) ··· 5041 5044 5042 5045 '@one-ini/wasm@0.1.1': 5043 5046 resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} 5047 + 5048 + '@opencode-ai/sdk@1.0.170': 5049 + resolution: {integrity: sha512-A44hiQib9cB/hAfeS9fAr5sTlz7B9teimfcz8dfIgpMLeT+ZAF5slf32vejhzOy4DoLGeLlJswN0UB+XzFPMrA==} 5044 5050 5045 5051 '@oxc-project/types@0.103.0': 5046 5052 resolution: {integrity: sha512-bkiYX5kaXWwUessFRSoXFkGIQTmc6dLGdxuRTrC+h8PSnIdZyuXHHlLAeTmOue5Br/a0/a7dHH0Gca6eXn9MKg==} ··· 14597 14603 '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) 14598 14604 ansi-colors: 4.1.3 14599 14605 autoprefixer: 10.4.20(postcss@8.5.2) 14600 - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) 14606 + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) 14601 14607 browserslist: 4.25.4 14602 14608 copy-webpack-plugin: 12.0.2(webpack@5.98.0) 14603 14609 css-loader: 7.1.2(webpack@5.98.0) ··· 14617 14623 picomatch: 4.0.2 14618 14624 piscina: 4.8.0 14619 14625 postcss: 8.5.2 14620 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) 14626 + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) 14621 14627 resolve-url-loader: 5.0.0 14622 14628 rxjs: 7.8.1 14623 14629 sass: 1.85.0 ··· 14685 14691 '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.2)) 14686 14692 ansi-colors: 4.1.3 14687 14693 autoprefixer: 10.4.20(postcss@8.5.2) 14688 - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) 14694 + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) 14689 14695 browserslist: 4.25.4 14690 14696 copy-webpack-plugin: 12.0.2(webpack@5.98.0) 14691 14697 css-loader: 7.1.2(webpack@5.98.0) ··· 14705 14711 picomatch: 4.0.2 14706 14712 piscina: 4.8.0 14707 14713 postcss: 8.5.2 14708 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) 14714 + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) 14709 14715 resolve-url-loader: 5.0.0 14710 14716 rxjs: 7.8.1 14711 14717 sass: 1.85.0 ··· 14793 14799 picomatch: 4.0.2 14794 14800 piscina: 4.8.0 14795 14801 postcss: 8.5.2 14796 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) 14802 + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) 14797 14803 resolve-url-loader: 5.0.0 14798 14804 rxjs: 7.8.1 14799 14805 sass: 1.85.0 ··· 19284 19290 19285 19291 '@one-ini/wasm@0.1.1': {} 19286 19292 19293 + '@opencode-ai/sdk@1.0.170': {} 19294 + 19287 19295 '@oxc-project/types@0.103.0': {} 19288 19296 19289 19297 '@parcel/watcher-android-arm64@2.5.1': ··· 22272 22280 schema-utils: 4.3.2 22273 22281 webpack: 5.98.0(esbuild@0.25.0) 22274 22282 22275 - babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)): 22283 + babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0): 22276 22284 dependencies: 22277 22285 '@babel/core': 7.26.9 22278 22286 find-cache-dir: 4.0.0 ··· 27762 27770 ts-node: 10.9.2(@types/node@22.10.5)(typescript@5.9.3) 27763 27771 optional: true 27764 27772 27765 - postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)): 27773 + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0): 27766 27774 dependencies: 27767 27775 cosmiconfig: 9.0.0(typescript@5.8.3) 27768 27776 jiti: 1.21.7