@kubb/core 5.0.0-beta.1 → 5.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +9 -39
  2. package/dist/{PluginDriver-BXibeQk-.cjs → PluginDriver-C1OsqGBJ.cjs} +106 -56
  3. package/dist/PluginDriver-C1OsqGBJ.cjs.map +1 -0
  4. package/dist/{PluginDriver-DV3p2Hky.js → PluginDriver-CGypdXHg.js} +101 -57
  5. package/dist/PluginDriver-CGypdXHg.js.map +1 -0
  6. package/dist/{types-CuNocrbJ.d.ts → createKubb-BSfMDBwR.d.ts} +1533 -1505
  7. package/dist/index.cjs +249 -209
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +2 -185
  10. package/dist/index.js +249 -209
  11. package/dist/index.js.map +1 -1
  12. package/dist/mocks.cjs +1 -1
  13. package/dist/mocks.cjs.map +1 -1
  14. package/dist/mocks.d.ts +1 -1
  15. package/dist/mocks.js +1 -1
  16. package/dist/mocks.js.map +1 -1
  17. package/package.json +5 -12
  18. package/src/FileManager.ts +8 -0
  19. package/src/FileProcessor.ts +12 -7
  20. package/src/PluginDriver.ts +49 -7
  21. package/src/constants.ts +6 -2
  22. package/src/createAdapter.ts +77 -1
  23. package/src/createKubb.ts +973 -141
  24. package/src/defineGenerator.ts +92 -4
  25. package/src/defineLogger.ts +42 -3
  26. package/src/defineMiddleware.ts +1 -1
  27. package/src/definePlugin.ts +304 -8
  28. package/src/defineResolver.ts +185 -52
  29. package/src/devtools.ts +8 -1
  30. package/src/index.ts +1 -1
  31. package/src/mocks.ts +1 -2
  32. package/src/storages/fsStorage.ts +6 -31
  33. package/src/types.ts +38 -1292
  34. package/dist/PluginDriver-BXibeQk-.cjs.map +0 -1
  35. package/dist/PluginDriver-DV3p2Hky.js.map +0 -1
  36. package/src/Kubb.ts +0 -300
  37. package/src/renderNode.ts +0 -35
  38. package/src/utils/diagnostics.ts +0 -18
  39. package/src/utils/isInputPath.ts +0 -10
  40. package/src/utils/packageJSON.ts +0 -99
