@kubb/core 1.15.0-canary.20231112T135011 → 2.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.
Files changed (55) hide show
  1. package/README.md +1 -1
  2. package/dist/index.cjs +1253 -1088
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +396 -411
  5. package/dist/index.d.ts +396 -411
  6. package/dist/index.js +1194 -1018
  7. package/dist/index.js.map +1 -1
  8. package/dist/utils.cjs +1272 -0
  9. package/dist/utils.cjs.map +1 -0
  10. package/dist/utils.d.cts +239 -0
  11. package/dist/utils.d.ts +239 -0
  12. package/dist/utils.js +1219 -0
  13. package/dist/utils.js.map +1 -0
  14. package/globals.d.ts +33 -16
  15. package/package.json +21 -14
  16. package/src/BarrelManager.ts +123 -0
  17. package/src/FileManager.ts +524 -0
  18. package/src/Generator.ts +34 -0
  19. package/src/PackageManager.ts +178 -0
  20. package/src/PluginManager.ts +629 -0
  21. package/src/PromiseManager.ts +51 -0
  22. package/src/SchemaGenerator.ts +8 -0
  23. package/src/build.ts +207 -0
  24. package/src/config.ts +22 -0
  25. package/src/errors.ts +12 -0
  26. package/src/index.ts +28 -0
  27. package/src/plugin.ts +80 -0
  28. package/src/types.ts +353 -0
  29. package/src/utils/EventEmitter.ts +24 -0
  30. package/src/utils/FunctionParams.ts +85 -0
  31. package/src/utils/Queue.ts +110 -0
  32. package/src/utils/TreeNode.ts +122 -0
  33. package/src/utils/URLPath.ts +133 -0
  34. package/src/utils/cache.ts +35 -0
  35. package/src/utils/clean.ts +5 -0
  36. package/src/utils/executeStrategies.ts +83 -0
  37. package/src/utils/index.ts +19 -0
  38. package/src/utils/logger.ts +76 -0
  39. package/src/utils/promise.ts +13 -0
  40. package/src/utils/randomColour.ts +39 -0
  41. package/src/utils/read.ts +68 -0
  42. package/src/utils/renderTemplate.ts +31 -0
  43. package/src/utils/throttle.ts +30 -0
  44. package/src/utils/timeout.ts +7 -0
  45. package/src/utils/transformers/combineCodes.ts +3 -0
  46. package/src/utils/transformers/createJSDocBlockText.ts +15 -0
  47. package/src/utils/transformers/escape.ts +31 -0
  48. package/src/utils/transformers/indent.ts +3 -0
  49. package/src/utils/transformers/index.ts +22 -0
  50. package/src/utils/transformers/nameSorter.ts +9 -0
  51. package/src/utils/transformers/searchAndReplace.ts +25 -0
  52. package/src/utils/transformers/transformReservedWord.ts +97 -0
  53. package/src/utils/transformers/trim.ts +3 -0
  54. package/src/utils/uniqueName.ts +20 -0
  55. package/src/utils/write.ts +63 -0
