@kubb/core 5.0.0-alpha.31 → 5.0.0-alpha.32

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.
package/src/build.ts CHANGED
@@ -1,13 +1,10 @@
1
- import { dirname, relative, resolve } from 'node:path'
1
+ import { dirname, resolve } from 'node:path'
2
2
  import { AsyncEventEmitter, BuildError, exists, formatMs, getElapsedMs, getRelativePath, URLPath } from '@internals/utils'
3
- import { transform, walk } from '@kubb/ast'
4
- import type { OperationNode } from '@kubb/ast/types'
5
- import type { Fabric as FabricType } from '@kubb/fabric-core/types'
6
- import { createFabric } from '@kubb/react-fabric'
7
- import { fsPlugin } from '@kubb/react-fabric/plugins'
3
+ import { createExport, createFile, transform, walk } from '@kubb/ast'
4
+ import type { ExportNode, FileNode, OperationNode } from '@kubb/ast/types'
8
5
  import { BARREL_FILENAME, DEFAULT_BANNER, DEFAULT_CONCURRENCY, DEFAULT_EXTENSION, DEFAULT_STUDIO_URL } from './constants.ts'
9
- import { defineParser } from './defineParser.ts'
10
- import type * as KubbFile from './KubbFile.ts'
6
+ import type { Parser } from './defineParser.ts'
7
+ import { FileProcessor } from './FileProcessor.ts'
11
8
  import { PluginDriver } from './PluginDriver.ts'
12
9
  import { applyHookResult } from './renderNode.tsx'
13
10
  import { fsStorage } from './storages/fsStorage.ts'
@@ -30,8 +27,7 @@ type BuildOutput = {
30
27
  * Plugins that threw during installation, paired with the caught error.
31
28
  */
32
29
  failedPlugins: Set<{ plugin: Plugin; error: Error }>
33
- fabric: FabricType
34
- files: Array<KubbFile.ResolvedFile>
30
+ files: Array<FileNode>
35
31
  driver: PluginDriver
36
32
  /**
37
33
  * Elapsed time in milliseconds for each plugin, keyed by plugin name.
@@ -41,7 +37,7 @@ type BuildOutput = {
41
37
  /**
42
38
  * Raw generated source, keyed by absolute file path.
43
39
  */
44
- sources: Map<KubbFile.Path, string>
40
+ sources: Map<string, string>
45
41
  }
46
42
 
47
43
  /**
@@ -49,10 +45,10 @@ type BuildOutput = {
49
45
  */
50
46
  type SetupResult = {
51
47
  events: AsyncEventEmitter<KubbEvents>
52
- fabric: FabricType
53
48
  driver: PluginDriver
54
- sources: Map<KubbFile.Path, string>
49
+ sources: Map<string, string>
55
50
  config: Config
51
+ storage: Storage | null
56
52
  }
57
53
 
