@kubb/cli 4.32.4 → 4.33.0

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.
Files changed (137) hide show
  1. package/dist/agent-CJ69TqoO.js +87 -0
  2. package/dist/agent-CJ69TqoO.js.map +1 -0
  3. package/dist/agent-CduUX7Ye.cjs +91 -0
  4. package/dist/agent-CduUX7Ye.cjs.map +1 -0
  5. package/dist/agent-D0A3RQho.js +57 -0
  6. package/dist/agent-D0A3RQho.js.map +1 -0
  7. package/dist/agent-DrnwQBZf.cjs +60 -0
  8. package/dist/agent-DrnwQBZf.cjs.map +1 -0
  9. package/dist/constants-CEKRremI.js +79 -0
  10. package/dist/constants-CEKRremI.js.map +1 -0
  11. package/dist/constants-CnPOlsJq.cjs +126 -0
  12. package/dist/constants-CnPOlsJq.cjs.map +1 -0
  13. package/dist/errors-BUjJsNoe.cjs +44 -0
  14. package/dist/errors-BUjJsNoe.cjs.map +1 -0
  15. package/dist/errors-bSLTEh4e.js +27 -0
  16. package/dist/errors-bSLTEh4e.js.map +1 -0
  17. package/dist/{generate-COj0aMS6.cjs → generate-ByMgAV76.cjs} +423 -577
  18. package/dist/generate-ByMgAV76.cjs.map +1 -0
  19. package/dist/generate-CiUPO5ds.cjs +65 -0
  20. package/dist/generate-CiUPO5ds.cjs.map +1 -0
  21. package/dist/generate-DIIxtkWT.js +66 -0
  22. package/dist/generate-DIIxtkWT.js.map +1 -0
  23. package/dist/{generate-CpWtSc45.js → generate-HP5ySfjV.js} +422 -577
  24. package/dist/generate-HP5ySfjV.js.map +1 -0
  25. package/dist/index.cjs +226 -35
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +226 -35
  29. package/dist/index.js.map +1 -1
  30. package/dist/init-Cd1hCb7q.cjs +296 -0
  31. package/dist/init-Cd1hCb7q.cjs.map +1 -0
  32. package/dist/init-DLNrkDF4.js +25 -0
  33. package/dist/init-DLNrkDF4.js.map +1 -0
  34. package/dist/init-Df_aXezV.cjs +24 -0
  35. package/dist/init-Df_aXezV.cjs.map +1 -0
  36. package/dist/init-DyKK2fTp.js +291 -0
  37. package/dist/init-DyKK2fTp.js.map +1 -0
  38. package/dist/jiti-BdskUHhD.cjs +16 -0
  39. package/dist/jiti-BdskUHhD.cjs.map +1 -0
  40. package/dist/jiti-Cl7t20dO.js +11 -0
  41. package/dist/jiti-Cl7t20dO.js.map +1 -0
  42. package/dist/mcp-B73FC8dF.cjs +42 -0
  43. package/dist/mcp-B73FC8dF.cjs.map +1 -0
  44. package/dist/mcp-Bd9LITaI.js +16 -0
  45. package/dist/mcp-Bd9LITaI.js.map +1 -0
  46. package/dist/mcp-Cf-1dsB-.js +41 -0
  47. package/dist/mcp-Cf-1dsB-.js.map +1 -0
  48. package/dist/mcp-Clg-Qnkr.cjs +15 -0
  49. package/dist/mcp-Clg-Qnkr.cjs.map +1 -0
  50. package/dist/package-681jTtCk.js +6 -0
  51. package/dist/package-681jTtCk.js.map +1 -0
  52. package/dist/{package-aNQWvWbS.cjs → package-aKgzEJtp.cjs} +2 -2
  53. package/dist/package-aKgzEJtp.cjs.map +1 -0
  54. package/dist/{telemetry-DYWvlxqs.js → telemetry-C4gOKX2x.js} +31 -10
  55. package/dist/telemetry-C4gOKX2x.js.map +1 -0
  56. package/dist/{telemetry-BDSSqUiG.cjs → telemetry-T5IA2dWA.cjs} +40 -7
  57. package/dist/telemetry-T5IA2dWA.cjs.map +1 -0
  58. package/dist/types-CLtz0jem.js +25 -0
  59. package/dist/types-CLtz0jem.js.map +1 -0
  60. package/dist/types-Ck2lzFON.cjs +36 -0
  61. package/dist/types-Ck2lzFON.cjs.map +1 -0
  62. package/dist/validate-Chjg23AE.js +41 -0
  63. package/dist/validate-Chjg23AE.js.map +1 -0
  64. package/dist/validate-Cr26q5xX.js +25 -0
  65. package/dist/validate-Cr26q5xX.js.map +1 -0
  66. package/dist/validate-DURmg-2Q.cjs +24 -0
  67. package/dist/validate-DURmg-2Q.cjs.map +1 -0
  68. package/dist/validate-Dqi9T_c4.cjs +42 -0
  69. package/dist/validate-Dqi9T_c4.cjs.map +1 -0
  70. package/package.json +5 -6
  71. package/src/cli/adapters/nodeAdapter.ts +159 -0
  72. package/src/cli/help.ts +36 -0
  73. package/src/cli/index.ts +16 -0
  74. package/src/cli/parse.ts +18 -0
  75. package/src/cli/schema.ts +38 -0
  76. package/src/cli/types.ts +95 -0
  77. package/src/commands/agent/start.ts +27 -136
  78. package/src/commands/agent.ts +6 -25
  79. package/src/commands/generate.ts +26 -158
  80. package/src/commands/init.ts +9 -360
  81. package/src/commands/mcp.ts +7 -52
  82. package/src/commands/validate.ts +9 -60
  83. package/src/constants.ts +77 -0
  84. package/src/index.ts +36 -42
  85. package/src/loggers/clackLogger.ts +42 -140
  86. package/src/loggers/fileSystemLogger.ts +1 -12
  87. package/src/loggers/githubActionsLogger.ts +36 -101
  88. package/src/loggers/plainLogger.ts +23 -70
  89. package/src/loggers/utils.ts +66 -2
  90. package/src/runners/agent.ts +100 -0
  91. package/src/runners/generate.ts +208 -100
  92. package/src/runners/init.ts +322 -0
  93. package/src/runners/mcp.ts +32 -0
  94. package/src/runners/validate.ts +35 -0
  95. package/src/utils/Writables.ts +2 -2
  96. package/src/utils/envDetection.ts +34 -0
  97. package/src/utils/errors.ts +23 -0
  98. package/src/utils/executeHooks.ts +18 -6
  99. package/src/utils/getCosmiConfig.ts +10 -11
  100. package/src/utils/getIntro.ts +17 -18
  101. package/src/utils/getSummary.ts +11 -15
  102. package/src/utils/jiti.ts +9 -0
  103. package/src/utils/packageManager.ts +3 -3
  104. package/src/utils/randomColor.ts +3 -12
  105. package/src/utils/runHook.ts +75 -0
  106. package/src/utils/spawnAsync.ts +47 -0
  107. package/src/utils/telemetry.ts +8 -25
  108. package/src/utils/watcher.ts +2 -4
  109. package/dist/agent-6COck3B9.cjs +0 -20
  110. package/dist/agent-6COck3B9.cjs.map +0 -1
  111. package/dist/agent-DMm6c5Vg.js +0 -20
  112. package/dist/agent-DMm6c5Vg.js.map +0 -1
  113. package/dist/generate-COj0aMS6.cjs.map +0 -1
  114. package/dist/generate-CpWtSc45.js.map +0 -1
  115. package/dist/init-Bdn3_qir.js +0 -304
  116. package/dist/init-Bdn3_qir.js.map +0 -1
  117. package/dist/init-CFW2kWY8.cjs +0 -308
  118. package/dist/init-CFW2kWY8.cjs.map +0 -1
  119. package/dist/mcp-DkwtARfo.cjs +0 -57
  120. package/dist/mcp-DkwtARfo.cjs.map +0 -1
  121. package/dist/mcp-DrH93Vq4.js +0 -57
  122. package/dist/mcp-DrH93Vq4.js.map +0 -1
  123. package/dist/package-BnJbGmLm.js +0 -6
  124. package/dist/package-BnJbGmLm.js.map +0 -1
  125. package/dist/package-aNQWvWbS.cjs.map +0 -1
  126. package/dist/start-CqTUu14n.js +0 -131
  127. package/dist/start-CqTUu14n.js.map +0 -1
  128. package/dist/start-D-rsIJGo.cjs +0 -134
  129. package/dist/start-D-rsIJGo.cjs.map +0 -1
  130. package/dist/telemetry-BDSSqUiG.cjs.map +0 -1
  131. package/dist/telemetry-DYWvlxqs.js.map +0 -1
  132. package/dist/validate-BlV8L8gC.js +0 -66
  133. package/dist/validate-BlV8L8gC.js.map +0 -1
  134. package/dist/validate-COhZUXF8.cjs +0 -66
  135. package/dist/validate-COhZUXF8.cjs.map +0 -1
  136. package/src/loggers/envDetection.ts +0 -28
  137. package/src/loggers/index.ts +0 -5
