fork of hey-api/openapi-ts because I need some additional things
at feat/skip-token 148 lines 4.4 kB view raw
1import type { Logger, Project } from '@hey-api/codegen-core'; 2 3import type { AnyConfig } from '../config/shared'; 4import type { Dependency } from '../config/utils/dependencies'; 5import { dependencyFactory } from '../config/utils/dependencies'; 6import type { Graph } from '../graph'; 7import { PluginInstance } from '../plugins/shared/utils/instance'; 8import type { Plugin, PluginConfigMap, PluginNames } from '../plugins/types'; 9import { resolveRef } from '../utils/ref'; 10import type { ExampleIntent } from './intents'; 11import type { IR } from './types'; 12 13export class Context<Spec extends Record<string, any> = any, Config extends AnyConfig = AnyConfig> { 14 /** 15 * Configuration for parsing and generating the output. This 16 * is a mix of user-provided and default values. 17 */ 18 config: Config; 19 /** 20 * The code generation project instance used to manage files, symbols, 21 */ 22 gen: Project; 23 /** 24 * The dependency graph built from the intermediate representation. 25 */ 26 graph: Graph | undefined; 27 /** 28 * Intents declared by plugins. 29 */ 30 intents: Array<ExampleIntent> = []; 31 /** 32 * Intermediate representation model obtained from `spec`. 33 */ 34 ir: IR.Model = {}; 35 /** 36 * Logger instance. 37 */ 38 logger: Logger; 39 /** 40 * The package metadata and utilities for the current context, constructed 41 * from the provided dependencies. Used for managing package-related 42 * information such as name, version, and dependency resolution during 43 * code generation. 44 */ 45 package: Dependency; 46 /** 47 * A map of registered plugin instances, keyed by plugin name. Plugins are 48 * registered through the `registerPlugin` method and can be accessed by 49 * their configured name from the config. 50 */ 51 plugins: Partial<Record<PluginNames, PluginInstance<PluginConfigMap[keyof PluginConfigMap]>>> = 52 {}; 53 /** 54 * Resolved specification from `input`. 55 */ 56 spec: Spec; 57 58 constructor({ 59 config, 60 dependencies, 61 logger, 62 project, 63 spec, 64 }: { 65 config: Config; 66 dependencies: Record<string, string>; 67 logger: Logger; 68 project: Project; 69 spec: Spec; 70 }) { 71 this.config = config; 72 this.gen = project; 73 this.logger = logger; 74 this.package = dependencyFactory(dependencies); 75 this.spec = spec; 76 } 77 78 /** 79 * Returns a resolved and dereferenced schema from `spec`. 80 */ 81 dereference<T>(schema: { $ref: string }) { 82 const resolved = this.resolveRef<T>(schema.$ref); 83 const dereferenced = { 84 ...schema, 85 ...resolved, 86 } as T; 87 // @ts-expect-error 88 delete dereferenced.$ref; 89 return dereferenced; 90 } 91 92 /** 93 * Registers a new plugin to the global context. 94 * 95 * @param name Plugin name. 96 * @returns Registered plugin instance. 97 */ 98 private registerPlugin<T extends PluginNames>( 99 name: T, 100 ): T extends keyof PluginConfigMap ? PluginInstance<PluginConfigMap[T]> : PluginInstance { 101 const plugin = (this.config.plugins as Record<string, Plugin.Config<Plugin.Types>>)[name]!; 102 const instance = new PluginInstance({ 103 api: plugin.api, 104 config: plugin.config as any, 105 context: this as any, 106 dependencies: plugin.dependencies ?? [], 107 gen: this.gen, 108 handler: plugin.handler, 109 name: plugin.name, 110 }); 111 (this.plugins as Record<string, any>)[instance.name] = instance; 112 return instance as T extends keyof PluginConfigMap 113 ? PluginInstance<PluginConfigMap[T]> 114 : PluginInstance; 115 } 116 117 /** 118 * Registers all plugins in the order specified by the configuration and returns 119 * an array of the registered PluginInstance objects. Each plugin is instantiated 120 * and added to the context's plugins map. 121 * 122 * @returns {ReadonlyArray<PluginInstance>} An array of registered plugin instances in order. 123 */ 124 registerPlugins(): ReadonlyArray<PluginInstance> { 125 return this.config.pluginOrder.map((name) => this.registerPlugin(name)); 126 } 127 128 // TODO: parser - works the same as resolveRef, but for IR schemas. 129 // for now, they map 1:1, but if they diverge (like with OpenAPI 2.0), 130 // we will want to rewrite $refs at parse time, so they continue pointing 131 // to the correct IR location 132 resolveIrRef<T>($ref: string) { 133 return resolveRef<T>({ 134 $ref, 135 spec: this.ir, 136 }); 137 } 138 139 /** 140 * Returns a resolved reference from `spec`. 141 */ 142 resolveRef<T>($ref: string) { 143 return resolveRef<T>({ 144 $ref, 145 spec: this.spec, 146 }); 147 } 148}