58
54
  /**
@@ -61,7 +57,7 @@ type SetupResult = {
61
57
  * - Validates the input path (when applicable).
62
58
  * - Applies config defaults (`root`, `output.*`, `devtools`).
63
59
  * - Creates the Fabric instance and wires storage, format, and lint hooks.
64
- * - Runs the adapter (if configured) to produce the universal `RootNode`.
60
+ * - Runs the adapter (if configured) to produce the universal `InputNode`.
65
61
  * When no adapter is supplied and `@kubb/adapter-oas` is installed as an
66
62
  *
67
63
  * Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
@@ -70,7 +66,7 @@ type SetupResult = {
70
66
  export async function setup(options: BuildOptions): Promise<SetupResult> {
71
67
  const { config: userConfig, events = new AsyncEventEmitter<KubbEvents>() } = options
72
68
 
73
- const sources: Map<KubbFile.Path, string> = new Map<KubbFile.Path, string>()
69
+ const sources: Map<string, string> = new Map<string, string>()
74
70
  const diagnosticInfo = getDiagnosticInfo()
75
71
 
76
72
  if (Array.isArray(userConfig.input)) {
@@ -145,8 +141,8 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
145
141
 
146
142
  // write: false is the explicit dry-run opt-out; otherwise use the provided
147
143
  // storage or fall back to fsStorage (backwards-compatible default).
148
- // Keys are root-relative (e.g. `src/gen/api/getPets.ts`) so fsStorage()
149
- // needs no configuration it resolves them against process.cwd().
144
+ // Storage keys are the absolute file.path values so fsStorage() resolves
145
+ // them correctly regardless of the current working directory.
150
146
  const storage: Storage | null = config.output.write === false ? null : (config.output.storage ?? fsStorage())
151
147
 
152
148
  if (config.output.clean) {
@@ -157,70 +153,12 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
157
153
  await storage?.clear(resolve(config.root, config.output.path))
158
154
  }
159
155
 
160
- const fabric = createFabric()
161
- fabric.use(fsPlugin)
162
-
163
- for (const parser of config.parsers) {
164
- fabric.use(parser)
165
- }
166
- // Catch-all fallback: joins all source values for any unhandled extension
167
- fabric.use(
168
- defineParser({
169
- name: 'fallback',
170
- extNames: undefined,
171
- parse(file) {
172
- return file.sources
173
- .map((item) => item.value)
174
- .filter((value): value is string => value != null)
175
- .join('\n\n')
176
- },
177
- }),
178
- )
179
-
180
- fabric.context.on('files:processing:start', (files) => {
181
- events.emit('files:processing:start', files)
182
- events.emit('debug', {
183
- date: new Date(),
184
- logs: [`Writing ${files.length} files...`],
185
- })
186
- })
187
-
188
- fabric.context.on('file:processing:update', async (params) => {
189
- const { file, source } = params
190
- await events.emit('file:processing:update', {
191
- ...params,
192
- config: config,
193
- source,
194
- })
195
-
196
- if (source) {
197
- // Key is root-relative so it's meaningful for any backend (fs, S3, Redis…)
198
- const key = relative(resolve(config.root), file.path)
199
- await storage?.setItem(key, source)
200
- sources.set(file.path, source)
201
- }
202
- })
203
-
204
- fabric.context.on('files:processing:end', async (files) => {
205
- await events.emit('files:processing:end', files)
206
- await events.emit('debug', {
207
- date: new Date(),
208
- logs: [`✓ File write process completed for ${files.length} files`],
209
- })
210
- })
211
-
212
- await events.emit('debug', {
213
- date: new Date(),
214
- logs: ['✓ Fabric initialized', ` • Storage: ${storage ? storage.name : 'disabled (dry-run)'}`, ` • Barrel type: ${config.output.barrelType || 'none'}`],
215
- })
216
-
217
156
  const driver = new PluginDriver(config, {
218
- fabric,
219
157
  events,
220
158
  concurrency: DEFAULT_CONCURRENCY,
221
159
  })
222
160
 
223
- // Run the adapter to produce the universal RootNode.
161
+ // Run the adapter to produce the universal InputNode.
224
162
 
225
163
  const adapter = config.adapter
226
164
  if (!adapter) {
@@ -234,23 +172,23 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
234
172
  })
235
173
 
236
174
  driver.adapter = adapter
237
- driver.rootNode = await adapter.parse(source)
175
+ driver.inputNode = await adapter.parse(source)
238
176
 
239
177
  await events.emit('debug', {
240
178
  date: new Date(),
241
179
  logs: [
242
- `✓ Adapter '${adapter.name}' resolved RootNode`,
243
- ` • Schemas: ${driver.rootNode.schemas.length}`,
244
- ` • Operations: ${driver.rootNode.operations.length}`,
180
+ `✓ Adapter '${adapter.name}' resolved InputNode`,
181
+ ` • Schemas: ${driver.inputNode.schemas.length}`,
182
+ ` • Operations: ${driver.inputNode.operations.length}`,
245
183
  ],
246
184
  })
247
185
 
248
186
  return {
249
187
  config,
250
188
  events,
251
- fabric,
252
189
  driver,
253
190
  sources,
191
+ storage,
254
192
  }
255
193
  }
256
194
 
@@ -261,7 +199,7 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
261
199
  * Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
262
200
  */