@@ -1,16 +1,16 @@
1
1
  import "./chunk--u3MIqq1.js";
2
- import { t as version } from "./package-BnJbGmLm.js";
3
- import { r as sendTelemetry, t as buildTelemetryEvent } from "./telemetry-DYWvlxqs.js";
2
+ import { t as version } from "./package-681jTtCk.js";
3
+ import { c as randomColors, i as WATCHER_IGNORED_PATHS, r as SUMMARY_SEPARATOR, t as KUBB_NPM_PACKAGE_URL } from "./constants-CEKRremI.js";
4
+ import { a as isGitHubActions, i as canUseTTY, r as sendTelemetry, t as buildTelemetryEvent } from "./telemetry-C4gOKX2x.js";
5
+ import { n as toCause, r as toError } from "./errors-bSLTEh4e.js";
4
6
  import { styleText } from "node:util";
5
- import { defineCommand, showUsage } from "citty";
6
7
  import { createHash } from "node:crypto";
7
- import * as process$2 from "node:process";
8
8
  import process$1 from "node:process";
9
9
  import { AsyncEventEmitter, detectFormatter, detectLinter, executeIfOnline, formatHrtime, formatMs, formatters, getConfigs, linters, tokenize } from "@kubb/core/utils";
10
10
  import path, { relative, resolve } from "node:path";
11
11
  import * as clack from "@clack/prompts";
12
12
  import { LogLevel, PromiseManager, defineLogger, isInputPath, safeBuild, setup } from "@kubb/core";
13
- import { x } from "tinyexec";
13
+ import { NonZeroExitError, x } from "tinyexec";
14
14
  import { Writable } from "node:stream";
15
15
  import { write } from "@kubb/core/fs";
16
16
  import { cosmiconfig } from "cosmiconfig";
