@kubb/core 5.0.0-alpha.7 → 5.0.0-alpha.70

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 (69) hide show
  1. package/README.md +3 -2
  2. package/dist/PluginDriver-BXibeQk-.cjs +1036 -0
  3. package/dist/PluginDriver-BXibeQk-.cjs.map +1 -0
  4. package/dist/PluginDriver-DV3p2Hky.js +945 -0
  5. package/dist/PluginDriver-DV3p2Hky.js.map +1 -0
  6. package/dist/index.cjs +730 -1773
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.ts +289 -240
  9. package/dist/index.js +717 -1741
  10. package/dist/index.js.map +1 -1
  11. package/dist/mocks.cjs +139 -0
  12. package/dist/mocks.cjs.map +1 -0
  13. package/dist/mocks.d.ts +74 -0
  14. package/dist/mocks.js +134 -0
  15. package/dist/mocks.js.map +1 -0
  16. package/dist/types-DWtkW2RX.d.ts +1915 -0
  17. package/package.json +51 -57
  18. package/src/FileManager.ts +115 -0
  19. package/src/FileProcessor.ts +86 -0
  20. package/src/Kubb.ts +205 -130
  21. package/src/PluginDriver.ts +326 -565
  22. package/src/constants.ts +20 -47
  23. package/src/createAdapter.ts +25 -0
  24. package/src/createKubb.ts +546 -0
  25. package/src/createRenderer.ts +57 -0
  26. package/src/createStorage.ts +58 -0
  27. package/src/defineGenerator.ts +88 -100
  28. package/src/defineLogger.ts +13 -3
  29. package/src/defineMiddleware.ts +64 -0
  30. package/src/defineParser.ts +45 -0
  31. package/src/definePlugin.ts +78 -7
  32. package/src/defineResolver.ts +521 -0
  33. package/src/devtools.ts +14 -14
  34. package/src/index.ts +13 -16
  35. package/src/mocks.ts +172 -0
  36. package/src/renderNode.ts +35 -0
  37. package/src/storages/fsStorage.ts +43 -13
  38. package/src/storages/memoryStorage.ts +6 -4
  39. package/src/types.ts +752 -226
  40. package/src/utils/diagnostics.ts +4 -1
  41. package/src/utils/isInputPath.ts +10 -0
  42. package/src/utils/packageJSON.ts +99 -0
  43. package/dist/PluginDriver-Dma9KhLK.d.ts +0 -1056
  44. package/dist/chunk-ByKO4r7w.cjs +0 -38
  45. package/dist/hooks.cjs +0 -102
  46. package/dist/hooks.cjs.map +0 -1
  47. package/dist/hooks.d.ts +0 -75
  48. package/dist/hooks.js +0 -97
  49. package/dist/hooks.js.map +0 -1
  50. package/src/PackageManager.ts +0 -180
  51. package/src/build.ts +0 -419
  52. package/src/config.ts +0 -56
  53. package/src/defineAdapter.ts +0 -22
  54. package/src/defineStorage.ts +0 -56
  55. package/src/errors.ts +0 -1
  56. package/src/hooks/index.ts +0 -4
  57. package/src/hooks/useKubb.ts +0 -138
  58. package/src/hooks/useMode.ts +0 -11
  59. package/src/hooks/usePlugin.ts +0 -11
  60. package/src/hooks/usePluginDriver.ts +0 -11
  61. package/src/utils/FunctionParams.ts +0 -155
  62. package/src/utils/TreeNode.ts +0 -215
  63. package/src/utils/executeStrategies.ts +0 -81
  64. package/src/utils/formatters.ts +0 -56
  65. package/src/utils/getBarrelFiles.ts +0 -141
  66. package/src/utils/getConfigs.ts +0 -30
  67. package/src/utils/getPlugins.ts +0 -23
  68. package/src/utils/linters.ts +0 -25
  69. package/src/utils/resolveOptions.ts +0 -93
