@kubb/core 5.0.0-alpha.3 → 5.0.0-alpha.30
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/PluginDriver-D110FoJ-.d.ts +1632 -0
- package/dist/hooks.cjs +12 -27
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +11 -36
- package/dist/hooks.js +13 -27
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +1410 -823
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +597 -95
- package/dist/index.js +1391 -818
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/Kubb.ts +40 -58
- package/src/{PluginManager.ts → PluginDriver.ts} +165 -177
- package/src/build.ts +167 -44
- package/src/config.ts +9 -8
- package/src/constants.ts +40 -7
- package/src/createAdapter.ts +25 -0
- package/src/createPlugin.ts +30 -0
- package/src/createStorage.ts +58 -0
- package/src/defineGenerator.ts +126 -0
- package/src/defineLogger.ts +13 -3
- package/src/definePresets.ts +16 -0
- package/src/defineResolver.ts +457 -0
- package/src/hooks/index.ts +1 -6
- package/src/hooks/useDriver.ts +11 -0
- package/src/hooks/useMode.ts +5 -5
- package/src/hooks/usePlugin.ts +3 -3
- package/src/index.ts +18 -7
- package/src/renderNode.tsx +25 -0
- package/src/storages/fsStorage.ts +2 -2
- package/src/storages/memoryStorage.ts +2 -2
- package/src/types.ts +589 -52
- package/src/utils/FunctionParams.ts +2 -2
- package/src/utils/TreeNode.ts +45 -7
- package/src/utils/diagnostics.ts +4 -1
- package/src/utils/executeStrategies.ts +29 -10
- package/src/utils/formatters.ts +10 -21
- package/src/utils/getBarrelFiles.ts +83 -10
- package/src/utils/getConfigs.ts +8 -22
- package/src/utils/getPreset.ts +78 -0
- package/src/utils/linters.ts +23 -3
- package/src/utils/packageJSON.ts +76 -0
- package/dist/types-CiPWLv-5.d.ts +0 -1001
- 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/useKubb.ts +0 -22
- package/src/hooks/usePluginManager.ts +0 -11
- package/src/utils/getPlugins.ts +0 -23
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { basename, extname, resolve } from 'node:path'
|
|
2
2
|
import { performance } from 'node:perf_hooks'
|
|
3
3
|
import type { AsyncEventEmitter } from '@internals/utils'
|
|
4
|
-
import {
|
|
4
|
+
import { isPromiseRejectedResult, transformReservedWord } from '@internals/utils'
|
|
5
5
|
import type { RootNode } from '@kubb/ast/types'
|
|
6
|
-
import type {
|
|
7
|
-
import
|
|
8
|
-
import { CORE_PLUGIN_NAME, DEFAULT_STUDIO_URL } from './constants.ts'
|
|
6
|
+
import type { FabricFile, Fabric as FabricType } from '@kubb/fabric-core/types'
|
|
7
|
+
import { DEFAULT_STUDIO_URL } from './constants.ts'
|
|
9
8
|
import { openInStudio as openInStudioFn } from './devtools.ts'
|
|
10
|
-
|
|
11
|
-
import { isPromiseRejectedResult, PromiseManager } from './PromiseManager.ts'
|
|
9
|
+
|
|
12
10
|
import type {
|
|
13
11
|
Adapter,
|
|
14
12
|
Config,
|
|
@@ -23,11 +21,19 @@ import type {
|
|
|
23
21
|
PluginWithLifeCycle,
|
|
24
22
|
ResolveNameParams,
|
|
25
23
|
ResolvePathParams,
|
|
26
|
-
UserPlugin,
|
|
27
24
|
} from './types.ts'
|
|
25
|
+
import { hookFirst, hookParallel, hookSeq } from './utils/executeStrategies.ts'
|
|
28
26
|
|
|
29
27
|
type RequiredPluginLifecycle = Required<PluginLifecycle>
|
|
30
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Hook dispatch strategy used by the `PluginDriver`.
|
|
31
|
+
*
|
|
32
|
+
* - `hookFirst` — stops at the first non-null result.
|
|
33
|
+
* - `hookForPlugin` — calls only the matching plugin.
|
|
34
|
+
* - `hookParallel` — calls all plugins concurrently.
|
|
35
|
+
* - `hookSeq` — calls all plugins in order, threading the result.
|
|
36
|
+
*/
|
|
31
37
|
export type Strategy = 'hookFirst' | 'hookForPlugin' | 'hookParallel' | 'hookSeq'
|
|
32
38
|
|
|
33
39
|
type ParseResult<H extends PluginLifecycleHooks> = RequiredPluginLifecycle[H]
|
|
@@ -40,7 +46,7 @@ type SafeParseResult<H extends PluginLifecycleHooks, Result = ReturnType<ParseRe
|
|
|
40
46
|
// inspired by: https://github.com/rollup/rollup/blob/master/src/utils/PluginDriver.ts#
|
|
41
47
|
|
|
42
48
|
type Options = {
|
|
43
|
-
fabric:
|
|
49
|
+
fabric: FabricType
|
|
44
50
|
events: AsyncEventEmitter<KubbEvents>
|
|
45
51
|
/**
|
|
46
52
|
* @default Number.POSITIVE_INFINITY
|
|
@@ -48,22 +54,36 @@ type Options = {
|
|
|
48
54
|
concurrency?: number
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Parameters accepted by `PluginDriver.getFile` to resolve a generated file descriptor.
|
|
59
|
+
*/
|
|
60
|
+
export type GetFileOptions<TOptions = object> = {
|
|
52
61
|
name: string
|
|
53
|
-
mode?:
|
|
54
|
-
extname:
|
|
62
|
+
mode?: FabricFile.Mode
|
|
63
|
+
extname: FabricFile.Extname
|
|
55
64
|
pluginName: string
|
|
56
65
|
options?: TOptions
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Returns `'single'` when `fileOrFolder` has a file extension, `'split'` otherwise.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* getMode('src/gen/types.ts') // 'single'
|
|
74
|
+
* getMode('src/gen/types') // 'split'
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function getMode(fileOrFolder: string | undefined | null): FabricFile.Mode {
|
|
60
78
|
if (!fileOrFolder) {
|
|
61
79
|
return 'split'
|
|
62
80
|
}
|
|
63
81
|
return extname(fileOrFolder) ? 'single' : 'split'
|
|
64
82
|
}
|
|
65
83
|
|
|
66
|
-
|
|
84
|
+
const hookFirstNullCheck = (state: unknown) => !!(state as SafeParseResult<'resolveName'> | null)?.result
|
|
85
|
+
|
|
86
|
+
export class PluginDriver {
|
|
67
87
|
readonly config: Config
|
|
68
88
|
readonly options: Options
|
|
69
89
|
|
|
@@ -75,21 +95,27 @@ export class PluginManager {
|
|
|
75
95
|
adapter: Adapter | undefined = undefined
|
|
76
96
|
#studioIsOpen = false
|
|
77
97
|
|
|
78
|
-
readonly
|
|
79
|
-
readonly #usedPluginNames: Record<string, number> = {}
|
|
80
|
-
readonly #promiseManager
|
|
98
|
+
readonly plugins = new Map<string, Plugin>()
|
|
81
99
|
|
|
82
100
|
constructor(config: Config, options: Options) {
|
|
83
101
|
this.config = config
|
|
84
102
|
this.options = options
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
103
|
+
config.plugins
|
|
104
|
+
.map((plugin) => Object.assign({ buildStart() {}, buildEnd() {} }, plugin) as unknown as Plugin)
|
|
105
|
+
.filter((plugin) => {
|
|
106
|
+
if (typeof plugin.apply === 'function') {
|
|
107
|
+
return plugin.apply(config)
|
|
108
|
+
}
|
|
109
|
+
return true
|
|
110
|
+
})
|
|
111
|
+
.sort((a, b) => {
|
|
112
|
+
if (b.pre?.includes(a.name)) return 1
|
|
113
|
+
if (b.post?.includes(a.name)) return -1
|
|
114
|
+
return 0
|
|
115
|
+
})
|
|
116
|
+
.forEach((plugin) => {
|
|
117
|
+
this.plugins.set(plugin.name, plugin)
|
|
118
|
+
})
|
|
93
119
|
}
|
|
94
120
|
|
|
95
121
|
get events() {
|
|
@@ -97,56 +123,75 @@ export class PluginManager {
|
|
|
97
123
|
}
|
|
98
124
|
|
|
99
125
|
getContext<TOptions extends PluginFactoryOptions>(plugin: Plugin<TOptions>): PluginContext<TOptions> & Record<string, unknown> {
|
|
100
|
-
const
|
|
101
|
-
const pluginManager = this
|
|
126
|
+
const driver = this
|
|
102
127
|
|
|
103
128
|
const baseContext = {
|
|
104
|
-
fabric:
|
|
105
|
-
config:
|
|
129
|
+
fabric: driver.options.fabric,
|
|
130
|
+
config: driver.config,
|
|
131
|
+
get root(): string {
|
|
132
|
+
return resolve(driver.config.root, driver.config.output.path)
|
|
133
|
+
},
|
|
134
|
+
getMode(output: { path: string }): FabricFile.Mode {
|
|
135
|
+
return getMode(resolve(driver.config.root, driver.config.output.path, output.path))
|
|
136
|
+
},
|
|
137
|
+
events: driver.options.events,
|
|
106
138
|
plugin,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
addFile: async (...files: Array<
|
|
139
|
+
getPlugin: driver.getPlugin.bind(driver),
|
|
140
|
+
requirePlugin: driver.requirePlugin.bind(driver),
|
|
141
|
+
driver: driver,
|
|
142
|
+
addFile: async (...files: Array<FabricFile.File>) => {
|
|
111
143
|
await this.options.fabric.addFile(...files)
|
|
112
144
|
},
|
|
113
|
-
upsertFile: async (...files: Array<
|
|
145
|
+
upsertFile: async (...files: Array<FabricFile.File>) => {
|
|
114
146
|
await this.options.fabric.upsertFile(...files)
|
|
115
147
|
},
|
|
116
148
|
get rootNode(): RootNode | undefined {
|
|
117
|
-
return
|
|
149
|
+
return driver.rootNode
|
|
118
150
|
},
|
|
119
151
|
get adapter(): Adapter | undefined {
|
|
120
|
-
return
|
|
152
|
+
return driver.adapter
|
|
153
|
+
},
|
|
154
|
+
get resolver() {
|
|
155
|
+
return plugin.resolver
|
|
156
|
+
},
|
|
157
|
+
get transformer() {
|
|
158
|
+
return plugin.transformer
|
|
159
|
+
},
|
|
160
|
+
warn(message: string) {
|
|
161
|
+
driver.events.emit('warn', message)
|
|
162
|
+
},
|
|
163
|
+
error(error: string | Error) {
|
|
164
|
+
driver.events.emit('error', typeof error === 'string' ? new Error(error) : error)
|
|
165
|
+
},
|
|
166
|
+
info(message: string) {
|
|
167
|
+
driver.events.emit('info', message)
|
|
121
168
|
},
|
|
122
169
|
openInStudio(options?: DevtoolsOptions) {
|
|
123
|
-
if (!
|
|
170
|
+
if (!driver.config.devtools || driver.#studioIsOpen) {
|
|
124
171
|
return
|
|
125
172
|
}
|
|
126
173
|
|
|
127
|
-
if (typeof
|
|
174
|
+
if (typeof driver.config.devtools !== 'object') {
|
|
128
175
|
throw new Error('Devtools must be an object')
|
|
129
176
|
}
|
|
130
177
|
|
|
131
|
-
if (!
|
|
178
|
+
if (!driver.rootNode || !driver.adapter) {
|
|
132
179
|
throw new Error('adapter is not defined, make sure you have set the parser in kubb.config.ts')
|
|
133
180
|
}
|
|
134
181
|
|
|
135
|
-
|
|
182
|
+
driver.#studioIsOpen = true
|
|
136
183
|
|
|
137
|
-
const studioUrl =
|
|
184
|
+
const studioUrl = driver.config.devtools?.studioUrl ?? DEFAULT_STUDIO_URL
|
|
138
185
|
|
|
139
|
-
return openInStudioFn(
|
|
186
|
+
return openInStudioFn(driver.rootNode, studioUrl, options)
|
|
140
187
|
},
|
|
141
188
|
} as unknown as PluginContext<TOptions>
|
|
142
189
|
|
|
143
190
|
const mergedExtras: Record<string, unknown> = {}
|
|
144
|
-
|
|
191
|
+
|
|
192
|
+
for (const p of this.plugins.values()) {
|
|
145
193
|
if (typeof p.inject === 'function') {
|
|
146
|
-
const result = (p.inject as (this: PluginContext
|
|
147
|
-
baseContext as unknown as PluginContext,
|
|
148
|
-
baseContext as unknown as PluginContext,
|
|
149
|
-
)
|
|
194
|
+
const result = (p.inject as (this: PluginContext) => unknown).call(baseContext as unknown as PluginContext)
|
|
150
195
|
if (result !== null && typeof result === 'object') {
|
|
151
196
|
Object.assign(mergedExtras, result)
|
|
152
197
|
}
|
|
@@ -158,12 +203,10 @@ export class PluginManager {
|
|
|
158
203
|
...mergedExtras,
|
|
159
204
|
}
|
|
160
205
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
getFile<TOptions = object>({ name, mode, extname, pluginName, options }: GetFileProps<TOptions>): KubbFile.File<{ pluginName: string }> {
|
|
206
|
+
/**
|
|
207
|
+
* @deprecated use resolvers context instead
|
|
208
|
+
*/
|
|
209
|
+
getFile<TOptions = object>({ name, mode, extname, pluginName, options }: GetFileOptions<TOptions>): FabricFile.File<{ pluginName: string }> {
|
|
167
210
|
const resolvedName = mode ? (mode === 'single' ? '' : this.resolveName({ name, pluginName, type: 'file' })) : name
|
|
168
211
|
|
|
169
212
|
const path = this.resolvePath({
|
|
@@ -179,7 +222,7 @@ export class PluginManager {
|
|
|
179
222
|
|
|
180
223
|
return {
|
|
181
224
|
path,
|
|
182
|
-
baseName: basename(path) as
|
|
225
|
+
baseName: basename(path) as FabricFile.File['baseName'],
|
|
183
226
|
meta: {
|
|
184
227
|
pluginName,
|
|
185
228
|
},
|
|
@@ -189,7 +232,10 @@ export class PluginManager {
|
|
|
189
232
|
}
|
|
190
233
|
}
|
|
191
234
|
|
|
192
|
-
|
|
235
|
+
/**
|
|
236
|
+
* @deprecated use resolvers context instead
|
|
237
|
+
*/
|
|
238
|
+
resolvePath = <TOptions = object>(params: ResolvePathParams<TOptions>): FabricFile.Path => {
|
|
193
239
|
const root = resolve(this.config.root, this.config.output.path)
|
|
194
240
|
const defaultPath = resolve(root, params.baseName)
|
|
195
241
|
|
|
@@ -210,7 +256,9 @@ export class PluginManager {
|
|
|
210
256
|
|
|
211
257
|
return firstResult?.result || defaultPath
|
|
212
258
|
}
|
|
213
|
-
|
|
259
|
+
/**
|
|
260
|
+
* @deprecated use resolvers context instead
|
|
261
|
+
*/
|
|
214
262
|
resolveName = (params: ResolveNameParams): string => {
|
|
215
263
|
if (params.pluginName) {
|
|
216
264
|
const names = this.hookForPluginSync({
|
|
@@ -219,9 +267,7 @@ export class PluginManager {
|
|
|
219
267
|
parameters: [params.name.trim(), params.type],
|
|
220
268
|
})
|
|
221
269
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return transformReservedWord([...uniqueNames].at(0) || params.name)
|
|
270
|
+
return transformReservedWord(names?.at(0) ?? params.name)
|
|
225
271
|
}
|
|
226
272
|
|
|
227
273
|
const name = this.hookFirstSync({
|
|
@@ -244,36 +290,32 @@ export class PluginManager {
|
|
|
244
290
|
hookName: H
|
|
245
291
|
parameters: PluginParameter<H>
|
|
246
292
|
}): Promise<Array<ReturnType<ParseResult<H>> | null>> {
|
|
247
|
-
const
|
|
293
|
+
const plugin = this.plugins.get(pluginName)
|
|
294
|
+
|
|
295
|
+
if (!plugin) {
|
|
296
|
+
return [null]
|
|
297
|
+
}
|
|
248
298
|
|
|
249
299
|
this.events.emit('plugins:hook:progress:start', {
|
|
250
300
|
hookName,
|
|
251
|
-
plugins,
|
|
301
|
+
plugins: [plugin],
|
|
252
302
|
})
|
|
253
303
|
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
parameters,
|
|
261
|
-
plugin,
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
if (result !== undefined && result !== null) {
|
|
265
|
-
items.push(result)
|
|
266
|
-
}
|
|
267
|
-
}
|
|
304
|
+
const result = await this.#execute<H>({
|
|
305
|
+
strategy: 'hookFirst',
|
|
306
|
+
hookName,
|
|
307
|
+
parameters,
|
|
308
|
+
plugin,
|
|
309
|
+
})
|
|
268
310
|
|
|
269
311
|
this.events.emit('plugins:hook:progress:end', { hookName })
|
|
270
312
|
|
|
271
|
-
return
|
|
313
|
+
return [result]
|
|
272
314
|
}
|
|
315
|
+
|
|
273
316
|
/**
|
|
274
317
|
* Run a specific hookName for plugin x.
|
|
275
318
|
*/
|
|
276
|
-
|
|
277
319
|
hookForPluginSync<H extends PluginLifecycleHooks>({
|
|
278
320
|
pluginName,
|
|
279
321
|
hookName,
|
|
@@ -283,20 +325,20 @@ export class PluginManager {
|
|
|
283
325
|
hookName: H
|
|
284
326
|
parameters: PluginParameter<H>
|
|
285
327
|
}): Array<ReturnType<ParseResult<H>>> | null {
|
|
286
|
-
const
|
|
328
|
+
const plugin = this.plugins.get(pluginName)
|
|
287
329
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
strategy: 'hookFirst',
|
|
292
|
-
hookName,
|
|
293
|
-
parameters,
|
|
294
|
-
plugin,
|
|
295
|
-
})
|
|
296
|
-
})
|
|
297
|
-
.filter((x): x is NonNullable<typeof x> => x !== null)
|
|
330
|
+
if (!plugin) {
|
|
331
|
+
return null
|
|
332
|
+
}
|
|
298
333
|
|
|
299
|
-
|
|
334
|
+
const result = this.#executeSync<H>({
|
|
335
|
+
strategy: 'hookFirst',
|
|
336
|
+
hookName,
|
|
337
|
+
parameters,
|
|
338
|
+
plugin,
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
return result !== null ? [result] : []
|
|
300
342
|
}
|
|
301
343
|
|
|
302
344
|
/**
|
|
@@ -311,9 +353,10 @@ export class PluginManager {
|
|
|
311
353
|
parameters: PluginParameter<H>
|
|
312
354
|
skipped?: ReadonlySet<Plugin> | null
|
|
313
355
|
}): Promise<SafeParseResult<H>> {
|
|
314
|
-
const plugins =
|
|
315
|
-
|
|
316
|
-
|
|
356
|
+
const plugins: Array<Plugin> = []
|
|
357
|
+
for (const plugin of this.plugins.values()) {
|
|
358
|
+
if (hookName in plugin && (skipped ? !skipped.has(plugin) : true)) plugins.push(plugin)
|
|
359
|
+
}
|
|
317
360
|
|
|
318
361
|
this.events.emit('plugins:hook:progress:start', { hookName, plugins })
|
|
319
362
|
|
|
@@ -333,7 +376,7 @@ export class PluginManager {
|
|
|
333
376
|
}
|
|
334
377
|
})
|
|
335
378
|
|
|
336
|
-
const result = await
|
|
379
|
+
const result = await hookFirst(promises, hookFirstNullCheck)
|
|
337
380
|
|
|
338
381
|
this.events.emit('plugins:hook:progress:end', { hookName })
|
|
339
382
|
|
|
@@ -353,11 +396,11 @@ export class PluginManager {
|
|
|
353
396
|
skipped?: ReadonlySet<Plugin> | null
|
|
354
397
|
}): SafeParseResult<H> | null {
|
|
355
398
|
let parseResult: SafeParseResult<H> | null = null
|
|
356
|
-
const plugins = this.#getSortedPlugins(hookName).filter((plugin) => {
|
|
357
|
-
return skipped ? !skipped.has(plugin) : true
|
|
358
|
-
})
|
|
359
399
|
|
|
360
|
-
for (const plugin of plugins) {
|
|
400
|
+
for (const plugin of this.plugins.values()) {
|
|
401
|
+
if (!(hookName in plugin)) continue
|
|
402
|
+
if (skipped?.has(plugin)) continue
|
|
403
|
+
|
|
361
404
|
parseResult = {
|
|
362
405
|
result: this.#executeSync<H>({
|
|
363
406
|
strategy: 'hookFirst',
|
|
@@ -368,9 +411,7 @@ export class PluginManager {
|
|
|
368
411
|
plugin,
|
|
369
412
|
} as SafeParseResult<H>
|
|
370
413
|
|
|
371
|
-
if (parseResult
|
|
372
|
-
break
|
|
373
|
-
}
|
|
414
|
+
if (parseResult.result != null) break
|
|
374
415
|
}
|
|
375
416
|
|
|
376
417
|
return parseResult
|
|
@@ -386,7 +427,10 @@ export class PluginManager {
|
|
|
386
427
|
hookName: H
|
|
387
428
|
parameters?: Parameters<RequiredPluginLifecycle[H]> | undefined
|
|
388
429
|
}): Promise<Awaited<TOutput>[]> {
|
|
389
|
-
const plugins =
|
|
430
|
+
const plugins: Array<Plugin> = []
|
|
431
|
+
for (const plugin of this.plugins.values()) {
|
|
432
|
+
if (hookName in plugin) plugins.push(plugin)
|
|
433
|
+
}
|
|
390
434
|
this.events.emit('plugins:hook:progress:start', { hookName, plugins })
|
|
391
435
|
|
|
392
436
|
const pluginStartTimes = new Map<Plugin, number>()
|
|
@@ -403,13 +447,11 @@ export class PluginManager {
|
|
|
403
447
|
}
|
|
404
448
|
})
|
|
405
449
|
|
|
406
|
-
const results = await
|
|
407
|
-
concurrency: this.options.concurrency,
|
|
408
|
-
})
|
|
450
|
+
const results = await hookParallel(promises, this.options.concurrency)
|
|
409
451
|
|
|
410
452
|
results.forEach((result, index) => {
|
|
411
453
|
if (isPromiseRejectedResult<Error>(result)) {
|
|
412
|
-
const plugin =
|
|
454
|
+
const plugin = plugins[index]
|
|
413
455
|
|
|
414
456
|
if (plugin) {
|
|
415
457
|
const startTime = pluginStartTimes.get(plugin) ?? performance.now()
|
|
@@ -438,7 +480,10 @@ export class PluginManager {
|
|
|
438
480
|
* Chains plugins
|
|
439
481
|
*/
|
|
440
482
|
async hookSeq<H extends PluginLifecycleHooks>({ hookName, parameters }: { hookName: H; parameters?: PluginParameter<H> }): Promise<void> {
|
|
441
|
-
const plugins =
|
|
483
|
+
const plugins: Array<Plugin> = []
|
|
484
|
+
for (const plugin of this.plugins.values()) {
|
|
485
|
+
if (hookName in plugin) plugins.push(plugin)
|
|
486
|
+
}
|
|
442
487
|
this.events.emit('plugins:hook:progress:start', { hookName, plugins })
|
|
443
488
|
|
|
444
489
|
const promises = plugins.map((plugin) => {
|
|
@@ -451,67 +496,28 @@ export class PluginManager {
|
|
|
451
496
|
})
|
|
452
497
|
})
|
|
453
498
|
|
|
454
|
-
await
|
|
499
|
+
await hookSeq(promises)
|
|
455
500
|
|
|
456
501
|
this.events.emit('plugins:hook:progress:end', { hookName })
|
|
457
502
|
}
|
|
458
503
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
return plugins.filter((plugin) => hookName in plugin)
|
|
464
|
-
}
|
|
465
|
-
// TODO add test case for sorting with pre/post
|
|
466
|
-
|
|
467
|
-
return plugins
|
|
468
|
-
.map((plugin) => {
|
|
469
|
-
if (plugin.pre) {
|
|
470
|
-
let missingPlugins = plugin.pre.filter((pluginName) => !plugins.find((pluginToFind) => pluginToFind.name === pluginName))
|
|
471
|
-
|
|
472
|
-
// when adapter is set, we can ignore the depends on plugin-oas, in v5 this will not be needed anymore
|
|
473
|
-
if (missingPlugins.includes('plugin-oas') && this.adapter) {
|
|
474
|
-
missingPlugins = missingPlugins.filter((pluginName) => pluginName !== 'plugin-oas')
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
if (missingPlugins.length > 0) {
|
|
478
|
-
throw new ValidationPluginError(`The plugin '${plugin.name}' has a pre set that references missing plugins for '${missingPlugins.join(', ')}'`)
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
return plugin
|
|
483
|
-
})
|
|
484
|
-
.sort((a, b) => {
|
|
485
|
-
if (b.pre?.includes(a.name)) {
|
|
486
|
-
return 1
|
|
487
|
-
}
|
|
488
|
-
if (b.post?.includes(a.name)) {
|
|
489
|
-
return -1
|
|
490
|
-
}
|
|
491
|
-
return 0
|
|
492
|
-
})
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
getPluginByName(pluginName: string): Plugin | undefined {
|
|
496
|
-
const plugins = [...this.#plugins]
|
|
497
|
-
|
|
498
|
-
return plugins.find((item) => item.name === pluginName)
|
|
504
|
+
getPlugin<TName extends keyof Kubb.PluginRegistry>(pluginName: TName): Plugin<Kubb.PluginRegistry[TName]> | undefined
|
|
505
|
+
getPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(pluginName: string): Plugin<TOptions> | undefined
|
|
506
|
+
getPlugin(pluginName: string): Plugin | undefined {
|
|
507
|
+
return this.plugins.get(pluginName) as Plugin | undefined
|
|
499
508
|
}
|
|
500
509
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
return corePlugin ? [corePlugin] : []
|
|
510
|
+
/**
|
|
511
|
+
* Like `getPlugin` but throws a descriptive error when the plugin is not found.
|
|
512
|
+
*/
|
|
513
|
+
requirePlugin<TName extends keyof Kubb.PluginRegistry>(pluginName: TName): Plugin<Kubb.PluginRegistry[TName]>
|
|
514
|
+
requirePlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(pluginName: string): Plugin<TOptions>
|
|
515
|
+
requirePlugin(pluginName: string): Plugin {
|
|
516
|
+
const plugin = this.plugins.get(pluginName)
|
|
517
|
+
if (!plugin) {
|
|
518
|
+
throw new Error(`[kubb] Plugin "${pluginName}" is required but not found. Make sure it is included in your Kubb config.`)
|
|
512
519
|
}
|
|
513
|
-
|
|
514
|
-
return pluginByPluginName
|
|
520
|
+
return plugin
|
|
515
521
|
}
|
|
516
522
|
|
|
517
523
|
/**
|
|
@@ -647,22 +653,4 @@ export class PluginManager {
|
|
|
647
653
|
return null
|
|
648
654
|
}
|
|
649
655
|
}
|
|
650
|
-
|
|
651
|
-
#parse(plugin: UserPlugin): Plugin {
|
|
652
|
-
const usedPluginNames = this.#usedPluginNames
|
|
653
|
-
|
|
654
|
-
setUniqueName(plugin.name, usedPluginNames)
|
|
655
|
-
|
|
656
|
-
const usageCount = usedPluginNames[plugin.name]
|
|
657
|
-
if (usageCount && usageCount > 1) {
|
|
658
|
-
throw new ValidationPluginError(
|
|
659
|
-
`Duplicate plugin "${plugin.name}" detected. Each plugin can only be used once. Use a different configuration instead of adding multiple instances of the same plugin.`,
|
|
660
|
-
)
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
return {
|
|
664
|
-
install() {},
|
|
665
|
-
...plugin,
|
|
666
|
-
} as unknown as Plugin
|
|
667
|
-
}
|
|
668
656
|
}
|