package/dist/mocks.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mocks.js","names":[],"sources":["../src/mocks.ts"],"sourcesContent":["import { resolve } from 'node:path'\nimport type { FileNode, OperationNode, SchemaNode, Visitor } from '@kubb/ast'\nimport { transform } from '@kubb/ast'\nimport { FileManager } from './FileManager.ts'\nimport { PluginDriver } from './PluginDriver.ts'\nimport { applyHookResult } from './renderNode.ts'\nimport type { Adapter, AdapterFactoryOptions, Config, Generator, GeneratorContext, NormalizedPlugin, PluginFactoryOptions } from './types.ts'\n\n/**\n\n * Creates a minimal `PluginDriver` mock for unit tests.\n */\nexport function createMockedPluginDriver(options: { name?: string; plugin?: NormalizedPlugin; config?: Config } = {}): PluginDriver {\n return {\n config: options?.config ?? {\n root: '.',\n output: {\n path: './path',\n },\n },\n getPlugin(_pluginName: string): NormalizedPlugin | undefined {\n return options?.plugin\n },\n getResolver: (_pluginName: string) => options?.plugin?.resolver,\n fileManager: new FileManager(),\n } as unknown as PluginDriver\n}\n\n/**\n * Creates a minimal `Adapter` mock for unit tests.\n * `parse` returns an empty `InputNode` by default; override via `options.parse`.\n * `getImports` returns `[]` by default.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n inputNode?: Adapter<TOptions>['inputNode']\n parse?: Adapter<TOptions>['parse']\n getImports?: Adapter<TOptions>['getImports']\n } = {},\n): Adapter<TOptions> {\n const inputNode = options.inputNode ?? null\n return {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n inputNode,\n parse: options.parse ?? (async () => ({ kind: 'Input' as const, schemas: [], operations: [] })),\n getImports: options.getImports ?? ((_node: SchemaNode, _resolve: (schemaName: string) => { name: string; path: string }) => []),\n } as Adapter<TOptions>\n}\n\n/**\n * Creates a minimal plugin mock for unit tests.\n *\n * @example\n * `const plugin = createMockedPlugin<PluginTs>({ name: '@kubb/plugin-ts', options })`\n */\nexport function createMockedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(params: {\n name: TOptions['name']\n options: TOptions['resolvedOptions']\n resolver?: TOptions['resolver']\n transformer?: Visitor\n dependencies?: Array<string>\n}): NormalizedPlugin<TOptions> {\n return {\n name: params.name,\n options: params.options,\n resolver: params.resolver,\n transformer: params.transformer,\n dependencies: params.dependencies,\n hooks: {},\n } as unknown as NormalizedPlugin<TOptions>\n}\n\ntype RenderGeneratorOptions<TOptions extends PluginFactoryOptions> = {\n config: Config\n adapter: Adapter\n driver: PluginDriver\n plugin: NormalizedPlugin<TOptions>\n options: TOptions['resolvedOptions']\n resolver: TOptions['resolver']\n}\n\nfunction createMockedPluginContext<TOptions extends PluginFactoryOptions>(opts: RenderGeneratorOptions<TOptions>): Omit<GeneratorContext<TOptions>, 'options'> {\n const root = resolve(opts.config.root, opts.config.output.path)\n\n return {\n config: opts.config,\n root,\n getMode: (output: { path: string }) => PluginDriver.getMode(resolve(root, output.path)),\n adapter: opts.adapter,\n resolver: opts.resolver,\n plugin: opts.plugin,\n driver: opts.driver,\n getResolver: (name: string) => opts.driver.getResolver(name),\n inputNode: { kind: 'Input', schemas: [], operations: [] },\n addFile: async (...files: Array<FileNode>) => opts.driver.fileManager.add(...files),\n upsertFile: async (...files: Array<FileNode>) => opts.driver.fileManager.upsert(...files),\n hooks: opts.driver.hooks ?? ({} as never),\n warn: (msg: string) => console.warn(msg),\n error: (msg: string) => console.error(msg),\n info: (msg: string) => console.info(msg),\n openInStudio: async () => {},\n } as unknown as Omit<GeneratorContext<TOptions>, 'options'>\n}\n\n/**\n * Renders a generator's `schema` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: SchemaNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.schema) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.transformer ? transform(node, opts.plugin.transformer) : node\n const result = await generator.schema(transformedNode, {\n ...context,\n options: opts.options,\n })\n await applyHookResult(result, opts.driver, generator.renderer ?? undefined)\n}\n\n/**\n * Renders a generator's `operation` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperation(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperation<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: OperationNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operation) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.transformer ? transform(node, opts.plugin.transformer) : node\n const result = await generator.operation(transformedNode, {\n ...context,\n options: opts.options,\n })\n await applyHookResult(result, opts.driver, generator.renderer ?? undefined)\n}\n\n/**\n * Renders a generator's `operations` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperations(classClientGenerator, nodes, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperations<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n nodes: Array<OperationNode>,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operations) return\n const context = createMockedPluginContext(opts)\n const transformedNodes = opts.plugin.transformer ? nodes.map((n) => transform(n, opts.plugin.transformer!)) : nodes\n const result = await generator.operations(transformedNodes, {\n ...context,\n options: opts.options,\n })\n await applyHookResult(result, opts.driver, generator.renderer ?? undefined)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mocks.js","names":[],"sources":["../src/mocks.ts"],"sourcesContent":["import { resolve } from 'node:path'\nimport type { FileNode, OperationNode, SchemaNode, Visitor } from '@kubb/ast'\nimport { transform } from '@kubb/ast'\nimport { FileManager } from './FileManager.ts'\nimport { applyHookResult, PluginDriver } from './PluginDriver.ts'\nimport type { Adapter, AdapterFactoryOptions, Config, Generator, GeneratorContext, NormalizedPlugin, PluginFactoryOptions } from './types.ts'\n\n/**\n\n * Creates a minimal `PluginDriver` mock for unit tests.\n */\nexport function createMockedPluginDriver(options: { name?: string; plugin?: NormalizedPlugin; config?: Config } = {}): PluginDriver {\n return {\n config: options?.config ?? {\n root: '.',\n output: {\n path: './path',\n },\n },\n getPlugin(_pluginName: string): NormalizedPlugin | undefined {\n return options?.plugin\n },\n getResolver: (_pluginName: string) => options?.plugin?.resolver,\n fileManager: new FileManager(),\n } as unknown as PluginDriver\n}\n\n/**\n * Creates a minimal `Adapter` mock for unit tests.\n * `parse` returns an empty `InputNode` by default; override via `options.parse`.\n * `getImports` returns `[]` by default.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n inputNode?: Adapter<TOptions>['inputNode']\n parse?: Adapter<TOptions>['parse']\n getImports?: Adapter<TOptions>['getImports']\n } = {},\n): Adapter<TOptions> {\n const inputNode = options.inputNode ?? null\n return {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n inputNode,\n parse: options.parse ?? (async () => ({ kind: 'Input' as const, schemas: [], operations: [] })),\n getImports: options.getImports ?? ((_node: SchemaNode, _resolve: (schemaName: string) => { name: string; path: string }) => []),\n } as Adapter<TOptions>\n}\n\n/**\n * Creates a minimal plugin mock for unit tests.\n *\n * @example\n * `const plugin = createMockedPlugin<PluginTs>({ name: '@kubb/plugin-ts', options })`\n */\nexport function createMockedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(params: {\n name: TOptions['name']\n options: TOptions['resolvedOptions']\n resolver?: TOptions['resolver']\n transformer?: Visitor\n dependencies?: Array<string>\n}): NormalizedPlugin<TOptions> {\n return {\n name: params.name,\n options: params.options,\n resolver: params.resolver,\n transformer: params.transformer,\n dependencies: params.dependencies,\n hooks: {},\n } as unknown as NormalizedPlugin<TOptions>\n}\n\ntype RenderGeneratorOptions<TOptions extends PluginFactoryOptions> = {\n config: Config\n adapter: Adapter\n driver: PluginDriver\n plugin: NormalizedPlugin<TOptions>\n options: TOptions['resolvedOptions']\n resolver: TOptions['resolver']\n}\n\nfunction createMockedPluginContext<TOptions extends PluginFactoryOptions>(opts: RenderGeneratorOptions<TOptions>): Omit<GeneratorContext<TOptions>, 'options'> {\n const root = resolve(opts.config.root, opts.config.output.path)\n\n return {\n config: opts.config,\n root,\n getMode: (output: { path: string }) => PluginDriver.getMode(resolve(root, output.path)),\n adapter: opts.adapter,\n resolver: opts.resolver,\n plugin: opts.plugin,\n driver: opts.driver,\n getResolver: (name: string) => opts.driver.getResolver(name),\n inputNode: { kind: 'Input', schemas: [], operations: [] },\n addFile: async (...files: Array<FileNode>) => opts.driver.fileManager.add(...files),\n upsertFile: async (...files: Array<FileNode>) => opts.driver.fileManager.upsert(...files),\n hooks: opts.driver.hooks ?? ({} as never),\n warn: (msg: string) => console.warn(msg),\n error: (msg: string) => console.error(msg),\n info: (msg: string) => console.info(msg),\n openInStudio: async () => {},\n } as unknown as Omit<GeneratorContext<TOptions>, 'options'>\n}\n\n/**\n * Renders a generator's `schema` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: SchemaNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.schema) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.transformer ? transform(node, opts.plugin.transformer) : node\n const result = await generator.schema(transformedNode, {\n ...context,\n options: opts.options,\n })\n await applyHookResult(result, opts.driver, generator.renderer ?? undefined)\n}\n\n/**\n * Renders a generator's `operation` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperation(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperation<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: OperationNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operation) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.transformer ? transform(node, opts.plugin.transformer) : node\n const result = await generator.operation(transformedNode, {\n ...context,\n options: opts.options,\n })\n await applyHookResult(result, opts.driver, generator.renderer ?? undefined)\n}\n\n/**\n * Renders a generator's `operations` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperations(classClientGenerator, nodes, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperations<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n nodes: Array<OperationNode>,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operations) return\n const context = createMockedPluginContext(opts)\n const transformedNodes = opts.plugin.transformer ? nodes.map((n) => transform(n, opts.plugin.transformer!)) : nodes\n const result = await generator.operations(transformedNodes, {\n ...context,\n options: opts.options,\n })\n await applyHookResult(result, opts.driver, generator.renderer ?? undefined)\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,yBAAyB,UAAyE,EAAE,EAAgB;CAClI,OAAO;EACL,QAAQ,SAAS,UAAU;GACzB,MAAM;GACN,QAAQ,EACN,MAAM,UACP;GACF;EACD,UAAU,aAAmD;GAC3D,OAAO,SAAS;;EAElB,cAAc,gBAAwB,SAAS,QAAQ;EACvD,aAAa,IAAI,aAAa;EAC/B;;;;;;;AAQH,SAAgB,oBACd,UAMI,EAAE,EACa;CACnB,MAAM,YAAY,QAAQ,aAAa;CACvC,OAAO;EACL,MAAO,QAAQ,QAAQ;EACvB,SAAU,QAAQ,mBAAmB,EAAE;EACvC;EACA,OAAO,QAAQ,UAAU,aAAa;GAAE,MAAM;GAAkB,SAAS,EAAE;GAAE,YAAY,EAAE;GAAE;EAC7F,YAAY,QAAQ,gBAAgB,OAAmB,aAAqE,EAAE;EAC/H;;;;;;;;AASH,SAAgB,mBAAiF,QAMlE;CAC7B,OAAO;EACL,MAAM,OAAO;EACb,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,aAAa,OAAO;EACpB,cAAc,OAAO;EACrB,OAAO,EAAE;EACV;;AAYH,SAAS,0BAAiE,MAAqF;CAC7J,MAAM,OAAO,QAAQ,KAAK,OAAO,MAAM,KAAK,OAAO,OAAO,KAAK;CAE/D,OAAO;EACL,QAAQ,KAAK;EACb;EACA,UAAU,WAA6B,aAAa,QAAQ,QAAQ,MAAM,OAAO,KAAK,CAAC;EACvF,SAAS,KAAK;EACd,UAAU,KAAK;EACf,QAAQ,KAAK;EACb,QAAQ,KAAK;EACb,cAAc,SAAiB,KAAK,OAAO,YAAY,KAAK;EAC5D,WAAW;GAAE,MAAM;GAAS,SAAS,EAAE;GAAE,YAAY,EAAE;GAAE;EACzD,SAAS,OAAO,GAAG,UAA2B,KAAK,OAAO,YAAY,IAAI,GAAG,MAAM;EACnF,YAAY,OAAO,GAAG,UAA2B,KAAK,OAAO,YAAY,OAAO,GAAG,MAAM;EACzF,OAAO,KAAK,OAAO,SAAU,EAAE;EAC/B,OAAO,QAAgB,QAAQ,KAAK,IAAI;EACxC,QAAQ,QAAgB,QAAQ,MAAM,IAAI;EAC1C,OAAO,QAAgB,QAAQ,KAAK,IAAI;EACxC,cAAc,YAAY;EAC3B;;;;;;;;;;;AAYH,eAAsB,sBACpB,WACA,MACA,MACe;CACf,IAAI,CAAC,UAAU,QAAQ;CACvB,MAAM,UAAU,0BAA0B,KAAK;CAC/C,MAAM,kBAAkB,KAAK,OAAO,cAAc,UAAU,MAAM,KAAK,OAAO,YAAY,GAAG;CAK7F,MAAM,gBAAgB,MAJD,UAAU,OAAO,iBAAiB;EACrD,GAAG;EACH,SAAS,KAAK;EACf,CAAC,EAC4B,KAAK,QAAQ,UAAU,YAAY,KAAA,EAAU;;;;;;;;;;;AAY7E,eAAsB,yBACpB,WACA,MACA,MACe;CACf,IAAI,CAAC,UAAU,WAAW;CAC1B,MAAM,UAAU,0BAA0B,KAAK;CAC/C,MAAM,kBAAkB,KAAK,OAAO,cAAc,UAAU,MAAM,KAAK,OAAO,YAAY,GAAG;CAK7F,MAAM,gBAAgB,MAJD,UAAU,UAAU,iBAAiB;EACxD,GAAG;EACH,SAAS,KAAK;EACf,CAAC,EAC4B,KAAK,QAAQ,UAAU,YAAY,KAAA,EAAU;;;;;;;;;;;AAY7E,eAAsB,0BACpB,WACA,OACA,MACe;CACf,IAAI,CAAC,UAAU,YAAY;CAC3B,MAAM,UAAU,0BAA0B,KAAK;CAC/C,MAAM,mBAAmB,KAAK,OAAO,cAAc,MAAM,KAAK,MAAM,UAAU,GAAG,KAAK,OAAO,YAAa,CAAC,GAAG;CAK9G,MAAM,gBAAgB,MAJD,UAAU,WAAW,kBAAkB;EAC1D,GAAG;EACH,SAAS,KAAK;EACf,CAAC,EAC4B,KAAK,QAAQ,UAAU,YAAY,KAAA,EAAU"}
package/package.json CHANGED
@@ -1,20 +1,13 @@
1
1
  {
2
2
  "name": "@kubb/core",
3
- "version": "5.0.0-beta.1",
4
- "description": "Core functionality for Kubb's plugin-based code generation system, providing the foundation for transforming OpenAPI specifications.",
3
+ "version": "5.0.0-beta.11",
4
+ "description": "Core engine for Kubb's plugin-based code generation system. Provides the plugin driver, file manager, defineConfig, and build orchestration used by every Kubb plugin.",
5
5
  "keywords": [
6
- "ast",
7
6
  "code-generator",
8
7
  "codegen",
9
- "core-library",
10
- "file-system",
11
8
  "kubb",
12
- "oas",
13
9
  "openapi",
14
- "plugin-framework",
15
10
  "plugin-system",
16
- "plugins",
17
- "swagger",
18
11
  "typescript"
19
12
  ],
20
13
  "license": "MIT",
@@ -65,15 +58,15 @@
65
58
  "dependencies": {
66
59
  "fflate": "^0.8.2",
67
60
  "tinyexec": "^1.1.2",
68
- "@kubb/ast": "5.0.0-beta.1"
61
+ "@kubb/ast": "5.0.0-beta.11"
69
62
  },
70
63
  "devDependencies": {
71
64
  "p-limit": "^7.3.0",
72
65
  "@internals/utils": "0.0.0",
73
- "@kubb/renderer-jsx": "5.0.0-beta.1"
66
+ "@kubb/renderer-jsx": "5.0.0-beta.11"
74
67
  },
75
68
  "peerDependencies": {
76
- "@kubb/renderer-jsx": "5.0.0-beta.1"
69
+ "@kubb/renderer-jsx": "5.0.0-beta.11"
77
70
  },
78
71
  "size-limit": [
79
72
  {
@@ -91,6 +91,14 @@ export class FileManager {
91
91
  this.#filesCache = null
92
92
  }
93
93
 
94
+ /**
95
+ * Releases all stored files. Called by the core after `kubb:build:end` to
96
+ * free the per-plugin FileNode caches for the rest of the process lifetime.
97
+ */
98
+ dispose(): void {
99
+ this.clear()
100
+ }
101
+
94
102
  /**
95
103
  * All stored files, sorted by path length (shorter paths first).
96
104
  */