package/src/mocks.ts ADDED
@@ -0,0 +1,172 @@
1
+ import { resolve } from 'node:path'
2
+ import type { FileNode, OperationNode, SchemaNode, Visitor } from '@kubb/ast'
3
+ import { transform } from '@kubb/ast'
4
+ import { FileManager } from './FileManager.ts'
5
+ import { PluginDriver } from './PluginDriver.ts'
6
+ import { applyHookResult } from './renderNode.ts'
7
+ import type { Adapter, AdapterFactoryOptions, Config, Generator, GeneratorContext, NormalizedPlugin, PluginFactoryOptions } from './types.ts'
8
+
9
+ /**
10
+ * Creates a minimal `PluginDriver` mock suitable for unit tests.
11
+ */
12
+ export function createMockedPluginDriver(options: { name?: string; plugin?: NormalizedPlugin; config?: Config } = {}): PluginDriver {
13
+ return {
14
+ config: options?.config ?? {
15
+ root: '.',
16
+ output: {
17
+ path: './path',
18
+ },
19
+ },
20
+ getPlugin(_pluginName: string): NormalizedPlugin | undefined {
21
+ return options?.plugin
22
+ },
23
+ getResolver: (_pluginName: string) => options?.plugin?.resolver,
24
+ fileManager: new FileManager(),
25
+ } as unknown as PluginDriver
26
+ }
27
+
28
+ /**
29
+ * Creates a minimal `Adapter` mock suitable for unit tests.
30
+ *
31
+ * - `parse` returns an empty `InputNode` by default; override via `options.parse`.
32
+ * - `getImports` returns `[]` by default (single-file mode, no cross-file imports).
33
+ */
34
+ export function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(
35
+ options: {
36
+ name?: TOptions['name']
37
+ resolvedOptions?: TOptions['resolvedOptions']
38
+ inputNode?: Adapter<TOptions>['inputNode']
39
+ parse?: Adapter<TOptions>['parse']
40
+ getImports?: Adapter<TOptions>['getImports']
41
+ } = {},
42
+ ): Adapter<TOptions> {
43
+ const inputNode = options.inputNode ?? null
44
+ return {
45
+ name: (options.name ?? 'oas') as TOptions['name'],
46
+ options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],
47
+ inputNode,
48
+ parse: options.parse ?? (async () => ({ kind: 'Input' as const, schemas: [], operations: [] })),
49
+ getImports: options.getImports ?? ((_node: SchemaNode, _resolve: (schemaName: string) => { name: string; path: string }) => []),
50
+ } as Adapter<TOptions>
51
+ }
52
+
53
+ /**
54
+ * Creates a minimal plugin mock suitable for unit tests.
55
+ *
56
+ * @example
57
+ * const plugin = createMockedPlugin<PluginTs>({ name: '@kubb/plugin-ts', options })
58
+ */
59
+ export function createMockedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(params: {
60
+ name: TOptions['name']
61
+ options: TOptions['resolvedOptions']
62
+ resolver?: TOptions['resolver']
63
+ transformer?: Visitor
64
+ dependencies?: Array<string>
65
+ }): NormalizedPlugin<TOptions> {
66
+ return {
67
+ name: params.name,
68
+ options: params.options,
69
+ resolver: params.resolver,
70
+ transformer: params.transformer,
71
+ dependencies: params.dependencies,
72
+ hooks: {},
73
+ } as unknown as NormalizedPlugin<TOptions>
74
+ }
75
+
76
+ type RenderGeneratorOptions<TOptions extends PluginFactoryOptions> = {
77
+ config: Config
78
+ adapter: Adapter
79
+ driver: PluginDriver
80
+ plugin: NormalizedPlugin<TOptions>
81
+ options: TOptions['resolvedOptions']
82
+ resolver: TOptions['resolver']
83
+ }
84
+
85
+ function createMockedPluginContext<TOptions extends PluginFactoryOptions>(opts: RenderGeneratorOptions<TOptions>): Omit<GeneratorContext<TOptions>, 'options'> {
86
+ const root = resolve(opts.config.root, opts.config.output.path)
87
+
88
+ return {
89
+ config: opts.config,
90
+ root,
91
+ getMode: (output: { path: string }) => PluginDriver.getMode(resolve(root, output.path)),
92
+ adapter: opts.adapter,
93
+ resolver: opts.resolver,
94
+ plugin: opts.plugin,
95
+ driver: opts.driver,
96
+ getResolver: (name: string) => opts.driver.getResolver(name),
97
+ inputNode: { kind: 'Input', schemas: [], operations: [] },
98
+ addFile: async (...files: Array<FileNode>) => opts.driver.fileManager.add(...files),
99
+ upsertFile: async (...files: Array<FileNode>) => opts.driver.fileManager.upsert(...files),
100
+ hooks: opts.driver.hooks ?? ({} as never),
101
+ warn: (msg: string) => console.warn(msg),
102
+ error: (msg: string) => console.error(msg),
103
+ info: (msg: string) => console.info(msg),
104
+ openInStudio: async () => {},
105
+ } as unknown as Omit<GeneratorContext<TOptions>, 'options'>
106
+ }
107
+
108
+ /**
109
+ * Renders a generator's `schema` method in a test context.
110
+ *
111
+ * @example
112
+ * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })
113
+ * await matchFiles(driver.fileManager.files)
114
+ */
115
+ export async function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(
116
+ generator: Generator<TOptions>,
117
+ node: SchemaNode,
118
+ opts: RenderGeneratorOptions<TOptions>,
119
+ ): Promise<void> {
120
+ if (!generator.schema) return
121
+ const context = createMockedPluginContext(opts)
122
+ const transformedNode = opts.plugin.transformer ? transform(node, opts.plugin.transformer) : node
123
+ const result = await generator.schema(transformedNode, {
124
+ ...context,
125
+ options: opts.options,
126
+ })
127
+ await applyHookResult(result, opts.driver, generator.renderer ?? undefined)
128
+ }
129
+
130
+ /**
131
+ * Renders a generator's `operation` method in a test context.
132
+ *
133
+ * @example
134
+ * await renderGeneratorOperation(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })
135
+ * await matchFiles(driver.fileManager.files)
136
+ */
137
+ export async function renderGeneratorOperation<TOptions extends PluginFactoryOptions>(
138
+ generator: Generator<TOptions>,
139
+ node: OperationNode,
140
+ opts: RenderGeneratorOptions<TOptions>,
141
+ ): Promise<void> {
142
+ if (!generator.operation) return
143
+ const context = createMockedPluginContext(opts)
144
+ const transformedNode = opts.plugin.transformer ? transform(node, opts.plugin.transformer) : node
145
+ const result = await generator.operation(transformedNode, {
146
+ ...context,
147
+ options: opts.options,
148
+ })
149
+ await applyHookResult(result, opts.driver, generator.renderer ?? undefined)
150
+ }
151
+
152
+ /**
153
+ * Renders a generator's `operations` method in a test context.
154
+ *
155
+ * @example
156
+ * await renderGeneratorOperations(classClientGenerator, nodes, { config, adapter, driver, plugin, options, resolver })
157
+ * await matchFiles(driver.fileManager.files)
158
+ */
159
+ export async function renderGeneratorOperations<TOptions extends PluginFactoryOptions>(
160
+ generator: Generator<TOptions>,
161
+ nodes: Array<OperationNode>,
162
+ opts: RenderGeneratorOptions<TOptions>,
163
+ ): Promise<void> {
164
+ if (!generator.operations) return
165
+ const context = createMockedPluginContext(opts)
166
+ const transformedNodes = opts.plugin.transformer ? nodes.map((n) => transform(n, opts.plugin.transformer!)) : nodes
167
+ const result = await generator.operations(transformedNodes, {
168
+ ...context,
169
+ options: opts.options,
170
+ })
171
+ await applyHookResult(result, opts.driver, generator.renderer ?? undefined)
172
+ }
@@ -0,0 +1,35 @@
1
+ import type { FileNode } from '@kubb/ast'
2
+ import type { RendererFactory } from './createRenderer.ts'
3
+ import type { PluginDriver } from './PluginDriver.ts'
4
+
5
+ /**
6
+ * Handles the return value of a plugin AST hook or generator method.
7
+ *
8
+ * - Renderer output → rendered via the provided `rendererFactory` (e.g. JSX), files stored in `driver.fileManager`
9
+ * - `Array<FileNode>` → added directly into `driver.fileManager`
10
+ * - `void` / `null` / `undefined` → no-op (plugin handled it via `this.upsertFile`)
11
+ *
12
+ * Pass a `rendererFactory` (e.g. `jsxRenderer` from `@kubb/renderer-jsx`) when the result
13
+ * may be a renderer element. Generators that only return `Array<FileNode>` do not need one.
14
+ */
15
+ export async function applyHookResult<TElement = unknown>(
16
+ result: TElement | Array<FileNode> | void,
17
+ driver: PluginDriver,
18
+ rendererFactory?: RendererFactory<TElement>,
19
+ ): Promise<void> {
20
+ if (!result) return
21
+
22
+ if (Array.isArray(result)) {
23
+ driver.fileManager.upsert(...(result as Array<FileNode>))
24
+ return
25
+ }
26
+
27
+ if (!rendererFactory) {
28
+ return
29
+ }
30
+
31
+ const renderer = rendererFactory()
32
+ await renderer.render(result)
33
+ driver.fileManager.upsert(...renderer.files)
34
+ renderer.unmount()
35
+ }
@@ -2,12 +2,19 @@ import type { Dirent } from 'node:fs'
2
2
  import { access, readdir, readFile, rm } from 'node:fs/promises'