263
201
  export async function build(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
264
- const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides)
202
+ const { files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides)
265
203
 
266
204
  if (error) {
267
205
  throw error
@@ -275,7 +213,6 @@ export async function build(options: BuildOptions, overrides?: SetupResult): Pro
275
213
 
276
214
  return {
277
215
  failedPlugins,
278
- fabric,
279
216
  files,
280
217
  driver,
281
218
  pluginTimings,
@@ -291,20 +228,20 @@ export async function build(options: BuildOptions, overrides?: SetupResult): Pro
291
228
  * - Each hook accepts a single handler **or an array** — all entries are called in sequence.
292
229
  * - Nodes that are excluded by `exclude`/`include` plugin options are skipped automatically.
293
230
  * - Return values are handled via `applyHookResult`: React elements are rendered,
294
- * `KubbFile.File[]` are written via upsert, and `void` is a no-op (manual handling).
231
+ * `FileNode[]` are written via upsert, and `void` is a no-op (manual handling).
295
232
  * - Barrel files are generated automatically when `output.barrelType` is set.
296
233
  */
297
234
  async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promise<void> {
298
- const { adapter, rootNode, resolver, fabric } = context
235
+ const { adapter, inputNode, resolver, driver } = context
299
236
  const { exclude, include, override } = plugin.options
300
237
 
301
- if (!adapter || !rootNode) {
238
+ if (!adapter || !inputNode) {
302
239
  throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. pluginOas()) before this plugin in your Kubb config.`)
303
240
  }
304
241
 
305
242
  const collectedOperations: Array<OperationNode> = []
306
243
 
307
- await walk(rootNode, {
244
+ await walk(inputNode, {
308
245
  depth: 'shallow',
309
246
  async schema(node) {
310
247
  if (!plugin.schema) return
@@ -313,7 +250,7 @@ async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promis
313
250
  if (options === null) return
314
251
  const result = await plugin.schema.call(context, transformedNode, options)
315
252
 
316
- await applyHookResult(result, fabric)
253
+ await applyHookResult(result, driver)
317
254
  },
318
255
  async operation(node) {
319
256
  const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
@@ -322,7 +259,7 @@ async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promis
322
259
  collectedOperations.push(transformedNode)
323
260
  if (plugin.operation) {
324
261
  const result = await plugin.operation.call(context, transformedNode, options)
325
- await applyHookResult(result, fabric)
262
+ await applyHookResult(result, driver)
326
263
  }
327
264
  }
328
265
  },
@@ -331,7 +268,7 @@ async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promis
331
268
  if (plugin.operations && collectedOperations.length > 0) {
332
269
  const result = await plugin.operations.call(context, collectedOperations, plugin.options)
333
270
 
334
- await applyHookResult(result, fabric)
271
+ await applyHookResult(result, driver)
335
272
  }
336
273
  }
337
274
 
@@ -340,13 +277,14 @@ async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promis
340
277
  *
341
278
  * - Installs each plugin in order, recording failures in `failedPlugins`.
342
279
  * - Generates the root barrel file when `output.barrelType` is set.
343
- * - Writes all files through Fabric.
280
+ * - Writes all files through the driver's FileManager and FileProcessor.
344
281
  *
345
282
  * Returns a {@link BuildOutput} even on failure — inspect `error` and
346
283
  * `failedPlugins` to determine whether the build succeeded.
347
284
  */
348
285
  export async function safeBuild(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
349
- const { fabric, driver, events, sources } = overrides ? overrides : await setup(options)
286
+ const setupResult = overrides ? overrides : await setup(options)
287
+ const { driver, events, sources, storage } = setupResult
350
288
 
351
289
  const failedPlugins = new Set<{ plugin: Plugin; error: Error }>()
352
290
  // in ms
@@ -379,7 +317,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
379
317
  }
380
318
 
381
319
  if (output) {
382
- const barrelFiles = await getBarrelFiles(fabric.files, {
320
+ const barrelFiles = await getBarrelFiles(driver.fileManager.files, {
383
321
  type: output.barrelType ?? 'named',
384
322
  root,
385
323
  output,
@@ -433,7 +371,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
433
371
  logs: ['Generating barrel file', ` • Type: ${config.output.barrelType}`, ` • Path: ${rootPath}`],
434
372
  })
435
373
 
436
- const barrelFiles = fabric.files.filter((file) => {
374
+ const barrelFiles = driver.fileManager.files.filter((file) => {
437
375
  return file.sources.some((source) => source.isIndexable)
438
376
  })
439
377
 
@@ -442,21 +380,21 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
442
380
  logs: [`Found ${barrelFiles.length} indexable files for barrel export`],
443
381
  })
444
382
 
445
- const existingBarrel = fabric.files.find((f) => f.path === rootPath)
383
+ const existingBarrel = driver.fileManager.files.find((f) => f.path === rootPath)
446
384
  const existingExports = new Set(
447
385
  existingBarrel?.exports?.flatMap((e) => (Array.isArray(e.name) ? e.name : [e.name])).filter((n): n is string => Boolean(n)) ?? [],
448
386
  )
449
387
 
450
- const rootFile: KubbFile.File = {
388
+ const rootFile = createFile<object>({
451
389
  path: rootPath,
452
390
  baseName: BARREL_FILENAME,
453
- exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }),
391
+ exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }).map((e) => createExport(e)),
454
392
  sources: [],
455
393
  imports: [],
456
394
  meta: {},
457
- }
395
+ })
458
396
 
459
- await fabric.upsertFile(rootFile)
397
+ driver.fileManager.upsert(rootFile)
460
398
 
461
399
  await events.emit('debug', {
462
400
  date: new Date(),
@@ -464,9 +402,55 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
464
402
  })
465
403
  }
466
404
 
467
- const files = [...fabric.files]
405
+ const files = driver.fileManager.files
406
+
407
+ // Build a parsers map from config.parsers
408
+ const parsersMap = new Map<FileNode['extname'], Parser>()
409
+ for (const parser of config.parsers) {
410
+ if (parser.extNames) {
411
+ for (const extname of parser.extNames) {
412
+ parsersMap.set(extname, parser)
413
+ }
414
+ }
415
+ }
416
+
417
+ const fileProcessor = new FileProcessor()
418
+
419
+ await events.emit('debug', {
420
+ date: new Date(),
421
+ logs: [`Writing ${files.length} files...`],
422
+ })
468
423
 
469
- await fabric.write({ extension: config.output.extension })
424
+ await fileProcessor.run(files, {
425
+ parsers: parsersMap,
426
+ extension: config.output.extension,
427
+ onStart: async (processingFiles) => {
428
+ await events.emit('files:processing:start', processingFiles)
429
+ },
430
+ onUpdate: async ({ file, source, processed, total, percentage }) => {
431
+ await events.emit('file:processing:update', {
432
+ file,
433
+ source,
434
+ processed,
435
+ total,
436
+ percentage,
437
+ config,
438
+ })
439
+ if (source) {
440
+ // Use the absolute file.path as the storage key so fsStorage resolves
441
+ // it correctly regardless of the current working directory.
442
+ await storage?.setItem(file.path, source)
443
+ sources.set(file.path, source)
444
+ }
445
+ },
446
+ onEnd: async (processedFiles) => {
447
+ await events.emit('files:processing:end', processedFiles)
448
+ await events.emit('debug', {
449
+ date: new Date(),
450
+ logs: [`✓ File write process completed for ${processedFiles.length} files`],
451
+ })
452
+ },
453
+ })
470
454
 
471
455
  // Call buildEnd() on each plugin after all files are written
472
456
  for (const plugin of driver.plugins.values()) {
@@ -478,7 +462,6 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
478
462
 
479
463
  return {
480
464
  failedPlugins,
481
- fabric,
482
465
  files,
483
466
  driver,
484
467
  pluginTimings,
@@ -487,7 +470,6 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
487
470
  } catch (error) {
488
471
  return {
489
472
  failedPlugins,
490
- fabric,
491
473
  files: [],
492
474
  driver,
493
475
  pluginTimings,
@@ -498,14 +480,14 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
498
480
  }
499
481
 
500
482
  type BuildBarrelExportsParams = {
501
- barrelFiles: KubbFile.ResolvedFile[]
483
+ barrelFiles: FileNode[]
502
484
  rootDir: string
503
485
  existingExports: Set<string>
504
486
  config: Config
505
487
  driver: PluginDriver
506
488
  }
507
489
 
508
- function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }: BuildBarrelExportsParams): KubbFile.Export[] {
490
+ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }: BuildBarrelExportsParams): ExportNode[] {
509
491
  const pluginNameMap = new Map<string, Plugin>()
510
492
  for (const plugin of driver.plugins.values()) {
511
493
  pluginNameMap.set(plugin.name, plugin)
@@ -533,11 +515,11 @@ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, dri
533
515
  }
534
516
 
535
517
  return [
536
- {
518
+ createExport({
537
519
  name: exportName,
538
520
  path: getRelativePath(rootDir, file.path),
539
521
  isTypeOnly: config.output.barrelType === 'all' ? containsOnlyTypes : source.isTypeOnly,
540
- } satisfies KubbFile.Export,
522
+ }),
541
523
  ]
542
524
  })
543
525
  })
package/src/constants.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type * as KubbFile from './KubbFile.ts'
1
+ import type { FileNode } from '@kubb/ast/types'
2
2
 
3
3
  /**
4
4
  * Base URL for the Kubb Studio web app.
@@ -10,6 +10,11 @@ export const DEFAULT_STUDIO_URL = 'https://studio.kubb.dev' as const
10
10
  */
11
11
  export const DEFAULT_CONCURRENCY = 15
12
12
 
13
+ /**
14
+ * Maximum number of files processed in parallel by FileProcessor.
15
+ */
16
+ export const PARALLEL_CONCURRENCY_LIMIT = 100
17
+
13
18
  /**
14
19
  * File name used for generated barrel (index) files.
15
20
  */
@@ -23,7 +28,7 @@ export const DEFAULT_BANNER = 'simple' as const
23
28
  /**
24
29
  * Default file-extension mapping used when no explicit mapping is configured.
25
30
  */
26
- export const DEFAULT_EXTENSION: Record<KubbFile.Extname, KubbFile.Extname | ''> = { '.ts': '.ts' }
31
+ export const DEFAULT_EXTENSION: Record<FileNode['extname'], FileNode['extname'] | ''> = { '.ts': '.ts' }
27
32
 
28
33
  /**
29
34
  * Characters recognized as path separators on both POSIX and Windows.
@@ -1,7 +1,6 @@
1
1
  import type { PossiblePromise } from '@internals/utils'
2
- import type { OperationNode, SchemaNode } from '@kubb/ast/types'
2
+ import type { FileNode, OperationNode, SchemaNode } from '@kubb/ast/types'
3
3
  import type { FabricReactNode } from '@kubb/react-fabric/types'
4
- import type * as KubbFile from './KubbFile.ts'
5
4
  import { applyHookResult } from './renderNode.tsx'
6
5
  import type { GeneratorContext, PluginFactoryOptions } from './types.ts'
7
6
 
@@ -10,10 +9,10 @@ export type { GeneratorContext } from './types.ts'
10
9
  /**
11
10
  * A generator is a named object with optional `schema`, `operation`, and `operations`
12
11
  * methods. Each method is called with `this = PluginContext` of the parent plugin,
13
- * giving full access to `this.config`, `this.resolver`, `this.adapter`, `this.fabric`,
12
+ * giving full access to `this.config`, `this.resolver`, `this.adapter`,
14
13
  * `this.driver`, etc.
15
14
  *
16
- * Return a React element, an array of `KubbFile.File`, or `void` to handle file
15
+ * Return a React element, an array of `FileNode`, or `void` to handle file
17
16
  * writing manually via `this.upsertFile`. Both React and core (non-React) generators
18
17
  * use the same method signatures — the return type determines how output is handled.
19
18
  *
@@ -36,32 +35,32 @@ export type Generator<TOptions extends PluginFactoryOptions = PluginFactoryOptio
36
35
  name: string
37
36
  /**
38
37
  * Called for each schema node in the AST walk.
39
- * `this` is the parent plugin's context with `adapter` and `rootNode` guaranteed present.
38
+ * `this` is the parent plugin's context with `adapter` and `inputNode` guaranteed present.
40
39
  * `options` contains the per-node resolved options (after exclude/include/override).
41
40
  */
42
41
  schema?: (
43
42
  this: GeneratorContext<TOptions>,
44
43
  node: SchemaNode,
45
44
  options: TOptions['resolvedOptions'],
46
- ) => PossiblePromise<FabricReactNode | Array<KubbFile.File> | void>
45
+ ) => PossiblePromise<FabricReactNode | Array<FileNode> | void>
47
46
  /**
48
47
  * Called for each operation node in the AST walk.
49
- * `this` is the parent plugin's context with `adapter` and `rootNode` guaranteed present.
48
+ * `this` is the parent plugin's context with `adapter` and `inputNode` guaranteed present.
50
49
  */
51
50
  operation?: (
52
51
  this: GeneratorContext<TOptions>,
53
52
  node: OperationNode,
54
53
  options: TOptions['resolvedOptions'],
55
- ) => PossiblePromise<FabricReactNode | Array<KubbFile.File> | void>
54
+ ) => PossiblePromise<FabricReactNode | Array<FileNode> | void>
56
55
  /**
57
56
  * Called once after all operations have been walked.
58
- * `this` is the parent plugin's context with `adapter` and `rootNode` guaranteed present.
57
+ * `this` is the parent plugin's context with `adapter` and `inputNode` guaranteed present.
59
58
  */
60
59
  operations?: (
61
60
  this: GeneratorContext<TOptions>,
62
61
  nodes: Array<OperationNode>,
63
62
  options: TOptions['resolvedOptions'],
64
- ) => PossiblePromise<FabricReactNode | Array<KubbFile.File> | void>
63
+ ) => PossiblePromise<FabricReactNode | Array<FileNode> | void>
65
64
  }
66
65
 
67
66
  /**
@@ -103,7 +102,7 @@ export function mergeGenerators<TOptions extends PluginFactoryOptions = PluginFa
103
102
  if (!gen.schema) continue
104
103
  const result = await gen.schema.call(this, node, options)
105
104
 
106
- await applyHookResult(result, this.fabric)
105
+ await applyHookResult(result, this.driver)
107
106
  }
108
107
  },
109
108
  async operation(node, options) {
@@ -111,7 +110,7 @@ export function mergeGenerators<TOptions extends PluginFactoryOptions = PluginFa
111
110
  if (!gen.operation) continue
112
111
  const result = await gen.operation.call(this, node, options)
113
112
 
114
- await applyHookResult(result, this.fabric)
113
+ await applyHookResult(result, this.driver)
115
114
  }
116
115
  },
117
116
  async operations(nodes, options) {
@@ -119,7 +118,7 @@ export function mergeGenerators<TOptions extends PluginFactoryOptions = PluginFa
119
118
  if (!gen.operations) continue
120
119
  const result = await gen.operations.call(this, nodes, options)
121
120
 
122
- await applyHookResult(result, this.fabric)
121
+ await applyHookResult(result, this.driver)
123
122
  }
124
123
  },
125
124
  }
@@ -1,7 +1,7 @@
1
- import type * as KubbFile from './KubbFile.ts'
1
+ import type { FileNode } from '@kubb/ast/types'
2
2
 
3
3
  type PrintOptions = {
4
- extname?: KubbFile.Extname
4
+ extname?: FileNode['extname']
5
5
  }
6
6
 
7
7
  export type Parser<TMeta extends object = any> = {
@@ -13,7 +13,7 @@ export type Parser<TMeta extends object = any> = {
13
13
  *
14
14
  * @example ['.ts', '.js']
15
15
  */
16
- extNames: Array<KubbFile.Extname> | undefined
16
+ extNames: Array<FileNode['extname']> | undefined
17
17
  /**
18
18
  * @deprecated Will be removed once Fabric no longer requires it.
19
19
  * @default () => {}
@@ -22,7 +22,7 @@ export type Parser<TMeta extends object = any> = {
22
22
  /**
23
23
  * Convert a resolved file to a string.
24
24
  */
25
- parse(file: KubbFile.ResolvedFile<TMeta>, options?: PrintOptions): Promise<string> | string
25
+ parse(file: FileNode<TMeta>, options?: PrintOptions): Promise<string> | string
26
26
  }
27
27
 
28
28
  export type UserParser<TMeta extends object = any> = Omit<Parser<TMeta>, 'type' | 'install'> & {
@@ -1,8 +1,7 @@
1
1
  import path from 'node:path'
2
2
  import { camelCase, pascalCase } from '@internals/utils'
3
- import { isOperationNode, isSchemaNode } from '@kubb/ast'
4
- import type { Node, OperationNode, RootNode, SchemaNode } from '@kubb/ast/types'
5
- import type * as KubbFile from './KubbFile.ts'
3
+ import { createFile, isOperationNode, isSchemaNode } from '@kubb/ast'
4
+ import type { FileNode, InputNode, Node, OperationNode, SchemaNode } from '@kubb/ast/types'
6
5
  import { getMode } from './PluginDriver.ts'
7
6
  import type {
8
7
  Config,
@@ -195,24 +194,24 @@ export function defaultResolveOptions<TOptions>(
195
194
  * // → '/src/types'
196
195
  * ```
197
196
  */
198
- export function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }: ResolverPathParams, { root, output, group }: ResolverContext): KubbFile.Path {
197
+ export function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }: ResolverPathParams, { root, output, group }: ResolverContext): string {
199
198
  const mode = pathMode ?? getMode(path.resolve(root, output.path))
200
199
 
201
200
  if (mode === 'single') {
202
- return path.resolve(root, output.path) as KubbFile.Path
201
+ return path.resolve(root, output.path)
203
202
  }
204
203
 
205
204
  if (group && (groupPath || tag)) {
206
- return path.resolve(root, output.path, group.name({ group: group.type === 'path' ? groupPath! : tag! }), baseName) as KubbFile.Path
205
+ return path.resolve(root, output.path, group.name({ group: group.type === 'path' ? groupPath! : tag! }), baseName)
207
206
  }
208
207
 
209
- return path.resolve(root, output.path, baseName) as KubbFile.Path
208
+ return path.resolve(root, output.path, baseName)
210
209
  }
211
210
 
212
211
  /**
213
212
  * Default file resolver used by `defineResolver`.
214
213
  *
215
- * Resolves a `KubbFile.File` by combining name resolution (`resolver.default`) with
214
+ * Resolves a `FileNode` by combining name resolution (`resolver.default`) with
216
215
  * path resolution (`resolver.resolvePath`). The resolved file always has empty
217
216
  * `sources`, `imports`, and `exports` arrays — consumers populate those separately.
218
217
  *
@@ -236,22 +235,22 @@ export function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }:
236
235
  * // → { baseName: 'listPets.ts', path: '/src/types/petsController/listPets.ts', ... }
237
236
  * ```
238
237
  */
239
- export function defaultResolveFile(this: Resolver, { name, extname, tag, path: groupPath }: ResolverFileParams, context: ResolverContext): KubbFile.File {
238
+ export function defaultResolveFile(this: Resolver, { name, extname, tag, path: groupPath }: ResolverFileParams, context: ResolverContext): FileNode {
240
239
  const pathMode = getMode(path.resolve(context.root, context.output.path))
241
240
  const resolvedName = pathMode === 'single' ? '' : this.default(name, 'file')
242
- const baseName = `${resolvedName}${extname}` as KubbFile.BaseName
241
+ const baseName = `${resolvedName}${extname}` as FileNode['baseName']
243
242
  const filePath = this.resolvePath({ baseName, pathMode, tag, path: groupPath }, context)
244
243
 
245
- return {
244
+ return createFile({
246
245
  path: filePath,
247
- baseName: path.basename(filePath) as KubbFile.BaseName,
246
+ baseName: path.basename(filePath) as `${string}.${string}`,
248
247
  meta: {
249
248
  pluginName: this.pluginName,
250
249
  },
251
250
  sources: [],
252
251
  imports: [],
253
252
  exports: [],
254
- }
253
+ })
255
254
  }
256
255
 
257
256
  /**
@@ -333,13 +332,13 @@ export function buildDefaultBanner({
333
332
  *
334
333
  * @example Function banner with node
335
334
  * ```ts
336
- * defaultResolveBanner(rootNode, { output: { banner: (node) => `// v${node.version}` }, config })
335
+ * defaultResolveBanner(inputNode, { output: { banner: (node) => `// v${node.version}` }, config })
337
336
  * // → '// v3.0.0'
338
337
  * ```
339
338
  *
340
339
  * @example No user banner — Kubb notice with OAS metadata
341
340
  * ```ts
342
- * defaultResolveBanner(rootNode, { config })
341
+ * defaultResolveBanner(inputNode, { config })
343
342
  * // → '/** Generated by Kubb ... Title: Pet Store ... *\/'
344
343
  * ```
345
344
  *
@@ -349,7 +348,7 @@ export function buildDefaultBanner({
349
348
  * // → undefined
350
349
  * ```
351
350
  */
352
- export function defaultResolveBanner(node: RootNode | undefined, { output, config }: ResolveBannerContext): string | undefined {
351
+ export function defaultResolveBanner(node: InputNode | undefined, { output, config }: ResolveBannerContext): string | undefined {
353
352
  if (typeof output?.banner === 'function') {
354
353
  return output.banner(node)
355
354
  }
@@ -381,11 +380,11 @@ export function defaultResolveBanner(node: RootNode | undefined, { output, confi
381
380
  *
382
381
  * @example Function footer with node
383
382
  * ```ts
384
- * defaultResolveFooter(rootNode, { output: { footer: (node) => `// ${node.title}` }, config })
383
+ * defaultResolveFooter(inputNode, { output: { footer: (node) => `// ${node.title}` }, config })
385
384
  * // → '// Pet Store'
386
385
  * ```
387
386
  */
388
- export function defaultResolveFooter(node: RootNode | undefined, { output }: ResolveBannerContext): string | undefined {
387
+ export function defaultResolveFooter(node: InputNode | undefined, { output }: ResolveBannerContext): string | undefined {
389
388
  if (typeof output?.footer === 'function') {
390
389
  return node ? output.footer(node) : undefined
391
390
  }
@@ -403,7 +402,7 @@ export function defaultResolveFooter(node: RootNode | undefined, { output }: Res
403
402
  * - `default` — name casing strategy (camelCase / PascalCase)
404
403
  * - `resolveOptions` — include/exclude/override filtering
405
404
  * - `resolvePath` — output path computation
406
- * - `resolveFile` — full `KubbFile.File` construction
405
+ * - `resolveFile` — full `FileNode` construction
407
406
  *
408
407
  * Methods in the builder have access to `this` (the full resolver object), so they
409
408
  * can call other resolver methods without circular imports.