@@ -1,5 +1,6 @@
1
1
  import type { CodeNode, FileNode } from '@kubb/ast'
2
2
  import { extractStringsFromNodes } from '@kubb/ast'
3
+ import { AsyncEventEmitter } from '@internals/utils'
3
4
  import pLimit from 'p-limit'
4
5
  import { PARALLEL_CONCURRENCY_LIMIT } from './constants.ts'
5
6
  import type { Parser } from './defineParser.ts'
@@ -14,9 +15,12 @@ type RunOptions = ParseOptions & {
14
15
  * @default 'sequential'
15
16
  */
16
17
  mode?: 'sequential' | 'parallel'
17
- onStart?: (files: Array<FileNode>) => Promise<void> | void
18
- onEnd?: (files: Array<FileNode>) => Promise<void> | void
19
- onUpdate?: (params: { file: FileNode; source?: string; processed: number; total: number; percentage: number }) => Promise<void> | void
18
+ }
19
+
20
+ export type FileProcessorEvents = {
21
+ start: [files: Array<FileNode>]
22
+ update: [params: { file: FileNode; source?: string; processed: number; total: number; percentage: number }]
23
+ end: [files: Array<FileNode>]
20
24
  }
21
25
 
22
26
  function joinSources(file: FileNode): string {
@@ -33,6 +37,7 @@ function joinSources(file: FileNode): string {
33
37
  * @internal
34
38
  */
35
39
  export class FileProcessor {
40
+ readonly events = new AsyncEventEmitter<FileProcessorEvents>()
36
41
  readonly #limit = pLimit(PARALLEL_CONCURRENCY_LIMIT)
37
42
 
38
43
  async parse(file: FileNode, { parsers, extension }: ParseOptions = {}): Promise<string> {
@@ -51,8 +56,8 @@ export class FileProcessor {
51
56
  return parser.parse(file, { extname: parseExtName })
52
57
  }
53
58
 
54
- async run(files: Array<FileNode>, { parsers, mode = 'sequential', extension, onStart, onEnd, onUpdate }: RunOptions = {}): Promise<Array<FileNode>> {
55
- await onStart?.(files)
59
+ async run(files: Array<FileNode>, { parsers, mode = 'sequential', extension }: RunOptions = {}): Promise<Array<FileNode>> {
60
+ await this.events.emit('start', files)
56
61
 
57
62
  const total = files.length
58
63
  let processed = 0
@@ -62,7 +67,7 @@ export class FileProcessor {
62
67
  const currentProcessed = ++processed
63
68
  const percentage = (currentProcessed / total) * 100
64
69
 
65
- await onUpdate?.({
70
+ await this.events.emit('update', {
66
71
  file,
67
72
  source,
68
73
  processed: currentProcessed,
@@ -79,7 +84,7 @@ export class FileProcessor {
79
84
  await Promise.all(files.map((file) => this.#limit(() => processOne(file))))
80
85
  }
81
86
 
82
- await onEnd?.(files)
87
+ await this.events.emit('end', files)
83
88
 
84
89
  return files
85
90
  }
@@ -1,14 +1,15 @@
1
- import { extname, resolve } from 'node:path'
1
+ import { resolve } from 'node:path'
2
2
  import type { AsyncEventEmitter } from '@internals/utils'
3
3
  import type { FileNode, InputNode, OperationNode, SchemaNode } from '@kubb/ast'
4
4
  import { createFile } from '@kubb/ast'
5
5
  import { DEFAULT_STUDIO_URL } from './constants.ts'
6
6
  import type { Generator } from './defineGenerator.ts'
7
7
  import type { Plugin } from './definePlugin.ts'
8
+ import { getMode } from './definePlugin.ts'
8
9
  import { defineResolver } from './defineResolver.ts'
9
10
  import { openInStudio as openInStudioFn } from './devtools.ts'
10
11
  import { FileManager } from './FileManager.ts'
11
- import { applyHookResult } from './renderNode.ts'
12
+ import type { RendererFactory } from './createRenderer.ts'
12
13
 
13
14
  import type {
14
15
  Adapter,
@@ -46,10 +47,7 @@ export class PluginDriver {
46
47
  * ```
47
48
  */
48
49
  static getMode(fileOrFolder: string | undefined | null): 'single' | 'split' {
49
- if (!fileOrFolder) {
50
- return 'split'
51
- }
52
- return extname(fileOrFolder) ? 'single' : 'split'
50
+ return getMode(fileOrFolder)
53
51
  }
54
52
 
55
53
  /**
@@ -138,6 +136,8 @@ export class PluginDriver {
138
136
  registerPluginHooks(hookPlugin: Plugin, normalizedPlugin: NormalizedPlugin): void {
139
137
  const { hooks } = hookPlugin
140
138
 
139
+ if (!hooks) return
140
+
141
141
  // kubb:plugin:setup gets special treatment: the globally emitted context is wrapped with
142
142
  // plugin-specific implementations so that addGenerator / setResolver / etc. target
143
143
  // this plugin's normalizedPlugin entry rather than being no-ops.
@@ -189,6 +189,7 @@ export class PluginDriver {
189
189
  */
190
190
  async emitSetupHooks(): Promise<void> {
191
191
  const noop = () => {}
192
+
192
193
  await this.hooks.emit('kubb:plugin:setup', {
193
194
  config: this.config,
194
195
  options: {},
@@ -283,6 +284,15 @@ export class PluginDriver {
283
284
  }
284
285
  this.#hookListeners.clear()
285
286
  this.#pluginsWithEventGenerators.clear()
287
+ // Release resolver closures — the driver is rebuilt for each build() call
288
+ // so there is no value in retaining these maps after disposal.
289
+ this.#resolvers.clear()
290
+ this.#defaultResolvers.clear()
291
+ // Release the parsed adapter graph and the FileNode cache once the build
292
+ // has finished; the returned `BuildOutput.files` array still references
293
+ // any FileNodes the caller needs to inspect.
294
+ this.fileManager.dispose()
295
+ this.inputNode = undefined
286
296
  }
287
297
 
288
298
  #trackHookListener(event: keyof KubbHooks, handler: (...args: never[]) => void | Promise<void>): void {
@@ -300,7 +310,7 @@ export class PluginDriver {
300
310
  return existingResolver
301
311
  }
302
312
 
303
- const resolver = defineResolver<PluginFactoryOptions>((_ctx) => ({
313
+ const resolver = defineResolver<PluginFactoryOptions>(() => ({
304
314
  name: 'default',
305
315
  pluginName,
306
316
  }))
@@ -422,3 +432,35 @@ export class PluginDriver {
422
432
  return plugin
423
433
  }
424
434
  }
435
+
436
+ /**
437
+ * Handles the return value of a plugin AST hook or generator method.
438
+ *
439
+ * - Renderer output → rendered via the provided `rendererFactory` (e.g. JSX), files stored in `driver.fileManager`
440
+ * - `Array<FileNode>` → added directly into `driver.fileManager`
441
+ * - `void` / `null` / `undefined` → no-op (plugin handled it via `this.upsertFile`)
442
+ *
443
+ * Pass a `rendererFactory` (e.g. `jsxRenderer` from `@kubb/renderer-jsx`) when the result
444
+ * may be a renderer element. Generators that only return `Array<FileNode>` do not need one.
445
+ */
446
+ export async function applyHookResult<TElement = unknown>(
447
+ result: TElement | Array<FileNode> | void,
448
+ driver: PluginDriver,
449
+ rendererFactory?: RendererFactory<TElement>,
450
+ ): Promise<void> {
451
+ if (!result) return
452
+
453
+ if (Array.isArray(result)) {
454
+ driver.fileManager.upsert(...(result as Array<FileNode>))
455
+ return
456
+ }
457
+
458
+ if (!rendererFactory) {
459
+ return
460
+ }
461
+
462
+ const renderer = rendererFactory()
463
+ await renderer.render(result)
464
+ driver.fileManager.upsert(...renderer.files)
465
+ renderer.unmount()
466
+ }
package/src/constants.ts CHANGED
@@ -3,12 +3,16 @@ import type { FileNode } from '@kubb/ast'
3
3
  /**
4
4
  * Base URL for the Kubb Studio web app.
5
5
  */
6
- export const DEFAULT_STUDIO_URL = 'https://studio.kubb.dev' as const
6
+ export const DEFAULT_STUDIO_URL = 'https://kubb.studio' as const
7
7
 
8
8
  /**
9
9
  * Maximum number of files processed in parallel by FileProcessor.
10
+ *
11
+ * Capped at 16 to bound the number of CodeNode trees that are alive simultaneously
12
+ * during rendering; I/O latency is the real bottleneck so higher values offer no
13
+ * meaningful throughput improvement.
10
14
  */
11
- export const PARALLEL_CONCURRENCY_LIMIT = 100
15
+ export const PARALLEL_CONCURRENCY_LIMIT = 16
12
16
 
13
17
  /**
14
18
  * Default banner style written at the top of every generated file.
@@ -1,4 +1,80 @@
1
- import type { Adapter, AdapterFactoryOptions } from './types.ts'
1
+ import type { PossiblePromise } from '@internals/utils'
2
+ import type { ImportNode, InputNode, SchemaNode } from '@kubb/ast'
3
+
4
+ /**
5
+ * Source data passed to an adapter's `parse` function.
6
+ * Mirrors the config input shape with paths resolved to absolute.
7
+ */
8
+ export type AdapterSource = { type: 'path'; path: string } | { type: 'data'; data: string | unknown } | { type: 'paths'; paths: Array<string> }
9
+
10
+ /**
11
+ * Generic type parameters for an adapter definition.
12
+ *
13
+ * - `TName` — unique identifier (e.g. `'oas'`, `'asyncapi'`)
14
+ * - `TOptions` — user-facing options passed to the adapter factory
15
+ * - `TResolvedOptions` — options after defaults applied
16
+ * - `TDocument` — type of the parsed source document
17
+ */
18
+ export type AdapterFactoryOptions<
19
+ TName extends string = string,
20
+ TOptions extends object = object,
21
+ TResolvedOptions extends object = TOptions,
22
+ TDocument = unknown,
23
+ > = {
24
+ name: TName
25
+ options: TOptions
26
+ resolvedOptions: TResolvedOptions
27
+ document: TDocument
28
+ }
29
+
30
+ /**
31
+ * Adapter that converts input files or data into an `InputNode`.
32
+ *
33
+ * Adapters parse different schema formats (OpenAPI, AsyncAPI, Drizzle, etc.) into Kubb's
34
+ * universal intermediate representation that all plugins consume.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { adapterOas } from '@kubb/adapter-oas'
39
+ *
40
+ * export default defineConfig({
41
+ * adapter: adapterOas(),
42
+ * input: { path: './openapi.yaml' },
43
+ * plugins: [pluginTs(), pluginZod()],
44
+ * })
45
+ * ```
46
+ */
47
+ export type Adapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions> = {
48
+ /**
49
+ * Human-readable adapter identifier (e.g. `'oas'`, `'asyncapi'`).
50
+ */
51
+ name: TOptions['name']
52
+ /**
53
+ * Resolved adapter options after defaults have been applied.
54
+ */
55
+ options: TOptions['resolvedOptions']
56
+ /**
57
+ * Parsed source document after the first `parse()` call. `null` before parsing.
58
+ */
59
+ document: TOptions['document'] | null
60
+ inputNode: InputNode | null
61
+ /**
62
+ * Parse the source into a universal `InputNode`.
63
+ */
64
+ parse: (source: AdapterSource) => PossiblePromise<InputNode>
65
+ /**
66
+ * Extract `ImportNode` entries for a schema tree.
67
+ * Returns an empty array before the first `parse()` call.
68
+ *
69
+ * The `resolve` callback receives the collision-corrected schema name and must
70
+ * return `{ name, path }` for the import, or `undefined` to skip it.
71
+ */
72
+ getImports: (node: SchemaNode, resolve: (schemaName: string) => { name: string; path: string }) => Array<ImportNode>
73
+ /**
74
+ * Validate the document at the given path or URL.
75
+ */
76
+ validate: (input: string, options?: { throwOnError?: boolean }) => Promise<void>
77
+ }
2
78
 
3
79
  type AdapterBuilder<T extends AdapterFactoryOptions> = (options: T['options']) => Adapter<T>
4
80