@kubb/core 5.0.0-alpha.9 → 5.0.0-beta.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 (62) hide show
  1. package/README.md +13 -40
  2. package/dist/PluginDriver-Cu1Kj9S-.cjs +1075 -0
  3. package/dist/PluginDriver-Cu1Kj9S-.cjs.map +1 -0
  4. package/dist/PluginDriver-D8Z0Htid.js +978 -0
  5. package/dist/PluginDriver-D8Z0Htid.js.map +1 -0
  6. package/dist/createKubb-ALdb8lmq.d.ts +2082 -0
  7. package/dist/index.cjs +747 -1667
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +175 -269
  10. package/dist/index.js +734 -1638
  11. package/dist/index.js.map +1 -1
  12. package/dist/mocks.cjs +145 -0
  13. package/dist/mocks.cjs.map +1 -0
  14. package/dist/mocks.d.ts +80 -0
  15. package/dist/mocks.js +140 -0
  16. package/dist/mocks.js.map +1 -0
  17. package/package.json +47 -60
  18. package/src/FileManager.ts +115 -0
  19. package/src/FileProcessor.ts +86 -0
  20. package/src/PluginDriver.ts +355 -561
  21. package/src/constants.ts +21 -48
  22. package/src/createAdapter.ts +88 -5
  23. package/src/createKubb.ts +1266 -0
  24. package/src/createRenderer.ts +57 -0
  25. package/src/createStorage.ts +13 -1
  26. package/src/defineGenerator.ts +160 -119
  27. package/src/defineLogger.ts +46 -5
  28. package/src/defineMiddleware.ts +62 -0
  29. package/src/defineParser.ts +44 -0
  30. package/src/definePlugin.ts +379 -0
  31. package/src/defineResolver.ts +548 -25
  32. package/src/devtools.ts +22 -15
  33. package/src/index.ts +13 -15
  34. package/src/mocks.ts +177 -0
  35. package/src/storages/fsStorage.ts +13 -8
  36. package/src/storages/memoryStorage.ts +4 -2
  37. package/src/types.ts +40 -547
  38. package/dist/PluginDriver-BkFepPdm.d.ts +0 -1054
  39. package/dist/chunk-ByKO4r7w.cjs +0 -38
  40. package/dist/hooks.cjs +0 -103
  41. package/dist/hooks.cjs.map +0 -1
  42. package/dist/hooks.d.ts +0 -77
  43. package/dist/hooks.js +0 -98
  44. package/dist/hooks.js.map +0 -1
  45. package/src/Kubb.ts +0 -224
  46. package/src/build.ts +0 -418
  47. package/src/config.ts +0 -56
  48. package/src/createPlugin.ts +0 -28
  49. package/src/hooks/index.ts +0 -4
  50. package/src/hooks/useKubb.ts +0 -143
  51. package/src/hooks/useMode.ts +0 -11
  52. package/src/hooks/usePlugin.ts +0 -11
  53. package/src/hooks/usePluginDriver.ts +0 -11
  54. package/src/utils/FunctionParams.ts +0 -155
  55. package/src/utils/TreeNode.ts +0 -215
  56. package/src/utils/diagnostics.ts +0 -15
  57. package/src/utils/executeStrategies.ts +0 -81
  58. package/src/utils/formatters.ts +0 -56
  59. package/src/utils/getBarrelFiles.ts +0 -141
  60. package/src/utils/getConfigs.ts +0 -12
  61. package/src/utils/linters.ts +0 -25
  62. package/src/utils/packageJSON.ts +0 -61