3
3
  import { join, resolve } from 'node:path'
4
4
  import { clean, write } from '@internals/utils'
5
- import { defineStorage } from '../defineStorage.ts'
5
+ import { createStorage } from '../createStorage.ts'
6
+
7
+ /**
8
+ * Detects the filesystem error used to indicate that a path does not exist.
9
+ */
10
+ function isMissingPathError(error: unknown): error is NodeJS.ErrnoException {
11
+ return typeof error === 'object' && error !== null && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT'
12
+ }
6
13
 
7
14
  /**
8
15
  * Built-in filesystem storage driver.
9
16
  *
10
- * This is the default storage when no `storage` option is configured in `output`.
17
+ * This is the default storage when no `storage` option is configured in the root config.
11
18
  * Keys are resolved against `process.cwd()`, so root-relative paths such as
12
19
  * `src/gen/api/getPets.ts` are written to the correct location without extra configuration.
13
20
  *
@@ -19,29 +26,43 @@ import { defineStorage } from '../defineStorage.ts'
19
26
  *
20
27
  * @example
21
28
  * ```ts
22
- * import { defineConfig, fsStorage } from '@kubb/core'
29
+ * import { fsStorage } from '@kubb/core'
30
+ * import { defineConfig } from 'kubb'
23
31
  *
24
32
  * export default defineConfig({
25
33
  * input: { path: './petStore.yaml' },
26
- * output: { path: './src/gen', storage: fsStorage() },
34
+ * output: { path: './src/gen' },
35
+ * storage: fsStorage(),
27
36
  * })
28
37
  * ```
29
38
  */
