@kubb/core 5.0.0-beta.3 → 5.0.0-beta.31

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 (46) hide show
  1. package/README.md +8 -38
  2. package/dist/KubbDriver-CFx2DdhF.js +2131 -0
  3. package/dist/KubbDriver-CFx2DdhF.js.map +1 -0
  4. package/dist/KubbDriver-vyD7F0Ip.cjs +2252 -0
  5. package/dist/KubbDriver-vyD7F0Ip.cjs.map +1 -0
  6. package/dist/{types-CC09VtBt.d.ts → createKubb-6zii1jo-.d.ts} +1610 -1257
  7. package/dist/index.cjs +351 -1125
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +3 -186
  10. package/dist/index.js +341 -1119
  11. package/dist/index.js.map +1 -1
  12. package/dist/mocks.cjs +30 -21
  13. package/dist/mocks.cjs.map +1 -1
  14. package/dist/mocks.d.ts +5 -5
  15. package/dist/mocks.js +29 -20
  16. package/dist/mocks.js.map +1 -1
  17. package/package.json +6 -18
  18. package/src/FileManager.ts +78 -61
  19. package/src/FileProcessor.ts +48 -38
  20. package/src/KubbDriver.ts +930 -0
  21. package/src/constants.ts +11 -6
  22. package/src/createAdapter.ts +113 -17
  23. package/src/createKubb.ts +1039 -478
  24. package/src/createRenderer.ts +58 -27
  25. package/src/createStorage.ts +36 -23
  26. package/src/defineGenerator.ts +127 -15
  27. package/src/defineLogger.ts +66 -7
  28. package/src/defineMiddleware.ts +19 -17
  29. package/src/defineParser.ts +30 -13
  30. package/src/definePlugin.ts +329 -14
  31. package/src/defineResolver.ts +365 -167
  32. package/src/devtools.ts +8 -1
  33. package/src/index.ts +2 -2
  34. package/src/mocks.ts +11 -14
  35. package/src/storages/fsStorage.ts +13 -37
  36. package/src/types.ts +48 -1292
  37. package/dist/PluginDriver-BXibeQk-.cjs +0 -1036
  38. package/dist/PluginDriver-BXibeQk-.cjs.map +0 -1
  39. package/dist/PluginDriver-DV3p2Hky.js +0 -945
  40. package/dist/PluginDriver-DV3p2Hky.js.map +0 -1
  41. package/src/Kubb.ts +0 -300
  42. package/src/PluginDriver.ts +0 -424
  43. package/src/renderNode.ts +0 -35
  44. package/src/utils/diagnostics.ts +0 -18
  45. package/src/utils/isInputPath.ts +0 -10
  46. package/src/utils/packageJSON.ts +0 -99