@@ -0,0 +1,379 @@
1
+ import { extname } from 'node:path'
2
+ import type { FileNode, HttpMethod, InputNode, UserFileNode, Visitor } from '@kubb/ast'
3
+ import type { RendererFactory } from './createRenderer.ts'
4
+ import type { Generator } from './defineGenerator.ts'
5
+ import type { Resolver } from './defineResolver.ts'
6
+ import type { Config, KubbHooks } from './types.ts'
7
+
8
+ /**
9
+ * Safely extracts a type from a registry, returning `{}` if the key doesn't exist.
10
+ * Enables optional interface augmentation for `Kubb.ConfigOptionsRegistry` and `Kubb.PluginOptionsRegistry`
11
+ * without requiring changes to core.
12
+ *
13
+ * @internal
14
+ */
15
+ type ExtractRegistryKey<T, K extends PropertyKey> = K extends keyof T ? T[K] : {}
16
+
17
+ /**
18
+ * Output configuration for generated files.
19
+ */
20
+ export type Output<_TOptions = unknown> = {
21
+ /**
22
+ * Output folder or file path for generated code.
23
+ */
24
+ path: string
25
+ /**
26
+ * Text or function prepended to every generated file.
27
+ * When a function, receives the current `InputNode` and returns a string.
28
+ */
29
+ banner?: string | ((node?: InputNode) => string)
30
+ /**
31
+ * Text or function appended to every generated file.
32
+ * When a function, receives the current `InputNode` and returns a string.
33
+ */
34
+ footer?: string | ((node?: InputNode) => string)
35
+ /**
36
+ * Whether to override existing external files if they already exist.
37
+ * @default false
38
+ */
39
+ override?: boolean
40
+ } & ExtractRegistryKey<Kubb.PluginOptionsRegistry, 'output'>
41
+
42
+ export type Group = {
43
+ /**
44
+ * How to group files into subdirectories.
45
+ * - `'tag'` — group by OpenAPI tags
46
+ * - `'path'` — group by OpenAPI paths
47
+ */
48
+ type: 'tag' | 'path'
49
+ /**
50
+ * Function that returns the subdirectory name for a group value.
51
+ * Defaults to `${camelCase(group)}Controller` for tags, first path segment for paths.
52
+ */
53
+ name?: (context: { group: string }) => string
54
+ }
55
+
56
+ type ByTag = {
57
+ /**
58
+ * Filter by OpenAPI `tags` field. Matches one or more tags assigned to operations.
59
+ */
60
+ type: 'tag'
61
+ /**
62
+ * Tag name to match (case-sensitive). Can be a literal string or regex pattern.
63
+ */
64
+ pattern: string | RegExp
65
+ }
66
+
67
+ type ByOperationId = {
68
+ /**
69
+ * Filter by OpenAPI `operationId` field. Each operation (GET, POST, etc.) has a unique identifier.
70
+ */
71
+ type: 'operationId'
72
+ /**
73
+ * Operation ID to match (case-sensitive). Can be a literal string or regex pattern.
74
+ */
75
+ pattern: string | RegExp
76
+ }
77
+
78
+ type ByPath = {
79
+ /**
80
+ * Filter by OpenAPI `path` (URL endpoint). Useful to group or filter by service segments like `/pets`, `/users`, etc.
81
+ */
82
+ type: 'path'
83
+ /**
84
+ * URL path to match (case-sensitive). Can be a literal string or regex pattern. Matches against the full path.
85
+ */
86
+ pattern: string | RegExp
87
+ }
88
+
89
+ type ByMethod = {
90
+ /**
91
+ * Filter by HTTP method: `'get'`, `'post'`, `'put'`, `'delete'`, `'patch'`, `'head'`, `'options'`.
92
+ */
93
+ type: 'method'
94
+ /**
95
+ * HTTP method to match (case-insensitive when using string, or regex for dynamic matching).
96
+ */
97
+ pattern: HttpMethod | RegExp
98
+ }
99
+ // TODO implement as alternative for ByMethod
100
+ // type ByMethods = {
101
+ // type: 'methods'
102
+ // pattern: Array<HttpMethod>
103
+ // }
104
+
105
+ type BySchemaName = {
106
+ /**
107
+ * Filter by schema component name (TypeScript or JSON schema). Matches schemas in `#/components/schemas`.
108
+ */
109
+ type: 'schemaName'
110
+ /**
111
+ * Schema name to match (case-sensitive). Can be a literal string or regex pattern.
112
+ */
113
+ pattern: string | RegExp
114
+ }
115
+
116
+ type ByContentType = {
117
+ /**
118
+ * Filter by response or request content type: `'application/json'`, `'application/xml'`, etc.
119
+ */
120
+ type: 'contentType'
121
+ /**
122
+ * Content type to match (case-sensitive). Can be a literal string or regex pattern.
123
+ */
124
+ pattern: string | RegExp
125
+ }
126
+
127
+ /**
128
+ * A pattern filter that prevents matching nodes from being generated.
129
+ *
130
+ * Use to skip code generation for specific operations or schemas. For example, exclude deprecated endpoints
131
+ * or internal-only schemas. Can filter by tag, operationId, path, HTTP method, content type, or schema name.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * exclude: [
136
+ * { type: 'tag', pattern: 'internal' }, // skip "internal" tag
137
+ * { type: 'path', pattern: /^\/admin/ }, // skip all /admin endpoints
138
+ * { type: 'operationId', pattern: 'deprecated_*' } // skip operationIds matching pattern
139
+ * ]
140
+ * ```
141
+ */
142
+ export type Exclude = ByTag | ByOperationId | ByPath | ByMethod | ByContentType | BySchemaName
143
+
144
+ /**
145
+ * A pattern filter that restricts generation to only matching nodes.
146
+ *
147
+ * Use to generate code for a subset of operations or schemas. For example, only generate for a specific service
148
+ * tag or only for "production" endpoints. Can filter by tag, operationId, path, HTTP method, content type, or schema name.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * include: [
153
+ * { type: 'tag', pattern: 'public' }, // generate only "public" tag
154
+ * { type: 'path', pattern: /^\/api\/v1/ }, // generate only v1 endpoints
155
+ * ]
156
+ * ```
157
+ */
158
+ export type Include = ByTag | ByOperationId | ByPath | ByMethod | ByContentType | BySchemaName
159
+
160
+ /**
161
+ * A pattern filter paired with partial option overrides applied when the pattern matches.
162
+ *
163
+ * Use to customize generation for specific operations or schemas. For example, apply different output paths
164
+ * for different tags, or use custom resolver functions per operation. Can filter by tag, operationId, path,
165
+ * HTTP method, schema name, or content type.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * override: [
170
+ * {
171
+ * type: 'tag',
172
+ * pattern: 'admin',
173
+ * options: { output: { path: './src/gen/admin' } } // admin APIs go to separate folder
174
+ * },
175
+ * {
176
+ * type: 'operationId',
177
+ * pattern: 'listPets',
178
+ * options: { exclude: true } // skip this specific operation
179
+ * }
180
+ * ]
181
+ * ```
182
+ */
183
+ export type Override<TOptions> = (ByTag | ByOperationId | ByPath | ByMethod | BySchemaName | ByContentType) & {
184
+ //TODO should be options: Omit<Partial<TOptions>, 'override'>
185
+ options: Partial<TOptions>
186
+ }
187
+
188
+ export type PluginFactoryOptions<
189
+ /**
190
+ * Unique plugin name.
191
+ */
192
+ TName extends string = string,
193
+ /**
194
+ * User-facing plugin options.
195
+ */
196
+ TOptions extends object = object,
197
+ /**
198
+ * Plugin options after defaults are applied.
199
+ */
200
+ TResolvedOptions extends object = TOptions,
201
+ /**
202
+ * Resolver that encapsulates naming and path-resolution helpers.
203
+ * Define with `defineResolver` and export alongside the plugin.
204
+ */
205
+ TResolver extends Resolver = Resolver,
206
+ > = {
207
+ name: TName
208
+ options: TOptions
209
+ resolvedOptions: TResolvedOptions
210
+ resolver: TResolver
211
+ }
212
+
213
+ /**
214
+ * Context for hook-style plugin `kubb:plugin:setup` handler.
215
+ * Provides methods to register generators, configure resolvers, transformers, and renderers.
216
+ */
217
+ export type KubbPluginSetupContext<TFactory extends PluginFactoryOptions = PluginFactoryOptions> = {
218
+ /**
219
+ * Register a generator dynamically. Generators fire during the AST walk (schema/operation/operations)
220
+ * just like generators declared statically on `createPlugin`.
221
+ */
222
+ addGenerator<TElement = unknown>(generator: Generator<TFactory, TElement>): void
223
+ /**
224
+ * Set or override the resolver for this plugin.
225
+ * The resolver controls file naming and path resolution.
226
+ */
227
+ setResolver(resolver: Partial<TFactory['resolver']>): void
228
+ /**
229
+ * Set the AST transformer to pre-process nodes before they reach generators.
230
+ */
231
+ setTransformer(visitor: Visitor): void
232
+ /**
233
+ * Set the renderer factory to process JSX elements from generators.
234
+ */
235
+ setRenderer(renderer: RendererFactory): void
236
+ /**
237
+ * Set resolved options merged into the normalized plugin's `options`.
238
+ * Call this in `kubb:plugin:setup` to provide options generators need.
239
+ */
240
+ setOptions(options: TFactory['resolvedOptions']): void
241
+ /**
242
+ * Inject a raw file into the build output, bypassing the generation pipeline.
243
+ */
244
+ injectFile(userFileNode: UserFileNode): void
245
+ /**
246
+ * Merge a partial config update into the current build configuration.
247
+ */
248
+ updateConfig(config: Partial<Config>): void
249
+ /**
250
+ * The resolved build configuration at setup time.
251
+ */
252
+ config: Config
253
+ /**
254
+ * The plugin's user-provided options.
255
+ */
256
+ options: TFactory['options']
257
+ }
258
+
259
+ /**
260
+ * A plugin object produced by `definePlugin`.
261
+ * Instead of flat lifecycle methods, it groups all handlers under a `hooks:` property
262
+ * (matching Astro's integration naming convention).
263
+ *
264
+ * @template TFactory - The plugin's `PluginFactoryOptions` type.
265
+ */
266
+ export type Plugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions> = {
267
+ /**
268
+ * Unique name for the plugin, following the same naming convention as `createPlugin`.
269
+ */
270
+ name: string
271
+ /**
272
+ * Plugins that must be registered before this plugin executes.
273
+ * An error is thrown at startup when any listed dependency is missing.
274
+ */
275
+ dependencies?: Array<string>
276
+ /**
277
+ * Controls the execution order of this plugin relative to others.
278
+ *
279
+ * - `'pre'` — runs before all normal plugins.
280
+ * - `'post'` — runs after all normal plugins.
281
+ * - `undefined` (default) — runs in declaration order among normal plugins.
282
+ *
283
+ * Dependency constraints always take precedence over `enforce`.
284
+ */
285
+ enforce?: 'pre' | 'post'
286
+ /**
287
+ * The options passed by the user when calling the plugin factory.
288
+ */
289
+ options?: TFactory['options']
290
+ /**
291
+ * Lifecycle event handlers for this plugin.
292
+ * Any event from the global `KubbHooks` map can be subscribed to here.
293
+ */
294
+ hooks: {
295
+ [K in keyof KubbHooks as K extends 'kubb:plugin:setup' ? never : K]?: (...args: KubbHooks[K]) => void | Promise<void>
296
+ } & {
297
+ 'kubb:plugin:setup'?(ctx: KubbPluginSetupContext<TFactory>): void | Promise<void>
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Normalized plugin after setup, with runtime fields populated.
303
+ * For internal use only — plugins use the public `Plugin` type externally.
304
+ *
305
+ * @internal
306
+ */
307
+ export type NormalizedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = Plugin<TOptions> & {
308
+ options: TOptions['resolvedOptions'] & {
309
+ output: Output
310
+ include?: Array<Include>
311
+ exclude: Array<Exclude>
312
+ override: Array<Override<TOptions['resolvedOptions']>>
313
+ }
314
+ resolver: TOptions['resolver']
315
+ transformer?: Visitor
316
+ renderer?: RendererFactory
317
+ generators?: Array<Generator>
318
+ apply?: (config: Config) => boolean
319
+ version?: string
320
+ }
321
+
322
+ export type KubbPluginStartContext = {
323
+ plugin: NormalizedPlugin
324
+ }
325
+
326
+ export type KubbPluginEndContext = {
327
+ plugin: NormalizedPlugin
328
+ duration: number
329
+ success: boolean
330
+ error?: Error
331
+ config: Config
332
+ /**
333
+ * Returns all files currently in the file manager (lazy snapshot).
334
+ * Includes files added by plugins that have already run.
335
+ */
336
+ readonly files: ReadonlyArray<FileNode>
337
+ /**
338
+ * Upsert one or more files into the file manager.
339
+ */
340
+ upsertFile: (...files: Array<FileNode>) => void
341
+ }
342
+
343
+ /**
344
+ * Wraps a factory function and returns a typed `Plugin` with lifecycle handlers grouped under `hooks`.
345
+ *
346
+ * Handlers live in a single `hooks` object (inspired by Astro integrations).
347
+ * All lifecycle events from `KubbHooks` are available for subscription.
348
+ *
349
+ * @note For real plugins, use a `PluginFactoryOptions` type parameter to get type-safe context in `kubb:plugin:setup`.
350
+ * Plugin names should follow the convention `plugin-<feature>` (e.g., `plugin-react-query`, `plugin-zod`).
351
+ *
352
+ * @example
353
+ * ```ts
354
+ * import { definePlugin } from '@kubb/core'
355
+ *
356
+ * export const pluginTs = definePlugin((options: { prefix?: string } = {}) => ({
357
+ * name: 'plugin-ts',
358
+ * hooks: {
359
+ * 'kubb:plugin:setup'(ctx) {
360
+ * ctx.setResolver(resolverTs)
361
+ * },
362
+ * },
363
+ * }))
364
+ * ```
365
+ */
366
+ export function definePlugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions>(
367
+ factory: (options: TFactory['options']) => Plugin<TFactory>,
368
+ ): (options?: TFactory['options']) => Plugin<TFactory> {
369
+ return (options) => factory(options ?? ({} as TFactory['options']))
370
+ }
371
+
372
+ /**
373
+ * Returns `'single'` when `fileOrFolder` has a file extension, `'split'` otherwise.
374
+ * Used to determine whether an output path targets a single file or a directory.
375
+ */
376
+ export function getMode(fileOrFolder: string | undefined | null): 'single' | 'split' {
377
+ if (!fileOrFolder) return 'split'
378
+ return extname(fileOrFolder) ? 'single' : 'split'
379
+ }