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

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 (44) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +20 -123
  3. package/dist/index.cjs +2430 -1184
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +136 -140
  6. package/dist/index.js +2419 -1173
  7. package/dist/index.js.map +1 -1
  8. package/dist/mocks.cjs +86 -32
  9. package/dist/mocks.cjs.map +1 -1
  10. package/dist/mocks.d.ts +37 -14
  11. package/dist/mocks.js +88 -36
  12. package/dist/mocks.js.map +1 -1
  13. package/dist/types-Ba5Mo-G8.d.ts +3016 -0
  14. package/dist/usingCtx-BdYw7ICK.cjs +954 -0
  15. package/dist/usingCtx-BdYw7ICK.cjs.map +1 -0
  16. package/dist/usingCtx-njZUKKsY.js +822 -0
  17. package/dist/usingCtx-njZUKKsY.js.map +1 -0
  18. package/package.json +7 -28
  19. package/dist/PluginDriver-C1OsqGBJ.cjs +0 -1086
  20. package/dist/PluginDriver-C1OsqGBJ.cjs.map +0 -1
  21. package/dist/PluginDriver-CGypdXHg.js +0 -989
  22. package/dist/PluginDriver-CGypdXHg.js.map +0 -1
  23. package/dist/createKubb-BSfMDBwR.d.ts +0 -2176
  24. package/src/FileManager.ts +0 -123
  25. package/src/FileProcessor.ts +0 -91
  26. package/src/PluginDriver.ts +0 -466
  27. package/src/constants.ts +0 -39
  28. package/src/createAdapter.ts +0 -108
  29. package/src/createKubb.ts +0 -1380
  30. package/src/createRenderer.ts +0 -57
  31. package/src/createStorage.ts +0 -70
  32. package/src/defineGenerator.ts +0 -175
  33. package/src/defineLogger.ts +0 -58
  34. package/src/defineMiddleware.ts +0 -62
  35. package/src/defineParser.ts +0 -44
  36. package/src/definePlugin.ts +0 -379
  37. package/src/defineResolver.ts +0 -654
  38. package/src/devtools.ts +0 -66
  39. package/src/index.ts +0 -20
  40. package/src/mocks.ts +0 -177
  41. package/src/storages/fsStorage.ts +0 -89
  42. package/src/storages/memoryStorage.ts +0 -55
  43. package/src/types.ts +0 -42
  44. /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/src/createKubb.ts DELETED