@@ -1,5 +1,273 @@
1
- import type { KubbHooks } from './Kubb.ts'
2
- import type { KubbPluginSetupContext, PluginFactoryOptions } from './types.ts'
1
+ import { extname } from 'node:path'
2
+ import type { FileNode, HttpMethod, UserFileNode, Visitor } from '@kubb/ast'
3
+ import type { RendererFactory } from './createRenderer.ts'
4
+ import type { Generator } from './defineGenerator.ts'
5
+ import type { BannerMeta, 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 shared by every plugin. Each plugin extends this with
19
+ * its own keys via the `Kubb.PluginOptionsRegistry.output` interface merge.
20
+ */
21
+ export type Output<_TOptions = unknown> = {
22
+ /**
23
+ * Folder (or single file) where the plugin writes its generated code.
24
+ * Resolved against the global `output.path` set on `defineConfig`.
25
+ */
26
+ path: string
27
+ /**
28
+ * Text prepended to every generated file. Useful for license headers,
29
+ * lint disables, or `@ts-nocheck` directives.
30
+ *
31
+ * A string is applied to every file (including barrel and aggregation re-export files).
32
+ * Pass a function to compute the banner from the file's `BannerMeta` — document metadata
33
+ * plus per-file context (`isBarrel`, `isAggregation`, `filePath`, `baseName`) — so you can
34
+ * skip the banner on specific files.
35
+ *
36
+ * @example Add a directive to source files but not re-export files
37
+ * `banner: (meta) => (meta.isBarrel || meta.isAggregation) ? '' : "'use server'"`
38
+ */
39
+ banner?: string | ((meta: BannerMeta) => string)
40
+ /**
41
+ * Text appended at the end of every generated file. Mirror of `banner`.
42
+ * Pass a function to compute the footer from the file's `BannerMeta`.
43
+ */
44
+ footer?: string | ((meta: BannerMeta) => string)
45
+ /**
46
+ * Allows the plugin to overwrite hand-written files at the same path.
47
+ * Defaults to `false` to protect manual edits.
48
+ *
49
+ * @default false
50
+ */
51
+ override?: boolean
52
+ } & ExtractRegistryKey<Kubb.PluginOptionsRegistry, 'output'>
53
+
54
+ /**
55
+ * Groups generated files into subdirectories based on an OpenAPI tag or path
56
+ * segment.
57
+ */
58
+ export type Group = {
59
+ /**
60
+ * Property used to assign each operation to a group.
61
+ * - `'tag'` — uses the first tag (`operation.getTags().at(0)?.name`).
62
+ * - `'path'` — uses the first segment of the operation's URL.
63
+ */
64
+ type: 'tag' | 'path'
65
+ /**
66
+ * Returns the subdirectory name from the group key. Defaults to
67
+ * `${camelCase(group)}Controller` for tags, or the first path segment.
68
+ */
69
+ name?: (context: { group: string }) => string
70
+ }
71
+
72
+ type ByTag = {
73
+ /**
74
+ * Filter by OpenAPI `tags` field. Matches one or more tags assigned to operations.
75
+ */
76
+ type: 'tag'
77
+ /**
78
+ * Tag name to match (case-sensitive). Can be a literal string or regex pattern.
79
+ */
80
+ pattern: string | RegExp
81
+ }
82
+
83
+ type ByOperationId = {
84
+ /**
85
+ * Filter by OpenAPI `operationId` field. Each operation (GET, POST, etc.) has a unique identifier.
86
+ */
87
+ type: 'operationId'
88
+ /**
89
+ * Operation ID to match (case-sensitive). Can be a literal string or regex pattern.
90
+ */
91
+ pattern: string | RegExp
92
+ }
93
+
94
+ type ByPath = {
95
+ /**
96
+ * Filter by OpenAPI `path` (URL endpoint). Useful to group or filter by service segments like `/pets`, `/users`, etc.
97
+ */
98
+ type: 'path'
99
+ /**
100
+ * URL path to match (case-sensitive). Can be a literal string or regex pattern. Matches against the full path.
101
+ */
102
+ pattern: string | RegExp
103
+ }
104
+
105
+ type ByMethod = {
106
+ /**
107
+ * Filter by HTTP method: `'get'`, `'post'`, `'put'`, `'delete'`, `'patch'`, `'head'`, `'options'`.
108
+ */
109
+ type: 'method'
110
+ /**
111
+ * HTTP method to match (case-insensitive when using string, or regex for dynamic matching).
112
+ */
113
+ pattern: HttpMethod | RegExp
114
+ }
115
+ // TODO implement as alternative for ByMethod
116
+ // type ByMethods = {
117
+ // type: 'methods'
118
+ // pattern: Array<HttpMethod>
119
+ // }
120
+
121
+ type BySchemaName = {
122
+ /**
123
+ * Filter by schema component name (TypeScript or JSON schema). Matches schemas in `#/components/schemas`.
124
+ */
125
+ type: 'schemaName'
126
+ /**
127
+ * Schema name to match (case-sensitive). Can be a literal string or regex pattern.
128
+ */
129
+ pattern: string | RegExp
130
+ }
131
+
132
+ type ByContentType = {
133
+ /**
134
+ * Filter by response or request content type: `'application/json'`, `'application/xml'`, etc.
135
+ */
136
+ type: 'contentType'
137
+ /**
138
+ * Content type to match (case-sensitive). Can be a literal string or regex pattern.
139
+ */
140
+ pattern: string | RegExp
141
+ }
142
+
143
+ /**
144
+ * Filter that skips matching operations or schemas during generation. Use it
145
+ * to drop deprecated endpoints, internal-only schemas, or anything you do
146
+ * not want code generated for.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * exclude: [
151
+ * { type: 'tag', pattern: 'internal' },
152
+ * { type: 'path', pattern: /^\/admin/ },
153
+ * { type: 'operationId', pattern: /^deprecated_/ },
154
+ * ]
155
+ * ```
156
+ */
157
+ export type Exclude = ByTag | ByOperationId | ByPath | ByMethod | ByContentType | BySchemaName
158
+
159
+ /**
160
+ * Filter that restricts generation to operations or schemas matching at least
161
+ * one entry. Useful for partial builds (one tag, one API version).
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * include: [
166
+ * { type: 'tag', pattern: 'public' },
167
+ * { type: 'path', pattern: /^\/api\/v1/ },
168
+ * ]
169
+ * ```
170
+ */
171
+ export type Include = ByTag | ByOperationId | ByPath | ByMethod | ByContentType | BySchemaName
172
+
173
+ /**
174
+ * Filter paired with a partial options object. When the filter matches, the
175
+ * options are merged on top of the plugin defaults for that operation only.
176
+ * Useful for "this one tag goes to a different folder" rules.
177
+ *
178
+ * Entries are evaluated top to bottom; the first matching entry wins.
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * override: [
183
+ * {
184
+ * type: 'tag',
185
+ * pattern: 'admin',
186
+ * options: { output: { path: './src/gen/admin' } },
187
+ * },
188
+ * {
189
+ * type: 'operationId',
190
+ * pattern: 'listPets',
191
+ * options: { enumType: 'literal' },
192
+ * },
193
+ * ]
194
+ * ```
195
+ */
196
+ export type Override<TOptions> = (ByTag | ByOperationId | ByPath | ByMethod | BySchemaName | ByContentType) & {
197
+ //TODO should be options: Omit<Partial<TOptions>, 'override'>
198
+ options: Partial<TOptions>
199
+ }
200
+
201
+ export type PluginFactoryOptions<
202
+ /**
203
+ * Unique plugin name.
204
+ */
205
+ TName extends string = string,
206
+ /**
207
+ * User-facing plugin options.
208
+ */
209
+ TOptions extends object = object,
210
+ /**
211
+ * Plugin options after defaults are applied.
212
+ */
213
+ TResolvedOptions extends object = TOptions,
214
+ /**
215
+ * Resolver that encapsulates naming and path-resolution helpers.
216
+ * Define with `defineResolver` and export alongside the plugin.
217
+ */
218
+ TResolver extends Resolver = Resolver,
219
+ > = {
220
+ name: TName
221
+ options: TOptions
222
+ resolvedOptions: TResolvedOptions
223
+ resolver: TResolver
224
+ }
225
+
226
+ /**
227
+ * Context for hook-style plugin `kubb:plugin:setup` handler.
228
+ * Provides methods to register generators, configure resolvers, transformers, and renderers.
229
+ */
230
+ export type KubbPluginSetupContext<TFactory extends PluginFactoryOptions = PluginFactoryOptions> = {
231
+ /**
232
+ * Register a generator dynamically. Generators fire during the AST walk (schema/operation/operations)
233
+ * just like generators declared statically on `createPlugin`.
234
+ */
235
+ addGenerator<TElement = unknown>(generator: Generator<TFactory, TElement>): void
236
+ /**
237
+ * Set or override the resolver for this plugin.
238
+ * The resolver controls file naming and path resolution.
239
+ */
240
+ setResolver(resolver: Partial<TFactory['resolver']>): void
241
+ /**
242
+ * Set the AST transformer to pre-process nodes before they reach generators.
243
+ */
244
+ setTransformer(visitor: Visitor): void
245
+ /**
246
+ * Set the renderer factory to process JSX elements from generators.
247
+ */
248
+ setRenderer(renderer: RendererFactory): void
249
+ /**
250
+ * Set resolved options merged into the normalized plugin's `options`.
251
+ * Call this in `kubb:plugin:setup` to provide options generators need.
252
+ */
253
+ setOptions(options: TFactory['resolvedOptions']): void
254
+ /**
255
+ * Inject a raw file into the build output, bypassing the generation pipeline.
256
+ */
257
+ injectFile(userFileNode: UserFileNode): void
258
+ /**
259
+ * Merge a partial config update into the current build configuration.
260
+ */
261
+ updateConfig(config: Partial<Config>): void
262
+ /**
263
+ * The resolved build configuration at setup time.
264
+ */
265
+ config: Config
266
+ /**
267
+ * The plugin's user-provided options.
268
+ */
269
+ options: TFactory['options']
270
+ }
3
271
 
4
272
  /**
5
273
  * A plugin object produced by `definePlugin`.
@@ -37,30 +305,62 @@ export type Plugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions>
37
305
  * Any event from the global `KubbHooks` map can be subscribed to here.
38
306
  */
39
307
  hooks: {
40
- [K in Exclude<keyof KubbHooks, 'kubb:plugin:setup'>]?: (...args: KubbHooks[K]) => void | Promise<void>
308
+ [K in keyof KubbHooks as K extends 'kubb:plugin:setup' ? never : K]?: (...args: KubbHooks[K]) => void | Promise<void>
41
309
  } & {
42
310
  'kubb:plugin:setup'?(ctx: KubbPluginSetupContext<TFactory>): void | Promise<void>
43
311
  }
44
312
  }
