···5959Reserved fields are prefixed with an underscore and are not exposed to the user.
6060:::
61616262-`config.ts` contains the runtime configuration for your plugin. It must implement the `Config` interface we created above and define `_handler()` and `_handlerLegacy()` functions from the `Plugin.Config` interface.
6262+`config.ts` contains the runtime configuration for your plugin. It must implement the `Config` interface we created above and define `handler()` and `handlerLegacy()` functions from the `Plugin.Config` interface.
63636464::: code-group
6565···7070import type { Config } from './types';
71717272export const defaultConfig: Plugin.Config<Config> = {
7373- _dependencies: ['@hey-api/typescript'],
7474- _handler: handler,
7575- _handlerLegacy: () => {},
7673 config: {
7774 myOption: false, // implements default value from types
7875 },
7676+ dependencies: ['@hey-api/typescript'],
7777+ handler,
7878+ handlerLegacy: () => {},
7979 name: 'my-plugin',
8080 output: 'my-plugin',
8181};
···101101102102## Handler
103103104104-Notice we defined `_handler` in our `config.ts` file. This method is responsible for generating the actual output. We recommend implementing it in `plugin.ts`.
104104+Notice we defined `handler` in our `config.ts` file. This method is responsible for generating the actual output. We recommend implementing it in `plugin.ts`.
105105106106::: code-group
107107···118118 path: plugin.output,
119119 });
120120121121- context.subscribe('before', () => {
121121+ plugin.subscribe('before', () => {
122122 // do something before parsing the input
123123 });
124124125125- context.subscribe('operation', ({ operation }) => {
125125+ plugin.subscribe('operation', ({ operation }) => {
126126 // do something with the operation model
127127 });
128128129129- context.subscribe('schema', ({ operation }) => {
129129+ plugin.subscribe('schema', ({ operation }) => {
130130 // do something with the schema model
131131 });
132132133133- context.subscribe('after', () => {
133133+ plugin.subscribe('after', () => {
134134 // do something after parsing the input
135135 });
136136···159159160160### Legacy
161161162162-Notice we defined `_handlerLegacy` in our `config.ts` file. This method is responsible for generating the actual output when using the legacy parser. We do not recommend implementing this method unless you must use the legacy parser. You can use one of our [`plugin-legacy.ts`](https://github.com/hey-api/openapi-ts/blob/main/packages/openapi-ts/src/plugins/%40hey-api/typescript/plugin-legacy.ts) files as an inspiration for potential implementation.
162162+Notice we defined `handlerLegacy` in our `config.ts` file. This method is responsible for generating the actual output when using the legacy parser. We do not recommend implementing this method unless you must use the legacy parser. You can use one of our [`plugin-legacy.ts`](https://github.com/hey-api/openapi-ts/blob/main/packages/openapi-ts/src/plugins/%40hey-api/typescript/plugin-legacy.ts) files as an inspiration for potential implementation.
163163164164## Usage
165165
···2233import { HeyApiError } from '../error';
44import { TypeScriptFile } from '../generate/files';
55+import { PluginInstance } from '../plugins/shared/utils/instance';
56import type { Config, StringCase } from '../types/config';
67import type { Files } from '../types/utils';
78import { resolveRef } from '../utils/ref';
89import type { IR } from './types';
9101010-interface ContextFile {
1111+// Helper type to extract the original config type from Plugin.Config and ensure it satisfies BaseConfig
1212+type ExtractConfig<T> = T extends { config: infer C }
1313+ ? C & { name: string }
1414+ : never;
1515+1616+// Helper type to map plugin names to their specific PluginInstance types
1717+type PluginInstanceMap = {
1818+ [K in keyof Config['plugins']]?: PluginInstance<
1919+ ExtractConfig<Config['plugins'][K]>
2020+ >;
2121+};
2222+2323+export interface ContextFile {
1124 /**
1225 * Should the exports from this file be re-exported in the index barrel file?
1326 */
···2235 identifierCase?: StringCase;
2336 /**
2437 * Relative file path to the output path.
3838+ *
2539 * @example
2640 * 'bar/foo.ts'
2741 */
···8397 * Intermediate representation model obtained from `spec`.
8498 */
8599 public ir: IR.Model;
100100+ /**
101101+ * A map of registered plugin instances, keyed by plugin name. Plugins are
102102+ * registered through the `registerPlugin` method and can be accessed by
103103+ * their configured name from the config.
104104+ */
105105+ public plugins: PluginInstanceMap = {};
86106 /**
87107 * Resolved specification from `input`.
88108 */
···175195 return this.files[id];
176196 }
177197198198+ /**
199199+ * Registers a new plugin to the global context.
200200+ *
201201+ * @param name Plugin name.
202202+ * @returns Registered plugin instance.
203203+ */
204204+ private registerPlugin(name: keyof Config['plugins']): PluginInstance {
205205+ const plugin = this.config.plugins[name]!;
206206+ const instance = new PluginInstance({
207207+ config: plugin.config,
208208+ context: this as any,
209209+ dependencies: plugin.dependencies ?? [],
210210+ handler: plugin.handler,
211211+ name: plugin.name,
212212+ output: plugin.output!,
213213+ });
214214+ this.plugins[instance.name] = instance;
215215+ return instance;
216216+ }
217217+218218+ /**
219219+ * Generator that iterates through plugin order and registers each plugin.
220220+ * Yields the registered plugin instance for each plugin name.
221221+ */
222222+ public *registerPlugins(): Generator<PluginInstance> {
223223+ for (const name of this.config.pluginOrder) {
224224+ yield this.registerPlugin(name);
225225+ }
226226+ }
227227+178228 // TODO: parser - works the same as resolveRef, but for IR schemas.
179229 // for now, they map 1:1, but if they diverge (like with OpenAPI 2.0),
180230 // we will want to rewrite $refs at parse time, so they continue pointing
···202252 public subscribe<T extends keyof Events>(
203253 event: T,
204254 callbackFn: Events[T],
205205- pluginName?: string,
255255+ pluginName: string,
206256 ): void {
207257 if (!this.listeners[event]) {
208258 this.listeners[event] = [];
+3-1
packages/openapi-ts/src/ir/types.d.ts
···33 SecuritySchemeObject,
44 ServerObject,
55} from '../openApi/3.1.x/types/spec';
66-import type { IRContext } from './context';
66+import type { ContextFile as CtxFile, Events, IRContext } from './context';
77import type { IRMediaType } from './mediaType';
8899interface IRBodyObject {
···217217 export type BodyObject = IRBodyObject;
218218 export type ComponentsObject = IRComponentsObject;
219219 export type Context<Spec extends Record<string, any> = any> = IRContext<Spec>;
220220+ export type ContextEvents = Events;
221221+ export type ContextFile = CtxFile;
220222 export type Model = IRModel;
221223 export type OperationObject = IROperationObject;
222224 export type ParameterObject = IRParameterObject;