fork of hey-api/openapi-ts because I need some additional things
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}