@@ -1,1380 +0,0 @@
1
- import { resolve } from 'node:path'
2
- import { version as nodeVersion } from 'node:process'
3
- import type { PossiblePromise } from '@internals/utils'
4
- import { AsyncEventEmitter, BuildError, exists, formatMs, getElapsedMs, URLPath } from '@internals/utils'
5
- import type { FileNode, InputNode, OperationNode, SchemaNode } from '@kubb/ast'
6
- import { collectUsedSchemaNames, transform, walk } from '@kubb/ast'
7
- import { version as KubbVersion } from '../package.json'
8
- import { DEFAULT_BANNER, DEFAULT_EXTENSION, DEFAULT_STUDIO_URL } from './constants.ts'
9
- import type { Adapter, AdapterSource } from './createAdapter.ts'
10
- import type { RendererFactory } from './createRenderer.ts'
11
- import { createStorage, type Storage } from './createStorage.ts'
12
- import type { GeneratorContext, Generator } from './defineGenerator.ts'
13
- import type { Middleware } from './defineMiddleware.ts'
14
- import type { Parser } from './defineParser.ts'
15
- import type { KubbPluginEndContext, KubbPluginSetupContext, KubbPluginStartContext, NormalizedPlugin, Plugin } from './definePlugin.ts'
16
- import { FileProcessor } from './FileProcessor.ts'
17
- import { applyHookResult, PluginDriver } from './PluginDriver.ts'
18
- import { fsStorage } from './storages/fsStorage.ts'
19
-
20
- /**
21
- * Safely extracts a type from a registry, returning `{}` if the key doesn't exist.
22
- * Enables optional interface augmentation for `Kubb.ConfigOptionsRegistry` and `Kubb.PluginOptionsRegistry`
23
- * without requiring changes to core.
24
- *
25
- * @internal
26
- */
27
- type ExtractRegistryKey<T, K extends PropertyKey> = K extends keyof T ? T[K] : {}
28
-
29
- /**
30
- * Reference to an input file to generate code from.
31
- *
32
- * Specify an absolute path or a path relative to the config file location.
33
- * The adapter will parse this file (e.g., OpenAPI YAML or JSON) into the universal AST.
34
- */
35
- export type InputPath = {
36
- /**
37
- * Path to your Swagger/OpenAPI file, absolute or relative to the config file location.
38
- *
39
- * @example
40
- * ```ts
41
- * { path: './petstore.yaml' }
42
- * { path: '/absolute/path/to/openapi.json' }
43
- * ```
44
- */
45
- path: string
46
- }
47
-
48
- /**
49
- * Inline input data to generate code from.
50
- *
51
- * Useful when you want to pass the specification directly instead of from a file.
52
- * Can be a string (YAML/JSON) or a parsed object.
53
- */
54
- export type InputData = {
55
- /**
56
- * Swagger/OpenAPI data as a string (YAML/JSON) or a parsed object.
57
- *
58
- * @example
59
- * ```ts
60
- * { data: fs.readFileSync('./openapi.yaml', 'utf8') }
61
- * { data: { openapi: '3.1.0', info: { ... } } }
62
- * ```
63
- */
64
- data: string | unknown
65
- }
66
-
67
- type Input = InputPath | InputData
68
-
69
- /**
70
- * Build configuration for Kubb code generation.
71
- *
72
- * The Config is the main entry point for customizing how Kubb generates code. It specifies:
73
- * - What to generate from (adapter + input)
74
- * - Where to output generated code (output)
75
- * - How to generate (plugins + middleware)
76
- * - Runtime details (parsers, storage, renderer)
77
- *
78
- * See `UserConfig` for a relaxed version with sensible defaults.
79
- *
80
- * @private
81
- */
82
- export type Config<TInput = Input> = {
83
- /**
84
- * Display name for this configuration in CLI output and logs.
85
- * Useful when running multiple builds with `defineConfig` arrays.
86
- *
87
- * @example
88
- * ```ts
89
- * name: 'api-client'
90
- * ```
91
- */
92
- name?: string
93
- /**
94
- * Project root directory, absolute or relative to the config file.
95
- * @default process.cwd()
96
- */
97
- root: string
98
- /**
99
- * Parsers that convert generated files to strings.
100
- * Each parser handles specific extensions (e.g. `.ts`, `.tsx`).
101
- * A fallback parser is appended for unhandled extensions.
102
- * When omitted, defaults to `parserTs` from `@kubb/parser-ts`.
103
- *
104
- * @default [parserTs] from `@kubb/parser-ts`
105
- * @example
106
- * ```ts
107
- * import { parserTs, tsxParser } from '@kubb/parser-ts'
108
- * export default defineConfig({
109
- * parsers: [parserTs, tsxParser],
110
- * })
111
- * ```
112
- */
113
- parsers: Array<Parser>
114
- /**
115
- * Adapter that parses input files into the universal `InputNode` representation.
116
- * Use `@kubb/adapter-oas` for OpenAPI/Swagger or `@kubb/adapter-asyncapi` for other formats.
117
- *
118
- * When omitted, Kubb runs in plugin-only mode: `kubb:plugin:setup` fires and files
119
- * injected via `injectFile` are written, but no AST walk occurs and generator hooks
120
- * (`kubb:generate:schema`, `kubb:generate:operation`) are never emitted.
121
- *
122
- * @example
123
- * ```ts
124
- * import { adapterOas } from '@kubb/adapter-oas'
125
- * export default defineConfig({
126
- * adapter: adapterOas(),
127
- * input: { path: './petstore.yaml' },
128
- * })
129
- * ```
130
- */
131
- adapter?: Adapter
132
- /**
133
- * Source file or data to generate code from.
134
- * Use `input.path` for a file path or `input.data` for inline data.
135
- * Required when an adapter is configured; omit when running in plugin-only mode.
136
- */
137
- input?: TInput
138
- output: {
139
- /**
140
- * Output directory for generated files, absolute or relative to `root`.
141
- *
142
- * All generated files will be written under this directory. Subdirectories can be created
143
- * by plugins based on grouping strategy (by tag, path, etc.).
144
- *
145
- * @example
146
- * ```ts
147
- * output: {
148
- * path: './src/gen', // generates ./src/gen/api.ts, ./src/gen/types.ts, etc.
149
- * }
150
- * ```
151
- */
152
- path: string
153
- /**
154
- * Remove all files from the output directory before starting the build.
155
- *
156
- * Useful to ensure old generated files aren't mixed with new ones.
157
- * Set to `true` for fresh builds, `false` to preserve manual edits in output dir.
158
- *
159
- * @default false
160
- * @example
161
- * ```ts
162
- * clean: true // wipes ./src/gen/* before generating
163
- * ```
164
- */
165
- clean?: boolean
166
- /**
167
- * Auto-format generated files after code generation completes.
168
- *
169
- * Applies a code formatter to all generated files. Use `'auto'` to detect which formatter
170
- * is available on your system. Pass `false` to skip formatting (useful for CI or specific workflows).
171
- *
172
- * @default false
173
- * @example
174
- * ```ts
175
- * format: 'auto' // auto-detect prettier, biome, or oxfmt
176
- * format: 'prettier' // force prettier
177
- * format: false // skip formatting
178
- * ```
179
- */
180
- format?: 'auto' | 'prettier' | 'biome' | 'oxfmt' | false
181
- /**
182
- * Auto-lint generated files after code generation completes.
183
- *
184
- * Analyzes all generated files for style/correctness issues. Use `'auto'` to detect which linter
185
- * is available on your system. Pass `false` to skip linting.
186
- *
187
- * @default false
188
- * @example
189
- * ```ts
190
- * lint: 'auto' // auto-detect oxlint, biome, or eslint
191
- * lint: 'eslint' // force eslint
192
- * lint: false // skip linting
193
- * ```
194
- */
195
- lint?: 'auto' | 'eslint' | 'biome' | 'oxlint' | false
196
- /**
197
- * Map file extensions to different output extensions.
198
- *
199
- * Useful when you want generated `.ts` imports to reference `.js` files or vice versa (e.g., for ESM dual packages).
200
- * Keys are the original extension, values are the output extension. Use empty string `''` to omit extension.
201
- *
202
- * @default { '.ts': '.ts' }
203
- * @example
204
- * ```ts
205
- * extension: { '.ts': '.js' } // generates import './api.js' instead of './api.ts'
206
- * extension: { '.ts': '', '.tsx': '.jsx' }
207
- * ```
208
- */
209
- extension?: Record<FileNode['extname'], FileNode['extname'] | ''>
210
- /**
211
- * Banner text prepended to every generated file.
212
- *
213
- * Useful for auto-generation notices or license headers. Choose a preset or write custom text.
214
- * Use `'simple'` for a basic Kubb banner, `'full'` for detailed metadata, or `false` to omit.
215
- *
216
- * @default 'simple'
217
- * @example
218
- * ```ts
219
- * defaultBanner: 'simple' // "This file was autogenerated by Kubb"
220
- * defaultBanner: 'full' // adds source, title, description, API version
221
- * defaultBanner: false // no banner
222
- * ```
223
- */
224
- defaultBanner?: 'simple' | 'full' | false
225
- /**
226
- * When `true`, overwrites existing files. When `false`, skips generated files that already exist.
227
- *
228
- * Individual plugins can override this setting. This is useful for preventing accidental data loss
229
- * when re-generating while you have local edits in the output folder.
230
- *
231
- * @default false
232
- * @example
233
- * ```ts
234
- * override: true // regenerate everything, even existing files
235
- * override: false // skip files that already exist
236
- * ```
237
- */
238
- override?: boolean
239
- } & ExtractRegistryKey<Kubb.ConfigOptionsRegistry, 'output'>
240
- /**
241
- * Storage backend that controls where and how generated files are persisted.
242
- *
243
- * Defaults to `fsStorage()` which writes to the file system. Pass `memoryStorage()` to keep files in RAM,
244
- * or implement a custom `Storage` interface to write to cloud storage, databases, or other backends.
245
- *
246
- * @default fsStorage()
247
- * @example
248
- * ```ts
249
- * import { memoryStorage } from '@kubb/core'
250
- *
251
- * // Keep generated files in memory (useful for testing, CI pipelines)
252
- * storage: memoryStorage()
253
- *
254
- * // Use custom S3 storage
255
- * storage: myS3Storage()
256
- * ```
257
- *
258
- * @see {@link Storage} interface for implementing custom backends.
259
- */
260
- storage: Storage
261
- /**
262
- * Plugins that execute during the build to generate code and transform the AST.
263
- *
264
- * Each plugin processes the AST produced by the adapter and can emit files for different
265
- * programming languages or formats (TypeScript, Zod schemas, Faker data, etc.).
266
- * Dependencies are enforced — an error is thrown if a plugin requires another plugin that isn't registered.
267
- *
268
- * Plugins can declare their own options via `PluginFactoryOptions`. See plugin documentation for details.
269
- *
270
- * @example
271
- * ```ts
272
- * import { pluginTs } from '@kubb/plugin-ts'
273
- * import { pluginZod } from '@kubb/plugin-zod'
274
- *
275
- * plugins: [
276
- * pluginTs({ output: { path: './src/gen' } }),
277
- * pluginZod({ output: { path: './src/gen' } }),
278
- * ]
279
- * ```
280
- */
281
- plugins: Array<Plugin>
282
- /**
283
- * Middleware instances that observe build events and post-process generated code.
284
- *
285
- * Middleware fires AFTER all plugins for each event. Perfect for tasks like:
286
- * - Auditing what was generated
287
- * - Adding barrel/index files
288
- * - Validating output
289
- * - Running custom transformations
290
- *
291
- * @example
292
- * ```ts
293
- * import { middlewareBarrel } from '@kubb/middleware-barrel'
294
- *
295
- * middleware: [middlewareBarrel()]
296
- * ```
297
- *
298
- * @see {@link defineMiddleware} to create custom middleware.
299
- */
300
- middleware?: Array<Middleware>
301
- /**
302
- * Renderer that converts generated AST nodes to code strings.
303
- *
304
- * By default, Kubb uses the JSX renderer (`rendererJsx`). Pass a custom renderer to support
305
- * different output formats (template engines, code generation DSLs, etc.).
306
- *
307
- * @default rendererJsx() // from @kubb/renderer-jsx
308
- * @example
309
- * ```ts
310
- * import { rendererJsx } from '@kubb/renderer-jsx'
311
- * renderer: rendererJsx()
312
- * ```
313
- *
314
- * @see {@link Renderer} to implement a custom renderer.
315
- */
316
- renderer?: RendererFactory
317
- /**
318
- * Kubb Studio cloud integration settings.
319
- *
320
- * Kubb Studio (https://kubb.studio) is a web-based IDE for managing API specs and generated code.
321
- * Set to `true` to enable with default settings, or pass an object to customize the Studio URL.
322
- *
323
- * @default false // disabled by default
324
- * @example
325
- * ```ts
326
- * devtools: true // use default Kubb Studio
327
- * devtools: { studioUrl: 'https://my-studio.dev' } // custom Studio instance
328
- * ```
329
- */
330
- devtools?:
331
- | true
332
- | {
333
- /**
334
- * Override the Kubb Studio base URL.
335
- * @default 'https://kubb.studio'
336
- */
337
- studioUrl?: typeof DEFAULT_STUDIO_URL | (string & {})
338
- }
339
- /**
340
- * Lifecycle hooks that execute during or after the build process.
341
- *
342
- * Hooks allow you to run external tools (prettier, eslint, custom scripts) based on build events.
343
- * Currently supports the `done` hook which fires after all plugins and middleware complete.
344
- *
345
- * @example
346
- * ```ts
347
- * hooks: {
348
- * done: 'prettier --write "./src/gen"', // auto-format generated files
349
- * // or multiple commands:
350
- * done: ['prettier --write "./src/gen"', 'eslint --fix "./src/gen"']
351
- * }
352
- * ```
353
- */
354
- hooks?: {
355
- /**
356
- * Command(s) to run after all plugins and middleware complete generation.
357
- *
358
- * Useful for post-processing: formatting, linting, copying files, or custom validation.
359
- * Pass a single command string or array of command strings to run sequentially.
360
- * Commands are executed relative to the `root` directory.
361
- *
362
- * @example
363
- * ```ts
364
- * done: 'prettier --write "./src/gen"'
365
- * done: ['prettier --write "./src/gen"', 'eslint --fix "./src/gen"']
366
- * ```
367
- */
368
- done?: string | Array<string>
369
- }
370
- }
371
-
372
- /**
373
- * Partial `Config` for user-facing entry points with sensible defaults.
374
- *
375
- * `UserConfig` is what you pass to `defineConfig()`. It has optional `root`, `plugins`, `parsers`, and `adapter`
376
- * fields (which fall back to sensible defaults). All other Config options are available, including `output`, `input`,
377
- * `storage`, `middleware`, `renderer`, `devtools`, and `hooks`.
378
- *
379
- * @example
380
- * ```ts
381
- * export default defineConfig({
382
- * input: { path: './petstore.yaml' },
383
- * output: { path: './src/gen' },
384
- * plugins: [pluginTs(), pluginZod()],
385
- * })
386
- * ```
387
- */
388
- export type UserConfig<TInput = Input> = Omit<Config<TInput>, 'root' | 'plugins' | 'parsers' | 'adapter' | 'storage'> & {
389
- /**
390
- * Project root directory, absolute or relative to the config file location.
391
- * @default process.cwd()
392
- */
393
- root?: string
394
- /**
395
- * Custom parsers that convert generated AST nodes to strings (TypeScript, JSON, markdown, etc.).
396
- * @default [parserTs] // from `@kubb/parser-ts`
397
- */
398
- parsers?: Array<Parser>
399
- /**
400
- * Adapter that parses your API specification into Kubb's universal AST.
401
- * When omitted, Kubb runs in plugin-only mode.
402
- */
403
- adapter?: Adapter
404
- /**
405
- * Plugins that execute during the build to generate code and transform the AST.
406
- * @default []
407
- */
408
- plugins?: Array<Plugin>
409
- /**
410
- * Storage backend that controls where and how generated files are persisted.
411
- * @default fsStorage()
412
- */
413
- storage?: Storage
414
- }
415
-
416
- declare global {
417
- namespace Kubb {
418
- /**
419
- * Registry that maps plugin names to their `PluginFactoryOptions`.
420
- * Augment this interface in each plugin's `types.ts` to enable automatic
421
- * typing for `getPlugin` and `requirePlugin`.
422
- *
423
- * @example
424
- * ```ts
425
- * // packages/plugin-ts/src/types.ts
426
- * declare global {
427
- * namespace Kubb {
428
- * interface PluginRegistry {
429
- * 'plugin-ts': PluginTs
430
- * }
431
- * }
432
- * }
433
- * ```
434
- */
435
- interface PluginRegistry {}
436
-
437
- /**
438
- * Extension point for root `Config['output']` options.
439
- * Augment the `output` key in middleware or plugin packages to add extra fields
440
- * to the global output configuration without touching core types.
441
- *
442
- * @example
443
- * ```ts
444
- * // packages/middleware-barrel/src/types.ts
445
- * declare global {
446
- * namespace Kubb {
447
- * interface ConfigOptionsRegistry {
448
- * output: {
449
- * barrel?: import('./types.ts').BarrelConfig | false
450
- * }
451
- * }
452
- * }
453
- * }
454
- * ```
455
- */
456
- interface ConfigOptionsRegistry {}
457
-
458
- /**
459
- * Extension point for per-plugin `Output` options.
460
- * Augment the `output` key in middleware or plugin packages to add extra fields
461
- * to the per-plugin output configuration without touching core types.
462
- *
463
- * @example
464
- * ```ts
465
- * // packages/middleware-barrel/src/types.ts
466
- * declare global {
467
- * namespace Kubb {
468
- * interface PluginOptionsRegistry {
469
- * output: {
470
- * barrel?: import('./types.ts').PluginBarrelConfig | false
471
- * }
472
- * }
473
- * }
474
- * }
475
- * ```
476
- */
477
- interface PluginOptionsRegistry {}
478
- }
479
- }
480
-
481
- /**
482
- * Lifecycle events emitted during Kubb code generation.
483
- * Use these for logging, progress tracking, and custom integrations.
484
- *
485
- * @example
486
- * ```typescript
487
- * import type { AsyncEventEmitter } from '@internals/utils'
488
- * import type { KubbHooks } from '@kubb/core'
489
- *
490
- * const hooks: AsyncEventEmitter<KubbHooks> = new AsyncEventEmitter()
491
- *
492
- * hooks.on('kubb:lifecycle:start', () => {
493
- * console.log('Starting Kubb generation')
494
- * })
495
- *
496
- * hooks.on('kubb:plugin:end', ({ plugin, duration }) => {
497
- * console.log(`Plugin ${plugin.name} completed in ${duration}ms`)
498
- * })
499
- * ```
500
- */
501
- export interface KubbHooks {
502
- 'kubb:lifecycle:start': [ctx: KubbLifecycleStartContext]
503
- 'kubb:lifecycle:end': []
504
- 'kubb:config:start': []
505
- 'kubb:config:end': [ctx: KubbConfigEndContext]
506
- 'kubb:generation:start': [ctx: KubbGenerationStartContext]
507
- 'kubb:generation:end': [ctx: KubbGenerationEndContext]
508
- 'kubb:generation:summary': [ctx: KubbGenerationSummaryContext]
509
- 'kubb:format:start': []
510
- 'kubb:format:end': []
511
- 'kubb:lint:start': []
512
- 'kubb:lint:end': []
513
- 'kubb:hooks:start': []
514
- 'kubb:hooks:end': []
515
- 'kubb:hook:start': [ctx: KubbHookStartContext]
516
- 'kubb:hook:end': [ctx: KubbHookEndContext]
517
- 'kubb:version:new': [ctx: KubbVersionNewContext]
518
- 'kubb:info': [ctx: KubbInfoContext]
519
- 'kubb:error': [ctx: KubbErrorContext]
520
- 'kubb:success': [ctx: KubbSuccessContext]
521
- 'kubb:warn': [ctx: KubbWarnContext]
522
- 'kubb:debug': [ctx: KubbDebugContext]
523
- 'kubb:files:processing:start': [ctx: KubbFilesProcessingStartContext]
524
- 'kubb:file:processing:update': [ctx: KubbFileProcessingUpdateContext]
525
- 'kubb:files:processing:end': [ctx: KubbFilesProcessingEndContext]
526
- 'kubb:plugin:start': [ctx: KubbPluginStartContext]
527
- 'kubb:plugin:end': [ctx: KubbPluginEndContext]
528
- 'kubb:plugin:setup': [ctx: KubbPluginSetupContext]
529
- 'kubb:build:start': [ctx: KubbBuildStartContext]
530
- 'kubb:plugins:end': [ctx: KubbPluginsEndContext]
531
- 'kubb:build:end': [ctx: KubbBuildEndContext]
532
- 'kubb:generate:schema': [node: SchemaNode, ctx: GeneratorContext]
533
- 'kubb:generate:operation': [node: OperationNode, ctx: GeneratorContext]
534
- 'kubb:generate:operations': [nodes: Array<OperationNode>, ctx: GeneratorContext]
535
- }
536
-
537
- export type KubbBuildStartContext = {
538
- config: Config
539
- adapter: Adapter
540
- inputNode: InputNode
541
- getPlugin<TName extends keyof Kubb.PluginRegistry>(name: TName): Plugin<Kubb.PluginRegistry[TName]> | undefined
542
- getPlugin(name: string): Plugin | undefined
543
- readonly files: ReadonlyArray<FileNode>
544
- upsertFile: (...files: Array<FileNode>) => void
545
- }
546
-
547
- export type KubbPluginsEndContext = {
548
- config: Config
549
- readonly files: ReadonlyArray<FileNode>
550
- upsertFile: (...files: Array<FileNode>) => void
551
- }
552
-
553
- export type KubbBuildEndContext = {
554
- files: Array<FileNode>
555
- config: Config
556
- outputDir: string
557
- }
558
-
559
- export type KubbLifecycleStartContext = {
560
- version: string
561
- }
562
-
563
- export type KubbConfigEndContext = {
564
- configs: Array<Config>
565
- }
566
-
567
- export type KubbGenerationStartContext = {
568
- config: Config
569
- }
570
-
571
- export type KubbGenerationEndContext = {
572
- config: Config
573
- /**
574
- * Read-only view of the files Kubb wrote during this build.
575
- *
576
- * Keys are scoped to this run; files from earlier builds are not included.
577
- * Reads go directly to `config.storage`, so nothing is buffered in memory.
578
- *
579
- * @example Read a generated file
580
- * ```ts
581
- * const code = await storage.getItem('/src/gen/pet.ts')
582
- * ```
583
- *
584
- * @example Walk every generated file
585
- * ```ts
586
- * for (const path of await storage.getKeys()) {
587
- * const code = await storage.getItem(path)
588
- * }
589
- * ```
590
- */
591
- storage: Storage
592
- }
593
-
594
- export type KubbGenerationSummaryContext = {
595
- config: Config
596
- failedPlugins: Set<{ plugin: Plugin; error: Error }>
597
- status: 'success' | 'failed'
598
- hrStart: [number, number]
599
- filesCreated: number
600
- pluginTimings?: Map<Plugin['name'], number>
601
- }
602
-
603
- export type KubbVersionNewContext = {
604
- currentVersion: string
605
- latestVersion: string
606
- }
607
-
608
- export type KubbInfoContext = {
609
- message: string
610
- info?: string
611
- }
612
-
613
- export type KubbErrorContext = {
614
- error: Error
615
- meta?: Record<string, unknown>
616
- }
617
-
618
- export type KubbSuccessContext = {
619
- message: string
620
- info?: string
621
- }
622
-
623
- export type KubbWarnContext = {
624
- message: string
625
- info?: string
626
- }
627
-
628
- export type KubbDebugContext = {
629
- date: Date
630
- logs: Array<string>
631
- fileName?: string
632
- }
633
-
634
- export type KubbFilesProcessingStartContext = {
635
- files: Array<FileNode>
636
- }
637
-
638
- export type KubbFileProcessingUpdateContext = {
639
- processed: number
640
- total: number
641
- percentage: number
642
- source?: string
643
- file: FileNode
644
- config: Config
645
- }
646
-
647
- export type KubbFilesProcessingEndContext = {
648
- files: Array<FileNode>
649
- }
650
-
651
- export type KubbHookStartContext = {
652
- id?: string
653
- command: string
654
- args?: readonly string[]
655
- }
656
-
657
- export type KubbHookEndContext = {
658
- id?: string
659
- command: string
660
- args?: readonly string[]
661
- success: boolean
662
- error: Error | null
663
- }
664
-
665
- /**
666
- * CLI options derived from command-line flags.
667
- */
668
- export type CLIOptions = {
669
- config?: string
670
- watch?: boolean
671
- /** @default 'silent' */
672
- logLevel?: 'silent' | 'info' | 'debug'
673
- }
674
-
675
- /**
676
- * All accepted forms of a Kubb configuration.
677
- * Accepts `Config`/`Config[]`/promise or a factory (optionally receiving `TCliOptions`).
678
- */
679
- export type PossibleConfig<TCliOptions = undefined> =
680
- | PossiblePromise<Config | Config[]>
681
- | ((...args: [TCliOptions] extends [undefined] ? [] : [TCliOptions]) => PossiblePromise<Config | Config[]>)
682
-
683
- type SetupOptions = {
684
- hooks?: AsyncEventEmitter<KubbHooks>
685
- }
686
-
687
- /**
688
- * Full output produced by a successful or failed build.
689
- */
690
- export type BuildOutput = {
691
- /**
692
- * Plugins that threw during installation, paired with the caught error.
693
- */
694
- failedPlugins: Set<{ plugin: Plugin; error: Error }>
695
- files: Array<FileNode>
696
- driver: PluginDriver
697
- /**
698
- * Elapsed time in milliseconds for each plugin, keyed by plugin name.
699
- */
700
- pluginTimings: Map<string, number>
701
- error?: Error
702
- /**
703
- * Read-only view of every file written during this build.
704
- *
705
- * Keys are limited to this run. Reads go straight to `config.storage`,
706
- * so nothing extra is held in memory.
707
- *
708
- * @example Read a generated file
709
- * ```ts
710
- * const code = await buildOutput.storage.getItem('/src/gen/pet.ts')
711
- * ```
712
- *
713
- * @example List all generated file paths
714
- * ```ts
715
- * const paths = await buildOutput.storage.getKeys()
716
- * ```
717
- */
718
- storage: Storage
719
- }
720
-
721
- /**
722
- * Kubb code generation instance returned by {@link createKubb}.
723
- *
724
- * Use this when orchestrating multiple builds, inspecting plugin timings, or integrating Kubb into a larger toolchain.
725
- * For a single one-off build, chain directly: `await createKubb(config).build()`.
726
- */
727
- export type Kubb = {
728
- /**
729
- * Shared event emitter for lifecycle and status events. Attach listeners before calling `setup()` or `build()`.
730
- */
731
- readonly hooks: AsyncEventEmitter<KubbHooks>
732
- /**
733
- * Read-only view of the files from the most recent `build()` or `safeBuild()` call.
734
- * Only populated after the build completes.
735
- *
736
- * Keys are scoped to the current run. Reads go straight to `config.storage`,
737
- * so nothing extra is held in memory.
738
- *
739
- * @example Read a generated file
740
- * ```ts
741
- * const { storage } = await kubb.safeBuild()
742
- * const code = await storage.getItem('/src/gen/pet.ts')
743
- * ```
744
- *
745
- * @example Walk every generated file
746
- * ```ts
747
- * for (const path of await kubb.storage.getKeys()) {
748
- * const code = await kubb.storage.getItem(path)
749
- * }
750
- * ```
751
- */
752
- readonly storage: Storage
753
- /**
754
- * Plugin driver managing all plugins. Available after `setup()` completes.
755
- */
756
- readonly driver: PluginDriver
757
- /**
758
- * Resolved configuration with defaults applied. Available after `setup()` completes.
759
- */
760
- readonly config: Config
761
- /**
762
- * Resolves config and initializes the driver. `build()` calls this automatically.
763
- */
764
- setup(): Promise<void>
765
- /**
766
- * Runs the full pipeline and throws on any plugin error. Automatically calls `setup()` if needed.
767
- */
768
- build(): Promise<BuildOutput>
769
- /**
770
- * Runs the full pipeline and captures errors in `BuildOutput` instead of throwing. Automatically calls `setup()` if needed.
771
- */
772
- safeBuild(): Promise<BuildOutput>
773
- }
774
-
775
- type SetupResult = {
776
- hooks: AsyncEventEmitter<KubbHooks>
777
- driver: PluginDriver
778
- storage: Storage
779
- config: Config
780
- }
781
-
782
- /**
783
- * Builds a `Storage` view scoped to the file paths produced by the current build.
784
- *
785
- * Reads delegate to the underlying `storage` (typically `fsStorage()`) so source bytes
786
- * stay where they were written instead of being held in an extra in-memory map.
787
- * Writing via `setItem` stores the content in the underlying storage and registers the
788
- * key so subsequent reads and `getKeys` are scoped to this build's output.
789
- */
790
- function createSourcesView(storage: Storage): Storage {
791
- const paths = new Set<string>()
792
- return createStorage(() => ({
793
- name: `${storage.name}:sources`,
794
- async hasItem(key: string) {
795
- return paths.has(key) && (await storage.hasItem(key))
796
- },
797
- async getItem(key: string) {
798
- return paths.has(key) ? storage.getItem(key) : null
799
- },
800
- async setItem(key: string, value: string) {
801
- paths.add(key)
802
- await storage.setItem(key, value)
803
- },
804
- async removeItem(key: string) {
805
- paths.delete(key)
806
- await storage.removeItem(key)
807
- },
808
- async getKeys(base?: string) {
809
- if (!base) return [...paths]
810
- const result: Array<string> = []
811
- for (const key of paths) {
812
- if (key.startsWith(base)) result.push(key)
813
- }
814
- return result
815
- },
816
- async clear() {
817
- paths.clear()
818
- await storage.clear()
819
- },
820
- }))()
821
- }
822
-
823
- async function setup(userConfig: UserConfig, options: SetupOptions = {}): Promise<SetupResult> {
824
- const hooks = options.hooks ?? new AsyncEventEmitter<KubbHooks>()
825
- const config: Config = {
826
- ...userConfig,
827
- root: userConfig.root || process.cwd(),
828
- parsers: userConfig.parsers ?? [],
829
- adapter: userConfig.adapter,
830
- output: {
831
- format: false,
832
- lint: false,
833
- extension: DEFAULT_EXTENSION,
834
- defaultBanner: DEFAULT_BANNER,
835
- ...userConfig.output,
836
- },
837
- storage: userConfig.storage ?? fsStorage(),
838
- devtools: userConfig.devtools
839
- ? {
840
- studioUrl: DEFAULT_STUDIO_URL,
841
- ...(typeof userConfig.devtools === 'boolean' ? {} : userConfig.devtools),
842
- }
843
- : undefined,
844
- plugins: (userConfig.plugins ?? []) as unknown as Config['plugins'],
845
- }
846
- const driver = new PluginDriver(config, {
847
- hooks,
848
- })
849
- const storage: Storage = createSourcesView(config.storage)
850
- const diagnosticInfo = getDiagnosticInfo()
851
-
852
- await hooks.emit('kubb:debug', {
853
- date: new Date(),
854
- logs: [
855
- 'Configuration:',
856
- ` • Name: ${userConfig.name || 'unnamed'}`,
857
- ` • Root: ${userConfig.root || process.cwd()}`,
858
- ` • Output: ${userConfig.output?.path || 'not specified'}`,
859
- ` • Plugins: ${userConfig.plugins?.length || 0}`,
860
- 'Output Settings:',
861
- ` • Storage: ${config.storage.name}`,
862
- ` • Formatter: ${userConfig.output?.format || 'none'}`,
863
- ` • Linter: ${userConfig.output?.lint || 'none'}`,
864
- 'Environment:',
865
- Object.entries(diagnosticInfo)
866
- .map(([key, value]) => ` • ${key}: ${value}`)
867
- .join('\n'),
868
- ],
869
- })
870
-
871
- try {
872
- if (isInputPath(userConfig) && !new URLPath(userConfig.input.path).isURL) {
873
- await exists(userConfig.input.path)
874
-
875
- await hooks.emit('kubb:debug', {
876
- date: new Date(),
877
- logs: [`✓ Input file validated: ${userConfig.input.path}`],
878
- })
879
- }
880
- } catch (caughtError) {
881
- if (isInputPath(userConfig)) {
882
- const error = caughtError as Error
883
-
884
- throw new Error(
885
- `Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${userConfig.input.path}`,
886
- {
887
- cause: error,
888
- },
889
- )
890
- }
891
- }
892
-
893
- if (config.output.clean) {
894
- await hooks.emit('kubb:debug', {
895
- date: new Date(),
896
- logs: ['Cleaning output directories', ` • Output: ${config.output.path}`],
897
- })
898
- await config.storage.clear(resolve(config.root, config.output.path))
899
- }
900
-
901
- // Register middleware hooks after all plugin hooks are registered.
902
- // Because AsyncEventEmitter calls listeners in registration order,
903
- // middleware hooks for any event fire after all plugin hooks for that event.
904
- function registerMiddlewareHook<K extends keyof KubbHooks & string>(event: K, middlewareHooks: Middleware['hooks']) {
905
- const handler = middlewareHooks[event]
906
- if (handler) {
907
- hooks.on(event, handler)
908
- }
909
- }
910
-
911
- for (const middleware of config.middleware ?? []) {
912
- for (const event of Object.keys(middleware.hooks) as Array<keyof KubbHooks & string>) {
913
- registerMiddlewareHook(event, middleware.hooks)
914
- }
915
- }
916
-
917
- if (config.adapter) {
918
- const source = inputToAdapterSource(config)
919
-
920
- await hooks.emit('kubb:debug', {
921
- date: new Date(),
922
- logs: [`Running adapter: ${config.adapter.name}`],
923
- })
924
-
925
- driver.adapter = config.adapter
926
- driver.inputNode = await config.adapter.parse(source)
927
-
928
- await hooks.emit('kubb:debug', {
929
- date: new Date(),
930
- logs: [
931
- `✓ Adapter '${config.adapter.name}' resolved InputNode`,
932
- ` • Schemas: ${driver.inputNode.schemas.length}`,
933
- ` • Operations: ${driver.inputNode.operations.length}`,
934
- ],
935
- })
936
- }
937
-
938
- return {
939
- config,
940
- hooks,
941
- driver,
942
- storage,
943
- }
944
- }
945
-
946
- /**
947
- * Walks the AST and dispatches nodes to a plugin's direct AST hooks
948
- * (`schema`, `operation`, `operations`).
949
- *
950
- * When `include` contains only operation-scoped filters (`tag`, `operationId`, `path`,
951
- * `method`, `contentType`) and no `schemaName` filter, the function pre-computes the set
952
- * of top-level schema names transitively reachable from the included operations and skips
953
- * schemas that fall outside that set. This ensures that component schemas referenced
954
- * exclusively by excluded operations are not generated.
955
- */
956
- async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorContext): Promise<void> {
957
- const { adapter, inputNode, resolver, driver } = context
958
- const { exclude, include, override } = plugin.options
959
-
960
- if (!adapter || !inputNode) {
961
- throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. adapterOas()) before this plugin in your Kubb config.`)
962
- }
963
-
964
- function resolveRenderer(gen: Generator): RendererFactory | undefined {
965
- return gen.renderer === null ? undefined : (gen.renderer ?? plugin.renderer ?? context.config.renderer)
966
- }
967
-
968
- const generators = plugin.generators ?? []
969
- const collectedOperations: Array<OperationNode> = []
970
-
971
- const generatorContext = {
972
- ...context,
973
- resolver: driver.getResolver(plugin.name),
974
- }
975
-
976
- // When `include` has operation-based filters (tag, operationId, path, method, contentType)
977
- // but no schema-level filters (schemaName), pre-compute the set of top-level schema names
978
- // that are transitively referenced by the included operations. Schemas outside that set are
979
- // skipped so that types belonging exclusively to excluded operations are not generated.
980
- const operationFilterTypes = new Set(['tag', 'operationId', 'path', 'method', 'contentType'])
981
- const hasOperationBasedIncludes = include?.some(({ type }) => operationFilterTypes.has(type)) ?? false
982
- const hasSchemaNameIncludes = include?.some(({ type }) => type === 'schemaName') ?? false
983
-
984
- let allowedSchemaNames: Set<string> | undefined
985
- if (hasOperationBasedIncludes && !hasSchemaNameIncludes) {
986
- const includedOps = inputNode.operations.filter((op) => resolver.resolveOptions(op, { options: plugin.options, exclude, include, override }) !== null)
987
- allowedSchemaNames = collectUsedSchemaNames(includedOps, inputNode.schemas)
988
- }
989
-
990
- await walk(inputNode, {
991
- depth: 'shallow',
992
- async schema(node) {
993
- const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
994
-
995
- // Skip named top-level schemas that are not reachable from any included operation.
996
- if (allowedSchemaNames !== undefined && transformedNode.name && !allowedSchemaNames.has(transformedNode.name)) {
997
- return
998
- }
999
-
1000
- const options = resolver.resolveOptions(transformedNode, {
1001
- options: plugin.options,
1002
- exclude,
1003
- include,
1004
- override,
1005
- })
1006
- if (options === null) return
1007
-
1008
- const ctx = { ...generatorContext, options }
1009
-
1010
- for (const gen of generators) {
1011
- if (!gen.schema) continue
1012
- const result = await gen.schema(transformedNode, ctx)
1013
- await applyHookResult(result, driver, resolveRenderer(gen))
1014
- }
1015
-
1016
- await driver.hooks.emit('kubb:generate:schema', transformedNode, ctx)
1017
- },
1018
- async operation(node) {
1019
- const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
1020
- const options = resolver.resolveOptions(transformedNode, {
1021
- options: plugin.options,
1022
- exclude,
1023
- include,
1024
- override,
1025
- })
1026
- if (options !== null) {
1027
- collectedOperations.push(transformedNode)
1028
-
1029
- const ctx = { ...generatorContext, options }
1030
-
1031
- for (const gen of generators) {
1032
- if (!gen.operation) continue
1033
- const result = await gen.operation(transformedNode, ctx)
1034
- await applyHookResult(result, driver, resolveRenderer(gen))
1035
- }
1036
-
1037
- await driver.hooks.emit('kubb:generate:operation', transformedNode, ctx)
1038
- }
1039
- },
1040
- })
1041
-
1042
- if (collectedOperations.length > 0) {
1043
- const ctx = { ...generatorContext, options: plugin.options }
1044
-
1045
- for (const gen of generators) {
1046
- if (!gen.operations) continue
1047
- const result = await gen.operations(collectedOperations, ctx)
1048
- await applyHookResult(result, driver, resolveRenderer(gen))
1049
- }
1050
-
1051
- await driver.hooks.emit('kubb:generate:operations', collectedOperations, ctx)
1052
- }
1053
- }
1054
-
1055
- async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
1056
- const { driver, hooks, storage } = setupResult
1057
-
1058
- const failedPlugins = new Set<{ plugin: Plugin; error: Error }>()
1059
- const pluginTimings = new Map<string, number>()
1060
- const config = driver.config
1061
- const writtenPaths = new Set<string>()
1062
- const parsersMap = new Map<FileNode['extname'], Parser>()
1063
- for (const parser of config.parsers) {
1064
- if (parser.extNames) {
1065
- for (const extname of parser.extNames) {
1066
- parsersMap.set(extname, parser)
1067
- }
1068
- }
1069
- }
1070
- const fileProcessor = new FileProcessor()
1071
-
1072
- fileProcessor.events.on('start', async (processingFiles) => {
1073
- await hooks.emit('kubb:files:processing:start', { files: processingFiles })
1074
- })
1075
-
1076
- fileProcessor.events.on('update', async ({ file, source, processed, total, percentage }) => {
1077
- await hooks.emit('kubb:file:processing:update', {
1078
- file,
1079
- source,
1080
- processed,
1081
- total,
1082
- percentage,
1083
- config,
1084
- })
1085
- if (source) {
1086
- await storage.setItem(file.path, source)
1087
- }
1088
- })
1089
-
1090
- fileProcessor.events.on('end', async (processed) => {
1091
- await hooks.emit('kubb:files:processing:end', { files: processed })
1092
- await hooks.emit('kubb:debug', {
1093
- date: new Date(),
1094
- logs: [`✓ File write process completed for ${processed.length} files`],
1095
- })
1096
- })
1097
-
1098
- async function flushPendingFiles(): Promise<void> {
1099
- const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path))
1100
- if (files.length === 0) {
1101
- return
1102
- }
1103
-
1104
- await hooks.emit('kubb:debug', {
1105
- date: new Date(),
1106
- logs: [`Writing ${files.length} files...`],
1107
- })
1108
-
1109
- await fileProcessor.run(files, {
1110
- parsers: parsersMap,
1111
- mode: 'parallel',
1112
- extension: config.output.extension,
1113
- })
1114
-
1115
- for (const file of files) {
1116
- writtenPaths.add(file.path)
1117
- }
1118
- }
1119
-
1120
- try {
1121
- await driver.emitSetupHooks()
1122
-
1123
- if (driver.adapter && driver.inputNode) {
1124
- await hooks.emit('kubb:build:start', {
1125
- config,
1126
- adapter: driver.adapter,
1127
- inputNode: driver.inputNode,
1128
- getPlugin: driver.getPlugin.bind(driver),
1129
- get files() {
1130
- return driver.fileManager.files
1131
- },
1132
- upsertFile: (...files) => driver.fileManager.upsert(...files),
1133
- })
1134
- }
1135
-
1136
- for (const plugin of driver.plugins.values()) {
1137
- const context = driver.getContext(plugin)
1138
- const hrStart = process.hrtime()
1139
-
1140
- try {
1141
- const timestamp = new Date()
1142
-
1143
- await hooks.emit('kubb:plugin:start', { plugin })
1144
- await hooks.emit('kubb:debug', {
1145
- date: timestamp,
1146
- logs: ['Starting plugin...', ` • Plugin Name: ${plugin.name}`],
1147
- })
1148
-
1149
- if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) {
1150
- await runPluginAstHooks(plugin, context)
1151
- }
1152
-
1153
- const duration = getElapsedMs(hrStart)
1154
- pluginTimings.set(plugin.name, duration)
1155
-
1156
- await hooks.emit('kubb:plugin:end', {
1157
- plugin,
1158
- duration,
1159
- success: true,
1160
- config,
1161
- get files() {
1162
- return driver.fileManager.files
1163
- },
1164
- upsertFile: (...files) => driver.fileManager.upsert(...files),
1165
- })
1166
-
1167
- await flushPendingFiles()
1168
-
1169
- await hooks.emit('kubb:debug', {
1170
- date: new Date(),
1171
- logs: [`✓ Plugin started successfully (${formatMs(duration)})`],
1172
- })
1173
- } catch (caughtError) {
1174
- const error = caughtError as Error
1175
- const errorTimestamp = new Date()
1176
- const duration = getElapsedMs(hrStart)
1177
-
1178
- await hooks.emit('kubb:plugin:end', {
1179
- plugin,
1180
- duration,
1181
- success: false,
1182
- error,
1183
- config,
1184
- get files() {
1185
- return driver.fileManager.files
1186
- },
1187
- upsertFile: (...files) => driver.fileManager.upsert(...files),
1188
- })
1189
-
1190
- await flushPendingFiles()
1191
-
1192
- await hooks.emit('kubb:debug', {
1193
- date: errorTimestamp,
1194
- logs: [
1195
- '✗ Plugin start failed',
1196
- ` • Plugin Name: ${plugin.name}`,
1197
- ` • Error: ${error.constructor.name} - ${error.message}`,
1198
- ' • Stack Trace:',
1199
- error.stack || 'No stack trace available',
1200
- ],
1201
- })
1202
-
1203
- failedPlugins.add({ plugin, error })
1204
- }
1205
- }
1206
-
1207
- await hooks.emit('kubb:plugins:end', {
1208
- config,
1209
- get files() {
1210
- return driver.fileManager.files
1211
- },
1212
- upsertFile: (...files) => driver.fileManager.upsert(...files),
1213
- })
1214
-
1215
- await flushPendingFiles()
1216
-
1217
- const files = driver.fileManager.files
1218
-
1219
- await hooks.emit('kubb:build:end', {
1220
- files,
1221
- config,
1222
- outputDir: resolve(config.root, config.output.path),
1223
- })
1224
-
1225
- return {
1226
- failedPlugins,
1227
- files,
1228
- driver,
1229
- pluginTimings,
1230
- storage,
1231
- }
1232
- } catch (error) {
1233
- return {
1234
- failedPlugins,
1235
- files: [],
1236
- driver,
1237
- pluginTimings,
1238
- error: error as Error,
1239
- storage,
1240
- }
1241
- } finally {
1242
- driver.dispose()
1243
- }
1244
- }
1245
-
1246
- async function build(setupResult: SetupResult): Promise<BuildOutput> {
1247
- const { files, driver, failedPlugins, pluginTimings, error, storage } = await safeBuild(setupResult)
1248
-
1249
- if (error) {
1250
- throw error
1251
- }
1252
-
1253
- if (failedPlugins.size > 0) {
1254
- const errors = [...failedPlugins].map(({ error }) => error)
1255
-
1256
- throw new BuildError(`Build Error with ${failedPlugins.size} failed plugins`, { errors })
1257
- }
1258
-
1259
- return {
1260
- failedPlugins,
1261
- files,
1262
- driver,
1263
- pluginTimings,
1264
- error: undefined,
1265
- storage,
1266
- }
1267
- }
1268
-
1269
- /**
1270
- * Returns a snapshot of the current runtime environment.
1271
- *
1272
- * Useful for attaching context to debug logs and error reports so that
1273
- * issues can be reproduced without manual information gathering.
1274
- */
1275
- export function getDiagnosticInfo() {
1276
- return {
1277
- nodeVersion,
1278
- KubbVersion,
1279
- platform: process.platform,
1280
- arch: process.arch,
1281
- cwd: process.cwd(),
1282
- } as const
1283
- }
1284
-
1285
- /**
1286
- * Type guard to check if a given config has an `input.path`.
1287
- */
1288
- export function isInputPath(config: UserConfig | undefined): config is UserConfig<InputPath> & { input: InputPath }
1289
- export function isInputPath(config: Config | undefined): config is Config<InputPath> & { input: InputPath }
1290
- export function isInputPath(config: Config | UserConfig | undefined): config is (Config<InputPath> | UserConfig<InputPath>) & { input: InputPath } {
1291
- return typeof config?.input === 'object' && config.input !== null && 'path' in config.input
1292
- }
1293
-
1294
- function inputToAdapterSource(config: Config): AdapterSource {
1295
- const input = config.input
1296
- if (!input) {
1297
- throw new Error('[kubb] input is required when using an adapter. Provide input.path or input.data in your config.')
1298
- }
1299
-
1300
- if ('data' in input) {
1301
- return { type: 'data', data: input.data }
1302
- }
1303
-
1304
- if (new URLPath(input.path).isURL) {
1305
- return { type: 'path', path: input.path }
1306
- }
1307
-
1308
- const resolved = resolve(config.root, input.path)
1309
-
1310
- return { type: 'path', path: resolved }
1311
- }
1312
-
1313
- type CreateKubbOptions = {
1314
- hooks?: AsyncEventEmitter<KubbHooks>
1315
- }
1316
-
1317
- /**
1318
- * Creates a Kubb instance bound to a single config entry.
1319
- *
1320
- * Accepts a user-facing config shape and resolves it to a full {@link Config} during
1321
- * `setup()`. The instance then holds shared state (`hooks`, `storage`, `driver`, `config`)
1322
- * across the `setup → build` lifecycle. Attach event listeners to `kubb.hooks` before
1323
- * calling `setup()` or `build()`.
1324
- *
1325
- * @example
1326
- * ```ts
1327
- * const kubb = createKubb(userConfig)
1328
- *
1329
- * kubb.hooks.on('kubb:plugin:end', ({ plugin, duration }) => {
1330
- * console.log(`${plugin.name} completed in ${duration}ms`)
1331
- * })
1332
- *
1333
- * const { files, failedPlugins } = await kubb.safeBuild()
1334
- * ```
1335
- */
1336
- export function createKubb(userConfig: UserConfig, options: CreateKubbOptions = {}): Kubb {
1337
- const hooks = options.hooks ?? new AsyncEventEmitter<KubbHooks>()
1338
- let setupResult: SetupResult | undefined
1339
-
1340
- const instance: Kubb = {
1341
- get hooks() {
1342
- return hooks
1343
- },
1344
- get storage() {
1345
- if (!setupResult) {
1346
- throw new Error('[kubb] setup() must be called before accessing storage')
1347
- }
1348
- return setupResult.storage
1349
- },
1350
- get driver() {
1351
- if (!setupResult) {
1352
- throw new Error('[kubb] setup() must be called before accessing driver')
1353
- }
1354
- return setupResult.driver
1355
- },
1356
- get config() {
1357
- if (!setupResult) {
1358
- throw new Error('[kubb] setup() must be called before accessing config')
1359
- }
1360
- return setupResult.config
1361
- },
1362
- async setup() {
1363
- setupResult = await setup(userConfig, { hooks })
1364
- },
1365
- async build() {
1366
- if (!setupResult) {
1367
- await instance.setup()
1368
- }
1369
- return build(setupResult!)
1370
- },
1371
- async safeBuild() {
1372
- if (!setupResult) {
1373
- await instance.setup()
1374
- }
1375
- return safeBuild(setupResult!)
1376
- },
1377
- }
1378
-
1379
- return instance
1380
- }