@kubb/core 5.0.0-alpha.26 → 5.0.0-alpha.27

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/core",
3
- "version": "5.0.0-alpha.26",
3
+ "version": "5.0.0-alpha.27",
4
4
  "description": "Core functionality for Kubb's plugin-based code generation system, providing the foundation for transforming OpenAPI specifications.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -71,7 +71,7 @@
71
71
  "remeda": "^2.33.7",
72
72
  "semver": "^7.7.4",
73
73
  "tinyexec": "^1.0.4",
74
- "@kubb/ast": "5.0.0-alpha.26"
74
+ "@kubb/ast": "5.0.0-alpha.27"
75
75
  },
76
76
  "devDependencies": {
77
77
  "@types/semver": "^7.7.1",
@@ -141,6 +141,9 @@ export class PluginDriver {
141
141
  get resolver() {
142
142
  return plugin.resolver
143
143
  },
144
+ get transformer() {
145
+ return plugin.transformer
146
+ },
144
147
  openInStudio(options?: DevtoolsOptions) {
145
148
  if (!driver.config.devtools || driver.#studioIsOpen) {
146
149
  return
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { AsyncEventEmitter, URLPath } from '@internals/utils'
2
- export { definePrinter } from '@kubb/ast'
2
+ export { composeTransformers, definePrinter } from '@kubb/ast'
3
3
  export { build, build as default, safeBuild, setup } from './build.ts'
4
4
  export { type CLIOptions, type ConfigInput, defineConfig, isInputPath } from './config.ts'
5
5
  export { formatters, linters, logLevel } from './constants.ts'
@@ -31,5 +31,4 @@ export { getBarrelFiles } from './utils/getBarrelFiles.ts'
31
31
  export { getConfigs } from './utils/getConfigs.ts'
32
32
  export { getPreset } from './utils/getPreset.ts'
33
33
  export { detectLinter } from './utils/linters.ts'
34
- export { mergeResolvers } from './utils/mergeResolvers.ts'
35
34
  export { satisfiesDependency } from './utils/packageJSON.ts'
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { AsyncEventEmitter, PossiblePromise } from '@internals/utils'
2
- import type { Node, RootNode, SchemaNode, Visitor } from '@kubb/ast/types'
2
+ import type { Node, Printer, RootNode, SchemaNode, Visitor } from '@kubb/ast/types'
3
3
  import type { FabricFile, Fabric as FabricType } from '@kubb/fabric-core/types'
4
4
  import type { HttpMethod } from '@kubb/oas'
5
5
  import type { DEFAULT_STUDIO_URL, logLevel } from './constants.ts'
@@ -8,7 +8,7 @@ import type { Generator } from './defineGenerator.ts'
8
8
  import type { KubbEvents } from './Kubb.ts'
9
9
  import type { PluginDriver } from './PluginDriver.ts'
10
10
 
11
- export type { Printer, PrinterFactoryOptions } from '@kubb/ast/types'
11
+ export type { Printer, PrinterFactoryOptions, PrinterPartial } from '@kubb/ast/types'
12
12
 
13
13
  declare global {
14
14
  namespace Kubb {
@@ -396,9 +396,16 @@ export type UserPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOpti
396
396
  */
397
397
  options: TOptions['resolvedOptions']
398
398
  /**
399
- * The resolver for this plugin, accessible via `driver.getPluginByName(name)?.resolver`.
399
+ * The resolver for this plugin.
400
+ * Composed by `getPreset` from the preset resolver and the user's `resolver` partial override.
400
401
  */
401
402
  resolver?: TOptions['resolver']
403
+ /**
404
+ * The composed transformer for this plugin.
405
+ * Composed by `getPreset` from the preset's transformers and the user's `transformer` visitor.
406
+ * When a visitor method returns `null`/`undefined`, the preset transformer's result is used instead.
407
+ */
408
+ transformer?: Visitor
402
409
  /**
403
410
  * Specifies the preceding plugins for the current plugin. You can pass an array of preceding plugin names, and the current plugin is executed after these plugins.
404
411
  * Can be used to validate dependent plugins.
@@ -435,9 +442,16 @@ export type Plugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>
435
442
  */
436
443
  options: TOptions['resolvedOptions']
437
444
  /**
438
- * The resolver for this plugin, accessible via `driver.getPluginByName(name)?.resolver`.
445
+ * The resolver for this plugin.
446
+ * Composed by `getPreset` from the preset resolver and the user's `resolver` partial override.
439
447
  */
440
448
  resolver: TOptions['resolver']
449
+ /**
450
+ * The composed transformer for this plugin. Accessible via `context.transformer`.
451
+ * Composed by `getPreset` from the preset's transformers and the user's `transformer` visitor.
452
+ * When a visitor method returns `null`/`undefined`, the preset transformer's result is used instead.
453
+ */
454
+ transformer?: Visitor
441
455
 
442
456
  install: (this: PluginContext<TOptions>, context: PluginContext<TOptions>) => PossiblePromise<void>
443
457
  /**
@@ -527,6 +541,11 @@ export type PluginContext<TOptions extends PluginFactoryOptions = PluginFactoryO
527
541
  * Resolver for the current plugin. Shorthand for `plugin.resolver`.
528
542
  */
529
543
  resolver: TOptions['resolver']
544
+ /**
545
+ * Composed transformer for the current plugin. Shorthand for `plugin.transformer`.
546
+ * Apply with `transform(node, context.transformer)` to pre-process AST nodes before printing.
547
+ */
548
+ transformer: Visitor | undefined
530
549
 
531
550
  /**
532
551
  * Opens the Kubb Studio URL for the current `rootNode` in the default browser.
@@ -639,7 +658,7 @@ export type { CoreGeneratorV2, Generator, ReactGeneratorV2 } from './defineGener
639
658
  export type { KubbEvents } from './Kubb.ts'
640
659
 
641
660
  /**
642
- * A preset bundles a name, one or more resolvers, optional AST transformers,
661
+ * A preset bundles a name, a resolver, optional AST transformers,
643
662
  * and optional generators into a single reusable configuration object.
644
663
  *
645
664
  * @template TResolver - The concrete resolver type for this preset.
@@ -650,9 +669,9 @@ export type Preset<TResolver extends Resolver = Resolver> = {
650
669
  */
651
670
  name: string
652
671
  /**
653
- * Ordered list of resolvers applied by this preset (last entry wins on merge).
672
+ * The resolver used by this preset.
654
673
  */
655
- resolvers: Array<TResolver>
674
+ resolver: TResolver
656
675
  /**
657
676
  * Optional AST visitors / transformers applied after resolving.
658
677
  */
@@ -662,6 +681,11 @@ export type Preset<TResolver extends Resolver = Resolver> = {
662
681
  * to their concrete generator type.
663
682
  */
664
683
  generators?: Array<Generator<any>>
684
+ /**
685
+ * Optional printer factory used by this preset.
686
+ * The generator calls this function at render-time to produce a configured printer instance.
687
+ */
688
+ printer?: (options: any) => Printer
665
689
  }
666
690
 
667
691
  /**
@@ -1,52 +1,78 @@
1
+ import { composeTransformers } from '@kubb/ast'
1
2
  import type { Visitor } from '@kubb/ast/types'
2
3
  import type { CompatibilityPreset, Generator, Preset, Presets, Resolver } from '../types.ts'
3
- import { mergeResolvers } from './mergeResolvers.ts'
4
+
5
+ /**
6
+ * Returns a copy of `defaults` where each function in `userOverrides` is wrapped
7
+ * so a `null`/`undefined` return falls back to the original. Non-function values
8
+ * are assigned directly. All calls use the merged object as `this`.
9
+ */
10
+ function withFallback<T extends object>(defaults: T, userOverrides: Partial<T>): T {
11
+ const merged = { ...defaults } as T
12
+
13
+ for (const key of Object.keys(userOverrides) as Array<keyof T>) {
14
+ const userVal = userOverrides[key]
15
+ const defaultVal = defaults[key]
16
+
17
+ if (typeof userVal === 'function' && typeof defaultVal === 'function') {
18
+ ;(merged as any)[key] = (...args: any[]) => (userVal as Function).apply(merged, args) ?? (defaultVal as Function).apply(merged, args)
19
+ } else if (userVal !== undefined) {
20
+ merged[key] = userVal as T[typeof key]
21
+ }
22
+ }
23
+
24
+ return merged
25
+ }
4
26
 
5
27
  type GetPresetParams<TResolver extends Resolver> = {
6
28
  preset: CompatibilityPreset
7
29
  presets: Presets<TResolver>
8
- resolvers?: Array<TResolver>
30
+ /**
31
+ * Optional single resolver whose methods override the preset resolver.
32
+ * When a method returns `null` or `undefined` the preset resolver's method is used instead.
33
+ */
34
+ resolver?: Partial<TResolver> & ThisType<TResolver>
9
35
  /**
10
36
  * User-supplied generators to append after the preset's generators.
11
37
  */
12
38
  generators?: Array<Generator<any>>
13
- transformers?: Array<Visitor>
39
+ /**
40
+ * Optional single transformer visitor whose methods override the preset transformer.
41
+ * When a method returns `null` or `undefined` the preset transformer's method is used instead.
42
+ */
43
+ transformer?: Visitor
14
44
  }
15
45
 
16
46
  type GetPresetResult<TResolver extends Resolver> = {
17
47
  resolver: TResolver
18
- transformers: Array<Visitor>
48
+ transformer: Visitor | undefined
19
49
  generators: Array<Generator<any>>
20
50
  preset: Preset<TResolver> | undefined
21
51
  }
22
52
 
23
53
  /**
24
- * Resolves a named preset into merged resolvers, transformers, and generators.
54
+ * Resolves a named preset into a resolver, transformer, and generators.
25
55
  *
26
- * - Merges the preset's resolvers on top of the first (default)
27
- * - Merges any additional user-supplied resolvers on top of that to produce the final `resolver`.
28
- * - Concatenates preset transformers before user-supplied transformers.
56
+ * - Selects the preset resolver; wraps it with user overrides using null/undefined fallback.
57
+ * - Composes the preset's transformers into a single visitor; wraps it with the user transformer using null/undefined fallback.
29
58
  * - Combines preset generators with user-supplied generators; falls back to the `default` preset's generators when neither provides any.
30
59
  */
31
60
  export function getPreset<TResolver extends Resolver = Resolver>(params: GetPresetParams<TResolver>): GetPresetResult<TResolver> {
32
- const { preset: presetName, presets, resolvers = [], transformers: userTransformers = [], generators: userGenerators = [] } = params
33
- const [defaultResolver, ...userResolvers] = resolvers
61
+ const { preset: presetName, presets, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = params
34
62
  const preset = presets[presetName]
35
63
 
36
- const baseResolver = mergeResolvers(defaultResolver!, ...(preset?.resolvers ?? []))
37
- const resolver = mergeResolvers(baseResolver, ...(userResolvers ?? []))
38
- const transformers = [...(preset?.transformers ?? []), ...(userTransformers ?? [])]
64
+ const presetResolver = preset?.resolver ?? presets['default']!.resolver
65
+ const resolver = userResolver ? withFallback(presetResolver, userResolver) : presetResolver
66
+
67
+ const presetTransformers = preset?.transformers ?? []
68
+ const presetTransformer = presetTransformers.length > 0 ? composeTransformers(...presetTransformers) : undefined
69
+ const transformer = presetTransformer && userTransformer ? withFallback(presetTransformer, userTransformer) : (userTransformer ?? presetTransformer)
39
70
 
40
71
  const presetGenerators = preset?.generators ?? []
41
- const defaultPresetGenerators = presets['default']?.generators ?? []
42
- const generators = (presetGenerators.length > 0 || userGenerators.length
43
- ? [...presetGenerators, ...userGenerators]
44
- : defaultPresetGenerators) as unknown as Array<Generator<any>>
45
-
46
- return {
47
- resolver,
48
- transformers,
49
- generators,
50
- preset,
51
- }
72
+ const defaultGenerators = presets['default']?.generators ?? []
73
+ const generators = (presetGenerators.length > 0 || userGenerators.length > 0 ? [...presetGenerators, ...userGenerators] : defaultGenerators) as Array<
74
+ Generator<any>
75
+ >
76
+
77
+ return { resolver, transformer, generators, preset }
52
78
  }
@@ -1,16 +0,0 @@
1
- import type { Resolver } from '../types.ts'
2
-
3
- /**
4
- * Merges an ordered list of resolvers into a single resolver by shallow-merging each entry left to right.
5
- *
6
- * Later entries win when keys conflict, so the last resolver in the list takes highest precedence.
7
- *
8
- * @example
9
- * ```ts
10
- * const resolver = mergeResolvers(resolverTs, resolverTsLegacy)
11
- * // resolverTsLegacy methods override resolverTs where they overlap
12
- * ```
13
- */
14
- export function mergeResolvers<T extends Resolver>(...resolvers: Array<T>): T {
15
- return resolvers.reduce<T>((acc, curr) => ({ ...acc, ...curr }), resolvers[0]!)
16
- }