@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.
- package/dist/{types-D30QAz2y.d.ts → PluginDriver-BkFepPdm.d.ts} +354 -291
- package/dist/hooks.cjs +85 -8
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +66 -4
- package/dist/hooks.js +83 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +346 -315
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +91 -77
- package/dist/index.js +338 -305
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/Kubb.ts +27 -55
- package/src/{PluginManager.ts → PluginDriver.ts} +69 -82
- package/src/build.ts +32 -33
- package/src/constants.ts +1 -1
- package/src/createAdapter.ts +25 -0
- package/src/createPlugin.ts +28 -0
- package/src/createStorage.ts +58 -0
- package/src/defineGenerator.ts +134 -0
- package/src/defineLogger.ts +13 -3
- package/src/defineResolver.ts +131 -0
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useKubb.ts +143 -0
- package/src/hooks/useMode.ts +5 -2
- package/src/hooks/usePlugin.ts +5 -2
- package/src/hooks/usePluginDriver.ts +11 -0
- package/src/index.ts +7 -7
- package/src/storages/fsStorage.ts +2 -2
- package/src/storages/memoryStorage.ts +2 -2
- package/src/types.ts +94 -48
- package/src/utils/FunctionParams.ts +2 -2
- package/src/utils/TreeNode.ts +1 -1
- package/src/utils/formatters.ts +1 -1
- package/src/utils/getBarrelFiles.ts +73 -11
- package/src/utils/getConfigs.ts +3 -21
- package/src/utils/linters.ts +1 -1
- package/src/utils/packageJSON.ts +61 -0
- package/src/BarrelManager.ts +0 -74
- package/src/PackageManager.ts +0 -180
- package/src/PromiseManager.ts +0 -40
- package/src/defineAdapter.ts +0 -22
- package/src/definePlugin.ts +0 -12
- package/src/defineStorage.ts +0 -56
- package/src/errors.ts +0 -1
- package/src/hooks/usePluginManager.ts +0 -8
- 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 {
|
|
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,
|
|
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:
|
|
22
|
+
fabric: FabricType
|
|
25
23
|
files: Array<KubbFile.ResolvedFile>
|
|
26
|
-
|
|
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:
|
|
35
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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: ${
|
|
190
|
-
` • Operations: ${
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
232
|
+
const config = driver.config
|
|
234
233
|
|
|
235
234
|
try {
|
|
236
|
-
for (const plugin of
|
|
237
|
-
const context =
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
359
|
+
driver: PluginDriver
|
|
361
360
|
}
|
|
362
361
|
|
|
363
|
-
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config,
|
|
364
|
-
const
|
|
365
|
-
for (const plugin of
|
|
366
|
-
|
|
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?.
|
|
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
|
+
}
|
package/src/defineLogger.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/hooks/index.ts
CHANGED