@kubb/core 5.0.0-alpha.2 → 5.0.0-alpha.21

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 (55) hide show
  1. package/dist/{types-B7eZvqwD.d.ts → PluginDriver-CEQPafXV.d.ts} +687 -298
  2. package/dist/hooks.cjs +15 -9
  3. package/dist/hooks.cjs.map +1 -1
  4. package/dist/hooks.d.ts +11 -5
  5. package/dist/hooks.js +16 -10
  6. package/dist/hooks.js.map +1 -1
  7. package/dist/index.cjs +1131 -536
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +674 -89
  10. package/dist/index.js +1114 -532
  11. package/dist/index.js.map +1 -1
  12. package/package.json +6 -6
  13. package/src/Kubb.ts +37 -55
  14. package/src/{PluginManager.ts → PluginDriver.ts} +51 -40
  15. package/src/build.ts +74 -29
  16. package/src/config.ts +9 -8
  17. package/src/constants.ts +44 -1
  18. package/src/createAdapter.ts +25 -0
  19. package/src/createPlugin.ts +28 -0
  20. package/src/createStorage.ts +58 -0
  21. package/src/defineBuilder.ts +26 -0
  22. package/src/defineGenerator.ts +137 -0
  23. package/src/defineLogger.ts +13 -3
  24. package/src/definePreset.ts +27 -0
  25. package/src/definePresets.ts +16 -0
  26. package/src/defineResolver.ts +448 -0
  27. package/src/hooks/index.ts +1 -1
  28. package/src/hooks/useDriver.ts +8 -0
  29. package/src/hooks/useMode.ts +5 -2
  30. package/src/hooks/usePlugin.ts +5 -2
  31. package/src/index.ts +21 -6
  32. package/src/renderNode.tsx +105 -0
  33. package/src/storages/fsStorage.ts +2 -2
  34. package/src/storages/memoryStorage.ts +2 -2
  35. package/src/types.ts +342 -42
  36. package/src/utils/FunctionParams.ts +2 -2
  37. package/src/utils/TreeNode.ts +24 -1
  38. package/src/utils/diagnostics.ts +4 -1
  39. package/src/utils/executeStrategies.ts +23 -10
  40. package/src/utils/formatters.ts +10 -21
  41. package/src/utils/getBarrelFiles.ts +79 -9
  42. package/src/utils/getConfigs.ts +8 -22
  43. package/src/utils/getPreset.ts +52 -0
  44. package/src/utils/linters.ts +23 -3
  45. package/src/utils/mergeResolvers.ts +8 -0
  46. package/src/utils/packageJSON.ts +76 -0
  47. package/src/BarrelManager.ts +0 -74
  48. package/src/PackageManager.ts +0 -180
  49. package/src/PromiseManager.ts +0 -40
  50. package/src/defineAdapter.ts +0 -22
  51. package/src/definePlugin.ts +0 -12
  52. package/src/defineStorage.ts +0 -56
  53. package/src/errors.ts +0 -1
  54. package/src/hooks/usePluginManager.ts +0 -8
  55. package/src/utils/getPlugins.ts +0 -23
package/src/build.ts CHANGED
@@ -1,16 +1,14 @@
1
1
  import { dirname, relative, resolve } from 'node:path'
2
- import { AsyncEventEmitter, exists, formatMs, getElapsedMs, getRelativePath, URLPath } from '@internals/utils'
3
- import type { KubbFile } from '@kubb/fabric-core/types'
4
- import type { Fabric } from '@kubb/react-fabric'
2
+ import { AsyncEventEmitter, BuildError, exists, formatMs, getElapsedMs, getRelativePath, URLPath } from '@internals/utils'
3
+ import type { Fabric as FabricType, KubbFile } from '@kubb/fabric-core/types'
5
4
  import { createFabric } from '@kubb/react-fabric'
6
5
  import { typescriptParser } from '@kubb/react-fabric/parsers'
7
6
  import { fsPlugin } from '@kubb/react-fabric/plugins'
8
7
  import { isInputPath } from './config.ts'
