@kubb/core 5.0.0-beta.15 → 5.0.0-beta.16
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-C1OsqGBJ.cjs → PluginDriver-C5hyNJfM.cjs} +13 -2
- package/dist/PluginDriver-C5hyNJfM.cjs.map +1 -0
- package/dist/{PluginDriver-CGypdXHg.js → PluginDriver-CT33kVoQ.js} +13 -2
- package/dist/PluginDriver-CT33kVoQ.js.map +1 -0
- package/dist/{createKubb-uVWTlN_w.d.ts → createKubb-BncBLGm_.d.ts} +45 -17
- package/dist/index.cjs +255 -192
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +256 -196
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.js +1 -1
- package/package.json +4 -9
- package/src/FileProcessor.ts +26 -31
- package/src/PluginDriver.ts +11 -4
- package/src/constants.ts +10 -9
- package/src/createAdapter.ts +17 -1
- package/src/createKubb.ts +314 -105
- package/src/defineGenerator.ts +4 -1
- package/src/types.ts +1 -1
- package/dist/PluginDriver-C1OsqGBJ.cjs.map +0 -1
- package/dist/PluginDriver-CGypdXHg.js.map +0 -1
package/src/createKubb.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { resolve } from 'node:path'
|
|
|
2
2
|
import { version as nodeVersion } from 'node:process'
|
|
3
3
|
import type { PossiblePromise } from '@internals/utils'
|
|
4
4
|
import { AsyncEventEmitter, BuildError, exists, formatMs, getElapsedMs, URLPath } from '@internals/utils'
|
|
5
|
-
import type { FileNode, InputNode, OperationNode, SchemaNode } from '@kubb/ast'
|
|
5
|
+
import type { FileNode, InputNode, InputStreamNode, OperationNode, SchemaNode } from '@kubb/ast'
|
|
6
6
|
import { collectUsedSchemaNames, transform, walk } from '@kubb/ast'
|
|
7
7
|
import { version as KubbVersion } from '../package.json'
|
|
8
|
-
import { DEFAULT_BANNER, DEFAULT_EXTENSION, DEFAULT_STUDIO_URL } from './constants.ts'
|
|
8
|
+
import { DEFAULT_BANNER, DEFAULT_EXTENSION, DEFAULT_STUDIO_URL, STREAM_FLUSH_EVERY, STREAM_SCHEMA_THRESHOLD } from './constants.ts'
|
|
9
9
|
import type { Adapter, AdapterSource } from './createAdapter.ts'
|
|
10
10
|
import type { RendererFactory } from './createRenderer.ts'
|
|
11
11
|
import { createStorage, type Storage } from './createStorage.ts'
|
|
@@ -674,7 +674,7 @@ export type CLIOptions = {
|
|
|
674
674
|
|
|
675
675
|
/**
|
|
676
676
|
* All accepted forms of a Kubb configuration.
|
|
677
|
-
* Accepts `Config`/`Config[]`/promise or a factory (optionally receiving `TCliOptions
|
|
677
|
+
* Accepts `Config`/`Config[]`/promise or a factory (optionally receiving `TCliOptions`.
|
|
678
678
|
*/
|
|
679
679
|
export type PossibleConfig<TCliOptions = undefined> =
|
|
680
680
|
| PossiblePromise<Config | Config[]>
|
|
@@ -929,16 +929,45 @@ async function setup(userConfig: UserConfig, options: SetupOptions = {}): Promis
|
|
|
929
929
|
})
|
|
930
930
|
|
|
931
931
|
driver.adapter = config.adapter
|
|
932
|
-
driver.inputNode = await config.adapter.parse(source)
|
|
933
932
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
933
|
+
if (config.adapter.count && config.adapter.stream) {
|
|
934
|
+
const { schemas: schemaCount, operations: operationCount } = await config.adapter.count(source)
|
|
935
|
+
|
|
936
|
+
if (schemaCount > STREAM_SCHEMA_THRESHOLD) {
|
|
937
|
+
driver.inputStreamNode = await config.adapter.stream(source)
|
|
938
|
+
|
|
939
|
+
await hooks.emit('kubb:debug', {
|
|
940
|
+
date: new Date(),
|
|
941
|
+
logs: [
|
|
942
|
+
`✓ Adapter '${config.adapter.name}' streaming InputStreamNode`,
|
|
943
|
+
` • Schemas: ${schemaCount} (threshold: ${STREAM_SCHEMA_THRESHOLD})`,
|
|
944
|
+
` • Operations: ${operationCount}`,
|
|
945
|
+
],
|
|
946
|
+
})
|
|
947
|
+
} else {
|
|
948
|
+
driver.inputNode = await config.adapter.parse(source)
|
|
949
|
+
|
|
950
|
+
await hooks.emit('kubb:debug', {
|
|
951
|
+
date: new Date(),
|
|
952
|
+
logs: [
|
|
953
|
+
`✓ Adapter '${config.adapter.name}' resolved InputNode`,
|
|
954
|
+
` • Schemas: ${driver.inputNode.schemas.length}`,
|
|
955
|
+
` • Operations: ${driver.inputNode.operations.length}`,
|
|
956
|
+
],
|
|
957
|
+
})
|
|
958
|
+
}
|
|
959
|
+
} else {
|
|
960
|
+
driver.inputNode = await config.adapter.parse(source)
|
|
961
|
+
|
|
962
|
+
await hooks.emit('kubb:debug', {
|
|
963
|
+
date: new Date(),
|
|
964
|
+
logs: [
|
|
965
|
+
`✓ Adapter '${config.adapter.name}' resolved InputNode`,
|
|
966
|
+
` • Schemas: ${driver.inputNode.schemas.length}`,
|
|
967
|
+
` • Operations: ${driver.inputNode.operations.length}`,
|
|
968
|
+
],
|
|
969
|
+
})
|
|
970
|
+
}
|
|
942
971
|
}
|
|
943
972
|
|
|
944
973
|
return {
|
|
@@ -965,6 +994,156 @@ async function setup(userConfig: UserConfig, options: SetupOptions = {}): Promis
|
|
|
965
994
|
* schemas that fall outside that set. This ensures that component schemas referenced
|
|
966
995
|
* exclusively by excluded operations are not generated.
|
|
967
996
|
*/
|
|
997
|
+
type PluginStreamEntry = {
|
|
998
|
+
plugin: NormalizedPlugin
|
|
999
|
+
context: GeneratorContext
|
|
1000
|
+
hrStart: ReturnType<typeof process.hrtime>
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Single-pass fan-out for streaming mode.
|
|
1005
|
+
*
|
|
1006
|
+
* Iterates `inputStreamNode.schemas` and `.operations` exactly once, distributing each
|
|
1007
|
+
* node to every plugin in parallel. This replaces the N-pass-per-plugin pattern (where
|
|
1008
|
+
* each plugin got its own `for await` iterator) with a single parse pass fanned to all
|
|
1009
|
+
* plugins — eliminating the N×parse-time overhead for multi-plugin builds.
|
|
1010
|
+
*
|
|
1011
|
+
* Each plugin still gets independent `plugin:start` / `plugin:end` events and its own
|
|
1012
|
+
* timing, but the schema and operation nodes are parsed only once total.
|
|
1013
|
+
*/
|
|
1014
|
+
async function runPluginStreamHooks(
|
|
1015
|
+
inputStreamNode: InputStreamNode,
|
|
1016
|
+
entries: PluginStreamEntry[],
|
|
1017
|
+
driver: PluginDriver,
|
|
1018
|
+
hooks: AsyncEventEmitter<KubbHooks>,
|
|
1019
|
+
config: Config,
|
|
1020
|
+
pluginTimings: Map<string, number>,
|
|
1021
|
+
failedPlugins: Set<{ plugin: Plugin; error: Error }>,
|
|
1022
|
+
flushPendingFiles: () => Promise<void>,
|
|
1023
|
+
): Promise<void> {
|
|
1024
|
+
type PluginState = {
|
|
1025
|
+
plugin: NormalizedPlugin
|
|
1026
|
+
generatorContext: GeneratorContext
|
|
1027
|
+
generators: Generator[]
|
|
1028
|
+
hrStart: ReturnType<typeof process.hrtime>
|
|
1029
|
+
failed: boolean
|
|
1030
|
+
error: Error | undefined
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function resolveRendererFor(gen: Generator, state: PluginState): RendererFactory | undefined {
|
|
1034
|
+
return gen.renderer === null ? undefined : (gen.renderer ?? state.plugin.renderer ?? state.generatorContext.config.renderer)
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
const states: PluginState[] = entries.map(({ plugin, context, hrStart }) => ({
|
|
1038
|
+
plugin,
|
|
1039
|
+
generatorContext: { ...context, resolver: driver.getResolver(plugin.name) },
|
|
1040
|
+
generators: plugin.generators ?? [],
|
|
1041
|
+
hrStart,
|
|
1042
|
+
failed: false,
|
|
1043
|
+
error: undefined,
|
|
1044
|
+
}))
|
|
1045
|
+
|
|
1046
|
+
let schemasProcessed = 0
|
|
1047
|
+
for await (const node of inputStreamNode.schemas) {
|
|
1048
|
+
for (const state of states) {
|
|
1049
|
+
if (state.failed) continue
|
|
1050
|
+
try {
|
|
1051
|
+
const { plugin, generatorContext, generators } = state
|
|
1052
|
+
const { exclude, include, override } = plugin.options
|
|
1053
|
+
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
1054
|
+
const options = generatorContext.resolver.resolveOptions(transformedNode, { options: plugin.options, exclude, include, override })
|
|
1055
|
+
if (options === null) continue
|
|
1056
|
+
|
|
1057
|
+
const ctx = { ...generatorContext, options }
|
|
1058
|
+
for (const gen of generators) {
|
|
1059
|
+
if (!gen.schema) continue
|
|
1060
|
+
const result = await gen.schema(transformedNode, ctx)
|
|
1061
|
+
await applyHookResult(result, driver, resolveRendererFor(gen, state))
|
|
1062
|
+
}
|
|
1063
|
+
await driver.hooks.emit('kubb:generate:schema', transformedNode, ctx)
|
|
1064
|
+
} catch (caughtError) {
|
|
1065
|
+
state.failed = true
|
|
1066
|
+
state.error = caughtError as Error
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
schemasProcessed++
|
|
1070
|
+
if (schemasProcessed % STREAM_FLUSH_EVERY === 0) {
|
|
1071
|
+
await flushPendingFiles()
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
const collectedOperations: OperationNode[] = []
|
|
1076
|
+
for await (const node of inputStreamNode.operations) {
|
|
1077
|
+
collectedOperations.push(node)
|
|
1078
|
+
for (const state of states) {
|
|
1079
|
+
if (state.failed) continue
|
|
1080
|
+
try {
|
|
1081
|
+
const { plugin, generatorContext, generators } = state
|
|
1082
|
+
const { exclude, include, override } = plugin.options
|
|
1083
|
+
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
1084
|
+
const options = generatorContext.resolver.resolveOptions(transformedNode, { options: plugin.options, exclude, include, override })
|
|
1085
|
+
if (options === null) continue
|
|
1086
|
+
|
|
1087
|
+
const ctx = { ...generatorContext, options }
|
|
1088
|
+
for (const gen of generators) {
|
|
1089
|
+
if (!gen.operation) continue
|
|
1090
|
+
const result = await gen.operation(transformedNode, ctx)
|
|
1091
|
+
await applyHookResult(result, driver, resolveRendererFor(gen, state))
|
|
1092
|
+
}
|
|
1093
|
+
await driver.hooks.emit('kubb:generate:operation', transformedNode, ctx)
|
|
1094
|
+
} catch (caughtError) {
|
|
1095
|
+
state.failed = true
|
|
1096
|
+
state.error = caughtError as Error
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// After stream: gen.operations for each plugin, then emit plugin:end
|
|
1102
|
+
for (const state of states) {
|
|
1103
|
+
if (!state.failed) {
|
|
1104
|
+
try {
|
|
1105
|
+
const { plugin, generatorContext, generators } = state
|
|
1106
|
+
const ctx = { ...generatorContext, options: plugin.options }
|
|
1107
|
+
for (const gen of generators) {
|
|
1108
|
+
if (!gen.operations) continue
|
|
1109
|
+
const result = await gen.operations(collectedOperations, ctx)
|
|
1110
|
+
await applyHookResult(result, driver, resolveRendererFor(gen, state))
|
|
1111
|
+
}
|
|
1112
|
+
await driver.hooks.emit('kubb:generate:operations', collectedOperations, ctx)
|
|
1113
|
+
} catch (caughtError) {
|
|
1114
|
+
state.failed = true
|
|
1115
|
+
state.error = caughtError as Error
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const duration = getElapsedMs(state.hrStart)
|
|
1120
|
+
pluginTimings.set(state.plugin.name, duration)
|
|
1121
|
+
|
|
1122
|
+
await hooks.emit('kubb:plugin:end', {
|
|
1123
|
+
plugin: state.plugin,
|
|
1124
|
+
duration,
|
|
1125
|
+
success: !state.failed,
|
|
1126
|
+
...(state.failed && state.error ? { error: state.error } : {}),
|
|
1127
|
+
config,
|
|
1128
|
+
get files() {
|
|
1129
|
+
return driver.fileManager.files
|
|
1130
|
+
},
|
|
1131
|
+
upsertFile: (...files) => driver.fileManager.upsert(...files),
|
|
1132
|
+
})
|
|
1133
|
+
|
|
1134
|
+
if (state.failed && state.error) {
|
|
1135
|
+
failedPlugins.add({ plugin: state.plugin, error: state.error })
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
await hooks.emit('kubb:debug', {
|
|
1139
|
+
date: new Date(),
|
|
1140
|
+
logs: [state.failed ? '✗ Plugin start failed' : `✓ Plugin started successfully (${formatMs(duration)})`],
|
|
1141
|
+
})
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
await flushPendingFiles()
|
|
1145
|
+
}
|
|
1146
|
+
|
|
968
1147
|
async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorContext): Promise<void> {
|
|
969
1148
|
const { adapter, inputNode, resolver, driver } = context
|
|
970
1149
|
const { exclude, include, override } = plugin.options
|
|
@@ -985,6 +1164,7 @@ async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorCon
|
|
|
985
1164
|
resolver: driver.getResolver(plugin.name),
|
|
986
1165
|
}
|
|
987
1166
|
|
|
1167
|
+
// ── BATCH PATH ────────────────────────────────────────────────────────────
|
|
988
1168
|
// When `include` has operation-based filters (tag, operationId, path, method, contentType)
|
|
989
1169
|
// but no schema-level filters (schemaName), pre-compute the set of top-level schema names
|
|
990
1170
|
// that are transitively referenced by the included operations. Schemas outside that set are
|
|
@@ -995,11 +1175,11 @@ async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorCon
|
|
|
995
1175
|
|
|
996
1176
|
let allowedSchemaNames: Set<string> | undefined
|
|
997
1177
|
if (hasOperationBasedIncludes && !hasSchemaNameIncludes) {
|
|
998
|
-
const includedOps = inputNode
|
|
999
|
-
allowedSchemaNames = collectUsedSchemaNames(includedOps, inputNode
|
|
1178
|
+
const includedOps = inputNode!.operations.filter((op) => resolver.resolveOptions(op, { options: plugin.options, exclude, include, override }) !== null)
|
|
1179
|
+
allowedSchemaNames = collectUsedSchemaNames(includedOps, inputNode!.schemas)
|
|
1000
1180
|
}
|
|
1001
1181
|
|
|
1002
|
-
await walk(inputNode
|
|
1182
|
+
await walk(inputNode!, {
|
|
1003
1183
|
depth: 'shallow',
|
|
1004
1184
|
async schema(node) {
|
|
1005
1185
|
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
@@ -1081,34 +1261,8 @@ async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
|
|
|
1081
1261
|
}
|
|
1082
1262
|
const fileProcessor = new FileProcessor()
|
|
1083
1263
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
})
|
|
1087
|
-
|
|
1088
|
-
fileProcessor.events.on('update', async ({ file, source, processed, total, percentage }) => {
|
|
1089
|
-
await hooks.emit('kubb:file:processing:update', {
|
|
1090
|
-
file,
|
|
1091
|
-
source,
|
|
1092
|
-
processed,
|
|
1093
|
-
total,
|
|
1094
|
-
percentage,
|
|
1095
|
-
config,
|
|
1096
|
-
})
|
|
1097
|
-
if (source) {
|
|
1098
|
-
await storage.setItem(file.path, source)
|
|
1099
|
-
}
|
|
1100
|
-
})
|
|
1101
|
-
|
|
1102
|
-
fileProcessor.events.on('end', async (processed) => {
|
|
1103
|
-
await hooks.emit('kubb:files:processing:end', { files: processed })
|
|
1104
|
-
await hooks.emit('kubb:debug', {
|
|
1105
|
-
date: new Date(),
|
|
1106
|
-
logs: [`✓ File write process completed for ${processed.length} files`],
|
|
1107
|
-
})
|
|
1108
|
-
})
|
|
1109
|
-
|
|
1110
|
-
async function flushPendingFiles(): Promise<void> {
|
|
1111
|
-
const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path))
|
|
1264
|
+
async function flushPendingFiles(snapshot?: ReadonlySet<string>): Promise<void> {
|
|
1265
|
+
const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path) && (!snapshot || !snapshot.has(f.path)))
|
|
1112
1266
|
if (files.length === 0) {
|
|
1113
1267
|
return
|
|
1114
1268
|
}
|
|
@@ -1118,25 +1272,33 @@ async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
|
|
|
1118
1272
|
logs: [`Writing ${files.length} files...`],
|
|
1119
1273
|
})
|
|
1120
1274
|
|
|
1121
|
-
await
|
|
1122
|
-
parsers: parsersMap,
|
|
1123
|
-
mode: 'parallel',
|
|
1124
|
-
extension: config.output.extension,
|
|
1125
|
-
})
|
|
1275
|
+
await hooks.emit('kubb:files:processing:start', { files })
|
|
1126
1276
|
|
|
1127
|
-
|
|
1277
|
+
const stream = fileProcessor.stream(files, { parsers: parsersMap, extension: config.output.extension })
|
|
1278
|
+
|
|
1279
|
+
for await (const { file, source, processed, total, percentage } of stream) {
|
|
1280
|
+
await hooks.emit('kubb:file:processing:update', { file, source, processed, total, percentage, config })
|
|
1281
|
+
if (source) {
|
|
1282
|
+
await storage.setItem(file.path, source)
|
|
1283
|
+
}
|
|
1128
1284
|
writtenPaths.add(file.path)
|
|
1129
1285
|
}
|
|
1286
|
+
|
|
1287
|
+
await hooks.emit('kubb:files:processing:end', { files })
|
|
1288
|
+
await hooks.emit('kubb:debug', {
|
|
1289
|
+
date: new Date(),
|
|
1290
|
+
logs: [`✓ File write process completed for ${files.length} files`],
|
|
1291
|
+
})
|
|
1130
1292
|
}
|
|
1131
1293
|
|
|
1132
1294
|
try {
|
|
1133
1295
|
await driver.emitSetupHooks()
|
|
1134
1296
|
|
|
1135
|
-
if (driver.adapter && driver.inputNode) {
|
|
1297
|
+
if (driver.adapter && (driver.inputNode || driver.inputStreamNode)) {
|
|
1136
1298
|
await hooks.emit('kubb:build:start', {
|
|
1137
1299
|
config,
|
|
1138
1300
|
adapter: driver.adapter,
|
|
1139
|
-
inputNode: driver.inputNode,
|
|
1301
|
+
inputNode: driver.inputNode ?? { kind: 'Input' as const, schemas: [], operations: [], meta: driver.inputStreamNode?.meta },
|
|
1140
1302
|
getPlugin: driver.getPlugin.bind(driver),
|
|
1141
1303
|
get files() {
|
|
1142
1304
|
return driver.fileManager.files
|
|
@@ -1145,70 +1307,117 @@ async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
|
|
|
1145
1307
|
})
|
|
1146
1308
|
}
|
|
1147
1309
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1310
|
+
const inputStreamNode = driver.inputStreamNode
|
|
1311
|
+
if (inputStreamNode) {
|
|
1312
|
+
// ── STREAMING: fan-out single-pass ────────────────────────────────────
|
|
1313
|
+
// Emit plugin:start for all plugins up front, collect generator-plugins
|
|
1314
|
+
// for the fan-out pass, then handle non-generator plugins immediately.
|
|
1315
|
+
const streamPluginEntries: PluginStreamEntry[] = []
|
|
1151
1316
|
|
|
1152
|
-
|
|
1153
|
-
const
|
|
1317
|
+
for (const plugin of driver.plugins.values()) {
|
|
1318
|
+
const context = driver.getContext(plugin)
|
|
1319
|
+
const hrStart = process.hrtime()
|
|
1154
1320
|
|
|
1155
1321
|
await hooks.emit('kubb:plugin:start', { plugin })
|
|
1156
1322
|
await hooks.emit('kubb:debug', {
|
|
1157
|
-
date:
|
|
1323
|
+
date: new Date(),
|
|
1158
1324
|
logs: ['Starting plugin...', ` • Plugin Name: ${plugin.name}`],
|
|
1159
1325
|
})
|
|
1160
1326
|
|
|
1161
1327
|
if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) {
|
|
1162
|
-
|
|
1328
|
+
streamPluginEntries.push({ plugin, context, hrStart })
|
|
1329
|
+
} else {
|
|
1330
|
+
// No generators: plugin ran via setup hooks; finish it now.
|
|
1331
|
+
const duration = getElapsedMs(hrStart)
|
|
1332
|
+
pluginTimings.set(plugin.name, duration)
|
|
1333
|
+
await hooks.emit('kubb:plugin:end', {
|
|
1334
|
+
plugin,
|
|
1335
|
+
duration,
|
|
1336
|
+
success: true,
|
|
1337
|
+
config,
|
|
1338
|
+
get files() {
|
|
1339
|
+
return driver.fileManager.files
|
|
1340
|
+
},
|
|
1341
|
+
upsertFile: (...files) => driver.fileManager.upsert(...files),
|
|
1342
|
+
})
|
|
1343
|
+
await hooks.emit('kubb:debug', {
|
|
1344
|
+
date: new Date(),
|
|
1345
|
+
logs: [`✓ Plugin started successfully (${formatMs(duration)})`],
|
|
1346
|
+
})
|
|
1163
1347
|
}
|
|
1348
|
+
}
|
|
1164
1349
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1350
|
+
if (streamPluginEntries.length > 0) {
|
|
1351
|
+
await runPluginStreamHooks(inputStreamNode, streamPluginEntries, driver, hooks, config, pluginTimings, failedPlugins, flushPendingFiles)
|
|
1352
|
+
}
|
|
1353
|
+
} else {
|
|
1354
|
+
// ── BATCH: existing per-plugin sequential loop ────────────────────────
|
|
1355
|
+
for (const plugin of driver.plugins.values()) {
|
|
1356
|
+
const context = driver.getContext(plugin)
|
|
1357
|
+
const hrStart = process.hrtime()
|
|
1358
|
+
|
|
1359
|
+
try {
|
|
1360
|
+
const timestamp = new Date()
|
|
1361
|
+
|
|
1362
|
+
await hooks.emit('kubb:plugin:start', { plugin })
|
|
1363
|
+
await hooks.emit('kubb:debug', {
|
|
1364
|
+
date: timestamp,
|
|
1365
|
+
logs: ['Starting plugin...', ` • Plugin Name: ${plugin.name}`],
|
|
1366
|
+
})
|
|
1367
|
+
|
|
1368
|
+
if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) {
|
|
1369
|
+
await runPluginAstHooks(plugin, context)
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
const duration = getElapsedMs(hrStart)
|
|
1373
|
+
pluginTimings.set(plugin.name, duration)
|
|
1374
|
+
|
|
1375
|
+
await hooks.emit('kubb:plugin:end', {
|
|
1376
|
+
plugin,
|
|
1377
|
+
duration,
|
|
1378
|
+
success: true,
|
|
1379
|
+
config,
|
|
1380
|
+
get files() {
|
|
1381
|
+
return driver.fileManager.files
|
|
1382
|
+
},
|
|
1383
|
+
upsertFile: (...files) => driver.fileManager.upsert(...files),
|
|
1384
|
+
})
|
|
1385
|
+
|
|
1386
|
+
await hooks.emit('kubb:debug', {
|
|
1387
|
+
date: new Date(),
|
|
1388
|
+
logs: [`✓ Plugin started successfully (${formatMs(duration)})`],
|
|
1389
|
+
})
|
|
1390
|
+
} catch (caughtError) {
|
|
1391
|
+
const error = caughtError as Error
|
|
1392
|
+
const errorTimestamp = new Date()
|
|
1393
|
+
const duration = getElapsedMs(hrStart)
|
|
1394
|
+
|
|
1395
|
+
await hooks.emit('kubb:plugin:end', {
|
|
1396
|
+
plugin,
|
|
1397
|
+
duration,
|
|
1398
|
+
success: false,
|
|
1399
|
+
error,
|
|
1400
|
+
config,
|
|
1401
|
+
get files() {
|
|
1402
|
+
return driver.fileManager.files
|
|
1403
|
+
},
|
|
1404
|
+
upsertFile: (...files) => driver.fileManager.upsert(...files),
|
|
1405
|
+
})
|
|
1406
|
+
|
|
1407
|
+
await hooks.emit('kubb:debug', {
|
|
1408
|
+
date: errorTimestamp,
|
|
1409
|
+
logs: [
|
|
1410
|
+
'✗ Plugin start failed',
|
|
1411
|
+
` • Plugin Name: ${plugin.name}`,
|
|
1412
|
+
` • Error: ${error.constructor.name} - ${error.message}`,
|
|
1413
|
+
' • Stack Trace:',
|
|
1414
|
+
error.stack || 'No stack trace available',
|
|
1415
|
+
],
|
|
1416
|
+
})
|
|
1417
|
+
|
|
1418
|
+
failedPlugins.add({ plugin, error })
|
|
1419
|
+
}
|
|
1420
|
+
await flushPendingFiles()
|
|
1212
1421
|
}
|
|
1213
1422
|
}
|
|
1214
1423
|
|
package/src/defineGenerator.ts
CHANGED
|
@@ -160,7 +160,10 @@ export type Generator<TOptions extends PluginFactoryOptions = PluginFactoryOptio
|
|
|
160
160
|
* `ctx` carries the plugin context with `adapter` and `inputNode` guaranteed present,
|
|
161
161
|
* plus `ctx.options` with the plugin-level options for the batch call.
|
|
162
162
|
*/
|
|
163
|
-
operations?: (
|
|
163
|
+
operations?: (
|
|
164
|
+
nodes: Array<OperationNode> | AsyncIterable<OperationNode>,
|
|
165
|
+
ctx: GeneratorContext<TOptions>,
|
|
166
|
+
) => PossiblePromise<TElement | Array<FileNode> | void>
|
|
164
167
|
}
|
|
165
168
|
|
|
166
169
|
/**
|
package/src/types.ts
CHANGED
|
@@ -32,7 +32,7 @@ export type {
|
|
|
32
32
|
} from './createKubb.ts'
|
|
33
33
|
export type { Renderer, RendererFactory } from './createRenderer.ts'
|
|
34
34
|
export type { Storage } from './createStorage.ts'
|
|
35
|
-
export type { FileProcessorEvents } from './FileProcessor.ts'
|
|
35
|
+
export type { FileProcessorEvents, ParsedFile } from './FileProcessor.ts'
|
|
36
36
|
export type { Generator, GeneratorContext } from './defineGenerator.ts'
|
|
37
37
|
export type { Logger, LoggerContext, LoggerOptions, UserLogger } from './defineLogger.ts'
|
|
38
38
|
export type { Middleware } from './defineMiddleware.ts'
|