@kubb/core 5.0.0-alpha.5 → 5.0.0-alpha.50
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/README.md +3 -2
- package/dist/PluginDriver-DtwggkXg.cjs +1082 -0
- package/dist/PluginDriver-DtwggkXg.cjs.map +1 -0
- package/dist/PluginDriver-mXeqWp-U.js +979 -0
- package/dist/PluginDriver-mXeqWp-U.js.map +1 -0
- package/dist/index.cjs +1013 -1829
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +274 -265
- package/dist/index.js +1003 -1799
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +138 -0
- package/dist/mocks.cjs.map +1 -0
- package/dist/mocks.d.ts +74 -0
- package/dist/mocks.js +133 -0
- package/dist/mocks.js.map +1 -0
- package/dist/types-DfEv9d_c.d.ts +1721 -0
- package/package.json +51 -57
- package/src/FileManager.ts +133 -0
- package/src/FileProcessor.ts +86 -0
- package/src/Kubb.ts +154 -101
- package/src/PluginDriver.ts +418 -0
- package/src/constants.ts +43 -47
- package/src/createAdapter.ts +25 -0
- package/src/createKubb.ts +614 -0
- package/src/createRenderer.ts +57 -0
- package/src/createStorage.ts +58 -0
- package/src/defineGenerator.ts +88 -100
- package/src/defineLogger.ts +13 -3
- package/src/defineParser.ts +45 -0
- package/src/definePlugin.ts +68 -7
- package/src/defineResolver.ts +501 -0
- package/src/devtools.ts +14 -14
- package/src/index.ts +12 -17
- package/src/mocks.ts +171 -0
- package/src/renderNode.ts +35 -0
- package/src/storages/fsStorage.ts +40 -11
- package/src/storages/memoryStorage.ts +4 -3
- package/src/types.ts +575 -205
- package/src/utils/TreeNode.ts +47 -9
- package/src/utils/diagnostics.ts +4 -1
- package/src/utils/getBarrelFiles.ts +94 -16
- package/src/utils/isInputPath.ts +10 -0
- package/src/utils/packageJSON.ts +99 -0
- package/dist/PluginManager-vZodFEMe.d.ts +0 -1056
- package/dist/chunk-ByKO4r7w.cjs +0 -38
- package/dist/hooks.cjs +0 -60
- package/dist/hooks.cjs.map +0 -1
- package/dist/hooks.d.ts +0 -56
- package/dist/hooks.js +0 -56
- package/dist/hooks.js.map +0 -1
- package/src/BarrelManager.ts +0 -74
- package/src/PackageManager.ts +0 -180
- package/src/PluginManager.ts +0 -667
- package/src/PromiseManager.ts +0 -40
- package/src/build.ts +0 -419
- package/src/config.ts +0 -56
- package/src/defineAdapter.ts +0 -22
- package/src/defineStorage.ts +0 -56
- package/src/errors.ts +0 -1
- package/src/hooks/index.ts +0 -4
- package/src/hooks/useKubb.ts +0 -46
- package/src/hooks/useMode.ts +0 -11
- package/src/hooks/usePlugin.ts +0 -11
- package/src/hooks/usePluginManager.ts +0 -11
- package/src/utils/FunctionParams.ts +0 -155
- package/src/utils/executeStrategies.ts +0 -81
- package/src/utils/formatters.ts +0 -56
- package/src/utils/getConfigs.ts +0 -30
- package/src/utils/getPlugins.ts +0 -23
- package/src/utils/linters.ts +0 -25
- package/src/utils/resolveOptions.ts +0 -93
package/src/PluginManager.ts
DELETED
|
@@ -1,667 +0,0 @@
|
|
|
1
|
-
import { basename, extname, resolve } from 'node:path'
|
|
2
|
-
import { performance } from 'node:perf_hooks'
|
|
3
|
-
import type { AsyncEventEmitter } from '@internals/utils'
|
|
4
|
-
import { setUniqueName, transformReservedWord } from '@internals/utils'
|
|
5
|
-
import type { RootNode } from '@kubb/ast/types'
|
|
6
|
-
import type { Fabric as FabricType, KubbFile } from '@kubb/fabric-core/types'
|
|
7
|
-
import { CORE_PLUGIN_NAME, DEFAULT_STUDIO_URL } from './constants.ts'
|
|
8
|
-
import { openInStudio as openInStudioFn } from './devtools.ts'
|
|
9
|
-
import { ValidationPluginError } from './errors.ts'
|
|
10
|
-
import { isPromiseRejectedResult, PromiseManager } from './PromiseManager.ts'
|
|
11
|
-
import type {
|
|
12
|
-
Adapter,
|
|
13
|
-
Config,
|
|
14
|
-
DevtoolsOptions,
|
|
15
|
-
KubbEvents,
|
|
16
|
-
Plugin,
|
|
17
|
-
PluginContext,
|
|
18
|
-
PluginFactoryOptions,
|
|
19
|
-
PluginLifecycle,
|
|
20
|
-
PluginLifecycleHooks,
|
|
21
|
-
PluginParameter,
|
|
22
|
-
PluginWithLifeCycle,
|
|
23
|
-
ResolveNameParams,
|
|
24
|
-
ResolvePathParams,
|
|
25
|
-
UserPlugin,
|
|
26
|
-
} from './types.ts'
|
|
27
|
-
|
|
28
|
-
type RequiredPluginLifecycle = Required<PluginLifecycle>
|
|
29
|
-
|
|
30
|
-
export type Strategy = 'hookFirst' | 'hookForPlugin' | 'hookParallel' | 'hookSeq'
|
|
31
|
-
|
|
32
|
-
type ParseResult<H extends PluginLifecycleHooks> = RequiredPluginLifecycle[H]
|
|
33
|
-
|
|
34
|
-
type SafeParseResult<H extends PluginLifecycleHooks, Result = ReturnType<ParseResult<H>>> = {
|
|
35
|
-
result: Result
|
|
36
|
-
plugin: Plugin
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// inspired by: https://github.com/rollup/rollup/blob/master/src/utils/PluginDriver.ts#
|
|
40
|
-
|
|
41
|
-
type Options = {
|
|
42
|
-
fabric: FabricType
|
|
43
|
-
events: AsyncEventEmitter<KubbEvents>
|
|
44
|
-
/**
|
|
45
|
-
* @default Number.POSITIVE_INFINITY
|
|
46
|
-
*/
|
|
47
|
-
concurrency?: number
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export type GetFileOptions<TOptions = object> = {
|
|
51
|
-
name: string
|
|
52
|
-
mode?: KubbFile.Mode
|
|
53
|
-
extname: KubbFile.Extname
|
|
54
|
-
pluginName: string
|
|
55
|
-
options?: TOptions
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function getMode(fileOrFolder: string | undefined | null): KubbFile.Mode {
|
|
59
|
-
if (!fileOrFolder) {
|
|
60
|
-
return 'split'
|
|
61
|
-
}
|
|
62
|
-
return extname(fileOrFolder) ? 'single' : 'split'
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export class PluginManager {
|
|
66
|
-
readonly config: Config
|
|
67
|
-
readonly options: Options
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* The universal `@kubb/ast` `RootNode` produced by the adapter, set by
|
|
71
|
-
* the build pipeline after the adapter's `parse()` resolves.
|
|
72
|
-
*/
|
|
73
|
-
rootNode: RootNode | undefined = undefined
|
|
74
|
-
adapter: Adapter | undefined = undefined
|
|
75
|
-
#studioIsOpen = false
|
|
76
|
-
|
|
77
|
-
readonly #plugins = new Set<Plugin>()
|
|
78
|
-
readonly #usedPluginNames: Record<string, number> = {}
|
|
79
|
-
readonly #promiseManager
|
|
80
|
-
|
|
81
|
-
constructor(config: Config, options: Options) {
|
|
82
|
-
this.config = config
|
|
83
|
-
this.options = options
|
|
84
|
-
this.#promiseManager = new PromiseManager({
|
|
85
|
-
nullCheck: (state: SafeParseResult<'resolveName'> | null) => !!state?.result,
|
|
86
|
-
})
|
|
87
|
-
;[...(config.plugins || [])].forEach((plugin) => {
|
|
88
|
-
const parsedPlugin = this.#parse(plugin as UserPlugin)
|
|
89
|
-
|
|
90
|
-
this.#plugins.add(parsedPlugin)
|
|
91
|
-
})
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
get events() {
|
|
95
|
-
return this.options.events
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
getContext<TOptions extends PluginFactoryOptions>(plugin: Plugin<TOptions>): PluginContext<TOptions> & Record<string, unknown> {
|
|
99
|
-
const plugins = [...this.#plugins]
|
|
100
|
-
const pluginManager = this
|
|
101
|
-
|
|
102
|
-
const baseContext = {
|
|
103
|
-
fabric: this.options.fabric,
|
|
104
|
-
config: this.config,
|
|
105
|
-
plugin,
|
|
106
|
-
events: this.options.events,
|
|
107
|
-
pluginManager: this,
|
|
108
|
-
mode: getMode(resolve(this.config.root, this.config.output.path)),
|
|
109
|
-
addFile: async (...files: Array<KubbFile.File>) => {
|
|
110
|
-
await this.options.fabric.addFile(...files)
|
|
111
|
-
},
|
|
112
|
-
upsertFile: async (...files: Array<KubbFile.File>) => {
|
|
113
|
-
await this.options.fabric.upsertFile(...files)
|
|
114
|
-
},
|
|
115
|
-
get rootNode(): RootNode | undefined {
|
|
116
|
-
return pluginManager.rootNode
|
|
117
|
-
},
|
|
118
|
-
get adapter(): Adapter | undefined {
|
|
119
|
-
return pluginManager.adapter
|
|
120
|
-
},
|
|
121
|
-
openInStudio(options?: DevtoolsOptions) {
|
|
122
|
-
if (!pluginManager.config.devtools || pluginManager.#studioIsOpen) {
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (typeof pluginManager.config.devtools !== 'object') {
|
|
127
|
-
throw new Error('Devtools must be an object')
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (!pluginManager.rootNode || !pluginManager.adapter) {
|
|
131
|
-
throw new Error('adapter is not defined, make sure you have set the parser in kubb.config.ts')
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
pluginManager.#studioIsOpen = true
|
|
135
|
-
|
|
136
|
-
const studioUrl = pluginManager.config.devtools?.studioUrl ?? DEFAULT_STUDIO_URL
|
|
137
|
-
|
|
138
|
-
return openInStudioFn(pluginManager.rootNode, studioUrl, options)
|
|
139
|
-
},
|
|
140
|
-
} as unknown as PluginContext<TOptions>
|
|
141
|
-
|
|
142
|
-
const mergedExtras: Record<string, unknown> = {}
|
|
143
|
-
for (const p of plugins) {
|
|
144
|
-
if (typeof p.inject === 'function') {
|
|
145
|
-
const result = (p.inject as (this: PluginContext, context: PluginContext) => unknown).call(
|
|
146
|
-
baseContext as unknown as PluginContext,
|
|
147
|
-
baseContext as unknown as PluginContext,
|
|
148
|
-
)
|
|
149
|
-
if (result !== null && typeof result === 'object') {
|
|
150
|
-
Object.assign(mergedExtras, result)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
...baseContext,
|
|
157
|
-
...mergedExtras,
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
get plugins(): Array<Plugin> {
|
|
162
|
-
return this.#getSortedPlugins()
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
getFile<TOptions = object>({ name, mode, extname, pluginName, options }: GetFileOptions<TOptions>): KubbFile.File<{ pluginName: string }> {
|
|
166
|
-
const resolvedName = mode ? (mode === 'single' ? '' : this.resolveName({ name, pluginName, type: 'file' })) : name
|
|
167
|
-
|
|
168
|
-
const path = this.resolvePath({
|
|
169
|
-
baseName: `${resolvedName}${extname}` as const,
|
|
170
|
-
mode,
|
|
171
|
-
pluginName,
|
|
172
|
-
options,
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
if (!path) {
|
|
176
|
-
throw new Error(`Filepath should be defined for resolvedName "${resolvedName}" and pluginName "${pluginName}"`)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return {
|
|
180
|
-
path,
|
|
181
|
-
baseName: basename(path) as KubbFile.File['baseName'],
|
|
182
|
-
meta: {
|
|
183
|
-
pluginName,
|
|
184
|
-
},
|
|
185
|
-
sources: [],
|
|
186
|
-
imports: [],
|
|
187
|
-
exports: [],
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
resolvePath = <TOptions = object>(params: ResolvePathParams<TOptions>): KubbFile.Path => {
|
|
192
|
-
const root = resolve(this.config.root, this.config.output.path)
|
|
193
|
-
const defaultPath = resolve(root, params.baseName)
|
|
194
|
-
|
|
195
|
-
if (params.pluginName) {
|
|
196
|
-
const paths = this.hookForPluginSync({
|
|
197
|
-
pluginName: params.pluginName,
|
|
198
|
-
hookName: 'resolvePath',
|
|
199
|
-
parameters: [params.baseName, params.mode, params.options as object],
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
return paths?.at(0) || defaultPath
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const firstResult = this.hookFirstSync({
|
|
206
|
-
hookName: 'resolvePath',
|
|
207
|
-
parameters: [params.baseName, params.mode, params.options as object],
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
return firstResult?.result || defaultPath
|
|
211
|
-
}
|
|
212
|
-
//TODO refactor by using the order of plugins and the cache of the fileManager instead of guessing and recreating the name/path
|
|
213
|
-
resolveName = (params: ResolveNameParams): string => {
|
|
214
|
-
if (params.pluginName) {
|
|
215
|
-
const names = this.hookForPluginSync({
|
|
216
|
-
pluginName: params.pluginName,
|
|
217
|
-
hookName: 'resolveName',
|
|
218
|
-
parameters: [params.name.trim(), params.type],
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
const uniqueNames = new Set(names)
|
|
222
|
-
|
|
223
|
-
return transformReservedWord([...uniqueNames].at(0) || params.name)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const name = this.hookFirstSync({
|
|
227
|
-
hookName: 'resolveName',
|
|
228
|
-
parameters: [params.name.trim(), params.type],
|
|
229
|
-
})?.result
|
|
230
|
-
|
|
231
|
-
return transformReservedWord(name ?? params.name)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Run a specific hookName for plugin x.
|
|
236
|
-
*/
|
|
237
|
-
async hookForPlugin<H extends PluginLifecycleHooks>({
|
|
238
|
-
pluginName,
|
|
239
|
-
hookName,
|
|
240
|
-
parameters,
|
|
241
|
-
}: {
|
|
242
|
-
pluginName: string
|
|
243
|
-
hookName: H
|
|
244
|
-
parameters: PluginParameter<H>
|
|
245
|
-
}): Promise<Array<ReturnType<ParseResult<H>> | null>> {
|
|
246
|
-
const plugins = this.getPluginsByName(hookName, pluginName)
|
|
247
|
-
|
|
248
|
-
this.events.emit('plugins:hook:progress:start', {
|
|
249
|
-
hookName,
|
|
250
|
-
plugins,
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
const items: Array<ReturnType<ParseResult<H>>> = []
|
|
254
|
-
|
|
255
|
-
for (const plugin of plugins) {
|
|
256
|
-
const result = await this.#execute<H>({
|
|
257
|
-
strategy: 'hookFirst',
|
|
258
|
-
hookName,
|
|
259
|
-
parameters,
|
|
260
|
-
plugin,
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
if (result !== undefined && result !== null) {
|
|
264
|
-
items.push(result)
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
this.events.emit('plugins:hook:progress:end', { hookName })
|
|
269
|
-
|
|
270
|
-
return items
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Run a specific hookName for plugin x.
|
|
274
|
-
*/
|
|
275
|
-
|
|
276
|
-
hookForPluginSync<H extends PluginLifecycleHooks>({
|
|
277
|
-
pluginName,
|
|
278
|
-
hookName,
|
|
279
|
-
parameters,
|
|
280
|
-
}: {
|
|
281
|
-
pluginName: string
|
|
282
|
-
hookName: H
|
|
283
|
-
parameters: PluginParameter<H>
|
|
284
|
-
}): Array<ReturnType<ParseResult<H>>> | null {
|
|
285
|
-
const plugins = this.getPluginsByName(hookName, pluginName)
|
|
286
|
-
|
|
287
|
-
const result = plugins
|
|
288
|
-
.map((plugin) => {
|
|
289
|
-
return this.#executeSync<H>({
|
|
290
|
-
strategy: 'hookFirst',
|
|
291
|
-
hookName,
|
|
292
|
-
parameters,
|
|
293
|
-
plugin,
|
|
294
|
-
})
|
|
295
|
-
})
|
|
296
|
-
.filter((x): x is NonNullable<typeof x> => x !== null)
|
|
297
|
-
|
|
298
|
-
return result
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Returns the first non-null result.
|
|
303
|
-
*/
|
|
304
|
-
async hookFirst<H extends PluginLifecycleHooks>({
|
|
305
|
-
hookName,
|
|
306
|
-
parameters,
|
|
307
|
-
skipped,
|
|
308
|
-
}: {
|
|
309
|
-
hookName: H
|
|
310
|
-
parameters: PluginParameter<H>
|
|
311
|
-
skipped?: ReadonlySet<Plugin> | null
|
|
312
|
-
}): Promise<SafeParseResult<H>> {
|
|
313
|
-
const plugins = this.#getSortedPlugins(hookName).filter((plugin) => {
|
|
314
|
-
return skipped ? !skipped.has(plugin) : true
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
this.events.emit('plugins:hook:progress:start', { hookName, plugins })
|
|
318
|
-
|
|
319
|
-
const promises = plugins.map((plugin) => {
|
|
320
|
-
return async () => {
|
|
321
|
-
const value = await this.#execute<H>({
|
|
322
|
-
strategy: 'hookFirst',
|
|
323
|
-
hookName,
|
|
324
|
-
parameters,
|
|
325
|
-
plugin,
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
return Promise.resolve({
|
|
329
|
-
plugin,
|
|
330
|
-
result: value,
|
|
331
|
-
} as SafeParseResult<H>)
|
|
332
|
-
}
|
|
333
|
-
})
|
|
334
|
-
|
|
335
|
-
const result = await this.#promiseManager.run('first', promises)
|
|
336
|
-
|
|
337
|
-
this.events.emit('plugins:hook:progress:end', { hookName })
|
|
338
|
-
|
|
339
|
-
return result
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Returns the first non-null result.
|
|
344
|
-
*/
|
|
345
|
-
hookFirstSync<H extends PluginLifecycleHooks>({
|
|
346
|
-
hookName,
|
|
347
|
-
parameters,
|
|
348
|
-
skipped,
|
|
349
|
-
}: {
|
|
350
|
-
hookName: H
|
|
351
|
-
parameters: PluginParameter<H>
|
|
352
|
-
skipped?: ReadonlySet<Plugin> | null
|
|
353
|
-
}): SafeParseResult<H> | null {
|
|
354
|
-
let parseResult: SafeParseResult<H> | null = null
|
|
355
|
-
const plugins = this.#getSortedPlugins(hookName).filter((plugin) => {
|
|
356
|
-
return skipped ? !skipped.has(plugin) : true
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
for (const plugin of plugins) {
|
|
360
|
-
parseResult = {
|
|
361
|
-
result: this.#executeSync<H>({
|
|
362
|
-
strategy: 'hookFirst',
|
|
363
|
-
hookName,
|
|
364
|
-
parameters,
|
|
365
|
-
plugin,
|
|
366
|
-
}),
|
|
367
|
-
plugin,
|
|
368
|
-
} as SafeParseResult<H>
|
|
369
|
-
|
|
370
|
-
if (parseResult?.result != null) {
|
|
371
|
-
break
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return parseResult
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Runs all plugins in parallel based on `this.plugin` order and `pre`/`post` settings.
|
|
380
|
-
*/
|
|
381
|
-
async hookParallel<H extends PluginLifecycleHooks, TOutput = void>({
|
|
382
|
-
hookName,
|
|
383
|
-
parameters,
|
|
384
|
-
}: {
|
|
385
|
-
hookName: H
|
|
386
|
-
parameters?: Parameters<RequiredPluginLifecycle[H]> | undefined
|
|
387
|
-
}): Promise<Awaited<TOutput>[]> {
|
|
388
|
-
const plugins = this.#getSortedPlugins(hookName)
|
|
389
|
-
this.events.emit('plugins:hook:progress:start', { hookName, plugins })
|
|
390
|
-
|
|
391
|
-
const pluginStartTimes = new Map<Plugin, number>()
|
|
392
|
-
|
|
393
|
-
const promises = plugins.map((plugin) => {
|
|
394
|
-
return () => {
|
|
395
|
-
pluginStartTimes.set(plugin, performance.now())
|
|
396
|
-
return this.#execute({
|
|
397
|
-
strategy: 'hookParallel',
|
|
398
|
-
hookName,
|
|
399
|
-
parameters,
|
|
400
|
-
plugin,
|
|
401
|
-
}) as Promise<TOutput>
|
|
402
|
-
}
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
const results = await this.#promiseManager.run('parallel', promises, {
|
|
406
|
-
concurrency: this.options.concurrency,
|
|
407
|
-
})
|
|
408
|
-
|
|
409
|
-
results.forEach((result, index) => {
|
|
410
|
-
if (isPromiseRejectedResult<Error>(result)) {
|
|
411
|
-
const plugin = this.#getSortedPlugins(hookName)[index]
|
|
412
|
-
|
|
413
|
-
if (plugin) {
|
|
414
|
-
const startTime = pluginStartTimes.get(plugin) ?? performance.now()
|
|
415
|
-
this.events.emit('error', result.reason, {
|
|
416
|
-
plugin,
|
|
417
|
-
hookName,
|
|
418
|
-
strategy: 'hookParallel',
|
|
419
|
-
duration: Math.round(performance.now() - startTime),
|
|
420
|
-
parameters,
|
|
421
|
-
})
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
})
|
|
425
|
-
|
|
426
|
-
this.events.emit('plugins:hook:progress:end', { hookName })
|
|
427
|
-
|
|
428
|
-
return results.reduce((acc, result) => {
|
|
429
|
-
if (result.status === 'fulfilled') {
|
|
430
|
-
acc.push(result.value)
|
|
431
|
-
}
|
|
432
|
-
return acc
|
|
433
|
-
}, [] as Awaited<TOutput>[])
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Chains plugins
|
|
438
|
-
*/
|
|
439
|
-
async hookSeq<H extends PluginLifecycleHooks>({ hookName, parameters }: { hookName: H; parameters?: PluginParameter<H> }): Promise<void> {
|
|
440
|
-
const plugins = this.#getSortedPlugins(hookName)
|
|
441
|
-
this.events.emit('plugins:hook:progress:start', { hookName, plugins })
|
|
442
|
-
|
|
443
|
-
const promises = plugins.map((plugin) => {
|
|
444
|
-
return () =>
|
|
445
|
-
this.#execute({
|
|
446
|
-
strategy: 'hookSeq',
|
|
447
|
-
hookName,
|
|
448
|
-
parameters,
|
|
449
|
-
plugin,
|
|
450
|
-
})
|
|
451
|
-
})
|
|
452
|
-
|
|
453
|
-
await this.#promiseManager.run('seq', promises)
|
|
454
|
-
|
|
455
|
-
this.events.emit('plugins:hook:progress:end', { hookName })
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
#getSortedPlugins(hookName?: keyof PluginLifecycle): Array<Plugin> {
|
|
459
|
-
const plugins = [...this.#plugins]
|
|
460
|
-
|
|
461
|
-
if (hookName) {
|
|
462
|
-
return plugins.filter((plugin) => hookName in plugin)
|
|
463
|
-
}
|
|
464
|
-
// TODO add test case for sorting with pre/post
|
|
465
|
-
|
|
466
|
-
return plugins
|
|
467
|
-
.map((plugin) => {
|
|
468
|
-
if (plugin.pre) {
|
|
469
|
-
let missingPlugins = plugin.pre.filter((pluginName) => !plugins.find((pluginToFind) => pluginToFind.name === pluginName))
|
|
470
|
-
|
|
471
|
-
// when adapter is set, we can ignore the depends on plugin-oas, in v5 this will not be needed anymore
|
|
472
|
-
if (missingPlugins.includes('plugin-oas') && this.adapter) {
|
|
473
|
-
missingPlugins = missingPlugins.filter((pluginName) => pluginName !== 'plugin-oas')
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (missingPlugins.length > 0) {
|
|
477
|
-
throw new ValidationPluginError(`The plugin '${plugin.name}' has a pre set that references missing plugins for '${missingPlugins.join(', ')}'`)
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
return plugin
|
|
482
|
-
})
|
|
483
|
-
.sort((a, b) => {
|
|
484
|
-
if (b.pre?.includes(a.name)) {
|
|
485
|
-
return 1
|
|
486
|
-
}
|
|
487
|
-
if (b.post?.includes(a.name)) {
|
|
488
|
-
return -1
|
|
489
|
-
}
|
|
490
|
-
return 0
|
|
491
|
-
})
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
getPluginByName(pluginName: string): Plugin | undefined {
|
|
495
|
-
const plugins = [...this.#plugins]
|
|
496
|
-
|
|
497
|
-
return plugins.find((item) => item.name === pluginName)
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
getPluginsByName(hookName: keyof PluginWithLifeCycle, pluginName: string): Plugin[] {
|
|
501
|
-
const plugins = [...this.plugins]
|
|
502
|
-
|
|
503
|
-
const pluginByPluginName = plugins.filter((plugin) => hookName in plugin).filter((item) => item.name === pluginName)
|
|
504
|
-
|
|
505
|
-
if (!pluginByPluginName?.length) {
|
|
506
|
-
// fallback on the core plugin when there is no match
|
|
507
|
-
|
|
508
|
-
const corePlugin = plugins.find((plugin) => plugin.name === CORE_PLUGIN_NAME && hookName in plugin)
|
|
509
|
-
|
|
510
|
-
return corePlugin ? [corePlugin] : []
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
return pluginByPluginName
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Run an async plugin hook and return the result.
|
|
518
|
-
* @param hookName Name of the plugin hook. Must be either in `PluginHooks` or `OutputPluginValueHooks`.
|
|
519
|
-
* @param args Arguments passed to the plugin hook.
|
|
520
|
-
* @param plugin The actual pluginObject to run.
|
|
521
|
-
*/
|
|
522
|
-
#emitProcessingEnd<H extends PluginLifecycleHooks>({
|
|
523
|
-
startTime,
|
|
524
|
-
output,
|
|
525
|
-
strategy,
|
|
526
|
-
hookName,
|
|
527
|
-
plugin,
|
|
528
|
-
parameters,
|
|
529
|
-
}: {
|
|
530
|
-
startTime: number
|
|
531
|
-
output: unknown
|
|
532
|
-
strategy: Strategy
|
|
533
|
-
hookName: H
|
|
534
|
-
plugin: PluginWithLifeCycle
|
|
535
|
-
parameters: unknown[] | undefined
|
|
536
|
-
}): void {
|
|
537
|
-
this.events.emit('plugins:hook:processing:end', {
|
|
538
|
-
duration: Math.round(performance.now() - startTime),
|
|
539
|
-
parameters,
|
|
540
|
-
output,
|
|
541
|
-
strategy,
|
|
542
|
-
hookName,
|
|
543
|
-
plugin,
|
|
544
|
-
})
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// Implementation signature
|
|
548
|
-
#execute<H extends PluginLifecycleHooks>({
|
|
549
|
-
strategy,
|
|
550
|
-
hookName,
|
|
551
|
-
parameters,
|
|
552
|
-
plugin,
|
|
553
|
-
}: {
|
|
554
|
-
strategy: Strategy
|
|
555
|
-
hookName: H
|
|
556
|
-
parameters: unknown[] | undefined
|
|
557
|
-
plugin: PluginWithLifeCycle
|
|
558
|
-
}): Promise<ReturnType<ParseResult<H>> | null> | null {
|
|
559
|
-
const hook = plugin[hookName]
|
|
560
|
-
|
|
561
|
-
if (!hook) {
|
|
562
|
-
return null
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
this.events.emit('plugins:hook:processing:start', {
|
|
566
|
-
strategy,
|
|
567
|
-
hookName,
|
|
568
|
-
parameters,
|
|
569
|
-
plugin,
|
|
570
|
-
})
|
|
571
|
-
|
|
572
|
-
const startTime = performance.now()
|
|
573
|
-
|
|
574
|
-
const task = (async () => {
|
|
575
|
-
try {
|
|
576
|
-
const output =
|
|
577
|
-
typeof hook === 'function' ? await Promise.resolve((hook as (...args: unknown[]) => unknown).apply(this.getContext(plugin), parameters ?? [])) : hook
|
|
578
|
-
|
|
579
|
-
this.#emitProcessingEnd({ startTime, output, strategy, hookName, plugin, parameters })
|
|
580
|
-
|
|
581
|
-
return output as ReturnType<ParseResult<H>>
|
|
582
|
-
} catch (error) {
|
|
583
|
-
this.events.emit('error', error as Error, {
|
|
584
|
-
plugin,
|
|
585
|
-
hookName,
|
|
586
|
-
strategy,
|
|
587
|
-
duration: Math.round(performance.now() - startTime),
|
|
588
|
-
})
|
|
589
|
-
|
|
590
|
-
return null
|
|
591
|
-
}
|
|
592
|
-
})()
|
|
593
|
-
|
|
594
|
-
return task
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Run a sync plugin hook and return the result.
|
|
599
|
-
* @param hookName Name of the plugin hook. Must be in `PluginHooks`.
|
|
600
|
-
* @param args Arguments passed to the plugin hook.
|
|
601
|
-
* @param plugin The actual plugin
|
|
602
|
-
*/
|
|
603
|
-
#executeSync<H extends PluginLifecycleHooks>({
|
|
604
|
-
strategy,
|
|
605
|
-
hookName,
|
|
606
|
-
parameters,
|
|
607
|
-
plugin,
|
|
608
|
-
}: {
|
|
609
|
-
strategy: Strategy
|
|
610
|
-
hookName: H
|
|
611
|
-
parameters: PluginParameter<H>
|
|
612
|
-
plugin: PluginWithLifeCycle
|
|
613
|
-
}): ReturnType<ParseResult<H>> | null {
|
|
614
|
-
const hook = plugin[hookName]
|
|
615
|
-
|
|
616
|
-
if (!hook) {
|
|
617
|
-
return null
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
this.events.emit('plugins:hook:processing:start', {
|
|
621
|
-
strategy,
|
|
622
|
-
hookName,
|
|
623
|
-
parameters,
|
|
624
|
-
plugin,
|
|
625
|
-
})
|
|
626
|
-
|
|
627
|
-
const startTime = performance.now()
|
|
628
|
-
|
|
629
|
-
try {
|
|
630
|
-
const output =
|
|
631
|
-
typeof hook === 'function'
|
|
632
|
-
? ((hook as (...args: unknown[]) => unknown).apply(this.getContext(plugin), parameters) as ReturnType<ParseResult<H>>)
|
|
633
|
-
: (hook as ReturnType<ParseResult<H>>)
|
|
634
|
-
|
|
635
|
-
this.#emitProcessingEnd({ startTime, output, strategy, hookName, plugin, parameters })
|
|
636
|
-
|
|
637
|
-
return output
|
|
638
|
-
} catch (error) {
|
|
639
|
-
this.events.emit('error', error as Error, {
|
|
640
|
-
plugin,
|
|
641
|
-
hookName,
|
|
642
|
-
strategy,
|
|
643
|
-
duration: Math.round(performance.now() - startTime),
|
|
644
|
-
})
|
|
645
|
-
|
|
646
|
-
return null
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
#parse(plugin: UserPlugin): Plugin {
|
|
651
|
-
const usedPluginNames = this.#usedPluginNames
|
|
652
|
-
|
|
653
|
-
setUniqueName(plugin.name, usedPluginNames)
|
|
654
|
-
|
|
655
|
-
const usageCount = usedPluginNames[plugin.name]
|
|
656
|
-
if (usageCount && usageCount > 1) {
|
|
657
|
-
throw new ValidationPluginError(
|
|
658
|
-
`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.`,
|
|
659
|
-
)
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
return {
|
|
663
|
-
install() {},
|
|
664
|
-
...plugin,
|
|
665
|
-
} as unknown as Plugin
|
|
666
|
-
}
|
|
667
|
-
}
|
package/src/PromiseManager.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { Strategy, StrategySwitch } from './utils/executeStrategies.ts'
|
|
2
|
-
import { hookFirst, hookParallel, hookSeq } from './utils/executeStrategies.ts'
|
|
3
|
-
|
|
4
|
-
type PromiseFunc<T = unknown, T2 = never> = () => T2 extends never ? Promise<T> : Promise<T> | T2
|
|
5
|
-
|
|
6
|
-
type Options<TState = unknown> = {
|
|
7
|
-
nullCheck?: (state: TState) => boolean
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export class PromiseManager<TState = unknown> {
|
|
11
|
-
#options: Options<TState> = {}
|
|
12
|
-
|
|
13
|
-
constructor(options: Options<TState> = {}) {
|
|
14
|
-
this.#options = options
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
run<TInput extends Array<PromiseFunc<TValue, null>>, TValue, TStrategy extends Strategy, TOutput = StrategySwitch<TStrategy, TInput, TValue>>(
|
|
18
|
-
strategy: TStrategy,
|
|
19
|
-
promises: TInput,
|
|
20
|
-
{ concurrency = Number.POSITIVE_INFINITY }: { concurrency?: number } = {},
|
|
21
|
-
): TOutput {
|
|
22
|
-
if (strategy === 'seq') {
|
|
23
|
-
return hookSeq<TInput, TValue, TOutput>(promises)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (strategy === 'first') {
|
|
27
|
-
return hookFirst<TInput, TValue, TOutput>(promises, this.#options.nullCheck as ((state: unknown) => boolean) | undefined)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (strategy === 'parallel') {
|
|
31
|
-
return hookParallel<TInput, TValue, TOutput>(promises, concurrency)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
throw new Error(`${strategy} not implemented`)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function isPromiseRejectedResult<T>(result: PromiseSettledResult<unknown>): result is Omit<PromiseRejectedResult, 'reason'> & { reason: T } {
|
|
39
|
-
return result.status === 'rejected'
|
|
40
|
-
}
|