@kubb/core 4.36.1 → 5.0.0-alpha.10

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 (47) hide show
  1. package/dist/{types-D30QAz2y.d.ts → PluginDriver-BkFepPdm.d.ts} +354 -291
  2. package/dist/hooks.cjs +85 -8
  3. package/dist/hooks.cjs.map +1 -1
  4. package/dist/hooks.d.ts +66 -4
  5. package/dist/hooks.js +83 -8
  6. package/dist/hooks.js.map +1 -1
  7. package/dist/index.cjs +346 -315
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +91 -77
  10. package/dist/index.js +338 -305
  11. package/dist/index.js.map +1 -1
  12. package/package.json +7 -7
  13. package/src/Kubb.ts +27 -55
  14. package/src/{PluginManager.ts → PluginDriver.ts} +69 -82
  15. package/src/build.ts +32 -33
  16. package/src/constants.ts +1 -1
  17. package/src/createAdapter.ts +25 -0
  18. package/src/createPlugin.ts +28 -0
  19. package/src/createStorage.ts +58 -0
  20. package/src/defineGenerator.ts +134 -0
  21. package/src/defineLogger.ts +13 -3
  22. package/src/defineResolver.ts +131 -0
  23. package/src/hooks/index.ts +2 -1
  24. package/src/hooks/useKubb.ts +143 -0
  25. package/src/hooks/useMode.ts +5 -2
  26. package/src/hooks/usePlugin.ts +5 -2
  27. package/src/hooks/usePluginDriver.ts +11 -0
  28. package/src/index.ts +7 -7
  29. package/src/storages/fsStorage.ts +2 -2
  30. package/src/storages/memoryStorage.ts +2 -2
  31. package/src/types.ts +94 -48
  32. package/src/utils/FunctionParams.ts +2 -2
  33. package/src/utils/TreeNode.ts +1 -1
  34. package/src/utils/formatters.ts +1 -1
  35. package/src/utils/getBarrelFiles.ts +73 -11
  36. package/src/utils/getConfigs.ts +3 -21
  37. package/src/utils/linters.ts +1 -1
  38. package/src/utils/packageJSON.ts +61 -0
  39. package/src/BarrelManager.ts +0 -74
  40. package/src/PackageManager.ts +0 -180
  41. package/src/PromiseManager.ts +0 -40
  42. package/src/defineAdapter.ts +0 -22
  43. package/src/definePlugin.ts +0 -12
  44. package/src/defineStorage.ts +0 -56
  45. package/src/errors.ts +0 -1
  46. package/src/hooks/usePluginManager.ts +0 -8
  47. 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
 
