@kubb/core 5.0.0-alpha.43 → 5.0.0-alpha.45
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-CgXFtmNP.js → PluginDriver-Bt_UCn7-.js} +88 -739
- package/dist/PluginDriver-Bt_UCn7-.js.map +1 -0
- package/dist/{PluginDriver-BQwm8hDd.cjs → PluginDriver-rVSfG8tW.cjs} +91 -751
- package/dist/PluginDriver-rVSfG8tW.cjs.map +1 -0
- package/dist/index.cjs +185 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +186 -14
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +5 -33
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +5 -5
- package/dist/mocks.js +5 -33
- package/dist/mocks.js.map +1 -1
- package/dist/{types-BJX_uR-y.d.ts → types-BUgxQiWY.d.ts} +72 -500
- package/package.json +5 -5
- package/src/FileManager.ts +20 -21
- package/src/FileProcessor.ts +2 -0
- package/src/Kubb.ts +3 -47
- package/src/PluginDriver.ts +53 -578
- package/src/constants.ts +9 -1
- package/src/createKubb.ts +6 -16
- package/src/definePlugin.ts +12 -34
- package/src/defineResolver.ts +23 -8
- package/src/mocks.ts +10 -84
- package/src/types.ts +40 -342
- package/src/utils/getBarrelFiles.ts +9 -3
- package/dist/PluginDriver-BQwm8hDd.cjs.map +0 -1
- package/dist/PluginDriver-CgXFtmNP.js.map +0 -1
- package/src/createPlugin.ts +0 -31
- package/src/utils/executeStrategies.ts +0 -84
package/src/PluginDriver.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { performance } from 'node:perf_hooks'
|
|
1
|
+
import { extname, resolve } from 'node:path'
|
|
3
2
|
import type { AsyncEventEmitter } from '@internals/utils'
|
|
4
|
-
import {
|
|
5
|
-
import type { FileNode, InputNode } from '@kubb/ast'
|
|
3
|
+
import type { FileNode, InputNode, OperationNode, SchemaNode } from '@kubb/ast'
|
|
6
4
|
import { createFile } from '@kubb/ast'
|
|
7
5
|
import { DEFAULT_STUDIO_URL } from './constants.ts'
|
|
8
6
|
import type { Generator } from './defineGenerator.ts'
|
|
9
|
-
import
|
|
7
|
+
import type { Plugin } from './definePlugin.ts'
|
|
10
8
|
import { defineResolver } from './defineResolver.ts'
|
|
11
9
|
import { openInStudio as openInStudioFn } from './devtools.ts'
|
|
12
10
|
import { FileManager } from './FileManager.ts'
|
|
@@ -16,65 +14,20 @@ import type {
|
|
|
16
14
|
Adapter,
|
|
17
15
|
Config,
|
|
18
16
|
DevtoolsOptions,
|
|
19
|
-
|
|
17
|
+
GeneratorContext,
|
|
20
18
|
KubbHooks,
|
|
21
19
|
KubbPluginSetupContext,
|
|
22
|
-
|
|
23
|
-
Plugin,
|
|
24
|
-
PluginContext,
|
|
20
|
+
NormalizedPlugin,
|
|
25
21
|
PluginFactoryOptions,
|
|
26
|
-
PluginLifecycle,
|
|
27
|
-
PluginLifecycleHooks,
|
|
28
|
-
PluginParameter,
|
|
29
|
-
PluginWithLifeCycle,
|
|
30
|
-
ResolveNameParams,
|
|
31
|
-
ResolvePathParams,
|
|
32
22
|
Resolver,
|
|
33
23
|
} from './types.ts'
|
|
34
|
-
import { hookFirst, hookParallel, hookSeq } from './utils/executeStrategies.ts'
|
|
35
|
-
|
|
36
|
-
type RequiredPluginLifecycle = Required<PluginLifecycle>
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Hook dispatch strategy used by the `PluginDriver`.
|
|
40
|
-
*
|
|
41
|
-
* - `hookFirst` — stops at the first non-null result.
|
|
42
|
-
* - `hookForPlugin` — calls only the matching plugin.
|
|
43
|
-
* - `hookParallel` — calls all plugins concurrently.
|
|
44
|
-
* - `hookSeq` — calls all plugins in order, threading the result.
|
|
45
|
-
*/
|
|
46
|
-
export type Strategy = 'hookFirst' | 'hookForPlugin' | 'hookParallel' | 'hookSeq'
|
|
47
|
-
|
|
48
|
-
type ParseResult<H extends PluginLifecycleHooks> = RequiredPluginLifecycle[H]
|
|
49
|
-
|
|
50
|
-
type SafeParseResult<H extends PluginLifecycleHooks, Result = ReturnType<ParseResult<H>>> = {
|
|
51
|
-
result: Result
|
|
52
|
-
plugin: Plugin
|
|
53
|
-
}
|
|
54
24
|
|
|
55
25
|
// inspired by: https://github.com/rollup/rollup/blob/master/src/utils/PluginDriver.ts#
|
|
56
26
|
|
|
57
27
|
type Options = {
|
|
58
|
-
hooks
|
|
59
|
-
/**
|
|
60
|
-
* @default Number.POSITIVE_INFINITY
|
|
61
|
-
*/
|
|
62
|
-
concurrency?: number
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Parameters accepted by `PluginDriver.getFile` to resolve a generated file descriptor.
|
|
67
|
-
*/
|
|
68
|
-
export type GetFileOptions<TOptions = object> = {
|
|
69
|
-
name: string
|
|
70
|
-
mode?: 'single' | 'split'
|
|
71
|
-
extname: FileNode['extname']
|
|
72
|
-
pluginName: string
|
|
73
|
-
options?: TOptions
|
|
28
|
+
hooks: AsyncEventEmitter<KubbHooks>
|
|
74
29
|
}
|
|
75
30
|
|
|
76
|
-
const hookFirstNullCheck = (state: unknown) => !!(state as SafeParseResult<'resolveName'> | null)?.result
|
|
77
|
-
|
|
78
31
|
export class PluginDriver {
|
|
79
32
|
readonly config: Config
|
|
80
33
|
readonly options: Options
|
|
@@ -110,7 +63,7 @@ export class PluginDriver {
|
|
|
110
63
|
*/
|
|
111
64
|
readonly fileManager = new FileManager()
|
|
112
65
|
|
|
113
|
-
readonly plugins = new Map<string,
|
|
66
|
+
readonly plugins = new Map<string, NormalizedPlugin>()
|
|
114
67
|
|
|
115
68
|
/**
|
|
116
69
|
* Tracks which plugins have generators registered via `addGenerator()` (event-based path).
|
|
@@ -123,17 +76,9 @@ export class PluginDriver {
|
|
|
123
76
|
|
|
124
77
|
constructor(config: Config, options: Options) {
|
|
125
78
|
this.config = config
|
|
126
|
-
this.options =
|
|
127
|
-
...options,
|
|
128
|
-
hooks: options.hooks,
|
|
129
|
-
}
|
|
79
|
+
this.options = options
|
|
130
80
|
config.plugins
|
|
131
|
-
.map((rawPlugin) =>
|
|
132
|
-
if (isHookStylePlugin(rawPlugin)) {
|
|
133
|
-
return this.#normalizeHookStylePlugin(rawPlugin as HookStylePlugin)
|
|
134
|
-
}
|
|
135
|
-
return { ...rawPlugin, buildStart: rawPlugin.buildStart ?? (() => {}), buildEnd: rawPlugin.buildEnd ?? (() => {}) } as unknown as Plugin
|
|
136
|
-
})
|
|
81
|
+
.map((rawPlugin) => this.#normalizePlugin(rawPlugin as Plugin))
|
|
137
82
|
.filter((plugin) => {
|
|
138
83
|
if (typeof plugin.apply === 'function') {
|
|
139
84
|
return plugin.apply(config)
|
|
@@ -151,53 +96,20 @@ export class PluginDriver {
|
|
|
151
96
|
}
|
|
152
97
|
|
|
153
98
|
get hooks() {
|
|
154
|
-
if (!this.options.hooks) {
|
|
155
|
-
throw new Error('hooks are not defined')
|
|
156
|
-
}
|
|
157
99
|
return this.options.hooks
|
|
158
100
|
}
|
|
159
101
|
|
|
160
102
|
/**
|
|
161
|
-
* Creates
|
|
103
|
+
* Creates an `NormalizedPlugin` from a hook-style plugin and registers
|
|
162
104
|
* its lifecycle handlers on the `AsyncEventEmitter`.
|
|
163
|
-
*
|
|
164
|
-
* The normalized plugin has an empty `buildStart` — generators registered via
|
|
165
|
-
* `addGenerator()` in `kubb:plugin:setup` are stored on `normalizedPlugin.generators`
|
|
166
|
-
* and used by `runPluginAstHooks` during the build.
|
|
167
105
|
*/
|
|
168
|
-
#
|
|
169
|
-
const generators: Plugin['generators'] = []
|
|
170
|
-
const driver = this
|
|
171
|
-
// The options shape is the minimal struct required by Plugin. Hook-style plugins
|
|
172
|
-
// use generators registered via addGenerator() and resolvers set via setResolver().
|
|
173
|
-
// `inject` and `resolver` are required by the Plugin type but are irrelevant for hook-style
|
|
174
|
-
// plugins: inject is a no-op and resolver is set dynamically via setResolver() in kubb:plugin:setup.
|
|
175
|
-
//
|
|
176
|
-
// `resolveName` and `resolvePath` bridge the legacy PluginDriver.resolveName/resolvePath
|
|
177
|
-
// lifecycle so that other plugins calling `driver.resolveName({ pluginName })` or
|
|
178
|
-
// `driver.getFile({ pluginName })` still get correct results from hook-style plugins.
|
|
106
|
+
#normalizePlugin(hookPlugin: Plugin): NormalizedPlugin {
|
|
179
107
|
const normalizedPlugin = {
|
|
180
108
|
name: hookPlugin.name,
|
|
181
109
|
dependencies: hookPlugin.dependencies,
|
|
182
110
|
options: { output: { path: '.' }, exclude: [], override: [] },
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
resolveName(name: string, type?: ResolveNameParams['type']) {
|
|
186
|
-
const resolver = driver.getResolver(hookPlugin.name)
|
|
187
|
-
return resolver.default(name, type)
|
|
188
|
-
},
|
|
189
|
-
resolvePath(baseName: FileNode['baseName'], pathMode?: 'single' | 'split', resolveOptions?: Record<string, unknown>) {
|
|
190
|
-
const resolver = driver.getResolver(hookPlugin.name)
|
|
191
|
-
const opts = normalizedPlugin.options as Record<string, unknown>
|
|
192
|
-
const group = resolveOptions?.group as Record<string, string> | undefined
|
|
193
|
-
return resolver.resolvePath(
|
|
194
|
-
{ baseName, pathMode, tag: group?.tag, path: group?.path },
|
|
195
|
-
{ root: resolve(driver.config.root, driver.config.output.path), output: opts.output as Output, group: opts.group as Group | undefined },
|
|
196
|
-
)
|
|
197
|
-
},
|
|
198
|
-
buildStart() {},
|
|
199
|
-
buildEnd() {},
|
|
200
|
-
} as unknown as Plugin
|
|
111
|
+
} as unknown as NormalizedPlugin
|
|
112
|
+
|
|
201
113
|
this.registerPluginHooks(hookPlugin, normalizedPlugin)
|
|
202
114
|
return normalizedPlugin
|
|
203
115
|
}
|
|
@@ -214,8 +126,10 @@ export class PluginDriver {
|
|
|
214
126
|
*
|
|
215
127
|
* External tooling can subscribe to any of these events via `hooks.on(...)` to observe
|
|
216
128
|
* the plugin lifecycle without modifying plugin behavior.
|
|
129
|
+
*
|
|
130
|
+
* @internal
|
|
217
131
|
*/
|
|
218
|
-
registerPluginHooks(hookPlugin:
|
|
132
|
+
registerPluginHooks(hookPlugin: Plugin, normalizedPlugin: NormalizedPlugin): void {
|
|
219
133
|
const { hooks } = hookPlugin
|
|
220
134
|
|
|
221
135
|
// kubb:plugin:setup gets special treatment: the globally emitted context is wrapped with
|
|
@@ -241,15 +155,8 @@ export class PluginDriver {
|
|
|
241
155
|
setOptions: (opts) => {
|
|
242
156
|
normalizedPlugin.options = { ...normalizedPlugin.options, ...opts }
|
|
243
157
|
},
|
|
244
|
-
injectFile: (
|
|
245
|
-
|
|
246
|
-
baseName: file.baseName,
|
|
247
|
-
path: file.path,
|
|
248
|
-
sources: file.sources ?? [],
|
|
249
|
-
imports: [],
|
|
250
|
-
exports: [],
|
|
251
|
-
})
|
|
252
|
-
this.fileManager.add(fileNode)
|
|
158
|
+
injectFile: ({ sources = [], ...rest }) => {
|
|
159
|
+
this.fileManager.add(createFile({ imports: [], exports: [], sources, ...rest }))
|
|
253
160
|
},
|
|
254
161
|
}
|
|
255
162
|
return hooks['kubb:plugin:setup']!(pluginCtx)
|
|
@@ -262,6 +169,7 @@ export class PluginDriver {
|
|
|
262
169
|
// All other hooks are registered as direct pass-through listeners on the shared emitter.
|
|
263
170
|
for (const [event, handler] of Object.entries(hooks) as Array<[keyof KubbHooks, ((...args: never[]) => void | Promise<void>) | undefined]>) {
|
|
264
171
|
if (event === 'kubb:plugin:setup' || !handler) continue
|
|
172
|
+
|
|
265
173
|
this.hooks.on(event, handler as never)
|
|
266
174
|
this.#trackHookListener(event, handler as (...args: never[]) => void | Promise<void>)
|
|
267
175
|
}
|
|
@@ -274,16 +182,17 @@ export class PluginDriver {
|
|
|
274
182
|
* Call this once from `safeBuild` before the plugin execution loop begins.
|
|
275
183
|
*/
|
|
276
184
|
async emitSetupHooks(): Promise<void> {
|
|
185
|
+
const noop = () => {}
|
|
277
186
|
await this.hooks.emit('kubb:plugin:setup', {
|
|
278
187
|
config: this.config,
|
|
279
|
-
addGenerator: () => {},
|
|
280
|
-
setResolver: () => {},
|
|
281
|
-
setTransformer: () => {},
|
|
282
|
-
setRenderer: () => {},
|
|
283
|
-
setOptions: () => {},
|
|
284
|
-
injectFile: () => {},
|
|
285
|
-
updateConfig: () => {},
|
|
286
188
|
options: {},
|
|
189
|
+
addGenerator: noop,
|
|
190
|
+
setResolver: noop,
|
|
191
|
+
setTransformer: noop,
|
|
192
|
+
setRenderer: noop,
|
|
193
|
+
setOptions: noop,
|
|
194
|
+
injectFile: noop,
|
|
195
|
+
updateConfig: noop,
|
|
287
196
|
})
|
|
288
197
|
}
|
|
289
198
|
|
|
@@ -308,7 +217,7 @@ export class PluginDriver {
|
|
|
308
217
|
}
|
|
309
218
|
|
|
310
219
|
if (gen.schema) {
|
|
311
|
-
const schemaHandler = async (node:
|
|
220
|
+
const schemaHandler = async (node: SchemaNode, ctx: GeneratorContext) => {
|
|
312
221
|
if (ctx.plugin.name !== pluginName) return
|
|
313
222
|
const result = await gen.schema!(node, ctx)
|
|
314
223
|
await applyHookResult(result, this, resolveRenderer())
|
|
@@ -319,7 +228,7 @@ export class PluginDriver {
|
|
|
319
228
|
}
|
|
320
229
|
|
|
321
230
|
if (gen.operation) {
|
|
322
|
-
const operationHandler = async (node:
|
|
231
|
+
const operationHandler = async (node: OperationNode, ctx: GeneratorContext) => {
|
|
323
232
|
if (ctx.plugin.name !== pluginName) return
|
|
324
233
|
const result = await gen.operation!(node, ctx)
|
|
325
234
|
await applyHookResult(result, this, resolveRenderer())
|
|
@@ -330,7 +239,7 @@ export class PluginDriver {
|
|
|
330
239
|
}
|
|
331
240
|
|
|
332
241
|
if (gen.operations) {
|
|
333
|
-
const operationsHandler = async (nodes:
|
|
242
|
+
const operationsHandler = async (nodes: Array<OperationNode>, ctx: GeneratorContext) => {
|
|
334
243
|
if (ctx.plugin.name !== pluginName) return
|
|
335
244
|
const result = await gen.operations!(nodes, ctx)
|
|
336
245
|
await applyHookResult(result, this, resolveRenderer())
|
|
@@ -354,6 +263,12 @@ export class PluginDriver {
|
|
|
354
263
|
return this.#pluginsWithEventGenerators.has(pluginName)
|
|
355
264
|
}
|
|
356
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Unregisters all plugin lifecycle listeners from the shared event emitter.
|
|
268
|
+
* Called at the end of a build to prevent listener leaks across repeated builds.
|
|
269
|
+
*
|
|
270
|
+
* @internal
|
|
271
|
+
*/
|
|
357
272
|
dispose(): void {
|
|
358
273
|
for (const [event, handlers] of this.#hookListeners) {
|
|
359
274
|
for (const handler of handlers) {
|
|
@@ -387,33 +302,32 @@ export class PluginDriver {
|
|
|
387
302
|
return resolver
|
|
388
303
|
}
|
|
389
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Merges `partial` with the plugin's default resolver and stores the result.
|
|
307
|
+
* Also mirrors it onto `plugin.resolver` so callers using `getPlugin(name).resolver`
|
|
308
|
+
* get the up-to-date resolver without going through `getResolver()`.
|
|
309
|
+
*/
|
|
390
310
|
setPluginResolver(pluginName: string, partial: Partial<Resolver>): void {
|
|
391
311
|
const defaultResolver = this.#createDefaultResolver(pluginName)
|
|
392
312
|
const merged = { ...defaultResolver, ...partial }
|
|
393
313
|
this.#resolvers.set(pluginName, merged)
|
|
394
|
-
// Mirror the resolved resolver onto the plugin so that consumers using
|
|
395
|
-
// `getPlugin(name).resolver` get the correct resolver without going through getResolver().
|
|
396
314
|
const plugin = this.plugins.get(pluginName)
|
|
397
315
|
if (plugin) {
|
|
398
316
|
plugin.resolver = merged
|
|
399
317
|
}
|
|
400
318
|
}
|
|
401
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Returns the resolver for the given plugin.
|
|
322
|
+
*
|
|
323
|
+
* Resolution order: dynamic resolver set via `setPluginResolver` → static resolver on the
|
|
324
|
+
* plugin → lazily created default resolver (identity name, no path transforms).
|
|
325
|
+
*/
|
|
402
326
|
getResolver(pluginName: string): Resolver {
|
|
403
|
-
|
|
404
|
-
if (dynamicResolver) {
|
|
405
|
-
return dynamicResolver
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const pluginResolver = this.plugins.get(pluginName)?.resolver
|
|
409
|
-
if (pluginResolver) {
|
|
410
|
-
return pluginResolver
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return this.#createDefaultResolver(pluginName)
|
|
327
|
+
return this.#resolvers.get(pluginName) ?? this.plugins.get(pluginName)?.resolver ?? this.#createDefaultResolver(pluginName)
|
|
414
328
|
}
|
|
415
329
|
|
|
416
|
-
getContext<TOptions extends PluginFactoryOptions>(plugin:
|
|
330
|
+
getContext<TOptions extends PluginFactoryOptions>(plugin: NormalizedPlugin<TOptions>): GeneratorContext<TOptions> & Record<string, unknown> {
|
|
417
331
|
const driver = this
|
|
418
332
|
|
|
419
333
|
const baseContext = {
|
|
@@ -428,7 +342,7 @@ export class PluginDriver {
|
|
|
428
342
|
plugin,
|
|
429
343
|
getPlugin: driver.getPlugin.bind(driver),
|
|
430
344
|
requirePlugin: driver.requirePlugin.bind(driver),
|
|
431
|
-
driver
|
|
345
|
+
driver,
|
|
432
346
|
addFile: async (...files: Array<FileNode>) => {
|
|
433
347
|
driver.fileManager.add(...files)
|
|
434
348
|
},
|
|
@@ -475,326 +389,15 @@ export class PluginDriver {
|
|
|
475
389
|
|
|
476
390
|
return openInStudioFn(driver.inputNode, studioUrl, options)
|
|
477
391
|
},
|
|
478
|
-
} as unknown as
|
|
479
|
-
|
|
480
|
-
let mergedExtras: Record<string, unknown> = {}
|
|
481
|
-
|
|
482
|
-
for (const p of this.plugins.values()) {
|
|
483
|
-
if (typeof p.inject === 'function') {
|
|
484
|
-
const result = (p.inject as (this: PluginContext) => unknown).call(baseContext as unknown as PluginContext)
|
|
485
|
-
if (result !== null && typeof result === 'object') {
|
|
486
|
-
mergedExtras = { ...mergedExtras, ...(result as Record<string, unknown>) }
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
return {
|
|
492
|
-
...baseContext,
|
|
493
|
-
...mergedExtras,
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* @deprecated use resolvers context instead
|
|
498
|
-
*/
|
|
499
|
-
getFile<TOptions = object>({ name, mode, extname, pluginName, options }: GetFileOptions<TOptions>): FileNode<{ pluginName: string }> {
|
|
500
|
-
const resolvedName = mode ? (mode === 'single' ? '' : this.resolveName({ name, pluginName, type: 'file' })) : name
|
|
501
|
-
|
|
502
|
-
const path = this.resolvePath({
|
|
503
|
-
baseName: `${resolvedName}${extname}` as const,
|
|
504
|
-
mode,
|
|
505
|
-
pluginName,
|
|
506
|
-
options,
|
|
507
|
-
})
|
|
508
|
-
|
|
509
|
-
if (!path) {
|
|
510
|
-
throw new Error(`Filepath should be defined for resolvedName "${resolvedName}" and pluginName "${pluginName}"`)
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
return createFile<{ pluginName: string }>({
|
|
514
|
-
path,
|
|
515
|
-
baseName: basename(path) as `${string}.${string}`,
|
|
516
|
-
meta: {
|
|
517
|
-
pluginName,
|
|
518
|
-
},
|
|
519
|
-
sources: [],
|
|
520
|
-
imports: [],
|
|
521
|
-
exports: [],
|
|
522
|
-
})
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* @deprecated use resolvers context instead
|
|
527
|
-
*/
|
|
528
|
-
resolvePath = <TOptions = object>(params: ResolvePathParams<TOptions>): string => {
|
|
529
|
-
const root = resolve(this.config.root, this.config.output.path)
|
|
530
|
-
const defaultPath = resolve(root, params.baseName)
|
|
531
|
-
|
|
532
|
-
if (params.pluginName) {
|
|
533
|
-
const paths = this.hookForPluginSync({
|
|
534
|
-
pluginName: params.pluginName,
|
|
535
|
-
hookName: 'resolvePath',
|
|
536
|
-
parameters: [params.baseName, params.mode, params.options as object],
|
|
537
|
-
})
|
|
538
|
-
|
|
539
|
-
return paths?.at(0) || defaultPath
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
const firstResult = this.hookFirstSync({
|
|
543
|
-
hookName: 'resolvePath',
|
|
544
|
-
parameters: [params.baseName, params.mode, params.options as object],
|
|
545
|
-
})
|
|
546
|
-
|
|
547
|
-
return firstResult?.result || defaultPath
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* @deprecated use resolvers context instead
|
|
551
|
-
*/
|
|
552
|
-
resolveName = (params: ResolveNameParams): string => {
|
|
553
|
-
if (params.pluginName) {
|
|
554
|
-
const names = this.hookForPluginSync({
|
|
555
|
-
pluginName: params.pluginName,
|
|
556
|
-
hookName: 'resolveName',
|
|
557
|
-
parameters: [params.name.trim(), params.type],
|
|
558
|
-
})
|
|
559
|
-
|
|
560
|
-
return transformReservedWord(names?.at(0) ?? params.name)
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
const name = this.hookFirstSync({
|
|
564
|
-
hookName: 'resolveName',
|
|
565
|
-
parameters: [params.name.trim(), params.type],
|
|
566
|
-
})?.result
|
|
567
|
-
|
|
568
|
-
return transformReservedWord(name ?? params.name)
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
/**
|
|
572
|
-
* Run a specific hookName for plugin x.
|
|
573
|
-
*/
|
|
574
|
-
async hookForPlugin<H extends PluginLifecycleHooks>({
|
|
575
|
-
pluginName,
|
|
576
|
-
hookName,
|
|
577
|
-
parameters,
|
|
578
|
-
}: {
|
|
579
|
-
pluginName: string
|
|
580
|
-
hookName: H
|
|
581
|
-
parameters: PluginParameter<H>
|
|
582
|
-
}): Promise<Array<ReturnType<ParseResult<H>> | null>> {
|
|
583
|
-
const plugin = this.plugins.get(pluginName)
|
|
584
|
-
|
|
585
|
-
if (!plugin) {
|
|
586
|
-
return [null]
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
this.hooks.emit('kubb:plugins:hook:progress:start', {
|
|
590
|
-
hookName,
|
|
591
|
-
plugins: [plugin],
|
|
592
|
-
})
|
|
593
|
-
|
|
594
|
-
const result = await this.#execute<H>({
|
|
595
|
-
strategy: 'hookFirst',
|
|
596
|
-
hookName,
|
|
597
|
-
parameters,
|
|
598
|
-
plugin,
|
|
599
|
-
})
|
|
600
|
-
|
|
601
|
-
this.hooks.emit('kubb:plugins:hook:progress:end', { hookName })
|
|
602
|
-
|
|
603
|
-
return [result]
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Run a specific hookName for plugin x.
|
|
608
|
-
*/
|
|
609
|
-
hookForPluginSync<H extends PluginLifecycleHooks>({
|
|
610
|
-
pluginName,
|
|
611
|
-
hookName,
|
|
612
|
-
parameters,
|
|
613
|
-
}: {
|
|
614
|
-
pluginName: string
|
|
615
|
-
hookName: H
|
|
616
|
-
parameters: PluginParameter<H>
|
|
617
|
-
}): Array<ReturnType<ParseResult<H>>> | null {
|
|
618
|
-
const plugin = this.plugins.get(pluginName)
|
|
619
|
-
|
|
620
|
-
if (!plugin) {
|
|
621
|
-
return null
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const result = this.#executeSync<H>({
|
|
625
|
-
strategy: 'hookFirst',
|
|
626
|
-
hookName,
|
|
627
|
-
parameters,
|
|
628
|
-
plugin,
|
|
629
|
-
})
|
|
630
|
-
|
|
631
|
-
return result !== null ? [result] : []
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* Returns the first non-null result.
|
|
636
|
-
*/
|
|
637
|
-
async hookFirst<H extends PluginLifecycleHooks>({
|
|
638
|
-
hookName,
|
|
639
|
-
parameters,
|
|
640
|
-
skipped,
|
|
641
|
-
}: {
|
|
642
|
-
hookName: H
|
|
643
|
-
parameters: PluginParameter<H>
|
|
644
|
-
skipped?: ReadonlySet<Plugin> | null
|
|
645
|
-
}): Promise<SafeParseResult<H>> {
|
|
646
|
-
const plugins: Array<Plugin> = []
|
|
647
|
-
for (const plugin of this.plugins.values()) {
|
|
648
|
-
if (hookName in plugin && (skipped ? !skipped.has(plugin) : true)) plugins.push(plugin)
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
this.hooks.emit('kubb:plugins:hook:progress:start', { hookName, plugins })
|
|
652
|
-
|
|
653
|
-
const promises = plugins.map((plugin) => {
|
|
654
|
-
return async () => {
|
|
655
|
-
const value = await this.#execute<H>({
|
|
656
|
-
strategy: 'hookFirst',
|
|
657
|
-
hookName,
|
|
658
|
-
parameters,
|
|
659
|
-
plugin,
|
|
660
|
-
})
|
|
661
|
-
|
|
662
|
-
return Promise.resolve({
|
|
663
|
-
plugin,
|
|
664
|
-
result: value,
|
|
665
|
-
} as SafeParseResult<H>)
|
|
666
|
-
}
|
|
667
|
-
})
|
|
668
|
-
|
|
669
|
-
const result = await hookFirst(promises, hookFirstNullCheck)
|
|
670
|
-
|
|
671
|
-
this.hooks.emit('kubb:plugins:hook:progress:end', { hookName })
|
|
672
|
-
|
|
673
|
-
return result
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
* Returns the first non-null result.
|
|
678
|
-
*/
|
|
679
|
-
hookFirstSync<H extends PluginLifecycleHooks>({
|
|
680
|
-
hookName,
|
|
681
|
-
parameters,
|
|
682
|
-
skipped,
|
|
683
|
-
}: {
|
|
684
|
-
hookName: H
|
|
685
|
-
parameters: PluginParameter<H>
|
|
686
|
-
skipped?: ReadonlySet<Plugin> | null
|
|
687
|
-
}): SafeParseResult<H> | null {
|
|
688
|
-
let parseResult: SafeParseResult<H> | null = null
|
|
689
|
-
|
|
690
|
-
for (const plugin of this.plugins.values()) {
|
|
691
|
-
if (!(hookName in plugin)) continue
|
|
692
|
-
if (skipped?.has(plugin)) continue
|
|
693
|
-
|
|
694
|
-
parseResult = {
|
|
695
|
-
result: this.#executeSync<H>({
|
|
696
|
-
strategy: 'hookFirst',
|
|
697
|
-
hookName,
|
|
698
|
-
parameters,
|
|
699
|
-
plugin,
|
|
700
|
-
}),
|
|
701
|
-
plugin,
|
|
702
|
-
} as SafeParseResult<H>
|
|
703
|
-
|
|
704
|
-
if (parseResult.result != null) break
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
return parseResult
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
/**
|
|
711
|
-
* Runs all plugins in parallel based on `this.plugin` order and `dependencies` settings.
|
|
712
|
-
*/
|
|
713
|
-
async hookParallel<H extends PluginLifecycleHooks, TOutput = void>({
|
|
714
|
-
hookName,
|
|
715
|
-
parameters,
|
|
716
|
-
}: {
|
|
717
|
-
hookName: H
|
|
718
|
-
parameters?: Parameters<RequiredPluginLifecycle[H]> | undefined
|
|
719
|
-
}): Promise<Awaited<TOutput>[]> {
|
|
720
|
-
const plugins: Array<Plugin> = []
|
|
721
|
-
for (const plugin of this.plugins.values()) {
|
|
722
|
-
if (hookName in plugin) plugins.push(plugin)
|
|
723
|
-
}
|
|
724
|
-
this.hooks.emit('kubb:plugins:hook:progress:start', { hookName, plugins })
|
|
725
|
-
|
|
726
|
-
const pluginStartTimes = new Map<Plugin, number>()
|
|
727
|
-
|
|
728
|
-
const promises = plugins.map((plugin) => {
|
|
729
|
-
return () => {
|
|
730
|
-
pluginStartTimes.set(plugin, performance.now())
|
|
731
|
-
return this.#execute({
|
|
732
|
-
strategy: 'hookParallel',
|
|
733
|
-
hookName,
|
|
734
|
-
parameters,
|
|
735
|
-
plugin,
|
|
736
|
-
}) as Promise<TOutput>
|
|
737
|
-
}
|
|
738
|
-
})
|
|
739
|
-
|
|
740
|
-
const results = await hookParallel(promises, this.options.concurrency)
|
|
741
|
-
|
|
742
|
-
results.forEach((result, index) => {
|
|
743
|
-
if (isPromiseRejectedResult<Error>(result)) {
|
|
744
|
-
const plugin = plugins[index]
|
|
745
|
-
|
|
746
|
-
if (plugin) {
|
|
747
|
-
const startTime = pluginStartTimes.get(plugin) ?? performance.now()
|
|
748
|
-
this.hooks.emit('kubb:error', result.reason, {
|
|
749
|
-
plugin,
|
|
750
|
-
hookName,
|
|
751
|
-
strategy: 'hookParallel',
|
|
752
|
-
duration: Math.round(performance.now() - startTime),
|
|
753
|
-
parameters,
|
|
754
|
-
})
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
})
|
|
758
|
-
|
|
759
|
-
this.hooks.emit('kubb:plugins:hook:progress:end', { hookName })
|
|
760
|
-
|
|
761
|
-
return results.reduce((acc, result) => {
|
|
762
|
-
if (result.status === 'fulfilled') {
|
|
763
|
-
acc.push(result.value)
|
|
764
|
-
}
|
|
765
|
-
return acc
|
|
766
|
-
}, [] as Awaited<TOutput>[])
|
|
767
|
-
}
|
|
392
|
+
} as unknown as GeneratorContext<TOptions>
|
|
768
393
|
|
|
769
|
-
|
|
770
|
-
* Execute a lifecycle hook sequentially for all plugins that implement it.
|
|
771
|
-
*/
|
|
772
|
-
async hookSeq<H extends PluginLifecycleHooks>({ hookName, parameters }: { hookName: H; parameters?: PluginParameter<H> }): Promise<void> {
|
|
773
|
-
const plugins: Array<Plugin> = []
|
|
774
|
-
for (const plugin of this.plugins.values()) {
|
|
775
|
-
if (hookName in plugin) plugins.push(plugin)
|
|
776
|
-
}
|
|
777
|
-
this.hooks.emit('kubb:plugins:hook:progress:start', { hookName, plugins })
|
|
778
|
-
|
|
779
|
-
const promises = plugins.map((plugin) => {
|
|
780
|
-
return () =>
|
|
781
|
-
this.#execute({
|
|
782
|
-
strategy: 'hookSeq',
|
|
783
|
-
hookName,
|
|
784
|
-
parameters,
|
|
785
|
-
plugin,
|
|
786
|
-
})
|
|
787
|
-
})
|
|
788
|
-
|
|
789
|
-
await hookSeq(promises)
|
|
790
|
-
|
|
791
|
-
this.hooks.emit('kubb:plugins:hook:progress:end', { hookName })
|
|
394
|
+
return baseContext
|
|
792
395
|
}
|
|
793
396
|
|
|
794
397
|
getPlugin<TName extends keyof Kubb.PluginRegistry>(pluginName: TName): Plugin<Kubb.PluginRegistry[TName]> | undefined
|
|
795
398
|
getPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(pluginName: string): Plugin<TOptions> | undefined
|
|
796
399
|
getPlugin(pluginName: string): Plugin | undefined {
|
|
797
|
-
return this.plugins.get(pluginName)
|
|
400
|
+
return this.plugins.get(pluginName)
|
|
798
401
|
}
|
|
799
402
|
|
|
800
403
|
/**
|
|
@@ -809,132 +412,4 @@ export class PluginDriver {
|
|
|
809
412
|
}
|
|
810
413
|
return plugin
|
|
811
414
|
}
|
|
812
|
-
|
|
813
|
-
/**
|
|
814
|
-
* Emit hook-processing completion metadata after a plugin hook resolves.
|
|
815
|
-
*/
|
|
816
|
-
#emitProcessingEnd<H extends PluginLifecycleHooks>({
|
|
817
|
-
startTime,
|
|
818
|
-
output,
|
|
819
|
-
strategy,
|
|
820
|
-
hookName,
|
|
821
|
-
plugin,
|
|
822
|
-
parameters,
|
|
823
|
-
}: {
|
|
824
|
-
startTime: number
|
|
825
|
-
output: unknown
|
|
826
|
-
strategy: Strategy
|
|
827
|
-
hookName: H
|
|
828
|
-
plugin: PluginWithLifeCycle
|
|
829
|
-
parameters: unknown[] | undefined
|
|
830
|
-
}): void {
|
|
831
|
-
this.hooks.emit('kubb:plugins:hook:processing:end', {
|
|
832
|
-
duration: Math.round(performance.now() - startTime),
|
|
833
|
-
parameters,
|
|
834
|
-
output,
|
|
835
|
-
strategy,
|
|
836
|
-
hookName,
|
|
837
|
-
plugin,
|
|
838
|
-
})
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
// Implementation signature
|
|
842
|
-
#execute<H extends PluginLifecycleHooks>({
|
|
843
|
-
strategy,
|
|
844
|
-
hookName,
|
|
845
|
-
parameters,
|
|
846
|
-
plugin,
|
|
847
|
-
}: {
|
|
848
|
-
strategy: Strategy
|
|
849
|
-
hookName: H
|
|
850
|
-
parameters: unknown[] | undefined
|
|
851
|
-
plugin: PluginWithLifeCycle
|
|
852
|
-
}): Promise<ReturnType<ParseResult<H>> | null> | null {
|
|
853
|
-
const hook = plugin[hookName]
|
|
854
|
-
|
|
855
|
-
if (!hook) {
|
|
856
|
-
return null
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
this.hooks.emit('kubb:plugins:hook:processing:start', {
|
|
860
|
-
strategy,
|
|
861
|
-
hookName,
|
|
862
|
-
parameters,
|
|
863
|
-
plugin,
|
|
864
|
-
})
|
|
865
|
-
|
|
866
|
-
const startTime = performance.now()
|
|
867
|
-
|
|
868
|
-
const task = (async () => {
|
|
869
|
-
try {
|
|
870
|
-
const output =
|
|
871
|
-
typeof hook === 'function' ? await Promise.resolve((hook as (...args: unknown[]) => unknown).apply(this.getContext(plugin), parameters ?? [])) : hook
|
|
872
|
-
|
|
873
|
-
this.#emitProcessingEnd({ startTime, output, strategy, hookName, plugin, parameters })
|
|
874
|
-
|
|
875
|
-
return output as ReturnType<ParseResult<H>>
|
|
876
|
-
} catch (error) {
|
|
877
|
-
this.hooks.emit('kubb:error', error as Error, {
|
|
878
|
-
plugin,
|
|
879
|
-
hookName,
|
|
880
|
-
strategy,
|
|
881
|
-
duration: Math.round(performance.now() - startTime),
|
|
882
|
-
})
|
|
883
|
-
|
|
884
|
-
return null
|
|
885
|
-
}
|
|
886
|
-
})()
|
|
887
|
-
|
|
888
|
-
return task
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
/**
|
|
892
|
-
* Execute a plugin lifecycle hook synchronously and return its output.
|
|
893
|
-
*/
|
|
894
|
-
#executeSync<H extends PluginLifecycleHooks>({
|
|
895
|
-
strategy,
|
|
896
|
-
hookName,
|
|
897
|
-
parameters,
|
|
898
|
-
plugin,
|
|
899
|
-
}: {
|
|
900
|
-
strategy: Strategy
|
|
901
|
-
hookName: H
|
|
902
|
-
parameters: PluginParameter<H>
|
|
903
|
-
plugin: PluginWithLifeCycle
|
|
904
|
-
}): ReturnType<ParseResult<H>> | null {
|
|
905
|
-
const hook = plugin[hookName]
|
|
906
|
-
|
|
907
|
-
if (!hook) {
|
|
908
|
-
return null
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
this.hooks.emit('kubb:plugins:hook:processing:start', {
|
|
912
|
-
strategy,
|
|
913
|
-
hookName,
|
|
914
|
-
parameters,
|
|
915
|
-
plugin,
|
|
916
|
-
})
|
|
917
|
-
|
|
918
|
-
const startTime = performance.now()
|
|
919
|
-
|
|
920
|
-
try {
|
|
921
|
-
const output =
|
|
922
|
-
typeof hook === 'function'
|
|
923
|
-
? ((hook as (...args: unknown[]) => unknown).apply(this.getContext(plugin), parameters) as ReturnType<ParseResult<H>>)
|
|
924
|
-
: (hook as ReturnType<ParseResult<H>>)
|
|
925
|
-
|
|
926
|
-
this.#emitProcessingEnd({ startTime, output, strategy, hookName, plugin, parameters })
|
|
927
|
-
|
|
928
|
-
return output
|
|
929
|
-
} catch (error) {
|
|
930
|
-
this.hooks.emit('kubb:error', error as Error, {
|
|
931
|
-
plugin,
|
|
932
|
-
hookName,
|
|
933
|
-
strategy,
|
|
934
|
-
duration: Math.round(performance.now() - startTime),
|
|
935
|
-
})
|
|
936
|
-
|
|
937
|
-
return null
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
415
|
}
|