45
313
 
46
314
  /**
47
- * Returns `true` when `plugin` is a hook-style plugin created with `definePlugin`.
315
+ * Normalized plugin after setup, with runtime fields populated.
316
+ * For internal use only — plugins use the public `Plugin` type externally.
48
317
  *
49
- * Used by `PluginDriver` to distinguish hook-style plugins from legacy `createPlugin` plugins
50
- * so it can normalize them and register their handlers on the `AsyncEventEmitter`.
318
+ * @internal
51
319
  */
52
- export function isPlugin(plugin: unknown): plugin is Plugin {
53
- return typeof plugin === 'object' && plugin !== null && 'hooks' in plugin
320
+ export type NormalizedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = Plugin<TOptions> & {
321
+ options: TOptions['resolvedOptions'] & {
322
+ output: Output
323
+ include?: Array<Include>
324
+ exclude: Array<Exclude>
325
+ override: Array<Override<TOptions['resolvedOptions']>>
326
+ }
327
+ resolver: TOptions['resolver']
328
+ transformer?: Visitor
329
+ renderer?: RendererFactory
330
+ generators?: Array<Generator>
331
+ apply?: (config: Config) => boolean
332
+ version?: string
333
+ }
334
+
335
+ export type KubbPluginStartContext = {
336
+ plugin: NormalizedPlugin
337
+ }
338
+
339
+ export type KubbPluginEndContext = {
340
+ plugin: NormalizedPlugin
341
+ duration: number
342
+ success: boolean
343
+ error?: Error
344
+ config: Config
345
+ /**
346
+ * Returns all files currently in the file manager (lazy snapshot).
347
+ * Includes files added by plugins that have already run.
348
+ */
349
+ readonly files: ReadonlyArray<FileNode>
350
+ /**
351
+ * Upsert one or more files into the file manager.
352
+ */
353
+ upsertFile: (...files: Array<FileNode>) => void
54
354
  }
55
355
 
56
356
  /**
57
- * Wraps a factory function and returns a typed `Plugin` with lifecycle handlers grouped under `hooks`.
58
- *
59
- * Handlers live in a single `hooks` object (inspired by Astro integrations).
60
- * All lifecycle events from `KubbHooks` are available for subscription.
357
+ * Wraps a plugin factory and returns a function that accepts user options and
358
+ * yields a fully typed `Plugin`. Lifecycle handlers go inside a single
359
+ * `hooks` object (inspired by Astro integrations).
61
360
  *
62
- * @note For real plugins, use a `PluginFactoryOptions` type parameter to get type-safe context in `kubb:plugin:setup`.
63
- * Plugin names should follow the convention `plugin-<feature>` (e.g., `plugin-react-query`, `plugin-zod`).
361
+ * Pass a `PluginFactoryOptions` type parameter to get a typed `ctx` inside
362
+ * `kubb:plugin:setup`. Plugin names should follow the `plugin-<feature>`
363
+ * convention (`plugin-react-query`, `plugin-zod`, ...).
64
364
  *
65
365
  * @example
66
366
  * ```ts
@@ -81,3 +381,18 @@ export function definePlugin<TFactory extends PluginFactoryOptions = PluginFacto
81
381
  ): (options?: TFactory['options']) => Plugin<TFactory> {
82
382
  return (options) => factory(options ?? ({} as TFactory['options']))
83
383
  }
384
+
385
+ /**
386
+ * Detects whether an output path points at a single file (`'single'`) or a
387
+ * directory (`'split'`). Decided purely from the presence of a file extension.
388
+ *
389
+ * @example Directory
390
+ * `getMode('./types') // 'split'`
391
+ *
392
+ * @example Single file
393
+ * `getMode('./api.ts') // 'single'`
394
+ */
395
+ export function getMode(fileOrFolder: string | undefined | null): 'single' | 'split' {
396
+ if (!fileOrFolder) return 'split'
397
+ return extname(fileOrFolder) ? 'single' : 'split'
398
+ }