@kubb/core 5.0.0-beta.18 → 5.0.0-beta.19

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/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_PluginDriver = require("./PluginDriver-Bc-k8EUQ.cjs");
2
+ const require_PluginDriver = require("./PluginDriver-DXp767s2.cjs");
3
3
  let node_events = require("node:events");
4
4
  let node_fs_promises = require("node:fs/promises");
5
5
  let node_path = require("node:path");
@@ -69,9 +69,12 @@ var AsyncEventEmitter = class {
69
69
  * await emitter.emit('build', 'petstore')
70
70
  * ```
71
71
  */
72
- async emit(eventName, ...eventArgs) {
72
+ emit(eventName, ...eventArgs) {
73
73
  const listeners = this.#emitter.listeners(eventName);
74
74
  if (listeners.length === 0) return;
75
+ return this.#emitAll(eventName, listeners, eventArgs);
76
+ }
77
+ async #emitAll(eventName, listeners, eventArgs) {
75
78
  for (const listener of listeners) try {
76
79
  await listener(...eventArgs);
77
80
  } catch (err) {
@@ -245,6 +248,19 @@ async function clean(path) {
245
248
  });
246
249
  }
247
250
  //#endregion
251
+ //#region ../../internals/utils/src/promise.ts
252
+ /** Returns `true` when `result` is a thenable `Promise`.
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * isPromise(Promise.resolve(1)) // true
257
+ * isPromise(42) // false
258
+ * ```
259
+ */
260
+ function isPromise(result) {
261
+ return result !== null && result !== void 0 && typeof result["then"] === "function";
262
+ }
263
+ //#endregion
248
264
  //#region ../../internals/utils/src/reserved.ts
249
265
  /**
250
266
  * JavaScript and Java reserved words.
@@ -525,7 +541,7 @@ function createAdapter(build) {
525
541
  }
526
542
  //#endregion
527
543
  //#region package.json
528
- var version = "5.0.0-beta.18";
544
+ var version = "5.0.0-beta.19";
529
545
  //#endregion
530
546
  //#region src/createStorage.ts
531
547
  /**
@@ -566,7 +582,14 @@ function createStorage(build) {
566
582
  //#endregion
567
583
  //#region src/FileProcessor.ts
568
584
  function joinSources(file) {
569
- return file.sources.map((item) => (0, _kubb_ast.extractStringsFromNodes)(item.nodes)).filter(Boolean).join("\n\n");
585
+ const sources = file.sources;
586
+ if (sources.length === 0) return "";
587
+ const parts = [];
588
+ for (const source of sources) {
589
+ const s = (0, _kubb_ast.extractStringsFromNodes)(source.nodes);
590
+ if (s) parts.push(s);
591
+ }
592
+ return parts.join("\n\n");
570
593
  }
571
594
  /**
572
595
  * Converts a single file to a string using the registered parsers.
@@ -576,25 +599,19 @@ function joinSources(file) {
576
599
  */
577
600
  var FileProcessor = class {
578
601
  events = new AsyncEventEmitter();
579
- async parse(file, { parsers, extension } = {}) {
602
+ parse(file, { parsers, extension } = {}) {
580
603
  const parseExtName = extension?.[file.extname] || void 0;
581
604
  if (!parsers || !file.extname) return joinSources(file);
582
605
  const parser = parsers.get(file.extname);
583
606
  if (!parser) return joinSources(file);
584
607
  return parser.parse(file, { extname: parseExtName });
585
608
  }
586
- /**
587
- * Streams parsed files one at a time as each is processed.
588
- *
589
- * Unlike `run()`, files are yielded immediately after parsing rather than batched.
590
- * Storage writes can begin as soon as the first file is ready, keeping peak
591
- * memory proportional to one file at a time instead of the full batch.
592
- */
593
- async *stream(files, options = {}) {
609
+ *stream(files, options = {}) {
594
610
  const total = files.length;
611
+ if (total === 0) return;
595
612
  let processed = 0;
596
613
  for (const file of files) {
597
- const source = await this.parse(file, options);
614
+ const source = this.parse(file, options);
598
615
  processed++;
599
616
  yield {
600
617
  file,
@@ -607,7 +624,7 @@ var FileProcessor = class {
607
624
  }
608
625
  async run(files, options = {}) {
609
626
  await this.events.emit("start", files);
610
- for await (const { file, source, processed, total, percentage } of this.stream(files, options)) await this.events.emit("update", {
627
+ for (const { file, source, processed, total, percentage } of this.stream(files, options)) await this.events.emit("update", {
611
628
  file,
612
629
  source,
613
630
  processed,
@@ -881,18 +898,9 @@ async function setup(userConfig, options = {}) {
881
898
  ` • Operations: ${operationCount}`
882
899
  ]
883
900
  });
884
- } else {
885
- driver.inputNode = await config.adapter.parse(source);
886
- await hooks.emit("kubb:debug", {
887
- date: /* @__PURE__ */ new Date(),
888
- logs: [
889
- `✓ Adapter '${config.adapter.name}' resolved InputNode`,
890
- ` • Schemas: ${driver.inputNode.schemas.length}`,
891
- ` • Operations: ${driver.inputNode.operations.length}`
892
- ]
893
- });
894
901
  }
895
- } else {
902
+ }
903
+ if (!driver.inputStreamNode) {
896
904
  driver.inputNode = await config.adapter.parse(source);
897
905
  await hooks.emit("kubb:debug", {
898
906
  date: /* @__PURE__ */ new Date(),
@@ -928,84 +936,101 @@ async function setup(userConfig, options = {}) {
928
936
  * Each plugin still gets independent `plugin:start` / `plugin:end` events and its own
929
937
  * timing, but the schema and operation nodes are parsed only once total.
930
938
  */
931
- async function runPluginStreamHooks(inputStreamNode, entries, driver, hooks, config, pluginTimings, failedPlugins, flushPendingFiles) {
939
+ async function runPluginStreamHooks({ entries, driver, pluginTimings, failedPlugins }) {
940
+ const inputStreamNode = driver.inputStreamNode;
932
941
  function resolveRendererFor(gen, state) {
933
942
  return gen.renderer === null ? void 0 : gen.renderer ?? state.plugin.renderer ?? state.generatorContext.config.renderer;
934
943
  }
935
- const states = entries.map(({ plugin, context, hrStart }) => ({
936
- plugin,
937
- generatorContext: {
938
- ...context,
939
- resolver: driver.getResolver(plugin.name)
940
- },
941
- generators: plugin.generators ?? [],
942
- hrStart,
943
- failed: false,
944
- error: void 0
945
- }));
946
- let schemasProcessed = 0;
947
- for await (const node of inputStreamNode.schemas) {
948
- for (const state of states) {
949
- if (state.failed) continue;
950
- try {
951
- const { plugin, generatorContext, generators } = state;
952
- const { exclude, include, override } = plugin.options;
953
- const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
954
- const options = generatorContext.resolver.resolveOptions(transformedNode, {
955
- options: plugin.options,
956
- exclude,
957
- include,
958
- override
944
+ const states = entries.map(({ plugin, context, hrStart }) => {
945
+ const { exclude, include, override } = plugin.options;
946
+ const hasExclude = Array.isArray(exclude) && exclude.length > 0;
947
+ const hasInclude = Array.isArray(include) && include.length > 0;
948
+ const hasOverride = Array.isArray(override) && override.length > 0;
949
+ return {
950
+ plugin,
951
+ generatorContext: {
952
+ ...context,
953
+ resolver: driver.getResolver(plugin.name)
954
+ },
955
+ generators: plugin.generators ?? [],
956
+ hrStart,
957
+ failed: false,
958
+ error: void 0,
959
+ optionsAreStatic: !hasExclude && !hasInclude && !hasOverride
960
+ };
961
+ });
962
+ async function dispatchSchema(state, node) {
963
+ if (state.failed) return;
964
+ try {
965
+ const { plugin, generatorContext, generators } = state;
966
+ const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
967
+ const { exclude, include, override } = plugin.options;
968
+ const options = state.optionsAreStatic ? plugin.options : generatorContext.resolver.resolveOptions(transformedNode, {
969
+ options: plugin.options,
970
+ exclude,
971
+ include,
972
+ override
973
+ });
974
+ if (options === null) return;
975
+ const ctx = {
976
+ ...generatorContext,
977
+ options
978
+ };
979
+ for (const gen of generators) {
980
+ if (!gen.schema) continue;
981
+ const raw = gen.schema(transformedNode, ctx);
982
+ const applied = require_PluginDriver.applyHookResult({
983
+ result: isPromise(raw) ? await raw : raw,
984
+ driver,
985
+ rendererFactory: resolveRendererFor(gen, state)
959
986
  });
960
- if (options === null) continue;
961
- const ctx = {
962
- ...generatorContext,
963
- options
964
- };
965
- for (const gen of generators) {
966
- if (!gen.schema) continue;
967
- await require_PluginDriver.applyHookResult(await gen.schema(transformedNode, ctx), driver, resolveRendererFor(gen, state));
968
- }
969
- await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
970
- } catch (caughtError) {
971
- state.failed = true;
972
- state.error = caughtError;
987
+ if (isPromise(applied)) await applied;
973
988
  }
989
+ await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
990
+ } catch (caughtError) {
991
+ state.failed = true;
992
+ state.error = caughtError;
974
993
  }
975
- schemasProcessed++;
976
- if (schemasProcessed % 50 === 0) await flushPendingFiles();
977
994
  }
978
- const collectedOperations = [];
979
- for await (const node of inputStreamNode.operations) {
980
- collectedOperations.push(node);
981
- for (const state of states) {
982
- if (state.failed) continue;
983
- try {
984
- const { plugin, generatorContext, generators } = state;
985
- const { exclude, include, override } = plugin.options;
986
- const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
987
- const options = generatorContext.resolver.resolveOptions(transformedNode, {
988
- options: plugin.options,
989
- exclude,
990
- include,
991
- override
995
+ async function dispatchOperation(state, node) {
996
+ if (state.failed) return;
997
+ try {
998
+ const { plugin, generatorContext, generators } = state;
999
+ const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
1000
+ const { exclude, include, override } = plugin.options;
1001
+ const options = state.optionsAreStatic ? plugin.options : generatorContext.resolver.resolveOptions(transformedNode, {
1002
+ options: plugin.options,
1003
+ exclude,
1004
+ include,
1005
+ override
1006
+ });
1007
+ if (options === null) return;
1008
+ const ctx = {
1009
+ ...generatorContext,
1010
+ options
1011
+ };
1012
+ for (const gen of generators) {
1013
+ if (!gen.operation) continue;
1014
+ const raw = gen.operation(transformedNode, ctx);
1015
+ const applied = require_PluginDriver.applyHookResult({
1016
+ result: isPromise(raw) ? await raw : raw,
1017
+ driver,
1018
+ rendererFactory: resolveRendererFor(gen, state)
992
1019
  });
993
- if (options === null) continue;
994
- const ctx = {
995
- ...generatorContext,
996
- options
997
- };
998
- for (const gen of generators) {
999
- if (!gen.operation) continue;
1000
- await require_PluginDriver.applyHookResult(await gen.operation(transformedNode, ctx), driver, resolveRendererFor(gen, state));
1001
- }
1002
- await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
1003
- } catch (caughtError) {
1004
- state.failed = true;
1005
- state.error = caughtError;
1020
+ if (isPromise(applied)) await applied;
1006
1021
  }
1022
+ await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
1023
+ } catch (caughtError) {
1024
+ state.failed = true;
1025
+ state.error = caughtError;
1007
1026
  }
1008
1027
  }
1028
+ for await (const node of inputStreamNode.schemas) await Promise.all(states.map((state) => dispatchSchema(state, node)));
1029
+ const collectedOperations = [];
1030
+ for await (const node of inputStreamNode.operations) {
1031
+ collectedOperations.push(node);
1032
+ await Promise.all(states.map((state) => dispatchOperation(state, node)));
1033
+ }
1009
1034
  for (const state of states) {
1010
1035
  if (!state.failed) try {
1011
1036
  const { plugin, generatorContext, generators } = state;
@@ -1015,7 +1040,11 @@ async function runPluginStreamHooks(inputStreamNode, entries, driver, hooks, con
1015
1040
  };
1016
1041
  for (const gen of generators) {
1017
1042
  if (!gen.operations) continue;
1018
- await require_PluginDriver.applyHookResult(await gen.operations(collectedOperations, ctx), driver, resolveRendererFor(gen, state));
1043
+ await require_PluginDriver.applyHookResult({
1044
+ result: await gen.operations(collectedOperations, ctx),
1045
+ driver,
1046
+ rendererFactory: resolveRendererFor(gen, state)
1047
+ });
1019
1048
  }
1020
1049
  await driver.hooks.emit("kubb:generate:operations", collectedOperations, ctx);
1021
1050
  } catch (caughtError) {
@@ -1024,12 +1053,12 @@ async function runPluginStreamHooks(inputStreamNode, entries, driver, hooks, con
1024
1053
  }
1025
1054
  const duration = getElapsedMs(state.hrStart);
1026
1055
  pluginTimings.set(state.plugin.name, duration);
1027
- await hooks.emit("kubb:plugin:end", {
1056
+ await driver.hooks.emit("kubb:plugin:end", {
1028
1057
  plugin: state.plugin,
1029
1058
  duration,
1030
1059
  success: !state.failed,
1031
1060
  ...state.failed && state.error ? { error: state.error } : {},
1032
- config,
1061
+ config: driver.config,
1033
1062
  get files() {
1034
1063
  return driver.fileManager.files;
1035
1064
  },
@@ -1039,13 +1068,22 @@ async function runPluginStreamHooks(inputStreamNode, entries, driver, hooks, con
1039
1068
  plugin: state.plugin,
1040
1069
  error: state.error
1041
1070
  });
1042
- await hooks.emit("kubb:debug", {
1071
+ await driver.hooks.emit("kubb:debug", {
1043
1072
  date: /* @__PURE__ */ new Date(),
1044
1073
  logs: [state.failed ? "✗ Plugin start failed" : `✓ Plugin started successfully (${formatMs(duration)})`]
1045
1074
  });
1046
1075
  }
1047
- await flushPendingFiles();
1048
1076
  }
1077
+ /**
1078
+ * Walks the AST and dispatches nodes to a plugin's direct AST hooks
1079
+ * (`schema`, `operation`, `operations`).
1080
+ *
1081
+ * When `include` contains only operation-scoped filters (`tag`, `operationId`, `path`,
1082
+ * `method`, `contentType`) and no `schemaName` filter, the function pre-computes the set
1083
+ * of top-level schema names transitively reachable from the included operations and skips
1084
+ * schemas that fall outside that set. This ensures that component schemas referenced
1085
+ * exclusively by excluded operations are not generated.
1086
+ */
1049
1087
  async function runPluginAstHooks(plugin, context) {
1050
1088
  const { adapter, inputNode, resolver, driver } = context;
1051
1089
  const { exclude, include, override } = plugin.options;
@@ -1068,13 +1106,15 @@ async function runPluginAstHooks(plugin, context) {
1068
1106
  ]);
1069
1107
  const hasOperationBasedIncludes = include?.some(({ type }) => operationFilterTypes.has(type)) ?? false;
1070
1108
  const hasSchemaNameIncludes = include?.some(({ type }) => type === "schemaName") ?? false;
1071
- let allowedSchemaNames;
1072
- if (hasOperationBasedIncludes && !hasSchemaNameIncludes) allowedSchemaNames = (0, _kubb_ast.collectUsedSchemaNames)(inputNode.operations.filter((op) => resolver.resolveOptions(op, {
1073
- options: plugin.options,
1074
- exclude,
1075
- include,
1076
- override
1077
- }) !== null), inputNode.schemas);
1109
+ const allowedSchemaNames = (() => {
1110
+ if (!hasOperationBasedIncludes || hasSchemaNameIncludes) return void 0;
1111
+ return (0, _kubb_ast.collectUsedSchemaNames)(inputNode.operations.filter((op) => resolver.resolveOptions(op, {
1112
+ options: plugin.options,
1113
+ exclude,
1114
+ include,
1115
+ override
1116
+ }) !== null), inputNode.schemas);
1117
+ })();
1078
1118
  await (0, _kubb_ast.walk)(inputNode, {
1079
1119
  depth: "shallow",
1080
1120
  async schema(node) {
@@ -1092,7 +1132,11 @@ async function runPluginAstHooks(plugin, context) {
1092
1132
  options
1093
1133
  };
1094
1134
  await Promise.all(generators.filter((gen) => gen.schema).map(async (gen) => {
1095
- return require_PluginDriver.applyHookResult(await gen.schema(transformedNode, ctx), driver, resolveRenderer(gen));
1135
+ return require_PluginDriver.applyHookResult({
1136
+ result: await gen.schema(transformedNode, ctx),
1137
+ driver,
1138
+ rendererFactory: resolveRenderer(gen)
1139
+ });
1096
1140
  }));
1097
1141
  await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
1098
1142
  },
@@ -1104,17 +1148,20 @@ async function runPluginAstHooks(plugin, context) {
1104
1148
  include,
1105
1149
  override
1106
1150
  });
1107
- if (options !== null) {
1108
- collectedOperations.push(transformedNode);
1109
- const ctx = {
1110
- ...generatorContext,
1111
- options
1112
- };
1113
- await Promise.all(generators.filter((gen) => gen.operation).map(async (gen) => {
1114
- return require_PluginDriver.applyHookResult(await gen.operation(transformedNode, ctx), driver, resolveRenderer(gen));
1115
- }));
1116
- await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
1117
- }
1151
+ if (options === null) return;
1152
+ collectedOperations.push(transformedNode);
1153
+ const ctx = {
1154
+ ...generatorContext,
1155
+ options
1156
+ };
1157
+ await Promise.all(generators.filter((gen) => gen.operation).map(async (gen) => {
1158
+ return require_PluginDriver.applyHookResult({
1159
+ result: await gen.operation(transformedNode, ctx),
1160
+ driver,
1161
+ rendererFactory: resolveRenderer(gen)
1162
+ });
1163
+ }));
1164
+ await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
1118
1165
  }
1119
1166
  });
1120
1167
  if (collectedOperations.length > 0) {
@@ -1124,7 +1171,11 @@ async function runPluginAstHooks(plugin, context) {
1124
1171
  };
1125
1172
  for (const gen of generators) {
1126
1173
  if (!gen.operations) continue;
1127
- await require_PluginDriver.applyHookResult(await gen.operations(collectedOperations, ctx), driver, resolveRenderer(gen));
1174
+ await require_PluginDriver.applyHookResult({
1175
+ result: await gen.operations(collectedOperations, ctx),
1176
+ driver,
1177
+ rendererFactory: resolveRenderer(gen)
1178
+ });
1128
1179
  }
1129
1180
  await driver.hooks.emit("kubb:generate:operations", collectedOperations, ctx);
1130
1181
  }
@@ -1141,8 +1192,8 @@ async function safeBuild(setupResult) {
1141
1192
  const parsersMap = /* @__PURE__ */ new Map();
1142
1193
  for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
1143
1194
  const fileProcessor = new FileProcessor();
1144
- async function flushPendingFiles(snapshot) {
1145
- const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path) && (!snapshot || !snapshot.has(f.path)));
1195
+ async function flushPendingFiles() {
1196
+ const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path));
1146
1197
  if (files.length === 0) return;
1147
1198
  await hooks.emit("kubb:debug", {
1148
1199
  date: /* @__PURE__ */ new Date(),
@@ -1153,7 +1204,7 @@ async function safeBuild(setupResult) {
1153
1204
  parsers: parsersMap,
1154
1205
  extension: config.output.extension
1155
1206
  });
1156
- for await (const { file, source, processed, total, percentage } of stream) {
1207
+ for (const { file, source, processed, total, percentage } of stream) {
1157
1208
  await hooks.emit("kubb:file:processing:update", {
1158
1209
  file,
1159
1210
  source,
@@ -1188,8 +1239,7 @@ async function safeBuild(setupResult) {
1188
1239
  },
1189
1240
  upsertFile: (...files) => driver.fileManager.upsert(...files)
1190
1241
  });
1191
- const inputStreamNode = driver.inputStreamNode;
1192
- if (inputStreamNode) {
1242
+ if (driver.inputStreamNode) {
1193
1243
  const streamPluginEntries = [];
1194
1244
  for (const plugin of driver.plugins.values()) {
1195
1245
  const context = driver.getContext(plugin);
@@ -1199,31 +1249,40 @@ async function safeBuild(setupResult) {
1199
1249
  date: /* @__PURE__ */ new Date(),
1200
1250
  logs: ["Starting plugin...", ` • Plugin Name: ${plugin.name}`]
1201
1251
  });
1202
- if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) streamPluginEntries.push({
1203
- plugin,
1204
- context,
1205
- hrStart
1206
- });
1207
- else {
1208
- const duration = getElapsedMs(hrStart);
1209
- pluginTimings.set(plugin.name, duration);
1210
- await hooks.emit("kubb:plugin:end", {
1252
+ if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) {
1253
+ streamPluginEntries.push({
1211
1254
  plugin,
1212
- duration,
1213
- success: true,
1214
- config,
1215
- get files() {
1216
- return driver.fileManager.files;
1217
- },
1218
- upsertFile: (...files) => driver.fileManager.upsert(...files)
1219
- });
1220
- await hooks.emit("kubb:debug", {
1221
- date: /* @__PURE__ */ new Date(),
1222
- logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
1255
+ context,
1256
+ hrStart
1223
1257
  });
1258
+ continue;
1224
1259
  }
1260
+ const duration = getElapsedMs(hrStart);
1261
+ pluginTimings.set(plugin.name, duration);
1262
+ await hooks.emit("kubb:plugin:end", {
1263
+ plugin,
1264
+ duration,
1265
+ success: true,
1266
+ config,
1267
+ get files() {
1268
+ return driver.fileManager.files;
1269
+ },
1270
+ upsertFile: (...files) => driver.fileManager.upsert(...files)
1271
+ });
1272
+ await hooks.emit("kubb:debug", {
1273
+ date: /* @__PURE__ */ new Date(),
1274
+ logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
1275
+ });
1276
+ }
1277
+ if (streamPluginEntries.length > 0) {
1278
+ await runPluginStreamHooks({
1279
+ entries: streamPluginEntries,
1280
+ driver,
1281
+ pluginTimings,
1282
+ failedPlugins
1283
+ });
1284
+ await flushPendingFiles();
1225
1285
  }
1226
- if (streamPluginEntries.length > 0) await runPluginStreamHooks(inputStreamNode, streamPluginEntries, driver, hooks, config, pluginTimings, failedPlugins, flushPendingFiles);
1227
1286
  } else for (const plugin of driver.plugins.values()) {
1228
1287
  const context = driver.getContext(plugin);
1229
1288
  const hrStart = process.hrtime();