30
- export const fsStorage = defineStorage(() => ({
39
+ export const fsStorage = createStorage(() => ({
31
40
  name: 'fs',
32
41
  async hasItem(key: string) {
33
42
  try {
34
43
  await access(resolve(key))
35
44
  return true
36
- } catch {
37
- return false
45
+ } catch (error) {
46
+ if (isMissingPathError(error)) {
47
+ return false
48
+ }
49
+
50
+ throw new Error(`Failed to access storage item "${key}"`, {
51
+ cause: error as Error,
52
+ })
38
53
  }
39
54
  },
40
55
  async getItem(key: string) {
41
56
  try {
42
57
  return await readFile(resolve(key), 'utf8')
43
- } catch {
44
- return null
58
+ } catch (error) {
59
+ if (isMissingPathError(error)) {
60
+ return null
61
+ }
62
+
63
+ throw new Error(`Failed to read storage item "${key}"`, {
64
+ cause: error as Error,
65
+ })
45
66
  }
46
67
  },
47
68
  async setItem(key: string, value: string) {
@@ -52,13 +73,22 @@ export const fsStorage = defineStorage(() => ({
52
73
  },
53
74
  async getKeys(base?: string) {
54
75
  const keys: Array<string> = []
76
+ const resolvedBase = resolve(base ?? process.cwd())
55
77
 
56
78
  async function walk(dir: string, prefix: string): Promise<void> {
57
79
  let entries: Array<Dirent>
58
80
  try {
59
- entries = (await readdir(dir, { withFileTypes: true })) as Array<Dirent>
60
- } catch {
61
- return
81
+ entries = (await readdir(dir, {
82
+ withFileTypes: true,
83
+ })) as Array<Dirent>
84
+ } catch (error) {
85
+ if (isMissingPathError(error)) {
86
+ return
87
+ }
88
+
89
+ throw new Error(`Failed to list storage keys under "${resolvedBase}"`, {
90
+ cause: error as Error,
91
+ })
62
92
  }
63
93
  for (const entry of entries) {
64
94
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name
@@ -70,7 +100,7 @@ export const fsStorage = defineStorage(() => ({
70
100
  }
71
101
  }
72
102
 
73
- await walk(resolve(base ?? process.cwd()), '')
103
+ await walk(resolvedBase, '')
74
104
 
75
105
  return keys
76
106
  },
@@ -1,4 +1,4 @@
1
- import { defineStorage } from '../defineStorage.ts'
1
+ import { createStorage } from '../createStorage.ts'
2
2
 
3
3
  /**
4
4
  * In-memory storage driver. Useful for testing and dry-run scenarios where
@@ -9,15 +9,17 @@ import { defineStorage } from '../defineStorage.ts'
9
9
  *
10
10
  * @example
11
11
  * ```ts
12
- * import { defineConfig, memoryStorage } from '@kubb/core'
12
+ * import { memoryStorage } from '@kubb/core'
13
+ * import { defineConfig } from 'kubb'
13
14
  *
14
15
  * export default defineConfig({
15
16
  * input: { path: './petStore.yaml' },
16
- * output: { path: './src/gen', storage: memoryStorage() },
17
+ * output: { path: './src/gen' },
18
+ * storage: memoryStorage(),
17
19
  * })
18
20
  * ```
19
21
  */
20
- export const memoryStorage = defineStorage(() => {
22
+ export const memoryStorage = createStorage(() => {
21
23
  const store = new Map<string, string>()
22
24
 
23
25
  return {