@@ -0,0 +1,629 @@
1
+ /* eslint-disable @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-argument */
2
+
3
+ import { EventEmitter } from './utils/EventEmitter.ts'
4
+ import { LogLevel } from './utils/logger.ts'
5
+ import { Queue } from './utils/Queue.ts'
6
+ import { transformReservedWord } from './utils/transformers/transformReservedWord.ts'
7
+ import { setUniqueName } from './utils/uniqueName.ts'
8
+ import { ValidationPluginError } from './errors.ts'
9
+ import { FileManager } from './FileManager.ts'
10
+ import { definePlugin as defineCorePlugin } from './plugin.ts'
11
+ import { isPromise, isPromiseRejectedResult } from './PromiseManager.ts'
12
+ import { PromiseManager } from './PromiseManager.ts'
13
+
14
+ import type { PossiblePromise } from '@kubb/types'
15
+ import type { KubbFile } from './FileManager.ts'
16
+ import type { CorePluginOptions } from './plugin.ts'
17
+ import type {
18
+ GetPluginFactoryOptions,
19
+ KubbConfig,
20
+ KubbPlugin,
21
+ KubbPluginWithLifeCycle,
22
+ KubbUserPlugin,
23
+ KubbUserPluginWithLifeCycle,
24
+ PluginFactoryOptions,
25
+ PluginLifecycle,
26
+ PluginLifecycleHooks,
27
+ PluginParameter,
28
+ ResolveNameParams,
29
+ ResolvePathParams,
30
+ } from './types.ts'
31
+ import type { Logger } from './utils/logger.ts'
32
+ import type { QueueJob } from './utils/Queue.ts'
33
+
34
+ type RequiredPluginLifecycle = Required<PluginLifecycle>
35
+
36
+ /**
37
+ * Get the type of the first argument in a function.
38
+ * @example Arg0<(a: string, b: number) => void> -> string
39
+ */
40
+ type Argument0<H extends keyof PluginLifecycle> = Parameters<RequiredPluginLifecycle[H]>[0]
41
+
42
+ type Strategy = 'hookFirst' | 'hookForPlugin' | 'hookParallel' | 'hookReduceArg0' | 'hookSeq'
43
+
44
+ type Executer<H extends PluginLifecycleHooks = PluginLifecycleHooks> = {
45
+ strategy: Strategy
46
+ hookName: H
47
+ plugin: KubbPlugin
48
+ parameters?: unknown[] | undefined
49
+ output?: unknown
50
+ }
51
+
52
+ type ParseResult<H extends PluginLifecycleHooks> = RequiredPluginLifecycle[H]
53
+
54
+ type SafeParseResult<H extends PluginLifecycleHooks, Result = ReturnType<ParseResult<H>>> = {
55
+ result: Result
56
+ plugin: KubbPlugin
57
+ }
58
+
59
+ // inspired by: https://github.com/rollup/rollup/blob/master/src/utils/PluginDriver.ts#
60
+
61
+ type Options = {
62
+ logger: Logger
63
+
64
+ /**
65
+ * Task for the FileManager
66
+ */
67
+ task: QueueJob<KubbFile.ResolvedFile>
68
+ /**
69
+ * Timeout between writes in the FileManager
70
+ */
71
+ writeTimeout?: number
72
+ }
73
+
74
+ type Events = {
75
+ execute: [executer: Executer]
76
+ executed: [executer: Executer]
77
+ error: [error: Error]
78
+ }
79
+
80
+ export class PluginManager {
81
+ readonly plugins: KubbPluginWithLifeCycle[]
82
+ readonly fileManager: FileManager
83
+ readonly eventEmitter: EventEmitter<Events> = new EventEmitter()
84
+
85
+ readonly queue: Queue
86
+ readonly config: KubbConfig
87
+
88
+ readonly executed: Executer[] = []
89
+ readonly logger: Logger
90
+ readonly #core: KubbPlugin<CorePluginOptions>
91
+
92
+ readonly #usedPluginNames: Record<string, number> = {}
93
+ readonly #promiseManager: PromiseManager
94
+
95
+ constructor(config: KubbConfig, options: Options) {
96
+ this.config = config
97
+ this.logger = options.logger
98
+ this.queue = new Queue(100, this.logger.logLevel === LogLevel.debug)
99
+ this.fileManager = new FileManager({ task: options.task, queue: this.queue, timeout: options.writeTimeout })
100
+ this.#promiseManager = new PromiseManager({ nullCheck: (state: SafeParseResult<'resolveName'> | null) => !!state?.result })
101
+
102
+ const plugins = config.plugins || []
103
+
104
+ const core = defineCorePlugin({
105
+ config,
106
+ logger: this.logger,
107
+ pluginManager: this,
108
+ fileManager: this.fileManager,
109
+ resolvePath: this.resolvePath.bind(this),
110
+ resolveName: this.resolveName.bind(this),
111
+ getPlugins: this.#getSortedPlugins.bind(this),
112
+ })
113
+
114
+ // call core.api.call with empty context so we can transform `api()` to `api: {}`
115
+ this.#core = this.#parse(core as unknown as KubbUserPlugin, this as any, core.api.call(null as any)) as KubbPlugin<CorePluginOptions>
116
+
117
+ this.plugins = [this.#core, ...plugins].map((plugin) => {
118
+ return this.#parse(plugin as KubbUserPlugin, this, this.#core.api)
119
+ })
120
+
121
+ return this
122
+ }
123
+
124
+ resolvePath = <TOptions = object>(params: ResolvePathParams<TOptions>): KubbFile.OptionalPath => {
125
+ if (params.pluginKey) {
126
+ const paths = this.hookForPluginSync({
127
+ pluginKey: params.pluginKey,
128
+ hookName: 'resolvePath',
129
+ parameters: [params.baseName, params.directory, params.options as object],
130
+ })
131
+
132
+ if (paths && paths?.length > 1) {
133
+ throw new Error(
134
+ `Cannot return a path where the 'pluginKey' ${params.pluginKey ? JSON.stringify(params.pluginKey) : '"'} is not unique enough\n\nPaths: ${
135
+ JSON.stringify(paths, undefined, 2)
136
+ }`,
137
+ )
138
+ }
139
+
140
+ return paths?.at(0)
141
+ }
142
+ return this.hookFirstSync({
143
+ hookName: 'resolvePath',
144
+ parameters: [params.baseName, params.directory, params.options as object],
145
+ }).result
146
+ }
147
+
148
+ resolveName = (params: ResolveNameParams): string => {
149
+ if (params.pluginKey) {
150
+ const names = this.hookForPluginSync({
151
+ pluginKey: params.pluginKey,
152
+ hookName: 'resolveName',
153
+ parameters: [params.name, params.type],
154
+ })
155
+
156
+ if (names && names?.length > 1) {
157
+ throw new Error(
158
+ `Cannot return a name where the 'pluginKey' ${params.pluginKey ? JSON.stringify(params.pluginKey) : '"'} is not unique enough\n\nNames: ${
159
+ JSON.stringify(names, undefined, 2)
160
+ }`,
161
+ )
162
+ }
163
+
164
+ return transformReservedWord(names?.at(0) || params.name)
165
+ }
166
+
167
+ const name = this.hookFirstSync({
168
+ hookName: 'resolveName',
169
+ parameters: [params.name, params.type],
170
+ }).result
171
+
172
+ return transformReservedWord(name)
173
+ }
174
+
175
+ on<TEventName extends keyof Events & string>(eventName: TEventName, handler: (...eventArg: Events[TEventName]) => void): void {
176
+ this.eventEmitter.on(eventName, handler as any)
177
+ }
178
+
179
+ /**
180
+ * Run only hook for a specific plugin name
181
+ */
182
+ hookForPlugin<H extends PluginLifecycleHooks>({
183
+ pluginKey,
184
+ hookName,
185
+ parameters,
186
+ }: {
187
+ pluginKey: KubbPlugin['key']
188
+ hookName: H
189
+ parameters: PluginParameter<H>
190
+ }): Promise<Array<ReturnType<ParseResult<H>> | null>> | null {
191
+ const plugins = this.getPluginsByKey(hookName, pluginKey)
192
+
193
+ const promises = plugins
194
+ .map((plugin) => {
195
+ return this.#execute<H>({
196
+ strategy: 'hookFirst',
197
+ hookName,
198
+ parameters,
199
+ plugin,
200
+ })
201
+ })
202
+ .filter(Boolean)
203
+
204
+ return Promise.all(promises)
205
+ }
206
+
207
+ hookForPluginSync<H extends PluginLifecycleHooks>({
208
+ pluginKey,
209
+ hookName,
210
+ parameters,
211
+ }: {
212
+ pluginKey: KubbPlugin['key']
213
+ hookName: H
214
+ parameters: PluginParameter<H>
215
+ }): Array<ReturnType<ParseResult<H>>> | null {
216
+ const plugins = this.getPluginsByKey(hookName, pluginKey)
217
+
218
+ return plugins
219
+ .map((plugin) => {
220
+ return this.#executeSync<H>({
221
+ strategy: 'hookFirst',
222
+ hookName,
223
+ parameters,
224
+ plugin,
225
+ })
226
+ })
227
+ .filter(Boolean)
228
+ }
229
+
230
+ /**
231
+ * Chains, first non-null result stops and returns
232
+ */
233
+ async hookFirst<H extends PluginLifecycleHooks>({
234
+ hookName,
235
+ parameters,
236
+ skipped,
237
+ }: {
238
+ hookName: H
239
+ parameters: PluginParameter<H>
240
+ skipped?: ReadonlySet<KubbPlugin> | null
241
+ }): Promise<SafeParseResult<H>> {
242
+ const promises = this.#getSortedPlugins().filter(plugin => {
243
+ return skipped ? skipped.has(plugin) : true
244
+ }).map((plugin) => {
245
+ return async () => {
246
+ const value = await this.#execute<H>({
247
+ strategy: 'hookFirst',
248
+ hookName,
249
+ parameters,
250
+ plugin,
251
+ })
252
+
253
+ return Promise.resolve(
254
+ {
255
+ plugin,
256
+ result: value,
257
+ } as SafeParseResult<H>,
258
+ )
259
+ }
260
+ })
261
+
262
+ return this.#promiseManager.run('first', promises)
263
+ }
264
+
265
+ /**
266
+ * Chains, first non-null result stops and returns
267
+ */
268
+ hookFirstSync<H extends PluginLifecycleHooks>({
269
+ hookName,
270
+ parameters,
271
+ skipped,
272
+ }: {
273
+ hookName: H
274
+ parameters: PluginParameter<H>
275
+ skipped?: ReadonlySet<KubbPlugin> | null
276
+ }): SafeParseResult<H> {
277
+ let parseResult: SafeParseResult<H> = null as unknown as SafeParseResult<H>
278
+
279
+ for (const plugin of this.#getSortedPlugins()) {
280
+ if (skipped && skipped.has(plugin)) {
281
+ continue
282
+ }
283
+
284
+ parseResult = {
285
+ result: this.#executeSync<H>({
286
+ strategy: 'hookFirst',
287
+ hookName,
288
+ parameters,
289
+ plugin,
290
+ }),
291
+ plugin,
292
+ } as SafeParseResult<H>
293
+
294
+ if (parseResult?.result != null) {
295
+ break
296
+ }
297
+ }
298
+ return parseResult
299
+ }
300
+
301
+ /**
302
+ * Parallel, runs all plugins
303
+ */
304
+ async hookParallel<H extends PluginLifecycleHooks, TOuput = void>({
305
+ hookName,
306
+ parameters,
307
+ }: {
308
+ hookName: H
309
+ parameters?: Parameters<RequiredPluginLifecycle[H]> | undefined
310
+ }): Promise<Awaited<TOuput>[]> {
311
+ const promises = this.#getSortedPlugins().map((plugin) => {
312
+ return () => this.#execute({ strategy: 'hookParallel', hookName, parameters, plugin }) as Promise<TOuput>
313
+ })
314
+
315
+ const results = await this.#promiseManager.run('parallel', promises)
316
+
317
+ results
318
+ .forEach((result, index) => {
319
+ if (isPromiseRejectedResult<Error>(result)) {
320
+ const plugin = this.#getSortedPlugins()[index]
321
+
322
+ this.#catcher<H>(result.reason, plugin, hookName)
323
+ }
324
+ })
325
+
326
+ return results.filter((result) => result.status === 'fulfilled').map((result) => (result as PromiseFulfilledResult<Awaited<TOuput>>).value)
327
+ }
328
+
329
+ /**
330
+ * Chains, reduces returned value, handling the reduced value as the first hook argument
331
+ */
332
+ hookReduceArg0<H extends PluginLifecycleHooks>({
333
+ hookName,
334
+ parameters,
335
+ reduce,
336
+ }: {
337
+ hookName: H
338
+ parameters: PluginParameter<H>
339
+ reduce: (reduction: Argument0<H>, result: ReturnType<ParseResult<H>>, plugin: KubbPlugin) => PossiblePromise<Argument0<H> | null>
340
+ }): Promise<Argument0<H>> {
341
+ const [argument0, ...rest] = parameters
342
+
343
+ let promise: Promise<Argument0<H>> = Promise.resolve(argument0)
344
+ for (const plugin of this.#getSortedPlugins()) {
345
+ promise = promise
346
+ .then((arg0) => {
347
+ const value = this.#execute({
348
+ strategy: 'hookReduceArg0',
349
+ hookName,
350
+ parameters: [arg0, ...rest] as PluginParameter<H>,
351
+ plugin,
352
+ })
353
+ return value
354
+ })
355
+ .then((result) => reduce.call(this.#core.api, argument0, result as ReturnType<ParseResult<H>>, plugin)) as Promise<Argument0<H>>
356
+ }
357
+
358
+ return promise
359
+ }
360
+
361
+ /**
362
+ * Chains plugins
363
+ */
364
+ async hookSeq<H extends PluginLifecycleHooks>({ hookName, parameters }: { hookName: H; parameters?: PluginParameter<H> }): Promise<void> {
365
+ const promises = this.#getSortedPlugins().map((plugin) => {
366
+ return () =>
367
+ this.#execute({
368
+ strategy: 'hookSeq',
369
+ hookName,
370
+ parameters,
371
+ plugin,
372
+ })
373
+ })
374
+
375
+ return this.#promiseManager.run('seq', promises)
376
+ }
377
+
378
+ #getSortedPlugins(hookName?: keyof PluginLifecycle): KubbPlugin[] {
379
+ const plugins = [...this.plugins].filter((plugin) => plugin.name !== 'core')
380
+
381
+ if (hookName) {
382
+ if (this.logger.logLevel === 'info') {
383
+ const containsHookName = plugins.some((item) => item[hookName])
384
+ if (!containsHookName) {
385
+ this.logger.warn(`No hook ${hookName} found`)
386
+ }
387
+ }
388
+
389
+ return plugins.filter((item) => item[hookName])
390
+ }
391
+
392
+ return plugins
393
+ }
394
+
395
+ getPluginsByKey(hookName: keyof PluginLifecycle, pluginKey: KubbPlugin['key']): KubbPlugin[] {
396
+ const plugins = [...this.plugins]
397
+ const [searchKind, searchPluginName, searchIdentifier] = pluginKey
398
+
399
+ const pluginByPluginName = plugins
400
+ .filter((plugin) => plugin[hookName])
401
+ .filter((item) => {
402
+ const [kind, name, identifier] = item.key
403
+
404
+ const identifierCheck = identifier?.toString() === searchIdentifier?.toString()
405
+ const kindCheck = kind === searchKind
406
+ const nameCheck = name === searchPluginName
407
+
408
+ if (searchIdentifier) {
409
+ return identifierCheck && kindCheck && nameCheck
410
+ }
411
+
412
+ return kindCheck && nameCheck
413
+ })
414
+
415
+ if (!pluginByPluginName?.length) {
416
+ // fallback on the core plugin when there is no match
417
+
418
+ const corePlugin = plugins.find((plugin) => plugin.name === 'core' && plugin[hookName])
419
+
420
+ if (this.logger.logLevel === 'info') {
421
+ if (corePlugin) {
422
+ this.logger.warn(`No hook '${hookName}' for pluginKey '${JSON.stringify(pluginKey)}' found, falling back on the '@kubb/core' plugin`)
423
+ } else {
424
+ this.logger.warn(`No hook '${hookName}' for pluginKey '${JSON.stringify(pluginKey)}' found, no fallback found in the '@kubb/core' plugin`)
425
+ }
426
+ }
427
+
428
+ return corePlugin ? [corePlugin] : []
429
+ }
430
+
431
+ return pluginByPluginName
432
+ }
433
+
434
+ #addExecutedToCallStack(executer: Executer | undefined) {
435
+ if (executer) {
436
+ this.eventEmitter.emit('executed', executer)
437
+ this.executed.push(executer)
438
+ }
439
+ }
440
+
441
+ /**
442
+ * Run an async plugin hook and return the result.
443
+ * @param hookName Name of the plugin hook. Must be either in `PluginHooks` or `OutputPluginValueHooks`.
444
+ * @param args Arguments passed to the plugin hook.
445
+ * @param plugin The actual pluginObject to run.
446
+ */
447
+ // Implementation signature
448
+ #execute<H extends PluginLifecycleHooks>({
449
+ strategy,
450
+ hookName,
451
+ parameters,
452
+ plugin,
453
+ }: {
454
+ strategy: Strategy
455
+ hookName: H
456
+ parameters: unknown[] | undefined
457
+ plugin: KubbPluginWithLifeCycle
458
+ }): Promise<ReturnType<ParseResult<H>> | null> | null {
459
+ const hook = plugin[hookName]
460
+ let output: unknown
461
+
462
+ if (!hook) {
463
+ return null
464
+ }
465
+
466
+ this.eventEmitter.emit('execute', { strategy, hookName, parameters, plugin })
467
+
468
+ const task = Promise.resolve()
469
+ .then(() => {
470
+ if (typeof hook === 'function') {
471
+ const possiblePromiseResult = (hook as Function).apply({ ...this.#core.api, plugin }, parameters) as Promise<ReturnType<ParseResult<H>>>
472
+
473
+ if (isPromise(possiblePromiseResult)) {
474
+ return Promise.resolve(possiblePromiseResult)
475
+ }
476
+ return possiblePromiseResult
477
+ }
478
+
479
+ return hook
480
+ })
481
+ .then((result) => {
482
+ output = result
483
+
484
+ this.#addExecutedToCallStack({
485
+ parameters,
486
+ output,
487
+ strategy,
488
+ hookName,
489
+ plugin,
490
+ })
491
+
492
+ return result
493
+ })
494
+ .catch((e: Error) => {
495
+ this.#catcher<H>(e, plugin, hookName)
496
+
497
+ return null
498
+ })
499
+
500
+ return task
501
+ }
502
+
503
+ /**
504
+ * Run a sync plugin hook and return the result.
505
+ * @param hookName Name of the plugin hook. Must be in `PluginHooks`.
506
+ * @param args Arguments passed to the plugin hook.
507
+ * @param plugin The acutal plugin
508
+ * @param replaceContext When passed, the plugin context can be overridden.
509
+ */
510
+ #executeSync<H extends PluginLifecycleHooks>({
511
+ strategy,
512
+ hookName,
513
+ parameters,
514
+ plugin,
515
+ }: {
516
+ strategy: Strategy
517
+ hookName: H
518
+ parameters: PluginParameter<H>
519
+ plugin: KubbPluginWithLifeCycle
520
+ }): ReturnType<ParseResult<H>> | null {
521
+ const hook = plugin[hookName]
522
+ let output: unknown
523
+
524
+ if (!hook) {
525
+ return null
526
+ }
527
+
528
+ this.eventEmitter.emit('execute', { strategy, hookName, parameters, plugin })
529
+
530
+ try {
531
+ if (typeof hook === 'function') {
532
+ const fn = (hook as Function).apply({ ...this.#core.api, plugin }, parameters) as ReturnType<ParseResult<H>>
533
+
534
+ output = fn
535
+ return fn
536
+ }
537
+
538
+ output = hook
539
+
540
+ this.#addExecutedToCallStack({
541
+ parameters,
542
+ output,
543
+ strategy,
544
+ hookName,
545
+ plugin,
546
+ })
547
+
548
+ return hook
549
+ } catch (e) {
550
+ this.#catcher<H>(e as Error, plugin, hookName)
551
+
552
+ return null
553
+ }
554
+ }
555
+
556
+ #catcher<H extends PluginLifecycleHooks>(e: Error, plugin?: KubbPlugin, hookName?: H) {
557
+ const text = `${e.message} (plugin: ${plugin?.name || 'unknown'}, hook: ${hookName || 'unknown'})\n`
558
+
559
+ this.logger.error(text)
560
+ this.eventEmitter.emit('error', e)
561
+ }
562
+
563
+ #parse<TPlugin extends KubbUserPluginWithLifeCycle>(
564
+ plugin: TPlugin,
565
+ pluginManager: PluginManager,
566
+ context: CorePluginOptions['api'] | undefined,
567
+ ): KubbPlugin<GetPluginFactoryOptions<TPlugin>> {
568
+ const usedPluginNames = pluginManager.#usedPluginNames
569
+
570
+ setUniqueName(plugin.name, usedPluginNames)
571
+
572
+ const key = plugin.key || ([plugin.kind, plugin.name, usedPluginNames[plugin.name]].filter(Boolean) as [typeof plugin.kind, typeof plugin.name, string])
573
+
574
+ if (plugin.name !== 'core' && usedPluginNames[plugin.name]! >= 2) {
575
+ pluginManager.logger.warn('Using multiple of the same plugin is an experimental feature')
576
+ }
577
+
578
+ // default transform
579
+ if (!plugin.transform) {
580
+ plugin.transform = function transform(code) {
581
+ return code
582
+ }
583
+ }
584
+
585
+ if (plugin.api && typeof plugin.api === 'function') {
586
+ const api = (plugin.api as Function).call(context) as typeof plugin.api
587
+
588
+ return {
589
+ ...plugin,
590
+ key,
591
+ api,
592
+ } as unknown as KubbPlugin<GetPluginFactoryOptions<TPlugin>>
593
+ }
594
+
595
+ return {
596
+ ...plugin,
597
+ key,
598
+ } as unknown as KubbPlugin<GetPluginFactoryOptions<TPlugin>>
599
+ }
600
+
601
+ static getDependedPlugins<
602
+ T1 extends PluginFactoryOptions,
603
+ T2 extends PluginFactoryOptions = never,
604
+ T3 extends PluginFactoryOptions = never,
605
+ TOutput = T3 extends never ? T2 extends never ? [T1: KubbPlugin<T1>]
606
+ : [T1: KubbPlugin<T1>, T2: KubbPlugin<T2>]
607
+ : [T1: KubbPlugin<T1>, T2: KubbPlugin<T2>, T3: KubbPlugin<T3>],
608
+ >(plugins: Array<KubbPlugin>, dependedPluginNames: string | string[]): TOutput {
609
+ let pluginNames: string[] = []
610
+ if (typeof dependedPluginNames === 'string') {
611
+ pluginNames = [dependedPluginNames]
612
+ } else {
613
+ pluginNames = dependedPluginNames
614
+ }
615
+
616
+ return pluginNames.map((pluginName) => {
617
+ const plugin = plugins.find((plugin) => plugin.name === pluginName)
618
+ if (!plugin) {
619
+ throw new ValidationPluginError(`This plugin depends on the ${pluginName} plugin.`)
620
+ }
621
+ return plugin
622
+ }) as TOutput
623
+ }
624
+
625
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
626
+ static get hooks() {
627
+ return ['validate', 'buildStart', 'resolvePath', 'resolveName', 'load', 'transform', 'writeFile', 'buildEnd'] as const
628
+ }
629
+ }
@@ -0,0 +1,51 @@
1
+ import { hookFirst, hookParallel, hookSeq } from './utils/executeStrategies.ts'
2
+
3
+ import type { PossiblePromise } from '@kubb/types'
4
+ import type { Strategy, StrategySwitch } from './utils/executeStrategies.ts'
5
+
6
+ type PromiseFunc<T = unknown, T2 = never> = () => T2 extends never ? Promise<T> : Promise<T> | T2
7
+
8
+ type Options<TState = any> = {
9
+ nullCheck?: (state: TState) => boolean
10
+ }
11
+
12
+ export class PromiseManager<TState = any> {
13
+ #options: Options<TState> = {}
14
+
15
+ constructor(options: Options<TState> = {}) {
16
+ this.#options = options
17
+
18
+ return this
19
+ }
20
+
21
+ run<TInput extends Array<PromiseFunc<TValue, null>>, TValue, TStrategy extends Strategy, TOutput = StrategySwitch<TStrategy, TInput, TValue>>(
22
+ strategy: TStrategy,
23
+ promises: TInput,
24
+ ): TOutput {
25
+ if (strategy === 'seq') {
26
+ return hookSeq<TInput, TValue, TOutput>(promises)
27
+ }
28
+
29
+ if (strategy === 'first') {
30
+ return hookFirst<TInput, TValue, TOutput>(promises, this.#options.nullCheck)
31
+ }
32
+
33
+ if (strategy === 'parallel') {
34
+ return hookParallel<TInput, TValue, TOutput>(promises)
35
+ }
36
+
37
+ throw new Error(`${strategy} not implemented`)
38
+ }
39
+ }
40
+
41
+ export function isPromise<T>(result: PossiblePromise<T>): result is Promise<T> {
42
+ return !!result && typeof (result as Promise<unknown>)?.then === 'function'
43
+ }
44
+
45
+ export function isPromiseFulfilledResult<T = unknown>(result: PromiseSettledResult<unknown>): result is PromiseFulfilledResult<T> {
46
+ return result.status === 'fulfilled'
47
+ }
48
+
49
+ export function isPromiseRejectedResult<T>(result: PromiseSettledResult<unknown>): result is Omit<PromiseRejectedResult, 'reason'> & { reason: T } {
50
+ return result.status === 'rejected'
51
+ }
@@ -0,0 +1,8 @@
1
+ import { Generator } from './Generator.ts'
2
+
3
+ /**
4
+ * Abstract class that contains the building blocks for plugins to create their own SchemaGenerator
5
+ */
6
+ export abstract class SchemaGenerator<TOptions extends object, TInput, TOutput> extends Generator<TOptions> {
7
+ abstract build(schema: TInput, name: string, description?: string): TOutput
8
+ }