9
8
  import { BARREL_FILENAME, DEFAULT_BANNER, DEFAULT_CONCURRENCY, DEFAULT_EXTENSION, DEFAULT_STUDIO_URL } from './constants.ts'
10
- import { BuildError } from './errors.ts'
11
- import { PluginManager } from './PluginManager.ts'
9
+ import { PluginDriver } from './PluginDriver.ts'
12
10
  import { fsStorage } from './storages/fsStorage.ts'
13
- import type { AdapterSource, Config, DefineStorage, KubbEvents, Output, Plugin, UserConfig } from './types.ts'
11
+ import type { AdapterSource, Config, KubbEvents, Output, Plugin, Storage, UserConfig } from './types.ts'
14
12
  import { getDiagnosticInfo } from './utils/diagnostics.ts'
15
13
  import type { FileMetaBase } from './utils/getBarrelFiles.ts'
16
14
 
@@ -19,23 +17,49 @@ type BuildOptions = {
19
17
  events?: AsyncEventEmitter<KubbEvents>
20
18
  }
21
19
 
20
+ /**
21
+ * Full output produced by a successful or failed build.
22
+ */
22
23
  type BuildOutput = {
24
+ /**
25
+ * Plugins that threw during installation, paired with the caught error.
26
+ */
23
27
  failedPlugins: Set<{ plugin: Plugin; error: Error }>
24
- fabric: Fabric
28
+ fabric: FabricType
25
29
  files: Array<KubbFile.ResolvedFile>
26
- pluginManager: PluginManager
30
+ driver: PluginDriver
31
+ /**
32
+ * Elapsed time in milliseconds for each plugin, keyed by plugin name.
33
+ */
27
34
  pluginTimings: Map<string, number>
28
35
  error?: Error
36
+ /**
37
+ * Raw generated source, keyed by absolute file path.
38
+ */
29
39
  sources: Map<KubbFile.Path, string>
30
40
  }
31
41
 
42
+ /**
43
+ * Intermediate result returned by {@link setup} and accepted by {@link safeBuild}.
44
+ */
32
45
  type SetupResult = {
33
46
  events: AsyncEventEmitter<KubbEvents>
34
- fabric: Fabric
35
- pluginManager: PluginManager
47
+ fabric: FabricType
48
+ driver: PluginDriver
36
49
  sources: Map<KubbFile.Path, string>
37
50
  }
38
51
 