@@ -34,28 +34,26 @@ function formatMsWithColor(ms) {
34
34
  * ANSI True Color (24-bit) utilities for terminal output
35
35
  * Supports hex color codes without external dependencies like chalk
36
36
  */
37
+ /** Parse a hex color string into RGB components, defaulting to 255 on invalid input. */
38
+ function parseHex(color) {
39
+ const c = color.replace("#", "");
40
+ const r = Number.parseInt(c.slice(0, 2), 16);
41
+ const g = Number.parseInt(c.slice(2, 4), 16);
42
+ const b = Number.parseInt(c.slice(4, 6), 16);
43
+ return {
44
+ r: Number.isNaN(r) ? 255 : r,
45
+ g: Number.isNaN(g) ? 255 : g,
46
+ b: Number.isNaN(b) ? 255 : b
47
+ };
48
+ }
37
49
  /**
38
50
  * Convert hex color to ANSI 24-bit true color escape sequence
39
51
  * @param color - Hex color code (with or without #), e.g., '#FF5500' or 'FF5500'
40
52
  * @returns Function that wraps text with the color
41
53
  */
42
54
  function hex(color) {
43
- const cleanHex = color.replace("#", "");
44
- const r = Number.parseInt(cleanHex.slice(0, 2), 16);
45
- const g = Number.parseInt(cleanHex.slice(2, 4), 16);
46
- const b = Number.parseInt(cleanHex.slice(4, 6), 16);
47
- const safeR = Number.isNaN(r) ? 255 : r;
48
- const safeG = Number.isNaN(g) ? 255 : g;
49
- const safeB = Number.isNaN(b) ? 255 : b;
50
- return (text) => `\x1b[38;2;${safeR};${safeG};${safeB}m${text}\x1b[0m`;
51
- }
52
- function hexToRgb(color) {
53
- const c = color.replace("#", "");
54
- return {
55
- r: Number.parseInt(c.slice(0, 2), 16),
56
- g: Number.parseInt(c.slice(2, 4), 16),
57
- b: Number.parseInt(c.slice(4, 6), 16)
58
- };
55
+ const { r, g, b } = parseHex(color);
56
+ return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
59
57
  }
60
58
  function gradient(colors) {
61
59
  return (text) => {
@@ -64,8 +62,8 @@ function gradient(colors) {
64
62
  const t = chars.length <= 1 ? 0 : i / (chars.length - 1);
65
63
  const seg = Math.min(Math.floor(t * (colors.length - 1)), colors.length - 2);
66
64
  const lt = t * (colors.length - 1) - seg;
67
- const from = hexToRgb(colors[seg]);
68
- const to = hexToRgb(colors[seg + 1]);
65
+ const from = parseHex(colors[seg]);
66
+ const to = parseHex(colors[seg + 1]);
69
67
  return `\x1b[38;2;${Math.round(from.r + (to.r - from.r) * lt)};${Math.round(from.g + (to.g - from.g) * lt)};${Math.round(from.b + (to.b - from.b) * lt)}m${char}\x1b[0m`;
70
68
  }).join("");
71
69
  };
@@ -102,35 +100,19 @@ function getIntro({ title, description, version, areEyesOpen }) {
102
100
  }
103
101
  //#endregion
104
102
  //#region src/utils/randomColor.ts
105
- function randomColor(text) {
106
- if (!text) return "white";
107
- const defaultColors = [
108
- "black",
109
- "red",
110
- "green",
111
- "yellow",
112
- "blue",
113
- "red",
114
- "green",
115
- "magenta",
116
- "cyan",
117
- "gray"
118
- ];
119
- return defaultColors[createHash("sha256").update(text).digest().readUInt32BE(0) % defaultColors.length] ?? "white";
120
- }
121
103
  function randomCliColor(text) {
122
104
  if (!text) return "";
123
- return styleText(randomColor(text), text);
105
+ return styleText(randomColors[createHash("sha256").update(text).digest().readUInt32BE(0) % randomColors.length] ?? "white", text);
124
106
  }
125
107
  //#endregion
126
108
  //#region src/utils/getSummary.ts
127
109
  function getSummary({ failedPlugins, filesCreated, status, hrStart, config, pluginTimings }) {
128
110
  const duration = formatHrtime(hrStart);
129
- const pluginsCount = config.plugins?.length || 0;
111
+ const pluginsCount = config.plugins?.length ?? 0;
130
112
  const successCount = pluginsCount - failedPlugins.size;
131
113
  const meta = {
132
114
  plugins: status === "success" ? `${styleText("green", `${successCount} successful`)}, ${pluginsCount} total` : `${styleText("green", `${successCount} successful`)}, ${styleText("red", `${failedPlugins.size} failed`)}, ${pluginsCount} total`,
133
- pluginsFailed: status === "failed" ? [...failedPlugins]?.map(({ plugin }) => randomCliColor(plugin.name))?.join(", ") : void 0,
115
+ pluginsFailed: status === "failed" ? [...failedPlugins].map(({ plugin }) => randomCliColor(plugin.name)).join(", ") : void 0,
134
116
  filesCreated,
135
117
  time: styleText("green", duration),
136
118
  output: path.isAbsolute(config.root) ? path.resolve(config.root, config.output.path) : config.root
@@ -148,23 +130,76 @@ function getSummary({ failedPlugins, filesCreated, status, hrStart, config, plug
148
130
  if (meta.pluginsFailed) summaryLines.push(`${labels.failed.padEnd(maxLength + 2)} ${meta.pluginsFailed}`);
149
131
  summaryLines.push(`${labels.generated.padEnd(maxLength + 2)} ${meta.filesCreated} files in ${meta.time}`);
150
132
  if (pluginTimings && pluginTimings.size > 0) {
151
- const TIME_SCALE_DIVISOR = 100;
152
- const MAX_BAR_LENGTH = 10;
153
133
  const sortedTimings = Array.from(pluginTimings.entries()).sort((a, b) => b[1] - a[1]);
154
- if (sortedTimings.length > 0) {
155
- summaryLines.push(`${labels.pluginTimings}`);
156
- sortedTimings.forEach(([name, time]) => {
157
- const timeStr = time >= 1e3 ? `${(time / 1e3).toFixed(2)}s` : `${Math.round(time)}ms`;
158
- const barLength = Math.min(Math.ceil(time / TIME_SCALE_DIVISOR), MAX_BAR_LENGTH);
159
- const bar = styleText("dim", "".repeat(barLength));
160
- summaryLines.push(`${styleText("dim", "•")} ${name.padEnd(maxLength + 1)}${bar} ${timeStr}`);
161
- });
162
- }
134
+ summaryLines.push(`${labels.pluginTimings}`);
135
+ sortedTimings.forEach(([name, time]) => {
136
+ const timeStr = time >= 1e3 ? `${(time / 1e3).toFixed(2)}s` : `${Math.round(time)}ms`;
137
+ const barLength = Math.min(Math.ceil(time / 100), 10);
138
+ const bar = styleText("dim", "█".repeat(barLength));
139
+ summaryLines.push(`${styleText("dim", "")} ${name.padEnd(maxLength + 1)}${bar} ${timeStr}`);
140
+ });
163
141
  }
164
142
  summaryLines.push(`${labels.output.padEnd(maxLength + 2)} ${meta.output}`);
165
143
  return summaryLines;
166
144
  }
167
145
  //#endregion
146
+ //#region src/utils/runHook.ts
147
+ /**
148
+ * Execute a hook command, emit debug/hook:end events, and forward output to
149
+ * an optional HookOutputSink. All three logger adapters share this function
150
+ * so the process-spawning logic lives in exactly one place.
151
+ */
152
+ async function runHook({ id, command, args, commandWithArgs, context, stream = false, sink }) {
153
+ try {
154
+ const proc = x(command, [...args ?? []], {
155
+ nodeOptions: { detached: true },
156
+ throwOnError: true
157
+ });
158
+ if (stream && sink?.onLine) for await (const line of proc) sink.onLine(line);
159
+ const result = await proc;
160
+ await context.emit("debug", {
161
+ date: /* @__PURE__ */ new Date(),
162
+ logs: [result.stdout.trimEnd()]
163
+ });
164
+ await context.emit("hook:end", {
165
+ command,
166
+ args,
167
+ id,
168
+ success: true,
169
+ error: null
170
+ });
171
+ } catch (err) {
172
+ if (!(err instanceof NonZeroExitError)) {
173
+ await context.emit("hook:end", {
174
+ command,
175
+ args,
176
+ id,
177
+ success: false,
178
+ error: toError(err)
179
+ });
180
+ await context.emit("error", toError(err));
181
+ return;
182
+ }
183
+ const stderr = err.output?.stderr ?? "";
184
+ const stdout = err.output?.stdout ?? "";
185
+ await context.emit("debug", {
186
+ date: /* @__PURE__ */ new Date(),
187
+ logs: [stdout, stderr].filter(Boolean)
188
+ });
189
+ if (stderr) sink?.onStderr?.(stderr);
190
+ if (stdout) sink?.onStdout?.(stdout);
191
+ const errorMessage = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
192
+ await context.emit("hook:end", {
193
+ command,
194
+ args,
195
+ id,
196
+ success: false,
197
+ error: errorMessage
198
+ });
199
+ await context.emit("error", errorMessage);
200
+ }
201
+ }
202
+ //#endregion
168
203
  //#region src/utils/Writables.ts
169
204
  var ClackWritable = class extends Writable {
170
205
  taskLog;
@@ -173,7 +208,7 @@ var ClackWritable = class extends Writable {
173
208
  this.taskLog = taskLog;
174
209
  }
175
210
  _write(chunk, _encoding, callback) {
176
- this.taskLog.message(`${styleText("dim", chunk?.toString())}`);
211
+ this.taskLog.message(`${styleText("dim", chunk.toString())}`);
177
212
  callback();
178
213
  }
179
214
  };
@@ -186,7 +221,7 @@ var ClackWritable = class extends Writable {
186
221
  const clackLogger = defineLogger({
187
222
  name: "clack",
188
223
  install(context, options) {
189
- const logLevel = options?.logLevel || LogLevel.info;
224
+ const logLevel = options?.logLevel ?? LogLevel.info;
190
225
  const state = {
191
226
  totalPlugins: 0,
192
227
  completedPlugins: 0,
@@ -215,26 +250,11 @@ const clackLogger = defineLogger({
215
250
  }
216
251
  function showProgressStep() {
217
252
  if (logLevel <= LogLevel.silent) return;
218
- const parts = [];
219
- const duration = formatHrtime(state.hrStart);
220
- if (state.totalPlugins > 0) {
221
- const pluginStr = state.failedPlugins > 0 ? `Plugins ${styleText("green", state.completedPlugins.toString())}/${state.totalPlugins} ${styleText("red", `(${state.failedPlugins} failed)`)}` : `Plugins ${styleText("green", state.completedPlugins.toString())}/${state.totalPlugins}`;
222
- parts.push(pluginStr);
223
- }
224
- if (state.totalFiles > 0) parts.push(`Files ${styleText("green", state.processedFiles.toString())}/${state.totalFiles}`);
225
- if (parts.length > 0) {
226
- parts.push(`${styleText("green", duration)} elapsed`);
227
- clack.log.step(getMessage(parts.join(styleText("dim", " | "))));
228
- }
253
+ const line = buildProgressLine(state);
254
+ if (line) clack.log.step(getMessage(line));
229
255
  }
230
256
  function getMessage(message) {
231
- if (logLevel >= LogLevel.verbose) return [styleText("dim", `[${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
232
- hour12: false,
233
- hour: "2-digit",
234
- minute: "2-digit",
235
- second: "2-digit"
236
- })}]`), message].join(" ");
237
- return message;
257
+ return formatMessage(message, logLevel);
238
258
  }
239
259
  function startSpinner(text) {
240
260
  state.spinner.start(text);
@@ -274,7 +294,7 @@ const clackLogger = defineLogger({
274
294
  clack.log.warn(text);
275
295
  });
276
296
  context.on("error", (error) => {
277
- const caused = error.cause;
297
+ const caused = toCause(error);
278
298
  const text = [styleText("red", "✗"), error.message].join(" ");
279
299
  if (state.isSpinning) stopSpinner(getMessage(text));
280
300
  else clack.log.error(getMessage(text));
@@ -321,10 +341,10 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
321
341
  clack.outro(text);
322
342
  });
323
343
  context.on("generation:start", (config) => {
324
- state.totalPlugins = config.plugins?.length || 0;
344
+ reset();
345
+ state.totalPlugins = config.plugins?.length ?? 0;
325
346
  const text = getMessage(["Generation started", config.name ? `for ${styleText("dim", config.name)}` : void 0].filter(Boolean).join(" "));
326
347
  clack.intro(text);
327
- reset();
328
348
  });
329
349
  context.on("plugin:start", (plugin) => {
330
350
  if (logLevel <= LogLevel.silent) return;
@@ -416,93 +436,43 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
416
436
  clack.outro(text);
417
437
  });
418
438
  context.on("hook:start", async ({ id, command, args }) => {
419
- const commandWithArgs = args?.length ? `${command} ${args.join(" ")}` : command;
439
+ const commandWithArgs = formatCommandWithArgs(command, args);
420
440
  const text = getMessage(`Hook ${styleText("dim", commandWithArgs)} started`);
421
441
  if (!id) return;
422
442
  if (logLevel <= LogLevel.silent) {
423
- try {
424
- const result = await x(command, [...args ?? []], {
425
- nodeOptions: { detached: true },
426
- throwOnError: true
427
- });
428
- await context.emit("debug", {
429
- date: /* @__PURE__ */ new Date(),
430
- logs: [result.stdout.trimEnd()]
431
- });
432
- await context.emit("hook:end", {
433
- command,
434
- args,
435
- id,
436
- success: true,
437
- error: null
438
- });
439
- } catch (err) {
440
- const error = err;
441
- const stderr = error.output?.stderr ?? "";
442
- const stdout = error.output?.stdout ?? "";
443
- await context.emit("debug", {
444
- date: /* @__PURE__ */ new Date(),
445
- logs: [stdout, stderr].filter(Boolean)
446
- });
447
- if (stderr) console.error(stderr);
448
- if (stdout) console.log(stdout);
449
- const errorMessage = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
450
- await context.emit("hook:end", {
451
- command,
452
- args,
453
- id,
454
- success: false,
455
- error: errorMessage
456
- });
457
- await context.emit("error", errorMessage);
458
- }
459
- return;
460
- }
461
- clack.intro(text);
462
- const logger = clack.taskLog({ title: getMessage(["Executing hook", logLevel >= LogLevel.info ? styleText("dim", commandWithArgs) : void 0].filter(Boolean).join(" ")) });
463
- const writable = new ClackWritable(logger);
464
- try {
465
- const proc = x(command, [...args ?? []], {
466
- nodeOptions: { detached: true },
467
- throwOnError: true
468
- });
469
- for await (const line of proc) writable.write(line);
470
- const result = await proc;
471
- await context.emit("debug", {
472
- date: /* @__PURE__ */ new Date(),
473
- logs: [result.stdout.trimEnd()]
474
- });
475
- await context.emit("hook:end", {
476
- command,
477
- args,
443
+ await runHook({
478
444
  id,
479
- success: true,
480
- error: null
481
- });
482
- } catch (err) {
483
- const error = err;
484
- const stderr = error.output?.stderr ?? "";
485
- const stdout = error.output?.stdout ?? "";
486
- await context.emit("debug", {
487
- date: /* @__PURE__ */ new Date(),
488
- logs: [stdout, stderr].filter(Boolean)
489
- });
490
- if (stderr) logger.error(stderr);
491
- if (stdout) logger.message(stdout);
492
- const errorMessage = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
493
- await context.emit("hook:end", {
494
445
  command,
495
446
  args,
496
- id,
497
- success: false,
498
- error: errorMessage
447
+ commandWithArgs,
448
+ context,
449
+ sink: {
450
+ onStderr: (s) => console.error(s),
451
+ onStdout: (s) => console.log(s)
452
+ }
499
453
  });
500
- await context.emit("error", errorMessage);
454
+ return;
501
455
  }
456
+ clack.intro(text);
457
+ const logger = clack.taskLog({ title: getMessage(["Executing hook", logLevel >= LogLevel.info ? styleText("dim", commandWithArgs) : void 0].filter(Boolean).join(" ")) });
458
+ const writable = new ClackWritable(logger);
459
+ await runHook({
460
+ id,
461
+ command,
462
+ args,
463
+ commandWithArgs,
464
+ context,
465
+ stream: true,
466
+ sink: {
467
+ onLine: (line) => writable.write(line),
468
+ onStderr: (s) => logger.error(s),
469
+ onStdout: (s) => logger.message(s)
470
+ }
471
+ });
502
472
  });
503
473
  context.on("hook:end", ({ command, args }) => {
504
474
  if (logLevel <= LogLevel.silent) return;
505
- const text = getMessage(`Hook ${styleText("dim", args?.length ? `${command} ${args.join(" ")}` : command)} successfully executed`);
475
+ const text = getMessage(`Hook ${styleText("dim", formatCommandWithArgs(command, args))} successfully executed`);
506
476
  clack.outro(text);
507
477
  });
508
478
  context.on("generation:summary", (config, { pluginTimings, failedPlugins, filesCreated, status, hrStart }) => {
@@ -517,20 +487,10 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
517
487
  const title = config.name || "";
518
488
  summary.unshift("\n");
519
489
  summary.push("\n");
520
- if (status === "success") {
521
- clack.box(summary.join("\n"), getMessage(title), {
522
- width: "auto",
523
- formatBorder: (s) => styleText("green", s),
524
- rounded: true,
525
- withGuide: false,
526
- contentAlign: "left",
527
- titleAlign: "center"
528
- });
529
- return;
530
- }
490
+ const borderColor = status === "success" ? "green" : "red";
531
491
  clack.box(summary.join("\n"), getMessage(title), {
532
492
  width: "auto",
533
- formatBorder: (s) => styleText("red", s),
493
+ formatBorder: (s) => styleText(borderColor, s),
534
494
  rounded: true,
535
495
  withGuide: false,
536
496
  contentAlign: "left",
@@ -543,26 +503,6 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
543
503
  }
544
504
  });
545
505
  //#endregion
546
- //#region src/loggers/envDetection.ts
547
- /**
548
- * Check if running in GitHub Actions environment
549
- */
550
- function isGitHubActions() {
551
- return !!process.env.GITHUB_ACTIONS;
552
- }
553
- /**
554
- * Check if running in any CI environment
555
- */
556
- function isCIEnvironment() {
557
- return !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.TRAVIS || process.env.JENKINS_URL || process.env.BUILDKITE);
558
- }
559
- /**
560
- * Check if TTY is available for interactive output
561
- */
562
- function canUseTTY() {
563
- return !!process.stdout.isTTY && !isCIEnvironment();
564
- }
565
- //#endregion
566
506
  //#region src/loggers/fileSystemLogger.ts
567
507
  /**
568
508
  * FileSystem logger for debug log persistence
@@ -591,7 +531,7 @@ const fileSystemLogger = defineLogger({
591
531
  name,
592
532
  state.startDate
593
533
  ].filter(Boolean).join("-")}.log`;
594
- const pathName = resolve(process.cwd(), ".kubb", baseName);
534
+ const pathName = resolve(process$1.cwd(), ".kubb", baseName);
595
535
  if (!files[pathName]) files[pathName] = [];
596
536
  if (log.logs.length > 0) {
597
537
  const timestamp = log.date.toLocaleString();
@@ -606,75 +546,66 @@ const fileSystemLogger = defineLogger({
606
546
  context.on("info", (message, info) => {
607
547
  state.cachedLogs.add({
608
548
  date: /* @__PURE__ */ new Date(),
609
- logs: [`ℹ ${message} ${info}`],
610
- fileName: void 0
549
+ logs: [`ℹ ${message} ${info}`]
611
550
  });
612
551
  });
613
552
  context.on("success", (message, info) => {
614
553
  state.cachedLogs.add({
615
554
  date: /* @__PURE__ */ new Date(),
616
- logs: [`✓ ${message} ${info}`],
617
- fileName: void 0
555
+ logs: [`✓ ${message} ${info}`]
618
556
  });
619
557
  });
620
558
  context.on("warn", (message, info) => {
621
559
  state.cachedLogs.add({
622
560
  date: /* @__PURE__ */ new Date(),
623
- logs: [`⚠ ${message} ${info}`],
624
- fileName: void 0
561
+ logs: [`⚠ ${message} ${info}`]
625
562
  });
626
563
  });
627
564
  context.on("error", (error) => {
628
565
  state.cachedLogs.add({
629
566
  date: /* @__PURE__ */ new Date(),
630
- logs: [`✗ ${error.message}`, error.stack || "unknown stack"],
631
- fileName: void 0
567
+ logs: [`✗ ${error.message}`, error.stack || "unknown stack"]
632
568
  });
633
569
  });
634
570
  context.on("debug", (message) => {
635
571
  state.cachedLogs.add({
636
572
  date: /* @__PURE__ */ new Date(),
637
- logs: message.logs,
638
- fileName: void 0
573
+ logs: message.logs
639
574
  });
640
575
  });
641
576
  context.on("plugin:start", (plugin) => {
642
577
  state.cachedLogs.add({
643
578
  date: /* @__PURE__ */ new Date(),
644
- logs: [`Generating ${plugin.name}`],
645
- fileName: void 0
579
+ logs: [`Generating ${plugin.name}`]
646
580
  });
647
581
  });
648
582
  context.on("plugin:end", (plugin, { duration, success }) => {
649
583
  const durationStr = formatMs(duration);
650
584
  state.cachedLogs.add({
651
585
  date: /* @__PURE__ */ new Date(),
652
- logs: [success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`],
653
- fileName: void 0
586
+ logs: [success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`]
654
587
  });
655
588
  });
656
589
  context.on("files:processing:start", (files) => {
657
590
  state.cachedLogs.add({
658
591
  date: /* @__PURE__ */ new Date(),
659
- logs: [`Start ${files.length} writing:`, ...files.map((file) => file.path)],
660
- fileName: void 0
592
+ logs: [`Start ${files.length} writing:`, ...files.map((file) => file.path)]
661
593
  });
662
594
  });
663
595
  context.on("generation:end", async (config) => {
664
596
  const writtenFilePaths = await writeLogs(config.name);
665
597
  if (writtenFilePaths.length > 0) {
666
- const files = writtenFilePaths.map((f) => relative(process.cwd(), f));
598
+ const files = writtenFilePaths.map((f) => relative(process$1.cwd(), f));
667
599
  await context.emit("info", "Debug files written to:", files.join(", "));
668
600
  }
669
601
  reset();
670
602
  });
671
- context.on("lifecycle:end", async () => {});
672
603
  const exitHandler = () => {
673
604
  if (state.cachedLogs.size > 0) writeLogs().catch(() => {});
674
605
  };
675
- process.once("exit", exitHandler);
676
- process.once("SIGINT", exitHandler);
677
- process.once("SIGTERM", exitHandler);
606
+ process$1.once("exit", exitHandler);
607
+ process$1.once("SIGINT", exitHandler);
608
+ process$1.once("SIGTERM", exitHandler);
678
609
  }
679
610
  });
680
611
  //#endregion
@@ -686,7 +617,7 @@ const fileSystemLogger = defineLogger({
686
617
  const githubActionsLogger = defineLogger({
687
618
  name: "github-actions",
688
619
  install(context, options) {
689
- const logLevel = options?.logLevel || LogLevel.info;
620
+ const logLevel = options?.logLevel ?? LogLevel.info;
690
621
  const state = {
691
622
  totalPlugins: 0,
692
623
  completedPlugins: 0,
@@ -703,29 +634,15 @@ const githubActionsLogger = defineLogger({
703
634
  state.totalFiles = 0;
704
635
  state.processedFiles = 0;
705
636
  state.hrStart = process.hrtime();
637
+ state.currentConfigs = [];
706
638
  }
707
639
  function showProgressStep() {
708
640
  if (logLevel <= LogLevel.silent) return;
709
- const parts = [];
710
- const duration = formatHrtime(state.hrStart);
711
- if (state.totalPlugins > 0) {
712
- const pluginStr = state.failedPlugins > 0 ? `Plugins ${styleText("green", state.completedPlugins.toString())}/${state.totalPlugins} ${styleText("red", `(${state.failedPlugins} failed)`)}` : `Plugins ${styleText("green", state.completedPlugins.toString())}/${state.totalPlugins}`;
713
- parts.push(pluginStr);
714
- }
715
- if (state.totalFiles > 0) parts.push(`Files ${styleText("green", state.processedFiles.toString())}/${state.totalFiles}`);
716
- if (parts.length > 0) {
717
- parts.push(`${styleText("green", duration)} elapsed`);
718
- console.log(getMessage(parts.join(styleText("dim", " | "))));
719
- }
641
+ const line = buildProgressLine(state);
642
+ if (line) console.log(getMessage(line));
720
643
  }
721
644
  function getMessage(message) {
722
- if (logLevel >= LogLevel.verbose) return [styleText("dim", `[${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
723
- hour12: false,
724
- hour: "2-digit",
725
- minute: "2-digit",
726
- second: "2-digit"
727
- })}]`), message].join(" ");
728
- return message;
645
+ return formatMessage(message, logLevel);
729
646
  }
730
647
  function openGroup(name) {
731
648
  console.log(`::group::${name}`);
@@ -761,7 +678,7 @@ const githubActionsLogger = defineLogger({
761
678
  console.warn(`::warning::${text}`);
762
679
  });
763
680
  context.on("error", (error) => {
764
- const caused = error.cause;
681
+ const caused = toCause(error);
765
682
  if (logLevel <= LogLevel.silent) return;
766
683
  const message = error.message || String(error);
767
684
  console.error(`::error::${message}`);
@@ -793,11 +710,11 @@ const githubActionsLogger = defineLogger({
793
710
  closeGroup("Configuration");
794
711
  });
795
712
  context.on("generation:start", (config) => {
796
- state.totalPlugins = config.plugins?.length || 0;
713
+ reset();
714
+ state.totalPlugins = config.plugins?.length ?? 0;
797
715
  const text = config.name ? `Generation for ${styleText("bold", config.name)}` : "Generation";
798
716
  if (state.currentConfigs.length > 1) openGroup(text);
799
717
  if (state.currentConfigs.length === 1) console.log(getMessage(text));
800
- reset();
801
718
  });
802
719
  context.on("plugin:start", (plugin) => {
803
720
  if (logLevel <= LogLevel.silent) return;
@@ -829,15 +746,12 @@ const githubActionsLogger = defineLogger({
829
746
  const text = getMessage("Files written successfully");
830
747
  console.log(text);
831
748
  if (state.currentConfigs.length === 1) closeGroup("File Generation");
749
+ showProgressStep();
832
750
  });
833
751
  context.on("file:processing:update", () => {
834
752
  if (logLevel <= LogLevel.silent) return;
835
753
  state.processedFiles++;
836
754
  });
837
- context.on("files:processing:end", () => {
838
- if (logLevel <= LogLevel.silent) return;
839
- showProgressStep();
840
- });
841
755
  context.on("generation:end", (config) => {
842
756
  const text = getMessage(config.name ? `${styleText("blue", "✓")} Generation completed for ${styleText("dim", config.name)}` : `${styleText("blue", "✓")} Generation completed`);
843
757
  console.log(text);
@@ -867,66 +781,43 @@ const githubActionsLogger = defineLogger({
867
781
  if (state.currentConfigs.length === 1) closeGroup("Linting");
868
782
  });
869
783
  context.on("hook:start", async ({ id, command, args }) => {
870
- const commandWithArgs = args?.length ? `${command} ${args.join(" ")}` : command;
784
+ const commandWithArgs = formatCommandWithArgs(command, args);
871
785
  const text = getMessage(`Hook ${styleText("dim", commandWithArgs)} started`);
872
786
  if (logLevel > LogLevel.silent) {
873
787
  if (state.currentConfigs.length === 1) openGroup(`Hook ${commandWithArgs}`);
874
788
  console.log(text);
875
789
  }
876
790
  if (!id) return;
877
- try {
878
- const result = await x(command, [...args ?? []], {
879
- nodeOptions: { detached: true },
880
- throwOnError: true
881
- });
882
- await context.emit("debug", {
883
- date: /* @__PURE__ */ new Date(),
884
- logs: [result.stdout.trimEnd()]
885
- });
886
- if (logLevel > LogLevel.silent) console.log(result.stdout.trimEnd());
887
- await context.emit("hook:end", {
888
- command,
889
- args,
890
- id,
891
- success: true,
892
- error: null
893
- });
894
- } catch (err) {
895
- const error = err;
896
- const stderr = error.output?.stderr ?? "";
897
- const stdout = error.output?.stdout ?? "";
898
- await context.emit("debug", {
899
- date: /* @__PURE__ */ new Date(),
900
- logs: [stdout, stderr].filter(Boolean)
901
- });
902
- if (stderr) console.error(`::error::${stderr}`);
903
- if (stdout) console.log(stdout);
904
- const errorMessage = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
905
- await context.emit("hook:end", {
906
- command,
907
- args,
908
- id,
909
- success: false,
910
- error: errorMessage
911
- });
912
- await context.emit("error", errorMessage);
913
- }
791
+ await runHook({
792
+ id,
793
+ command,
794
+ args,
795
+ commandWithArgs,
796
+ context,
797
+ sink: {
798
+ onStdout: logLevel > LogLevel.silent ? (s) => console.log(s) : void 0,
799
+ onStderr: logLevel > LogLevel.silent ? (s) => console.error(`::error::${s}`) : void 0
800
+ }
801
+ });
914
802
  });
915
803
  context.on("hook:end", ({ command, args }) => {
916
804
  if (logLevel <= LogLevel.silent) return;
917
- const commandWithArgs = args?.length ? `${command} ${args.join(" ")}` : command;
805
+ const commandWithArgs = formatCommandWithArgs(command, args);
918
806
  const text = getMessage(`Hook ${styleText("dim", commandWithArgs)} completed`);
919
807
  console.log(text);
920
808
  if (state.currentConfigs.length === 1) closeGroup(`Hook ${commandWithArgs}`);
921
809
  });
922
810
  context.on("generation:summary", (config, { status, hrStart, failedPlugins }) => {
923
- const pluginsCount = config.plugins?.length || 0;
811
+ const pluginsCount = config.plugins?.length ?? 0;
924
812
  const successCount = pluginsCount - failedPlugins.size;
925
813
  const duration = formatHrtime(hrStart);
926
814
  if (state.currentConfigs.length > 1) console.log(" ");
927
815
  console.log(status === "success" ? `Kubb Summary: ${styleText("blue", "✓")} ${`${successCount} successful`}, ${pluginsCount} total, ${styleText("green", duration)}` : `Kubb Summary: ${styleText("blue", "✓")} ${`${successCount} successful`}, ✗ ${`${failedPlugins.size} failed`}, ${pluginsCount} total, ${styleText("green", duration)}`);
928
816
  if (state.currentConfigs.length > 1) closeGroup(config.name ? `Generation for ${styleText("bold", config.name)}` : "Generation");
929
817
  });
818
+ context.on("lifecycle:end", () => {
819
+ reset();
820
+ });
930
821
  }
931
822
  });
932
823
  //#endregion
@@ -938,15 +829,9 @@ const githubActionsLogger = defineLogger({
938
829
  const plainLogger = defineLogger({
939
830
  name: "plain",
940
831
  install(context, options) {
941
- const logLevel = options?.logLevel || 3;
832
+ const logLevel = options?.logLevel ?? LogLevel.info;
942
833
  function getMessage(message) {
943
- if (logLevel >= LogLevel.verbose) return [`[${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
944
- hour12: false,
945
- hour: "2-digit",
946
- minute: "2-digit",
947
- second: "2-digit"
948
- })}]`, message].join(" ");
949
- return message;
834
+ return formatMessage(message, logLevel);
950
835
  }
951
836
  context.on("info", (message, info) => {
952
837
  if (logLevel <= LogLevel.silent) return;
@@ -976,7 +861,7 @@ const plainLogger = defineLogger({
976
861
  console.log(text);
977
862
  });
978
863
  context.on("error", (error) => {
979
- const caused = error.cause;
864
+ const caused = toCause(error);
980
865
  const text = getMessage(["✗", error.message].join(" "));
981
866
  console.log(text);
982
867
  if (logLevel >= LogLevel.debug && error.stack) {
@@ -1003,7 +888,7 @@ const plainLogger = defineLogger({
1003
888
  console.log(text);
1004
889
  });
1005
890
  context.on("generation:start", () => {
1006
- const text = getMessage("Configuration started");
891
+ const text = getMessage("Generation started");
1007
892
  console.log(text);
1008
893
  });
1009
894
  context.on("plugin:start", (plugin) => {
@@ -1057,51 +942,25 @@ const plainLogger = defineLogger({
1057
942
  console.log(text);
1058
943
  });
1059
944
  context.on("hook:start", async ({ id, command, args }) => {
1060
- const commandWithArgs = args?.length ? `${command} ${args.join(" ")}` : command;
945
+ const commandWithArgs = formatCommandWithArgs(command, args);
1061
946
  const text = getMessage(`Hook ${commandWithArgs} started`);
1062
947
  if (logLevel > LogLevel.silent) console.log(text);
1063
948
  if (!id) return;
1064
- try {
1065
- const result = await x(command, [...args ?? []], {
1066
- nodeOptions: { detached: true },
1067
- throwOnError: true
1068
- });
1069
- await context.emit("debug", {
1070
- date: /* @__PURE__ */ new Date(),
1071
- logs: [result.stdout.trimEnd()]
1072
- });
1073
- if (logLevel > LogLevel.silent) console.log(result.stdout.trimEnd());
1074
- await context.emit("hook:end", {
1075
- command,
1076
- args,
1077
- id,
1078
- success: true,
1079
- error: null
1080
- });
1081
- } catch (err) {
1082
- const error = err;
1083
- const stderr = error.output?.stderr ?? "";
1084
- const stdout = error.output?.stdout ?? "";
1085
- await context.emit("debug", {
1086
- date: /* @__PURE__ */ new Date(),
1087
- logs: [stdout, stderr].filter(Boolean)
1088
- });
1089
- if (stderr) console.error(stderr);
1090
- if (stdout) console.log(stdout);
1091
- const errorMessage = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
1092
- await context.emit("hook:end", {
1093
- command,
1094
- args,
1095
- id,
1096
- success: false,
1097
- error: errorMessage
1098
- });
1099
- await context.emit("error", errorMessage);
1100
- }
949
+ await runHook({
950
+ id,
951
+ command,
952
+ args,
953
+ commandWithArgs,
954
+ context,
955
+ sink: {
956
+ onStdout: logLevel > LogLevel.silent ? (s) => console.log(s) : void 0,
957
+ onStderr: logLevel > LogLevel.silent ? (s) => console.error(s) : void 0
958
+ }
959
+ });
1101
960
  });
1102
961
  context.on("hook:end", ({ command, args }) => {
1103
962
  if (logLevel <= LogLevel.silent) return;
1104
- const text = getMessage(`Hook ${args?.length ? `${command} ${args.join(" ")}` : command} completed`);
963
+ const text = getMessage(`Hook ${formatCommandWithArgs(command, args)} completed`);
1105
964
  console.log(text);
1106
965
  });
1107
966
  context.on("generation:summary", (config, { pluginTimings, status, hrStart, failedPlugins, filesCreated }) => {
@@ -1113,14 +972,50 @@ const plainLogger = defineLogger({
1113
972
  hrStart,
1114
973
  pluginTimings: logLevel >= LogLevel.verbose ? pluginTimings : void 0
1115
974
  });
1116
- console.log("---------------------------");
975
+ console.log(SUMMARY_SEPARATOR);
1117
976
  console.log(summary.join("\n"));
1118
- console.log("---------------------------");
977
+ console.log(SUMMARY_SEPARATOR);
1119
978
  });
1120
979
  }
1121
980
  });
1122
981
  //#endregion
1123
982
  //#region src/loggers/utils.ts
983
+ /**
984
+ * Optionally prefix a message with a [HH:MM:SS] timestamp when logLevel >= verbose.
985
+ * Shared across all logger adapters to avoid duplication.
986
+ */
987
+ function formatMessage(message, logLevel) {
988
+ if (logLevel >= LogLevel.verbose) return `${styleText("dim", `[${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
989
+ hour12: false,
990
+ hour: "2-digit",
991
+ minute: "2-digit",
992
+ second: "2-digit"
993
+ })}]`)} ${message}`;
994
+ return message;
995
+ }
996
+ /**
997
+ * Build the progress summary line shared by clack and GitHub Actions loggers.
998
+ * Returns null when there is nothing to display.
999
+ */
1000
+ function buildProgressLine(state) {
1001
+ const parts = [];
1002
+ const duration = formatHrtime(state.hrStart);
1003
+ if (state.totalPlugins > 0) {
1004
+ const pluginStr = state.failedPlugins > 0 ? `Plugins ${styleText("green", state.completedPlugins.toString())}/${state.totalPlugins} ${styleText("red", `(${state.failedPlugins} failed)`)}` : `Plugins ${styleText("green", state.completedPlugins.toString())}/${state.totalPlugins}`;
1005
+ parts.push(pluginStr);
1006
+ }
1007
+ if (state.totalFiles > 0) parts.push(`Files ${styleText("green", state.processedFiles.toString())}/${state.totalFiles}`);
1008
+ if (parts.length === 0) return null;
1009
+ parts.push(`${styleText("green", duration)} elapsed`);
1010
+ return parts.join(styleText("dim", " | "));
1011
+ }
1012
+ /**
1013
+ * Join a command and its optional args into a single display string.
1014
+ * e.g. ("prettier", ["--write", "."]) → "prettier --write ."
1015
+ */
1016
+ function formatCommandWithArgs(command, args) {
1017
+ return args?.length ? `${command} ${args.join(" ")}` : command;
1018
+ }
1124
1019
  function detectLogger() {
1125
1020
  if (isGitHubActions()) return "github-actions";
1126
1021
  if (canUseTTY()) return "clack";
@@ -1147,19 +1042,140 @@ async function executeHooks({ hooks, events }) {
1147
1042
  const [cmd, ...args] = tokenize(command);
1148
1043
  if (!cmd) continue;
1149
1044
  const hookId = createHash("sha256").update(command).digest("hex");
1045
+ const hookEndPromise = new Promise((resolve, reject) => {
1046
+ const handler = ({ id, success, error }) => {
1047
+ if (id !== hookId) return;
1048
+ events.off("hook:end", handler);
1049
+ if (!success) {
1050
+ reject(error ?? /* @__PURE__ */ new Error(`Hook failed: ${command}`));
1051
+ return;
1052
+ }
1053
+ events.emit("success", `${styleText("dim", command)} successfully executed`).then(resolve).catch(reject);
1054
+ };
1055
+ events.on("hook:end", handler);
1056
+ });
1150
1057
  await events.emit("hook:start", {
1151
1058
  id: hookId,
1152
1059
  command: cmd,
1153
1060
  args
1154
1061
  });
1155
- await events.onOnce("hook:end", async ({ success, error }) => {
1156
- if (!success) throw error;
1157
- await events.emit("success", `${styleText("dim", command)} successfully executed`);
1158
- });
1062
+ await hookEndPromise;
1063
+ }
1064
+ }
1065
+ //#endregion
1066
+ //#region src/utils/getCosmiConfig.ts
1067
+ const jiti = createJiti(import.meta.url, {
1068
+ jsx: {
1069
+ runtime: "automatic",
1070
+ importSource: "@kubb/react-fabric"
1071
+ },
1072
+ sourceMaps: true,
1073
+ interopDefault: true
1074
+ });
1075
+ const tsLoader = async (configFile) => {
1076
+ return await jiti.import(configFile, { default: true });
1077
+ };
1078
+ async function getCosmiConfig(moduleName, config) {
1079
+ let result;
1080
+ const searchPlaces = [
1081
+ "package.json",
1082
+ `.${moduleName}rc`,
1083
+ `.${moduleName}rc.json`,
1084
+ `.${moduleName}rc.yaml`,
1085
+ `.${moduleName}rc.yml`,
1086
+ `.${moduleName}rc.ts`,
1087
+ `.${moduleName}rc.js`,
1088
+ `.${moduleName}rc.mjs`,
1089
+ `.${moduleName}rc.cjs`,
1090
+ `${moduleName}.config.ts`,
1091
+ `${moduleName}.config.js`,
1092
+ `${moduleName}.config.mjs`,
1093
+ `${moduleName}.config.cjs`
1094
+ ];
1095
+ const explorer = cosmiconfig(moduleName, {
1096
+ cache: false,
1097
+ searchPlaces: [
1098
+ ...searchPlaces.map((searchPlace) => {
1099
+ return `.config/${searchPlace}`;
1100
+ }),
1101
+ ...searchPlaces.map((searchPlace) => {
1102
+ return `configs/${searchPlace}`;
1103
+ }),
1104
+ ...searchPlaces
1105
+ ],
1106
+ loaders: { ".ts": tsLoader }
1107
+ });
1108
+ try {
1109
+ result = config ? await explorer.load(config) : await explorer.search();
1110
+ } catch (error) {
1111
+ throw new Error("Config failed loading", { cause: error });
1159
1112
  }
1113
+ if (result?.isEmpty || !result || !result.config) throw new Error("Config not defined, create a kubb.config.js or pass through your config with the option --config");
1114
+ return result;
1115
+ }
1116
+ //#endregion
1117
+ //#region src/utils/watcher.ts
1118
+ async function startWatcher(path, cb) {
1119
+ const { watch } = await import("chokidar");
1120
+ watch(path, {
1121
+ ignorePermissionErrors: true,
1122
+ ignored: WATCHER_IGNORED_PATHS
1123
+ }).on("all", async (type, file) => {
1124
+ console.log(styleText("yellow", styleText("bold", `Change detected: ${type} ${file}`)));
1125
+ try {
1126
+ await cb(path);
1127
+ } catch (_e) {
1128
+ console.log(styleText("red", "Watcher failed"));
1129
+ }
1130
+ });
1160
1131
  }
1161
1132
  //#endregion
1162
1133
  //#region src/runners/generate.ts
1134
+ async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefix, noToolMessage, configName, outputPath, logLevel, events, onStart, onEnd }) {
1135
+ await onStart();
1136
+ let resolvedTool = toolValue;
1137
+ if (resolvedTool === "auto") {
1138
+ const detected = await detect();
1139
+ if (!detected) await events.emit("warn", noToolMessage);
1140
+ else {
1141
+ resolvedTool = detected;
1142
+ await events.emit("info", `Auto-detected ${toolLabel}: ${styleText("dim", resolvedTool)}`);
1143
+ }
1144
+ }
1145
+ if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap) {
1146
+ const toolConfig = toolMap[resolvedTool];
1147
+ try {
1148
+ const hookId = createHash("sha256").update([configName, resolvedTool].filter(Boolean).join("-")).digest("hex");
1149
+ const hookEndPromise = new Promise((resolve, reject) => {
1150
+ const handler = ({ id, success, error }) => {
1151
+ if (id !== hookId) return;
1152
+ events.off("hook:end", handler);
1153
+ if (!success) {
1154
+ reject(error ?? /* @__PURE__ */ new Error(`${toolConfig.errorMessage}`));
1155
+ return;
1156
+ }
1157
+ events.emit("success", [
1158
+ `${successPrefix} with ${styleText("dim", resolvedTool)}`,
1159
+ logLevel >= LogLevel.info ? `on ${styleText("dim", outputPath)}` : void 0,
1160
+ "successfully"
1161
+ ].filter(Boolean).join(" ")).then(resolve).catch(reject);
1162
+ };
1163
+ events.on("hook:end", handler);
1164
+ });
1165
+ await events.emit("hook:start", {
1166
+ id: hookId,
1167
+ command: toolConfig.command,
1168
+ args: toolConfig.args(outputPath)
1169
+ });
1170
+ await hookEndPromise;
1171
+ } catch (caughtError) {
1172
+ const err = new Error(toolConfig.errorMessage);
1173
+ err.cause = caughtError;
1174
+ await events.emit("error", err);
1175
+ }
1176
+ }
1177
+ await onEnd();
1178
+ }
1163
1179
  async function generate({ input, config: userConfig, events, logLevel }) {
1164
1180
  const inputPath = input ?? ("path" in userConfig.input ? userConfig.input.path : void 0);
1165
1181
  const hrStart = process$1.hrtime();
@@ -1196,9 +1212,8 @@ async function generate({ input, config: userConfig, events, logLevel }) {
1196
1212
  });
1197
1213
  await events.emit("info", "Load summary");
1198
1214
  if (failedPlugins.size > 0 || error) {
1199
- [error, ...Array.from(failedPlugins).filter((it) => it.error).map((it) => it.error)].filter(Boolean).forEach((err) => {
1200
- events.emit("error", err);
1201
- });
1215
+ const allErrors = [error, ...Array.from(failedPlugins).filter((it) => it.error).map((it) => it.error)].filter(Boolean);
1216
+ for (const err of allErrors) await events.emit("error", err);
1202
1217
  await events.emit("generation:end", config, files, sources);
1203
1218
  await events.emit("generation:summary", config, {
1204
1219
  failedPlugins,
@@ -1222,80 +1237,35 @@ async function generate({ input, config: userConfig, events, logLevel }) {
1222
1237
  }
1223
1238
  await events.emit("success", "Generation successfully", inputPath);
1224
1239
  await events.emit("generation:end", config, files, sources);
1225
- if (config.output.format) {
1226
- await events.emit("format:start");
1227
- let formatter = config.output.format;
1228
- if (formatter === "auto") {
1229
- const detectedFormatter = await detectFormatter();
1230
- if (!detectedFormatter) await events.emit("warn", "No formatter found (biome, prettier, or oxfmt). Skipping formatting.");
1231
- else {
1232
- formatter = detectedFormatter;
1233
- await events.emit("info", `Auto-detected formatter: ${styleText("dim", formatter)}`);
1234
- }
1235
- }
1236
- if (formatter && formatter !== "auto" && formatter in formatters) {
1237
- const formatterConfig = formatters[formatter];
1238
- const outputPath = path.resolve(config.root, config.output.path);
1239
- try {
1240
- const hookId = createHash("sha256").update([config.name, formatter].filter(Boolean).join("-")).digest("hex");
1241
- await events.emit("hook:start", {
1242
- id: hookId,
1243
- command: formatterConfig.command,
1244
- args: formatterConfig.args(outputPath)
1245
- });
1246
- await events.onOnce("hook:end", async ({ success, error }) => {
1247
- if (!success) throw error;
1248
- await events.emit("success", [
1249
- `Formatting with ${styleText("dim", formatter)}`,
1250
- logLevel >= LogLevel.info ? `on ${styleText("dim", outputPath)}` : void 0,
1251
- "successfully"
1252
- ].filter(Boolean).join(" "));
1253
- });
1254
- } catch (caughtError) {
1255
- const error = new Error(formatterConfig.errorMessage);
1256
- error.cause = caughtError;
1257
- await events.emit("error", error);
1258
- }
1259
- }
1260
- await events.emit("format:end");
1261
- }
1262
- if (config.output.lint) {
1263
- await events.emit("lint:start");
1264
- let linter = config.output.lint;
1265
- if (linter === "auto") {
1266
- const detectedLinter = await detectLinter();
1267
- if (!detectedLinter) await events.emit("warn", "No linter found (biome, oxlint, or eslint). Skipping linting.");
1268
- else {
1269
- linter = detectedLinter;
1270
- await events.emit("info", `Auto-detected linter: ${styleText("dim", linter)}`);
1271
- }
1272
- }
1273
- if (linter && linter !== "auto" && linter in linters) {
1274
- const linterConfig = linters[linter];
1275
- const outputPath = path.resolve(config.root, config.output.path);
1276
- try {
1277
- const hookId = createHash("sha256").update([config.name, linter].filter(Boolean).join("-")).digest("hex");
1278
- await events.emit("hook:start", {
1279
- id: hookId,
1280
- command: linterConfig.command,
1281
- args: linterConfig.args(outputPath)
1282
- });
1283
- await events.onOnce("hook:end", async ({ success, error }) => {
1284
- if (!success) throw error;
1285
- await events.emit("success", [
1286
- `Linting with ${styleText("dim", linter)}`,
1287
- logLevel >= LogLevel.info ? `on ${styleText("dim", outputPath)}` : void 0,
1288
- "successfully"
1289
- ].filter(Boolean).join(" "));
1290
- });
1291
- } catch (caughtError) {
1292
- const error = new Error(linterConfig.errorMessage);
1293
- error.cause = caughtError;
1294
- await events.emit("error", error);
1295
- }
1296
- }
1297
- await events.emit("lint:end");
1298
- }
1240
+ const outputPath = path.resolve(config.root, config.output.path);
1241
+ if (config.output.format) await runToolPass({
1242
+ toolValue: config.output.format,
1243
+ detect: detectFormatter,
1244
+ toolMap: formatters,
1245
+ toolLabel: "formatter",
1246
+ successPrefix: "Formatting",
1247
+ noToolMessage: "No formatter found (biome, prettier, or oxfmt). Skipping formatting.",
1248
+ configName: config.name,
1249
+ outputPath,
1250
+ logLevel,
1251
+ events,
1252
+ onStart: () => events.emit("format:start"),
1253
+ onEnd: () => events.emit("format:end")
1254
+ });
1255
+ if (config.output.lint) await runToolPass({
1256
+ toolValue: config.output.lint,
1257
+ detect: detectLinter,
1258
+ toolMap: linters,
1259
+ toolLabel: "linter",
1260
+ successPrefix: "Linting",
1261
+ noToolMessage: "No linter found (biome, oxlint, or eslint). Skipping linting.",
1262
+ configName: config.name,
1263
+ outputPath,
1264
+ logLevel,
1265
+ events,
1266
+ onStart: () => events.emit("lint:start"),
1267
+ onEnd: () => events.emit("lint:end")
1268
+ });
1299
1269
  if (config.hooks) {
1300
1270
  await events.emit("hooks:start");
1301
1271
  await executeHooks({
@@ -1304,11 +1274,10 @@ async function generate({ input, config: userConfig, events, logLevel }) {
1304
1274
  });
1305
1275
  await events.emit("hooks:end");
1306
1276
  }
1307
- const generationStatus = failedPlugins.size > 0 || error ? "failed" : "success";
1308
1277
  await events.emit("generation:summary", config, {
1309
1278
  failedPlugins,
1310
1279
  filesCreated: files.length,
1311
- status: generationStatus,
1280
+ status: "success",
1312
1281
  hrStart,
1313
1282
  pluginTimings
1314
1283
  });
@@ -1321,183 +1290,59 @@ async function generate({ input, config: userConfig, events, logLevel }) {
1321
1290
  })),
1322
1291
  hrStart,
1323
1292
  filesCreated: files.length,
1324
- status: generationStatus
1293
+ status: "success"
1325
1294
  }));
1326
1295
  }
1327
- //#endregion
1328
- //#region src/utils/getCosmiConfig.ts
1329
- const tsLoader = async (configFile) => {
1330
- return await createJiti(import.meta.url, {
1331
- jsx: {
1332
- runtime: "automatic",
1333
- importSource: "@kubb/react-fabric"
1334
- },
1335
- sourceMaps: true,
1336
- interopDefault: true
1337
- }).import(configFile, { default: true });
1338
- };
1339
- async function getCosmiConfig(moduleName, config) {
1340
- let result;
1341
- const searchPlaces = [
1342
- "package.json",
1343
- `.${moduleName}rc`,
1344
- `.${moduleName}rc.json`,
1345
- `.${moduleName}rc.yaml`,
1346
- `.${moduleName}rc.yml`,
1347
- `.${moduleName}rc.ts`,
1348
- `.${moduleName}rc.js`,
1349
- `.${moduleName}rc.mjs`,
1350
- `.${moduleName}rc.cjs`,
1351
- `${moduleName}.config.ts`,
1352
- `${moduleName}.config.js`,
1353
- `${moduleName}.config.mjs`,
1354
- `${moduleName}.config.cjs`
1355
- ];
1356
- const explorer = cosmiconfig(moduleName, {
1357
- cache: false,
1358
- searchPlaces: [
1359
- ...searchPlaces.map((searchPlace) => {
1360
- return `.config/${searchPlace}`;
1361
- }),
1362
- ...searchPlaces.map((searchPlace) => {
1363
- return `configs/${searchPlace}`;
1364
- }),
1365
- ...searchPlaces
1366
- ],
1367
- loaders: { ".ts": tsLoader }
1368
- });
1369
- try {
1370
- result = config ? await explorer.load(config) : await explorer.search();
1371
- } catch (error) {
1372
- throw new Error("Config failed loading", { cause: error });
1373
- }
1374
- if (result?.isEmpty || !result || !result.config) throw new Error("Config not defined, create a kubb.config.js or pass through your config with the option --config");
1375
- return result;
1376
- }
1377
- //#endregion
1378
- //#region src/utils/watcher.ts
1379
- async function startWatcher(path, cb) {
1380
- const { watch } = await import("chokidar");
1381
- watch(path, {
1382
- ignorePermissionErrors: true,
1383
- ignored: "**/{.git,node_modules}/**"
1384
- }).on("all", async (type, file) => {
1385
- console.log(styleText("yellow", styleText("bold", `Change detected: ${type} ${file}`)));
1296
+ async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }) {
1297
+ const logLevel = LogLevel[logLevelKey] ?? LogLevel.info;
1298
+ const events = new AsyncEventEmitter();
1299
+ const promiseManager = new PromiseManager();
1300
+ await setupLogger(events, { logLevel });
1301
+ await executeIfOnline(async () => {
1386
1302
  try {
1387
- await cb(path);
1388
- } catch (_e) {
1389
- console.log(styleText("red", "Watcher failed"));
1390
- }
1303
+ const latestVersion = (await (await fetch(KUBB_NPM_PACKAGE_URL)).json()).version;
1304
+ if (latestVersion && version < latestVersion) await events.emit("version:new", version, latestVersion);
1305
+ } catch {}
1391
1306
  });
1392
- }
1393
- //#endregion
1394
- //#region src/commands/generate.ts
1395
- const command = defineCommand({
1396
- meta: {
1397
- name: "generate",
1398
- description: "[input] Generate files based on a 'kubb.config.ts' file"
1399
- },
1400
- args: {
1401
- config: {
1402
- type: "string",
1403
- description: "Path to the Kubb config",
1404
- alias: "c"
1405
- },
1406
- logLevel: {
1407
- type: "string",
1408
- description: "Info, silent, verbose or debug",
1409
- alias: "l",
1410
- default: "info",
1411
- valueHint: "silent|info|verbose|debug"
1412
- },
1413
- watch: {
1414
- type: "boolean",
1415
- description: "Watch mode based on the input file",
1416
- alias: "w",
1417
- default: false
1418
- },
1419
- debug: {
1420
- type: "boolean",
1421
- description: "Override logLevel to debug",
1422
- alias: "d",
1423
- default: false
1424
- },
1425
- verbose: {
1426
- type: "boolean",
1427
- description: "Override logLevel to verbose",
1428
- alias: "v",
1429
- default: false
1430
- },
1431
- silent: {
1432
- type: "boolean",
1433
- description: "Override logLevel to silent",
1434
- alias: "s",
1435
- default: false
1436
- },
1437
- help: {
1438
- type: "boolean",
1439
- description: "Show help",
1440
- alias: "h",
1441
- default: false
1442
- }
1443
- },
1444
- async run(commandContext) {
1445
- const { args } = commandContext;
1446
- const input = args._[0];
1447
- const events = new AsyncEventEmitter();
1448
- const promiseManager = new PromiseManager();
1449
- if (args.help) return showUsage(command);
1450
- if (args.debug) args.logLevel = "debug";
1451
- if (args.verbose) args.logLevel = "verbose";
1452
- if (args.silent) args.logLevel = "silent";
1453
- const logLevel = LogLevel[args.logLevel] || 3;
1454
- await setupLogger(events, { logLevel });
1455
- await executeIfOnline(async () => {
1456
- try {
1457
- const latestVersion = (await (await fetch("https://registry.npmjs.org/@kubb/cli/latest")).json()).version;
1458
- if (latestVersion && version < latestVersion) await events.emit("version:new", version, latestVersion);
1459
- } catch {}
1460
- });
1461
- try {
1462
- const result = await getCosmiConfig("kubb", args.config);
1463
- const configs = await getConfigs(result.config, args);
1464
- await events.emit("config:start");
1465
- await events.emit("info", "Config loaded", path.relative(process$2.cwd(), result.filepath));
1466
- await events.emit("success", "Config loaded successfully", path.relative(process$2.cwd(), result.filepath));
1467
- await events.emit("config:end", configs);
1468
- await events.emit("lifecycle:start", version);
1469
- const promises = configs.map((config) => {
1470
- return async () => {
1471
- if (isInputPath(config) && args.watch) {
1472
- await startWatcher([input || config.input.path], async (paths) => {
1473
- events.removeAll();
1474
- await generate({
1475
- input,
1476
- config,
1477
- logLevel,
1478
- events
1479
- });
1480
- clack.log.step(styleText("yellow", `Watching for changes in ${paths.join(" and ")}`));
1307
+ try {
1308
+ const result = await getCosmiConfig("kubb", configPath);
1309
+ const configs = await getConfigs(result.config, { input });
1310
+ await events.emit("config:start");
1311
+ await events.emit("info", "Config loaded", path.relative(process$1.cwd(), result.filepath));
1312
+ await events.emit("success", "Config loaded successfully", path.relative(process$1.cwd(), result.filepath));
1313
+ await events.emit("config:end", configs);
1314
+ await events.emit("lifecycle:start", version);
1315
+ const promises = configs.map((config) => {
1316
+ return async () => {
1317
+ if (isInputPath(config) && watch) {
1318
+ await startWatcher([input || config.input.path], async (paths) => {
1319
+ events.removeAll();
1320
+ await generate({
1321
+ input,
1322
+ config,
1323
+ logLevel,
1324
+ events
1481
1325
  });
1482
- return;
1483
- }
1484
- await generate({
1485
- input,
1486
- config,
1487
- logLevel,
1488
- events
1326
+ clack.log.step(styleText("yellow", `Watching for changes in ${paths.join(" and ")}`));
1489
1327
  });
1490
- };
1491
- });
1492
- await promiseManager.run("seq", promises);
1493
- await events.emit("lifecycle:end");
1494
- } catch (error) {
1495
- await events.emit("error", error);
1496
- process$2.exit(1);
1497
- }
1328
+ return;
1329
+ }
1330
+ await generate({
1331
+ input,
1332
+ config,
1333
+ logLevel,
1334
+ events
1335
+ });
1336
+ };
1337
+ });
1338
+ await promiseManager.run("seq", promises);
1339
+ await events.emit("lifecycle:end");
1340
+ } catch (error) {
1341
+ await events.emit("error", toError(error));
1342
+ process$1.exit(1);
1498
1343
  }
1499
- });
1344
+ }
1500
1345
  //#endregion
1501
- export { command as default };
1346
+ export { runGenerateCommand };
1502
1347
 
1503
- //# sourceMappingURL=generate-CpWtSc45.js.map
1348
+ //# sourceMappingURL=generate-HP5ySfjV.js.map