@@ -21,9 +19,9 @@ type BuildOptions = {
21
19
 
22
20
  type BuildOutput = {
23
21
  failedPlugins: Set<{ plugin: Plugin; error: Error }>
24
- fabric: Fabric
22
+ fabric: FabricType
25
23
  files: Array<KubbFile.ResolvedFile>
26
- pluginManager: PluginManager
24
+ driver: PluginDriver
27
25
  pluginTimings: Map<string, number>
28
26
  error?: Error
29
27
  sources: Map<KubbFile.Path, string>
@@ -31,8 +29,8 @@ type BuildOutput = {
31
29
 
32
30
  type SetupResult = {
33
31
  events: AsyncEventEmitter<KubbEvents>
34
- fabric: Fabric
35
- pluginManager: PluginManager
32
+ fabric: FabricType
33
+ driver: PluginDriver
36
34
  sources: Map<KubbFile.Path, string>
37
35
  }
38
36
 
@@ -110,7 +108,7 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
110
108
  // storage or fall back to fsStorage (backwards-compatible default).
111
109
  // Keys are root-relative (e.g. `src/gen/api/getPets.ts`) so fsStorage()
112
110
  // needs no configuration — it resolves them against process.cwd().
113
- const storage: DefineStorage | null = definedConfig.output.write === false ? null : (definedConfig.output.storage ?? fsStorage())
111
+ const storage: Storage | null = definedConfig.output.write === false ? null : (definedConfig.output.storage ?? fsStorage())
114
112
 
115
113
  if (definedConfig.output.clean) {
116
114
  await events.emit('debug', {
@@ -165,7 +163,7 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
165
163
  ],
166
164
  })
167
165
 
168
- const pluginManager = new PluginManager(definedConfig, {
166
+ const pluginDriver = new PluginDriver(definedConfig, {
169
167
  fabric,
170
168
  events,
171
169
  concurrency: DEFAULT_CONCURRENCY,
@@ -180,14 +178,15 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
180
178
  logs: [`Running adapter: ${definedConfig.adapter.name}`],
181
179
  })
182
180
 
183
- pluginManager.rootNode = await definedConfig.adapter.parse(source)
181
+ pluginDriver.adapter = definedConfig.adapter
182
+ pluginDriver.rootNode = await definedConfig.adapter.parse(source)
184
183
 
185
184
  await events.emit('debug', {
186
185
  date: new Date(),
187
186
  logs: [
188
187
  `✓ Adapter '${definedConfig.adapter.name}' resolved RootNode`,
189
- ` • Schemas: ${pluginManager.rootNode.schemas.length}`,
190
- ` • Operations: ${pluginManager.rootNode.operations.length}`,
188
+ ` • Schemas: ${pluginDriver.rootNode.schemas.length}`,
189
+ ` • Operations: ${pluginDriver.rootNode.operations.length}`,
191
190
  ],
192
191
  })
193
192
  }
@@ -195,13 +194,13 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
195
194
  return {
196
195
  events,
197
196
  fabric,
198
- pluginManager,
197
+ driver: pluginDriver,
199
198
  sources,
200
199
  }
201
200
  }
202
201
 
203
202
  export async function build(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
204
- const { fabric, files, pluginManager, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides)
203
+ const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides)
205
204
 
206
205
  if (error) {
207
206
  throw error
@@ -217,7 +216,7 @@ export async function build(options: BuildOptions, overrides?: SetupResult): Pro
217
216
  failedPlugins,
218
217
  fabric,
219
218
  files,
220
- pluginManager,
219
+ driver,
221
220
  pluginTimings,
222
221
  error: undefined,
223
222
  sources,
@@ -225,16 +224,16 @@ export async function build(options: BuildOptions, overrides?: SetupResult): Pro
225
224
  }
226
225
 
227
226
  export async function safeBuild(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
228
- const { fabric, pluginManager, events, sources } = overrides ? overrides : await setup(options)
227
+ const { fabric, driver, events, sources } = overrides ? overrides : await setup(options)
229
228
 
230
229
  const failedPlugins = new Set<{ plugin: Plugin; error: Error }>()
231
230
  // in ms
232
231
  const pluginTimings = new Map<string, number>()
233
- const config = pluginManager.config
232
+ const config = driver.config
234
233
 
235
234
  try {
236
- for (const plugin of pluginManager.plugins) {
237
- const context = pluginManager.getContext(plugin)
235
+ for (const plugin of driver.plugins) {
236
+ const context = driver.getContext(plugin)
238
237
  const hrStart = process.hrtime()
239
238
 
240
239
  const installer = plugin.install.bind(context)
@@ -246,7 +245,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
246
245
 
247
246
  await events.emit('debug', {
248
247
  date: timestamp,
249
- logs: ['Installing plugin...', ` • Plugin Key: [${plugin.key.join(', ')}]`],
248
+ logs: ['Installing plugin...', ` • Plugin Name: ${plugin.name}`],
250
249
  })
251
250
 
252
251
  await installer(context)
@@ -275,7 +274,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
275
274
  date: errorTimestamp,
276
275
  logs: [
277
276
  '✗ Plugin installation failed',
278
- ` • Plugin Key: ${JSON.stringify(plugin.key)}`,
277
+ ` • Plugin Name: ${plugin.name}`,
279
278
  ` • Error: ${error.constructor.name} - ${error.message}`,
280
279
  ' • Stack Trace:',
281
280
  error.stack || 'No stack trace available',
@@ -313,7 +312,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
313
312
  const rootFile: KubbFile.File = {
314
313
  path: rootPath,
315
314
  baseName: BARREL_FILENAME,
316
- exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, pluginManager }),
315
+ exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }),
317
316
  sources: [],
318
317
  imports: [],
319
318
  meta: {},
@@ -335,7 +334,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
335
334
  failedPlugins,
336
335
  fabric,
337
336
  files,
338
- pluginManager,
337
+ driver,
339
338
  pluginTimings,
340
339
  sources,
341
340
  }
@@ -344,7 +343,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
344
343
  failedPlugins,
345
344
  fabric,
346
345
  files: [],
347
- pluginManager,
346
+ driver,
348
347
  pluginTimings,
349
348
  error: error as Error,
350
349
  sources,
@@ -357,13 +356,13 @@ type BuildBarrelExportsParams = {
357
356
  rootDir: string
358
357
  existingExports: Set<string>
359
358
  config: Config
360
- pluginManager: PluginManager
359
+ driver: PluginDriver
361
360
  }
362
361
 
363
- function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, pluginManager }: BuildBarrelExportsParams): KubbFile.Export[] {
364
- const pluginKeyMap = new Map<string, Plugin>()
365
- for (const plugin of pluginManager.plugins) {
366
- pluginKeyMap.set(JSON.stringify(plugin.key), plugin)
362
+ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }: BuildBarrelExportsParams): KubbFile.Export[] {
363
+ const pluginNameMap = new Map<string, Plugin>()
364
+ for (const plugin of driver.plugins) {
365
+ pluginNameMap.set(plugin.name, plugin)
367
366
  }
368
367
 
369
368
  return barrelFiles.flatMap((file) => {
@@ -375,7 +374,7 @@ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, plu
375
374
  }
376
375
 
377
376
  const meta = file.meta as FileMetaBase | undefined
378
- const plugin = meta?.pluginKey ? pluginKeyMap.get(JSON.stringify(meta.pluginKey)) : undefined
377
+ const plugin = meta?.pluginName ? pluginNameMap.get(meta.pluginName) : undefined
379
378
  const pluginOptions = plugin?.options as { output?: Output<unknown> } | undefined
380
379
 
381
380
  if (!pluginOptions || pluginOptions.output?.barrelType === false) {
package/src/constants.ts CHANGED
@@ -14,7 +14,7 @@ export const DEFAULT_BANNER = 'simple' as const
14
14
 
15
15
  export const DEFAULT_EXTENSION: Record<KubbFile.Extname, KubbFile.Extname | ''> = { '.ts': '.ts' }
16
16
 
17
- export const PATH_SEPARATORS = ['/', '\\'] as const
17
+ export const PATH_SEPARATORS = new Set(['/', '\\'] as const)
18
18
 
19
19
  export const logLevel = {
20
20
  silent: Number.NEGATIVE_INFINITY,
@@ -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,134 @@
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
+ adapter: Adapter
14
+ options: Plugin<TPlugin>['options']
15
+ nodes: Array<OperationNode>
16
+ }
17
+
18
+ /**
19
+ * Props for the `operation` lifecycle — receives a single operation node.
20
+ */
21
+ export type OperationV2Props<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
22
+ config: Config
23
+ adapter: Adapter
24
+ options: Plugin<TPlugin>['options']
25
+ node: OperationNode
26
+ }
27
+
28
+ /**
29
+ * Props for the `schema` lifecycle — receives a single schema node.
30
+ */
31
+ export type SchemaV2Props<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
32
+ config: Config
33
+ adapter: Adapter
34
+ options: Plugin<TPlugin>['options']
35
+ node: SchemaNode
36
+ }
37
+
38
+ type UserCoreGeneratorV2<TPlugin extends PluginFactoryOptions> = {
39
+ name: string
40
+ type: 'core'
41
+ version?: '2'
42
+ operations?(props: OperationsV2Props<TPlugin>): Promise<Array<KubbFile.File>>
43
+ operation?(props: OperationV2Props<TPlugin>): Promise<Array<KubbFile.File>>
44
+ schema?(props: SchemaV2Props<TPlugin>): Promise<Array<KubbFile.File>>
45
+ }
46
+
47
+ type UserReactGeneratorV2<TPlugin extends PluginFactoryOptions> = {
48
+ name: string
49
+ type: 'react'
50
+ version?: '2'
51
+ Operations?(props: OperationsV2Props<TPlugin>): FabricReactNode
52
+ Operation?(props: OperationV2Props<TPlugin>): FabricReactNode
53
+ Schema?(props: SchemaV2Props<TPlugin>): FabricReactNode
54
+ }
55
+
56
+ export type CoreGeneratorV2<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
57
+ name: string
58
+ type: 'core'
59
+ version: '2'
60
+ operations(props: OperationsV2Props<TPlugin>): Promise<Array<KubbFile.File>>
61
+ operation(props: OperationV2Props<TPlugin>): Promise<Array<KubbFile.File>>
62
+ schema(props: SchemaV2Props<TPlugin>): Promise<Array<KubbFile.File>>
63
+ }
64
+
65
+ export type ReactGeneratorV2<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
66
+ name: string
67
+ type: 'react'
68
+ version: '2'
69
+ Operations(props: OperationsV2Props<TPlugin>): FabricReactNode
70
+ Operation(props: OperationV2Props<TPlugin>): FabricReactNode
71
+ Schema(props: SchemaV2Props<TPlugin>): FabricReactNode
72
+ }
73
+
74
+ export type Generator<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = UserCoreGeneratorV2<TPlugin> | UserReactGeneratorV2<TPlugin>
75
+
76
+ /**
77
+ * Defines a generator with no-op defaults for any omitted lifecycle methods.
78
+ * Works for both `core` (async file output) and `react` (JSX component) generators.
79
+ *
80
+ * @example
81
+ * // react generator
82
+ * export const typeGenerator = defineGenerator<PluginTs>({
83
+ * name: 'typescript',
84
+ * type: 'react',
85
+ * Operation({ node, options }) { return <File>...</File> },
86
+ * Schema({ node, options }) { return <File>...</File> },
87
+ * })
88
+ *
89
+ * @example
90
+ * // core generator
91
+ * export const myGenerator = defineGenerator<MyPlugin>({
92
+ * name: 'my-generator',
93
+ * type: 'core',
94
+ * async operation({ node, options }) { return [{ path: '...', content: '...' }] },
95
+ * })
96
+ */
97
+ export function defineGenerator<TPlugin extends PluginFactoryOptions = PluginFactoryOptions>(
98
+ generator: UserReactGeneratorV2<TPlugin>,
99
+ ): ReactGeneratorV2<TPlugin>
100
+
101
+ export function defineGenerator<TPlugin extends PluginFactoryOptions = PluginFactoryOptions>(generator: UserCoreGeneratorV2<TPlugin>): CoreGeneratorV2<TPlugin>
102
+ export function defineGenerator<TPlugin extends PluginFactoryOptions = PluginFactoryOptions>(
103
+ generator: UserCoreGeneratorV2<TPlugin> | UserReactGeneratorV2<TPlugin>,
104
+ ): unknown {
105
+ if (generator.type === 'react') {
106
+ return {
107
+ version: '2',
108
+ Operations() {
109
+ return null
110
+ },
111
+ Operation() {
112
+ return null
113
+ },
114
+ Schema() {
115
+ return null
116
+ },
117
+ ...generator,
118
+ }
119
+ }
120
+
121
+ return {
122
+ version: '2',
123
+ async operations() {
124
+ return []
125
+ },
126
+ async operation() {
127
+ return []
128
+ },
129
+ async schema() {
130
+ return []
131
+ },
132
+ ...generator,
133
+ }
134
+ }
@@ -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,131 @@
1
+ import { camelCase, pascalCase } from '@internals/utils'
2
+ import { isOperationNode, isSchemaNode } from '@kubb/ast'
3
+ import type { Node, OperationNode, SchemaNode } from '@kubb/ast/types'
4
+ import type { PluginFactoryOptions, ResolveNameParams, ResolveOptionsContext } from './types.ts'
5
+
6
+ /**
7
+ * Builder type for the plugin-specific resolver fields.
8
+ * `default` and `resolveOptions` are optional — built-in fallbacks are used when omitted.
9
+ */
10
+ type ResolverBuilder<T extends PluginFactoryOptions> = () => Omit<T['resolver'], 'default' | 'resolveOptions'> &
11
+ Partial<Pick<T['resolver'], 'default' | 'resolveOptions'>> &
12
+ ThisType<T['resolver']>
13
+
14
+ /**
15
+ * Checks if an operation matches a pattern for a given filter type (`tag`, `operationId`, `path`, `method`).
16
+ */
17
+ function matchesOperationPattern(node: OperationNode, type: string, pattern: string | RegExp): boolean {
18
+ switch (type) {
19
+ case 'tag':
20
+ return node.tags.some((tag) => !!tag.match(pattern))
21
+ case 'operationId':
22
+ return !!node.operationId.match(pattern)
23
+ case 'path':
24
+ return !!node.path.match(pattern)
25
+ case 'method':
26
+ return !!(node.method.toLowerCase() as string).match(pattern)
27
+ default:
28
+ return false
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Checks if a schema matches a pattern for a given filter type (`schemaName`).
34
+ * Returns `null` when the filter type doesn't apply to schemas.
35
+ */
36
+ function matchesSchemaPattern(node: SchemaNode, type: string, pattern: string | RegExp): boolean | null {
37
+ switch (type) {
38
+ case 'schemaName':
39
+ return node.name ? !!node.name.match(pattern) : false
40
+ default:
41
+ return null
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Default name resolver — `camelCase` for most types, `PascalCase` for `type`.
47
+ */
48
+ function defaultResolver(name: ResolveNameParams['name'], type: ResolveNameParams['type']): string {
49
+ let resolvedName = camelCase(name)
50
+
51
+ if (type === 'file' || type === 'function') {
52
+ resolvedName = camelCase(name, {
53
+ isFile: type === 'file',
54
+ })
55
+ }
56
+
57
+ if (type === 'type') {
58
+ resolvedName = pascalCase(name)
59
+ }
60
+
61
+ return resolvedName
62
+ }
63
+
64
+ /**
65
+ * Default option resolver — applies include/exclude filters and merges any matching override options.
66
+ * Returns `null` when the node is filtered out.
67
+ */
68
+ export function defaultResolveOptions<TOptions>(
69
+ node: Node,
70
+ { options, exclude = [], include, override = [] }: ResolveOptionsContext<TOptions>,
71
+ ): TOptions | null {
72
+ if (isOperationNode(node)) {
73
+ const isExcluded = exclude.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))
74
+ if (isExcluded) {
75
+ return null
76
+ }
77
+
78
+ if (include && !include.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) {
79
+ return null
80
+ }
81
+
82
+ const overrideOptions = override.find(({ type, pattern }) => matchesOperationPattern(node, type, pattern))?.options
83
+
84
+ return { ...options, ...overrideOptions }
85
+ }
86
+
87
+ if (isSchemaNode(node)) {
88
+ if (exclude.some(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)) {
89
+ return null
90
+ }
91
+
92
+ if (include) {
93
+ const results = include.map(({ type, pattern }) => matchesSchemaPattern(node, type, pattern))
94
+ const applicable = results.filter((r) => r !== null)
95
+ if (applicable.length > 0 && !applicable.includes(true)) {
96
+ return null
97
+ }
98
+ }
99
+
100
+ const overrideOptions = override.find(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)?.options
101
+
102
+ return { ...options, ...overrideOptions }
103
+ }
104
+
105
+ return options
106
+ }
107
+
108
+ /**
109
+ * Defines a resolver for a plugin, with built-in defaults for name casing and include/exclude/override filtering.
110
+ * Override `default` or `resolveOptions` in the builder to customize the behavior.
111
+ *
112
+ * @example
113
+ * export const resolver = defineResolver<PluginTs>(() => ({
114
+ * resolveName(name) {
115
+ * return this.default(name, 'function')
116
+ * },
117
+ * resolveTypedName(name) {
118
+ * return this.default(name, 'type')
119
+ * },
120
+ * resolveParamName(node, param) {
121
+ * return this.resolveName(`${node.operationId} ${param.in} ${param.name}`)
122
+ * },
123
+ * }))
124
+ */
125
+ export function defineResolver<T extends PluginFactoryOptions>(build: ResolverBuilder<T>): T['resolver'] {
126
+ return {
127
+ default: defaultResolver,
128
+ resolveOptions: defaultResolveOptions,
129
+ ...build(),
130
+ } as T['resolver']
131
+ }
@@ -1,3 +1,4 @@
1
+ export { useKubb } from './useKubb.ts'
1
2
  export { useMode } from './useMode.ts'
2
3
  export { usePlugin } from './usePlugin.ts'
3
- export { usePluginManager } from './usePluginManager.ts'
4
+ export { usePluginDriver } from './usePluginDriver.ts'