52
+ /**
53
+ * Initializes all Kubb infrastructure for a build without executing any plugins.
54
+ *
55
+ * - Validates the input path (when applicable).
56
+ * - Applies config defaults (`root`, `output.*`, `devtools`).
57
+ * - Creates the Fabric instance and wires storage, format, and lint hooks.
58
+ * - Runs the adapter (if configured) to produce the universal `RootNode`.
59
+ *
60
+ * Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
61
+ * via the `overrides` argument to reuse the same infrastructure across multiple runs.
62
+ */
39
63
  export async function setup(options: BuildOptions): Promise<SetupResult> {
40
64
  const { config: userConfig, events = new AsyncEventEmitter<KubbEvents>() } = options
41
65
 
@@ -110,7 +134,7 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
110
134
  // storage or fall back to fsStorage (backwards-compatible default).
111
135
  // Keys are root-relative (e.g. `src/gen/api/getPets.ts`) so fsStorage()
112
136
  // needs no configuration — it resolves them against process.cwd().
113
- const storage: DefineStorage | null = definedConfig.output.write === false ? null : (definedConfig.output.storage ?? fsStorage())
137
+ const storage: Storage | null = definedConfig.output.write === false ? null : (definedConfig.output.storage ?? fsStorage())
114
138
 
115
139
  if (definedConfig.output.clean) {
116
140
  await events.emit('debug', {
@@ -165,7 +189,7 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
165
189
  ],
166
190
  })
167
191
 
168
- const pluginManager = new PluginManager(definedConfig, {
192
+ const pluginDriver = new PluginDriver(definedConfig, {
169
193
  fabric,
170
194
  events,
171
195
  concurrency: DEFAULT_CONCURRENCY,
@@ -180,14 +204,15 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
180
204
  logs: [`Running adapter: ${definedConfig.adapter.name}`],
181
205
  })
182
206
 
183
- pluginManager.rootNode = await definedConfig.adapter.parse(source)
207
+ pluginDriver.adapter = definedConfig.adapter
208
+ pluginDriver.rootNode = await definedConfig.adapter.parse(source)
184
209
 
185
210
  await events.emit('debug', {
186
211
  date: new Date(),
187
212
  logs: [
188
213
  `✓ Adapter '${definedConfig.adapter.name}' resolved RootNode`,
189
- ` • Schemas: ${pluginManager.rootNode.schemas.length}`,
190
- ` • Operations: ${pluginManager.rootNode.operations.length}`,
214
+ ` • Schemas: ${pluginDriver.rootNode.schemas.length}`,
215
+ ` • Operations: ${pluginDriver.rootNode.operations.length}`,
191
216
  ],
192
217
  })
193
218
  }
@@ -195,13 +220,19 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
195
220
  return {
196
221
  events,
197
222
  fabric,
198
- pluginManager,
223
+ driver: pluginDriver,
199
224
  sources,
200
225
  }
201
226
  }
202
227
 
228
+ /**
229
+ * Runs a full Kubb build and throws on any error or plugin failure.
230
+ *
231
+ * Internally delegates to {@link safeBuild} and rethrows collected errors.
232
+ * Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
233
+ */
203
234
  export async function build(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
204
- const { fabric, files, pluginManager, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides)
235
+ const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides)
205
236
 
206
237
  if (error) {
207
238
  throw error
@@ -217,24 +248,34 @@ export async function build(options: BuildOptions, overrides?: SetupResult): Pro
217
248
  failedPlugins,
218
249
  fabric,
219
250
  files,
220
- pluginManager,
251
+ driver,
221
252
  pluginTimings,
222
253
  error: undefined,
223
254
  sources,
224
255
  }
225
256
  }
226
257
 
258
+ /**
259
+ * Runs a full Kubb build and captures errors instead of throwing.
260
+ *
261
+ * - Installs each plugin in order, recording failures in `failedPlugins`.
262
+ * - Generates the root barrel file when `output.barrelType` is set.
263
+ * - Writes all files through Fabric.
264
+ *
265
+ * Returns a {@link BuildOutput} even on failure — inspect `error` and
266
+ * `failedPlugins` to determine whether the build succeeded.
267
+ */
227
268
  export async function safeBuild(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
228
- const { fabric, pluginManager, events, sources } = overrides ? overrides : await setup(options)
269
+ const { fabric, driver, events, sources } = overrides ? overrides : await setup(options)
229
270
 
230
271
  const failedPlugins = new Set<{ plugin: Plugin; error: Error }>()
231
272
  // in ms
232
273
  const pluginTimings = new Map<string, number>()
233
- const config = pluginManager.config
274
+ const config = driver.config
234
275
 
235
276
  try {
236
- for (const plugin of pluginManager.plugins) {
237
- const context = pluginManager.getContext(plugin)
277
+ for (const plugin of driver.plugins) {
278
+ const context = driver.getContext(plugin)
238
279
  const hrStart = process.hrtime()
239
280
 
240
281
  const installer = plugin.install.bind(context)
@@ -313,7 +354,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
313
354
  const rootFile: KubbFile.File = {
314
355
  path: rootPath,
315
356
  baseName: BARREL_FILENAME,
316
- exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, pluginManager }),
357
+ exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }),
317
358
  sources: [],
318
359
  imports: [],
319
360
  meta: {},
@@ -335,7 +376,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
335
376
  failedPlugins,
336
377
  fabric,
337
378
  files,
338
- pluginManager,
379
+ driver,
339
380
  pluginTimings,
340
381
  sources,
341
382
  }
@@ -344,7 +385,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
344
385
  failedPlugins,
345
386
  fabric,
346
387
  files: [],
347
- pluginManager,
388
+ driver,
348
389
  pluginTimings,
349
390
  error: error as Error,
350
391
  sources,
@@ -357,12 +398,12 @@ type BuildBarrelExportsParams = {
357
398
  rootDir: string
358
399
  existingExports: Set<string>
359
400
  config: Config
360
- pluginManager: PluginManager
401
+ driver: PluginDriver
361
402
  }
362
403
 
363
- function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, pluginManager }: BuildBarrelExportsParams): KubbFile.Export[] {
404
+ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }: BuildBarrelExportsParams): KubbFile.Export[] {
364
405
  const pluginNameMap = new Map<string, Plugin>()
365
- for (const plugin of pluginManager.plugins) {
406
+ for (const plugin of driver.plugins) {
366
407
  pluginNameMap.set(plugin.name, plugin)
367
408
  }
368
409
 
@@ -406,7 +447,7 @@ function inputToAdapterSource(config: Config): AdapterSource {
406
447
  if (Array.isArray(config.input)) {
407
448
  return {
408
449
  type: 'paths',
409
- paths: config.input.map((i) => resolve(config.root, i.path)),
450
+ paths: config.input.map((i) => (new URLPath(i.path).isURL ? i.path : resolve(config.root, i.path))),
410
451
  }
411
452
  }
412
453
 
@@ -414,6 +455,10 @@ function inputToAdapterSource(config: Config): AdapterSource {
414
455
  return { type: 'data', data: config.input.data }
415
456
  }
416
457
 
458
+ if (new URLPath(config.input.path).isURL) {
459
+ return { type: 'path', path: config.input.path }
460
+ }
461
+
417
462
  const resolved = resolve(config.root, config.input.path)
418
463
  return { type: 'path', path: resolved }
419
464
  }
package/src/config.ts CHANGED
@@ -5,12 +5,14 @@ import type { InputPath, UserConfig } from './types.ts'
5
5
  * CLI options derived from command-line flags.
6
6
  */
7
7
  export type CLIOptions = {
8
- /** Path to `kubb.config.js` */
8
+ /**
9
+ * Path to `kubb.config.js`.
10
+ */
9
11
  config?: string
10
-
11
- /** Enable watch mode for input files */
12
+ /**
13
+ * Enable watch mode for input files.
14
+ */
12
15
  watch?: boolean
13
-
14
16
  /**
15
17
  * Logging verbosity for CLI usage.
16
18
  *
@@ -20,12 +22,11 @@ export type CLIOptions = {
20
22
  * @default 'silent'
21
23
  */
22
24
  logLevel?: 'silent' | 'info' | 'debug'
23
-
24
- /** Run Kubb with Bun */
25
- bun?: boolean
26
25
  }
27
26
 
28
- /** All accepted forms of a Kubb configuration. */
27
+ /**
28
+ * All accepted forms of a Kubb configuration.
29
+ */
29
30
  export type ConfigInput = PossiblePromise<UserConfig | UserConfig[]> | ((cli: CLIOptions) => PossiblePromise<UserConfig | UserConfig[]>)
30
31
 
31
32
  /**
package/src/constants.ts CHANGED
@@ -1,21 +1,50 @@
1
1
  import type { KubbFile } from '@kubb/fabric-core/types'
2
2
 
3
+ /**
4
+ * Base URL for the Kubb Studio web app.
5
+ */
3
6
  export const DEFAULT_STUDIO_URL = 'https://studio.kubb.dev' as const
4
7
 
8
+ /**
9
+ * Internal plugin name used to identify the core Kubb runtime.
10
+ */
5
11
  export const CORE_PLUGIN_NAME = 'core' as const
6
12
 
13
+ /**
14
+ * Maximum number of event-emitter listeners before Node.js emits a warning.
15
+ */
7
16
  export const DEFAULT_MAX_LISTENERS = 100
8
17
 
18
+ /**
19
+ * Default number of plugins that may run concurrently during a build.
20
+ */
9
21
  export const DEFAULT_CONCURRENCY = 15
10
22
 
23
+ /**
24
+ * File name used for generated barrel (index) files.
25
+ */
11
26
  export const BARREL_FILENAME = 'index.ts' as const
12
27
 
28
+ /**
29
+ * Default banner style written at the top of every generated file.
30
+ */
13
31
  export const DEFAULT_BANNER = 'simple' as const
14
32
 
33
+ /**
34
+ * Default file-extension mapping used when no explicit mapping is configured.
35
+ */
15
36
  export const DEFAULT_EXTENSION: Record<KubbFile.Extname, KubbFile.Extname | ''> = { '.ts': '.ts' }
16
37
 
17
- export const PATH_SEPARATORS = ['/', '\\'] as const
38
+ /**
39
+ * Characters recognized as path separators on both POSIX and Windows.
40
+ */
41
+ export const PATH_SEPARATORS = new Set(['/', '\\'] as const)
18
42
 
43
+ /**
44
+ * Numeric log-level thresholds used internally to compare verbosity.
45
+ *
46
+ * Higher numbers are more verbose.
47
+ */
19
48
  export const logLevel = {
20
49
  silent: Number.NEGATIVE_INFINITY,
21
50
  error: 0,
@@ -25,6 +54,13 @@ export const logLevel = {
25
54
  debug: 5,
26
55
  } as const
27
56
 
57
+ /**
58
+ * CLI command descriptors for each supported linter.
59
+ *
60
+ * Each entry contains the executable `command`, an `args` factory that maps an
61
+ * output path to the correct argument list, and an `errorMessage` shown when
62
+ * the linter is not found.
63
+ */
28
64
  export const linters = {
29
65
  eslint: {
30
66
  command: 'eslint',
@@ -43,6 +79,13 @@ export const linters = {
43
79
  },
44
80
  } as const
45
81
 
82
+ /**
83
+ * CLI command descriptors for each supported code formatter.
84
+ *
85
+ * Each entry contains the executable `command`, an `args` factory that maps an
86
+ * output path to the correct argument list, and an `errorMessage` shown when
87
+ * the formatter is not found.
88
+ */
46
89
  export const formatters = {
47
90
  prettier: {
48
91
  command: 'prettier',
@@ -0,0 +1,25 @@
1
+ import type { Adapter, AdapterFactoryOptions } from './types.ts'
2
+
3
+ /**
4
+ * Builder type for an {@link Adapter} — takes options and returns the adapter instance.
5
+ */
6
+ type AdapterBuilder<T extends AdapterFactoryOptions> = (options: T['options']) => Adapter<T>
7
+
8
+ /**
9
+ * Creates an adapter factory. Call the returned function with optional options to get the adapter instance.
10
+ *
11
+ * @example
12
+ * export const myAdapter = createAdapter<MyAdapter>((options) => {
13
+ * return {
14
+ * name: 'my-adapter',
15
+ * options,
16
+ * async parse(source) { ... },
17
+ * }
18
+ * })
19
+ *
20
+ * // instantiate
21
+ * const adapter = myAdapter({ validate: true })
22
+ */
23
+ export function createAdapter<T extends AdapterFactoryOptions = AdapterFactoryOptions>(build: AdapterBuilder<T>): (options?: T['options']) => Adapter<T> {
24
+ return (options) => build(options ?? ({} as T['options']))
25
+ }
@@ -0,0 +1,28 @@
1
+ import type { PluginFactoryOptions, UserPluginWithLifeCycle } from './types.ts'
2
+
3
+ /**
4
+ * Builder type for a {@link UserPluginWithLifeCycle} — takes options and returns the plugin instance.
5
+ */
6
+ type PluginBuilder<T extends PluginFactoryOptions = PluginFactoryOptions> = (options: T['options']) => UserPluginWithLifeCycle<T>
7
+
8
+ /**
9
+ * Creates a plugin factory. Call the returned function with optional options to get the plugin instance.
10
+ *
11
+ * @example
12
+ * export const myPlugin = createPlugin<MyPlugin>((options) => {
13
+ * return {
14
+ * name: 'my-plugin',
15
+ * options,
16
+ * resolvePath(baseName) { ... },
17
+ * resolveName(name, type) { ... },
18
+ * }
19
+ * })
20
+ *
21
+ * // instantiate
22
+ * const plugin = myPlugin({ output: { path: 'src/gen' } })
23
+ */
24
+ export function createPlugin<T extends PluginFactoryOptions = PluginFactoryOptions>(
25
+ build: PluginBuilder<T>,
26
+ ): (options?: T['options']) => UserPluginWithLifeCycle<T> {
27
+ return (options) => build(options ?? ({} as T['options']))
28
+ }
@@ -0,0 +1,58 @@
1
+ export type Storage = {
2
+ /**
3
+ * Identifier used for logging and debugging (e.g. `'fs'`, `'s3'`).
4
+ */
5
+ readonly name: string
6
+ /**
7
+ * Returns `true` when an entry for `key` exists in storage.
8
+ */
9
+ hasItem(key: string): Promise<boolean>
10
+ /**
11
+ * Returns the stored string value, or `null` when `key` does not exist.
12
+ */
13
+ getItem(key: string): Promise<string | null>
14
+ /**
15
+ * Persists `value` under `key`, creating any required structure.
16
+ */
17
+ setItem(key: string, value: string): Promise<void>
18
+ /**
19
+ * Removes the entry for `key`. No-ops when the key does not exist.
20
+ */
21
+ removeItem(key: string): Promise<void>
22
+ /**
23
+ * Returns all keys, optionally filtered to those starting with `base`.
24
+ */
25
+ getKeys(base?: string): Promise<Array<string>>
26
+ /**
27
+ * Removes all entries, optionally scoped to those starting with `base`.
28
+ */
29
+ clear(base?: string): Promise<void>
30
+ /**
31
+ * Optional teardown hook called after the build completes.
32
+ */
33
+ dispose?(): Promise<void>
34
+ }
35
+
36
+ /**
37
+ * Creates a storage factory. Call the returned function with optional options to get the storage instance.
38
+ *
39
+ * @example
40
+ * export const memoryStorage = createStorage(() => {
41
+ * const store = new Map<string, string>()
42
+ * return {
43
+ * name: 'memory',
44
+ * async hasItem(key) { return store.has(key) },
45
+ * async getItem(key) { return store.get(key) ?? null },
46
+ * async setItem(key, value) { store.set(key, value) },
47
+ * async removeItem(key) { store.delete(key) },
48
+ * async getKeys(base) {
49
+ * const keys = [...store.keys()]
50
+ * return base ? keys.filter((k) => k.startsWith(base)) : keys
51
+ * },
52
+ * async clear(base) { if (!base) store.clear() },
53
+ * }
54
+ * })
55
+ */
56
+ export function createStorage<TOptions = Record<string, never>>(build: (options: TOptions) => Storage): (options?: TOptions) => Storage {
57
+ return (options) => build(options ?? ({} as TOptions))
58
+ }
@@ -0,0 +1,26 @@
1
+ import type { PluginFactoryOptions } from './types.ts'
2
+
3
+ /**
4
+ * Builder type for the plugin-specific builder fields.
5
+ * `name` is required; all other methods are defined by the concrete plugin builder type.
6
+ */
7
+ type BuilderBuilder<T extends PluginFactoryOptions> = () => T['builder'] & ThisType<T['builder']>
8
+
9
+ /**
10
+ * Defines a builder for a plugin — a named collection of schema-building helpers that
11
+ * can be exported alongside the plugin and imported by other plugins or generators.
12
+ *
13
+ * @example
14
+ * export const builder = defineBuilder<PluginTs>(() => ({
15
+ * name: 'default',
16
+ * buildParamsSchema({ params, node, resolver }) {
17
+ * return createSchema({ type: 'object', properties: [] })
18
+ * },
19
+ * buildDataSchemaNode({ node, resolver }) {
20
+ * return createSchema({ type: 'object', properties: [] })
21
+ * },
22
+ * }))
23
+ */
24
+ export function defineBuilder<T extends PluginFactoryOptions>(build: BuilderBuilder<T>): T['builder'] {
25
+ return build() as T['builder']
26
+ }
@@ -0,0 +1,137 @@
1
+ import type { OperationNode, SchemaNode } from '@kubb/ast/types'
2
+ import type { KubbFile } from '@kubb/fabric-core/types'
3
+ import type { FabricReactNode } from '@kubb/react-fabric/types'
4
+ import type { Adapter, Config, Plugin, PluginFactoryOptions } from './types.ts'
5
+
6
+ export type Version = '1' | '2'
7
+
8
+ /**
9
+ * Props for the `operations` lifecycle — receives all operation nodes at once.
10
+ */
11
+ export type OperationsV2Props<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
12
+ config: Config
13
+ plugin: Plugin<TPlugin>
14
+ adapter: Adapter
15
+ options: Plugin<TPlugin>['options']
16
+ nodes: Array<OperationNode>
17
+ }
18
+
19
+ /**
20
+ * Props for the `operation` lifecycle — receives a single operation node.
21
+ */
22
+ export type OperationV2Props<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
23
+ config: Config
24
+ adapter: Adapter
25
+ plugin: Plugin<TPlugin>
26
+ options: Plugin<TPlugin>['options']
27
+ node: OperationNode
28
+ }
29
+
30
+ /**
31
+ * Props for the `schema` lifecycle — receives a single schema node.
32
+ */
33
+ export type SchemaV2Props<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
34
+ config: Config
35
+ adapter: Adapter
36
+ plugin: Plugin<TPlugin>
37
+ options: Plugin<TPlugin>['options']
38
+ node: SchemaNode
39
+ }
40
+
41
+ type UserCoreGeneratorV2<TPlugin extends PluginFactoryOptions> = {
42
+ name: string
43
+ type: 'core'
44
+ version?: '2'
45
+ operations?(props: OperationsV2Props<TPlugin>): Promise<Array<KubbFile.File>>
46
+ operation?(props: OperationV2Props<TPlugin>): Promise<Array<KubbFile.File>>
47
+ schema?(props: SchemaV2Props<TPlugin>): Promise<Array<KubbFile.File>>
48
+ }
49
+
50
+ type UserReactGeneratorV2<TPlugin extends PluginFactoryOptions> = {
51
+ name: string
52
+ type: 'react'
53
+ version?: '2'
54
+ Operations?(props: OperationsV2Props<TPlugin>): FabricReactNode
55
+ Operation?(props: OperationV2Props<TPlugin>): FabricReactNode
56
+ Schema?(props: SchemaV2Props<TPlugin>): FabricReactNode
57
+ }
58
+
59
+ export type CoreGeneratorV2<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
60
+ name: string
61
+ type: 'core'
62
+ version: '2'
63
+ operations(props: OperationsV2Props<TPlugin>): Promise<Array<KubbFile.File>>
64
+ operation(props: OperationV2Props<TPlugin>): Promise<Array<KubbFile.File>>
65
+ schema(props: SchemaV2Props<TPlugin>): Promise<Array<KubbFile.File>>
66
+ }
67
+
68
+ export type ReactGeneratorV2<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
69
+ name: string
70
+ type: 'react'
71
+ version: '2'
72
+ Operations(props: OperationsV2Props<TPlugin>): FabricReactNode
73
+ Operation(props: OperationV2Props<TPlugin>): FabricReactNode
74
+ Schema(props: SchemaV2Props<TPlugin>): FabricReactNode
75
+ }
76
+
77
+ export type Generator<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = UserCoreGeneratorV2<TPlugin> | UserReactGeneratorV2<TPlugin>
78
+
79
+ /**
80
+ * Defines a generator with no-op defaults for any omitted lifecycle methods.
81
+ * Works for both `core` (async file output) and `react` (JSX component) generators.
82
+ *
83
+ * @example
84
+ * // react generator
85
+ * export const typeGenerator = defineGenerator<PluginTs>({
86
+ * name: 'typescript',
87
+ * type: 'react',
88
+ * Operation({ node, options }) { return <File>...</File> },
89
+ * Schema({ node, options }) { return <File>...</File> },
90
+ * })
91
+ *
92
+ * @example
93
+ * // core generator
94
+ * export const myGenerator = defineGenerator<MyPlugin>({
95
+ * name: 'my-generator',
96
+ * type: 'core',
97
+ * async operation({ node, options }) { return [{ path: '...', content: '...' }] },
98
+ * })
99
+ */
100
+ export function defineGenerator<TPlugin extends PluginFactoryOptions = PluginFactoryOptions>(
101
+ generator: UserReactGeneratorV2<TPlugin>,
102
+ ): ReactGeneratorV2<TPlugin>
103
+
104
+ export function defineGenerator<TPlugin extends PluginFactoryOptions = PluginFactoryOptions>(generator: UserCoreGeneratorV2<TPlugin>): CoreGeneratorV2<TPlugin>
105
+ export function defineGenerator<TPlugin extends PluginFactoryOptions = PluginFactoryOptions>(
106
+ generator: UserCoreGeneratorV2<TPlugin> | UserReactGeneratorV2<TPlugin>,
107
+ ): unknown {
108
+ if (generator.type === 'react') {
109
+ return {
110
+ version: '2',
111
+ Operations() {
112
+ return null
113
+ },
114
+ Operation() {
115
+ return null
116
+ },
117
+ Schema() {
118
+ return null
119
+ },
120
+ ...generator,
121
+ }
122
+ }
123
+
124
+ return {
125
+ version: '2',
126
+ async operations() {
127
+ return []
128
+ },
129
+ async operation() {
130
+ return []
131
+ },
132
+ async schema() {
133
+ return []
134
+ },
135
+ ...generator,
136
+ }
137
+ }
@@ -1,7 +1,17 @@
1
1
  import type { Logger, LoggerOptions, UserLogger } from './types.ts'
2
2
 
3
+ /**
4
+ * Wraps a logger definition into a typed {@link Logger}.
5
+ *
6
+ * @example
7
+ * export const myLogger = defineLogger({
8
+ * name: 'my-logger',
9
+ * install(context, options) {
10
+ * context.on('info', (message) => console.log('ℹ', message))
11
+ * context.on('error', (error) => console.error('✗', error.message))
12
+ * },
13
+ * })
14
+ */
3
15
  export function defineLogger<Options extends LoggerOptions = LoggerOptions>(logger: UserLogger<Options>): Logger<Options> {
4
- return {
5
- ...logger,
6
- }
16
+ return logger
7
17
  }
@@ -0,0 +1,27 @@
1
+ import type { Visitor } from '@kubb/ast/types'
2
+ import type { Generator, Preset, Resolver } from './types.ts'
3
+
4
+ /**
5
+ * Creates a typed preset object that bundles a name, resolvers, optional
6
+ * transformers, and optional generators — the building block for composable plugin presets.
7
+ *
8
+ * @example
9
+ * import { definePreset } from '@kubb/core'
10
+ * import { resolverTsLegacy } from '@kubb/plugin-ts'
11
+ *
12
+ * export const myPreset = definePreset('myPreset', { resolvers: [resolverTsLegacy] })
13
+ *
14
+ * @example
15
+ * // With custom transformers
16
+ * export const myPreset = definePreset('myPreset', { resolvers: [resolverTsLegacy], transformers: [myTransformer] })
17
+ *
18
+ * @example
19
+ * // With generators
20
+ * export const myPreset = definePreset('myPreset', { resolvers: [resolverTsLegacy], generators: [typeGeneratorLegacy] })
21
+ */
22
+ export function definePreset<TResolver extends Resolver = Resolver, TName extends string = string>(
23
+ name: TName,
24
+ { resolvers, transformers, generators }: { resolvers: Array<TResolver>; transformers?: Array<Visitor>; generators?: Array<Generator<any>> },
25
+ ): Preset<TResolver> & { name: TName } {
26
+ return { name, resolvers, transformers, generators }
27
+ }