@kubb/cli 5.5.6 → 5.6.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 (146) hide show
  1. package/dist/{Telemetry-CMXOVUKB.js → Telemetry-RBcAjNxj.js} +18 -43
  2. package/dist/Telemetry-RBcAjNxj.js.map +1 -0
  3. package/dist/{Telemetry-DDHXBdEz.cjs → Telemetry-bGdFBaGt.cjs} +27 -89
  4. package/dist/Telemetry-bGdFBaGt.cjs.map +1 -0
  5. package/dist/agent-BPIqnEl2.js +59 -0
  6. package/dist/agent-BPIqnEl2.js.map +1 -0
  7. package/dist/agent-DElUuYuy.cjs +120 -0
  8. package/dist/agent-DElUuYuy.cjs.map +1 -0
  9. package/dist/{background-DxlRE9O5.js → background-0PMj56FJ.js} +6 -5
  10. package/dist/background-0PMj56FJ.js.map +1 -0
  11. package/dist/background-BqS01QDb.js +2 -0
  12. package/dist/{background-Bbcc2TdN.cjs → background-R0T_7Szu.cjs} +8 -7
  13. package/dist/background-R0T_7Szu.cjs.map +1 -0
  14. package/dist/commands-BAgGrcvc.js +65 -0
  15. package/dist/commands-BAgGrcvc.js.map +1 -0
  16. package/dist/commands-Si-RFAS6.cjs +71 -0
  17. package/dist/commands-Si-RFAS6.cjs.map +1 -0
  18. package/dist/config-8Xl4yZmw.js +146 -0
  19. package/dist/config-8Xl4yZmw.js.map +1 -0
  20. package/dist/config-ByfJSj3q.cjs +164 -0
  21. package/dist/config-ByfJSj3q.cjs.map +1 -0
  22. package/dist/constants-CdxpgX3_.cjs.map +1 -1
  23. package/dist/constants-DHRspBTo.js.map +1 -1
  24. package/dist/{errors-C-wEou02.cjs → errors-CMbMAwyW.cjs} +2 -2
  25. package/dist/{errors-C-wEou02.cjs.map → errors-CMbMAwyW.cjs.map} +1 -1
  26. package/dist/index.cjs +431 -22
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.js +429 -20
  29. package/dist/index.js.map +1 -1
  30. package/dist/output-B6Ondw0g.js +776 -0
  31. package/dist/output-B6Ondw0g.js.map +1 -0
  32. package/dist/output-i3s5c4Xu.cjs +878 -0
  33. package/dist/output-i3s5c4Xu.cjs.map +1 -0
  34. package/dist/run-3I2NPeXG.cjs +31 -0
  35. package/dist/run-3I2NPeXG.cjs.map +1 -0
  36. package/dist/{run-C-jBKNbI.cjs → run-BkLJ5mXn.cjs} +22 -43
  37. package/dist/run-BkLJ5mXn.cjs.map +1 -0
  38. package/dist/run-CZlkqkDc.js +30 -0
  39. package/dist/run-CZlkqkDc.js.map +1 -0
  40. package/dist/run-CmNDNEjR.js +346 -0
  41. package/dist/run-CmNDNEjR.js.map +1 -0
  42. package/dist/{run-BdTwxLCW.js → run-D5AeacqK.js} +9 -23
  43. package/dist/run-D5AeacqK.js.map +1 -0
  44. package/dist/{run-y6LRyWdS.cjs → run-D8Wz_17X.cjs} +41 -60
  45. package/dist/run-D8Wz_17X.cjs.map +1 -0
  46. package/dist/{run-Dn8HVjnM.js → run-DQoKTiGk.js} +23 -44
  47. package/dist/run-DQoKTiGk.js.map +1 -0
  48. package/dist/{run-BDaZPU-1.js → run-Dgph0WYk.js} +25 -49
  49. package/dist/run-Dgph0WYk.js.map +1 -0
  50. package/dist/run-t8nm7zcT.cjs +348 -0
  51. package/dist/run-t8nm7zcT.cjs.map +1 -0
  52. package/dist/{run-CZz_GbE4.cjs → run-yvuot2N9.cjs} +12 -26
  53. package/dist/run-yvuot2N9.cjs.map +1 -0
  54. package/dist/{snapshot-B5mc4gpC.js → snapshot-CkHXR0Nl.js} +5 -5
  55. package/dist/snapshot-CkHXR0Nl.js.map +1 -0
  56. package/dist/{snapshot-DnCxZfmM.cjs → snapshot-D4nKbwkw.cjs} +9 -9
  57. package/dist/snapshot-D4nKbwkw.cjs.map +1 -0
  58. package/dist/studioWorker.js +2 -2
  59. package/package.json +7 -7
  60. package/dist/Telemetry-CMXOVUKB.js.map +0 -1
  61. package/dist/Telemetry-DDHXBdEz.cjs.map +0 -1
  62. package/dist/agent-DiB4CRre.cjs +0 -38
  63. package/dist/agent-DiB4CRre.cjs.map +0 -1
  64. package/dist/agent-yA5vrXJB.js +0 -26
  65. package/dist/agent-yA5vrXJB.js.map +0 -1
  66. package/dist/background-Bbcc2TdN.cjs.map +0 -1
  67. package/dist/background-CFNncNYT.js +0 -2
  68. package/dist/background-DxlRE9O5.js.map +0 -1
  69. package/dist/connect-C9tTngda.js +0 -11
  70. package/dist/connect-C9tTngda.js.map +0 -1
  71. package/dist/connect-CJ7BhOcs.cjs +0 -11
  72. package/dist/connect-CJ7BhOcs.cjs.map +0 -1
  73. package/dist/generate-BUqZ_7GB.cjs +0 -92
  74. package/dist/generate-BUqZ_7GB.cjs.map +0 -1
  75. package/dist/generate-q2m01zRd.js +0 -92
  76. package/dist/generate-q2m01zRd.js.map +0 -1
  77. package/dist/gunshiDryRun-DdKJuVn1.js +0 -2
  78. package/dist/init-Ckpwkfvz.cjs +0 -58
  79. package/dist/init-Ckpwkfvz.cjs.map +0 -1
  80. package/dist/init-DukE9Uw4.js +0 -58
  81. package/dist/init-DukE9Uw4.js.map +0 -1
  82. package/dist/login-Bc19oiFE.cjs +0 -11
  83. package/dist/login-Bc19oiFE.cjs.map +0 -1
  84. package/dist/login-Do3nns0B.js +0 -11
  85. package/dist/login-Do3nns0B.js.map +0 -1
  86. package/dist/logout-BvHFvCeI.cjs +0 -16
  87. package/dist/logout-BvHFvCeI.cjs.map +0 -1
  88. package/dist/logout-COxoIMum.js +0 -16
  89. package/dist/logout-COxoIMum.js.map +0 -1
  90. package/dist/mcp-B-GxGgVr.js +0 -19
  91. package/dist/mcp-B-GxGgVr.js.map +0 -1
  92. package/dist/mcp-B3B-rE7W.cjs +0 -18
  93. package/dist/mcp-B3B-rE7W.cjs.map +0 -1
  94. package/dist/output-CVLosNZq.cjs +0 -411
  95. package/dist/output-CVLosNZq.cjs.map +0 -1
  96. package/dist/output-FDo-db-w.js +0 -321
  97. package/dist/output-FDo-db-w.js.map +0 -1
  98. package/dist/package-Ci2fcoOV.js +0 -6
  99. package/dist/package-Ci2fcoOV.js.map +0 -1
  100. package/dist/package-Do9rJ7D3.cjs +0 -11
  101. package/dist/package-Do9rJ7D3.cjs.map +0 -1
  102. package/dist/rolldown-runtime-qbf5tadS.cjs +0 -38
  103. package/dist/run-BDaZPU-1.js.map +0 -1
  104. package/dist/run-BdTwxLCW.js.map +0 -1
  105. package/dist/run-C-jBKNbI.cjs.map +0 -1
  106. package/dist/run-CAcQlhub.js +0 -295
  107. package/dist/run-CAcQlhub.js.map +0 -1
  108. package/dist/run-CSoFhDyK.cjs +0 -297
  109. package/dist/run-CSoFhDyK.cjs.map +0 -1
  110. package/dist/run-CZz_GbE4.cjs.map +0 -1
  111. package/dist/run-CaEbo-te.js +0 -40
  112. package/dist/run-CaEbo-te.js.map +0 -1
  113. package/dist/run-CihzLZmR.cjs +0 -41
  114. package/dist/run-CihzLZmR.cjs.map +0 -1
  115. package/dist/run-Dn8HVjnM.js.map +0 -1
  116. package/dist/run-y6LRyWdS.cjs.map +0 -1
  117. package/dist/shared-Cl-OLpNh.cjs +0 -81
  118. package/dist/shared-Cl-OLpNh.cjs.map +0 -1
  119. package/dist/shared-DaHA7QFQ.js +0 -58
  120. package/dist/shared-DaHA7QFQ.js.map +0 -1
  121. package/dist/snapshot-B5mc4gpC.js.map +0 -1
  122. package/dist/snapshot-DnCxZfmM.cjs.map +0 -1
  123. package/dist/start-B-GXO8tS.cjs +0 -12
  124. package/dist/start-B-GXO8tS.cjs.map +0 -1
  125. package/dist/start-Dbfx_8zG.js +0 -12
  126. package/dist/start-Dbfx_8zG.js.map +0 -1
  127. package/dist/status-DDajNc8w.js +0 -17
  128. package/dist/status-DDajNc8w.js.map +0 -1
  129. package/dist/status-DusS7DfO.cjs +0 -17
  130. package/dist/status-DusS7DfO.cjs.map +0 -1
  131. package/dist/stop-C5t7Yx0t.js +0 -11
  132. package/dist/stop-C5t7Yx0t.js.map +0 -1
  133. package/dist/stop-zmM3A0Re.cjs +0 -11
  134. package/dist/stop-zmM3A0Re.cjs.map +0 -1
  135. package/dist/studio--IOOl_VB.js +0 -174
  136. package/dist/studio--IOOl_VB.js.map +0 -1
  137. package/dist/studio-CJ2XhtRd.cjs +0 -174
  138. package/dist/studio-CJ2XhtRd.cjs.map +0 -1
  139. package/dist/utils-C3HZOWL_.js +0 -1107
  140. package/dist/utils-C3HZOWL_.js.map +0 -1
  141. package/dist/utils-ClRbJfxu.cjs +0 -1210
  142. package/dist/utils-ClRbJfxu.cjs.map +0 -1
  143. package/dist/validate-BcB0tFQ6.cjs +0 -19
  144. package/dist/validate-BcB0tFQ6.cjs.map +0 -1
  145. package/dist/validate-QpkCbIFJ.js +0 -20
  146. package/dist/validate-QpkCbIFJ.js.map +0 -1
@@ -1,1210 +0,0 @@
1
- const require_rolldown_runtime = require("./rolldown-runtime-qbf5tadS.cjs");
2
- const require_errors = require("./errors-C-wEou02.cjs");
3
- const require_agent = require("./agent-DiB4CRre.cjs");
4
- const require_Telemetry = require("./Telemetry-DDHXBdEz.cjs");
5
- const require_output = require("./output-CVLosNZq.cjs");
6
- let node_util = require("node:util");
7
- let node_crypto = require("node:crypto");
8
- let node_process = require("node:process");
9
- node_process = require_rolldown_runtime.__toESM(node_process, 1);
10
- let node_child_process = require("node:child_process");
11
- let node_path = require("node:path");
12
- let _kubb_core = require("@kubb/core");
13
- let _clack_prompts = require("@clack/prompts");
14
- _clack_prompts = require_rolldown_runtime.__toESM(_clack_prompts, 1);
15
- let jiti = require("jiti");
16
- let tinyexec = require("tinyexec");
17
- let unconfig = require("unconfig");
18
- let verkit = require("verkit");
19
- //#region ../../internals/utils/src/tools.ts
20
- /**
21
- * CLI command descriptors for each supported code formatter.
22
- */
23
- const formatters = {
24
- prettier: {
25
- command: "prettier",
26
- args: (outputPath) => [
27
- "--ignore-unknown",
28
- "--write",
29
- outputPath
30
- ],
31
- errorMessage: "Prettier not found"
32
- },
33
- biome: {
34
- command: "biome",
35
- args: (outputPath) => [
36
- "format",
37
- "--write",
38
- outputPath
39
- ],
40
- errorMessage: "Biome not found"
41
- },
42
- oxfmt: {
43
- command: "oxfmt",
44
- args: (outputPath) => [outputPath],
45
- errorMessage: "Oxfmt not found"
46
- }
47
- };
48
- /**
49
- * CLI command descriptors for each supported linter.
50
- */
51
- const linters = {
52
- eslint: {
53
- command: "eslint",
54
- args: (outputPath) => [outputPath, "--fix"],
55
- errorMessage: "Eslint not found"
56
- },
57
- biome: {
58
- command: "biome",
59
- args: (outputPath) => [
60
- "lint",
61
- "--fix",
62
- outputPath
63
- ],
64
- errorMessage: "Biome not found"
65
- },
66
- oxlint: {
67
- command: "oxlint",
68
- args: (outputPath) => [
69
- "--fix",
70
- "--no-ignore",
71
- outputPath
72
- ],
73
- errorMessage: "Oxlint not found"
74
- }
75
- };
76
- /**
77
- * Preference order for `format: 'auto'`, most-preferred first. Spelled out rather than taken from
78
- * the table's key order, which is arbitrary and would silently change what `auto` picks.
79
- */
80
- const FORMATTER_PREFERENCE = [
81
- "oxfmt",
82
- "biome",
83
- "prettier"
84
- ];
85
- /**
86
- * Preference order for `lint: 'auto'`, most-preferred first.
87
- */
88
- const LINTER_PREFERENCE = [
89
- "oxlint",
90
- "biome",
91
- "eslint"
92
- ];
93
- /**
94
- * Whether `name` is on PATH and answers `--version` with a zero exit.
95
- */
96
- function isToolAvailable(name) {
97
- return new Promise((resolve) => {
98
- const child = (0, node_child_process.spawn)(name, ["--version"], { stdio: "ignore" });
99
- child.on("close", (code) => resolve(code === 0));
100
- child.on("error", () => resolve(false));
101
- });
102
- }
103
- /**
104
- * Returns the first installed executable from `candidates`, or `null` when none are found.
105
- *
106
- * Not memoized: a long-running host that probes repeatedly should cache the result itself, and a
107
- * `--watch` build should keep noticing a tool installed mid-session.
108
- */
109
- async function detectTool(candidates) {
110
- for (const candidate of candidates) if (await isToolAvailable(candidate)) return candidate;
111
- return null;
112
- }
113
- /**
114
- * Tokenizes a shell command string, respecting single and double quotes.
115
- *
116
- * @example
117
- * ```ts
118
- * tokenize('git commit -m "initial commit"')
119
- * // → ['git', 'commit', '-m', 'initial commit']
120
- * ```
121
- */
122
- function tokenize(command) {
123
- return (command.match(/[^\s"']+|"([^"]*)"|'([^']*)'/g) ?? []).map((token) => token.replace(/^["']|["']$/g, ""));
124
- }
125
- //#endregion
126
- //#region src/loggers/createLogger.ts
127
- /**
128
- * The three output phases, in the order the generate runner runs them.
129
- */
130
- const PHASES = {
131
- format: {
132
- running: "Formatting",
133
- success: "Formatted",
134
- failure: "Formatting failed"
135
- },
136
- lint: {
137
- running: "Linting",
138
- success: "Linted",
139
- failure: "Linting failed"
140
- },
141
- hooks: {
142
- running: "Running post-generate hooks",
143
- success: "Post-generate hooks completed",
144
- failure: "Post-generate hooks failed",
145
- showHookResults: true
146
- }
147
- };
148
- /**
149
- * Drops the blank lines a subprocess leaves at either end of its output, so its block does not open
150
- * or close on an empty row.
151
- */
152
- function trimBlankEdges(lines) {
153
- const start = lines.findIndex((line) => line.trim());
154
- if (start === -1) return [];
155
- return lines.slice(start, lines.findLastIndex((line) => line.trim()) + 1);
156
- }
157
- /**
158
- * Wires a {@link LoggerWriter} onto the run's hooks. This owns what a run says and in what order:
159
- * one group per config, a step over the plugin work, a progress bar for the writes, one step per
160
- * output phase, then the summary and the group's closing result. The writer only decides how each
161
- * line looks, so every logger reports the same run the same way.
162
- *
163
- * @example
164
- * ```ts
165
- * export const plainLogger = { name: 'plain', install: (context, options) => createLogger(writer)(context, options) }
166
- * ```
167
- */
168
- function createLogger(writer) {
169
- return function install(context, options) {
170
- const logLevel = options?.logLevel ?? _kubb_core.logLevel.info;
171
- const silent = logLevel <= _kubb_core.logLevel.silent;
172
- const state = {
173
- ...createProgressCounters(),
174
- spinner: null,
175
- progress: null,
176
- hooks: /* @__PURE__ */ new Map(),
177
- /**
178
- * Plugin results held back while the generation step owns the line.
179
- */
180
- pluginLines: [],
181
- phase: null,
182
- groupOpen: false
183
- };
184
- function text(message) {
185
- return formatMessage(message, logLevel);
186
- }
187
- function startSpinner(message) {
188
- if (silent) return;
189
- state.spinner = writer.spinner();
190
- state.spinner.start(text(message));
191
- }
192
- function stopSpinner(message, status = "success") {
193
- const spinner = state.spinner;
194
- if (!spinner) return;
195
- state.spinner = null;
196
- if (message === void 0) {
197
- spinner.clear();
198
- return;
199
- }
200
- if (status === "failed") {
201
- spinner.error(text(message));
202
- return;
203
- }
204
- spinner.stop(text(message));
205
- }
206
- function stopProgress(message) {
207
- const progress = state.progress;
208
- if (!progress) return;
209
- state.progress = null;
210
- progress.stop(message === void 0 ? "" : text(message));
211
- }
212
- function startPhase({ running, success, failure, showHookResults = false }) {
213
- state.phase = {
214
- successLabel: success,
215
- failureLabel: failure,
216
- showHookResults,
217
- failed: false,
218
- hrStart: node_process.default.hrtime(),
219
- lines: [],
220
- output: []
221
- };
222
- startSpinner(running);
223
- }
224
- function endPhase() {
225
- const phase = state.phase;
226
- state.phase = null;
227
- if (!phase || silent) return;
228
- const duration = require_output.formatMsWithColor(require_output.getElapsedMs(phase.hrStart));
229
- stopSpinner(phase.failed ? `${phase.failureLabel} after ${duration}` : `${phase.successLabel} in ${duration}`, phase.failed ? "failed" : "success");
230
- if (phase.lines.length) writer.block(phase.lines);
231
- writeOutput(phase.output);
232
- }
233
- function writeOutput(lines) {
234
- const output = trimBlankEdges(lines);
235
- if (output.length) writer.raw(output);
236
- }
237
- function closeGroup(status) {
238
- if (!state.groupOpen) return;
239
- state.groupOpen = false;
240
- stopSpinner();
241
- stopProgress();
242
- writer.groupEnd(status === "failed" ? (0, node_util.styleText)("red", "✗ Generation failed") : (0, node_util.styleText)("green", "✓ Generation succeeded"));
243
- }
244
- function reset() {
245
- stopSpinner();
246
- stopProgress();
247
- resetProgressCounters(state);
248
- state.hooks.clear();
249
- state.pluginLines = [];
250
- state.phase = null;
251
- state.groupOpen = false;
252
- }
253
- context.hook("kubb:info", ({ message, info }) => {
254
- if (silent) return;
255
- const line = text([
256
- (0, node_util.styleText)("blue", "ℹ"),
257
- message,
258
- info ? (0, node_util.styleText)("dim", info) : void 0
259
- ].filter(Boolean).join(" "));
260
- if (state.phase) {
261
- state.phase.lines.push(line);
262
- return;
263
- }
264
- if (state.spinner) {
265
- state.spinner.message(line);
266
- return;
267
- }
268
- writer.info(line);
269
- });
270
- context.hook("kubb:success", ({ message, info }) => {
271
- if (silent) return;
272
- const line = text([
273
- (0, node_util.styleText)("green", "✓"),
274
- message,
275
- logLevel >= _kubb_core.logLevel.info && info ? (0, node_util.styleText)("dim", info) : void 0
276
- ].filter(Boolean).join(" "));
277
- if (state.spinner) {
278
- state.spinner.message(line);
279
- return;
280
- }
281
- writer.success(line);
282
- });
283
- context.hook("kubb:warn", ({ message, info }) => {
284
- if (logLevel < _kubb_core.logLevel.warn) return;
285
- writer.warn(text([
286
- (0, node_util.styleText)("yellow", "⚠"),
287
- message,
288
- logLevel >= _kubb_core.logLevel.info && info ? (0, node_util.styleText)("dim", info) : void 0
289
- ].filter(Boolean).join(" ")));
290
- });
291
- context.hook("kubb:error", ({ error }) => {
292
- if (state.phase) state.phase.failed = true;
293
- if (state.spinner) stopSpinner(error.message, "failed");
294
- else writer.error(text(error.message));
295
- const frames = logLevel >= _kubb_core.logLevel.verbose ? formatErrorFrames(error) : null;
296
- if (!frames) return;
297
- writer.block(frames.frames.map((frame) => text((0, node_util.styleText)("dim", frame))));
298
- if (frames.cause) writer.block([(0, node_util.styleText)("dim", frames.cause.header), ...frames.cause.frames.map((frame) => text(` ${(0, node_util.styleText)("dim", frame)}`))]);
299
- });
300
- context.hook("kubb:diagnostic", ({ diagnostic }) => {
301
- if (silent && diagnostic.severity !== "error") return;
302
- stopSpinner();
303
- stopProgress();
304
- if (_kubb_core.Diagnostics.isUpdate(diagnostic)) {
305
- writer.update([`\`v${diagnostic.currentVersion}\` → \`v${diagnostic.latestVersion}\``, "Run `npm install -g @kubb/cli` to update"], "Update available for `Kubb`");
306
- return;
307
- }
308
- const { headline, details } = _kubb_core.Diagnostics.format(diagnostic);
309
- writer.diagnostic([headline, ...details]);
310
- });
311
- context.hook("studio:connecting", ({ url }) => {
312
- startSpinner(`Connecting to ${(0, node_util.styleText)("cyan", url)}`);
313
- });
314
- context.hook("studio:connected", ({ url, versions }) => {
315
- if (silent) return;
316
- const line = `Connected to ${(0, node_util.styleText)("cyan", url)} ${(0, node_util.styleText)("dim", `(${formatVersions(versions)})`)}`;
317
- if (state.spinner) {
318
- stopSpinner(line);
319
- return;
320
- }
321
- writer.success(text(`✓ ${line}`));
322
- });
323
- context.hook("studio:ready", () => {
324
- if (silent) return;
325
- startSpinner("✓ Ready to receive jobs");
326
- });
327
- context.hook("studio:disconnected", ({ reason }) => {
328
- if (logLevel < _kubb_core.logLevel.warn) return;
329
- stopSpinner();
330
- writer.warn(text(`⚠ Kubb Studio ended the session (${reason})`));
331
- });
332
- context.hook("studio:command:start", ({ command }) => {
333
- if (silent) return;
334
- stopSpinner();
335
- writer.info(text(`Kubb Studio asked to ${(0, node_util.styleText)("bold", command)}`));
336
- });
337
- context.hook("studio:command:end", ({ command, info }) => {
338
- if (silent) return;
339
- writer.success(text(`✓ Finished ${command}${info ? ` ${(0, node_util.styleText)("dim", `(${info})`)}` : ""}`));
340
- startSpinner("✓ Ready to receive jobs");
341
- });
342
- context.hook("studio:reconnecting", ({ delayMs }) => {
343
- if (silent) return;
344
- stopSpinner();
345
- writer.info(text((0, node_util.styleText)("dim", `Retrying connection to Kubb Studio in ${require_output.formatMs(delayMs)}`)));
346
- });
347
- context.hook("studio:warn", ({ message, permission }) => {
348
- if (logLevel < _kubb_core.logLevel.warn) return;
349
- const remedy = permission ? `; pass --${permission.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`)} to allow it` : "";
350
- writer.warn(text(`⚠ ${message}${remedy}`));
351
- });
352
- context.hook("studio:error", ({ error }) => {
353
- if (state.spinner) {
354
- stopSpinner(error.message, "failed");
355
- return;
356
- }
357
- writer.error(text(error.message));
358
- });
359
- context.hook("kubb:lifecycle:start", () => {
360
- reset();
361
- });
362
- context.hook("kubb:generation:start", ({ config }) => {
363
- closeGroup("failed");
364
- reset();
365
- state.totalPlugins = config.plugins?.length ?? 0;
366
- if (silent) return;
367
- const inputPath = getInputPath(config);
368
- writer.group(text([(0, node_util.styleText)("bold", config.name ?? "kubb"), inputPath ? (0, node_util.styleText)("dim", inputPath) : void 0].filter(Boolean).join(" ")));
369
- state.groupOpen = true;
370
- startSpinner("Generating");
371
- });
372
- context.hook("kubb:plugin:start", ({ plugin }) => {
373
- if (silent) return;
374
- state.spinner?.message(text(`Generating ${(0, node_util.styleText)("dim", plugin.name)}`));
375
- });
376
- context.hook("kubb:plugin:end", ({ plugin, duration, success }) => {
377
- if (silent) return;
378
- recordPluginResult(state, success);
379
- state.pluginLines.push(text(`${plugin.name} ${success ? "completed" : "failed"} in ${require_output.formatMsWithColor(duration)}`));
380
- });
381
- context.hook("kubb:plugins:end", () => {
382
- if (silent) return;
383
- const failed = state.failedPlugins > 0;
384
- stopSpinner(buildProgressLine(state) ?? "No plugins ran", failed ? "failed" : "success");
385
- if (state.pluginLines.length) {
386
- writer.block(state.pluginLines);
387
- state.pluginLines = [];
388
- }
389
- });
390
- context.hook("kubb:files:processing:start", ({ files }) => {
391
- if (silent) return;
392
- stopSpinner();
393
- state.totalFiles = files.length;
394
- state.processedFiles = 0;
395
- state.progress = writer.progress(files.length);
396
- state.progress.start(text(`Writing ${pluralize(files.length, "file")}`));
397
- });
398
- context.hook("kubb:files:processing:update", ({ files }) => {
399
- if (silent) return;
400
- for (const { file, config } of files) {
401
- state.processedFiles++;
402
- state.progress?.advance(`Writing ${(0, node_path.relative)(config.root, file.path)}`);
403
- }
404
- });
405
- context.hook("kubb:files:processing:end", () => {
406
- if (silent) return;
407
- stopProgress(`Wrote ${pluralize(state.processedFiles, "file")}`);
408
- });
409
- context.hook("kubb:format:start", () => startPhase(PHASES.format));
410
- context.hook("kubb:format:end", endPhase);
411
- context.hook("kubb:lint:start", () => startPhase(PHASES.lint));
412
- context.hook("kubb:lint:end", endPhase);
413
- context.hook("kubb:hooks:start", () => startPhase(PHASES.hooks));
414
- context.hook("kubb:hooks:end", endPhase);
415
- context.hook("kubb:hook:start", ({ id }) => {
416
- if (silent || !id) return;
417
- state.hooks.set(id, {
418
- hrStart: node_process.default.hrtime(),
419
- lines: []
420
- });
421
- });
422
- if (!silent) context.hook("kubb:hook:line", ({ id, line }) => {
423
- const active = state.hooks.get(id);
424
- if (!active) return;
425
- if (state.phase) {
426
- active.lines.push(line);
427
- return;
428
- }
429
- writer.raw([line]);
430
- });
431
- context.hook("kubb:hook:end", ({ id, command, name, args, success, error, stdout, stderr }) => {
432
- if (!id) return;
433
- if (!success && state.phase) state.phase.failed = true;
434
- if (silent) {
435
- if (!success) {
436
- if (stdout) console.log(stdout);
437
- if (stderr) console.error(stderr);
438
- }
439
- return;
440
- }
441
- const active = state.hooks.get(id);
442
- if (!active) return;
443
- state.hooks.delete(id);
444
- const label = (0, node_util.styleText)("dim", name ?? formatCommandWithArgs(command, args));
445
- const reason = error?.message ? ` (${error.message})` : "";
446
- const result = success ? `${(0, node_util.styleText)("green", "✓")} ${label} in ${require_output.formatMsWithColor(require_output.getElapsedMs(active.hrStart))}` : `${(0, node_util.styleText)("red", "✗")} ${label} failed${reason}`;
447
- const phase = state.phase;
448
- if (phase) {
449
- if (phase.showHookResults || !success) phase.lines.push(result);
450
- phase.output.push(...active.lines);
451
- return;
452
- }
453
- stopSpinner();
454
- writer.step(text(result));
455
- writeOutput(active.lines);
456
- });
457
- context.hook("kubb:generation:end", () => {
458
- stopSpinner();
459
- stopProgress();
460
- });
461
- context.hook("kubb:lifecycle:end", () => {
462
- closeGroup("failed");
463
- reset();
464
- });
465
- return { renderSummary(lines, { status }) {
466
- if (silent || !state.groupOpen) return;
467
- closeGroup(status);
468
- writer.diagnostic([...lines]);
469
- } };
470
- };
471
- }
472
- /**
473
- * TTY logger for local development, with animated steps and progress bars.
474
- */
475
- const clackLogger = {
476
- name: "clack",
477
- install: createLogger({
478
- group: (title) => _clack_prompts.intro(title),
479
- groupEnd: (text) => _clack_prompts.outro(text),
480
- step: (text) => _clack_prompts.log.step(text, { spacing: 0 }),
481
- info: (text) => _clack_prompts.log.info(text),
482
- success: (text) => _clack_prompts.log.success(text),
483
- warn: (text) => _clack_prompts.log.warn(text),
484
- error: (text) => _clack_prompts.log.error(text),
485
- block: (lines) => _clack_prompts.log.message(lines, { spacing: 0 }),
486
- raw: (lines) => console.log((0, node_util.styleText)("dim", lines.join("\n"))),
487
- diagnostic: (lines) => _clack_prompts.log.message(lines, {
488
- symbol: "",
489
- secondarySymbol: ""
490
- }),
491
- update: (lines, title) => _clack_prompts.box(lines.join("\n"), title, {
492
- width: "auto",
493
- formatBorder: (border) => (0, node_util.styleText)("yellow", border),
494
- rounded: true,
495
- withGuide: false,
496
- contentAlign: "center",
497
- titleAlign: "center"
498
- }),
499
- spinner() {
500
- const spinner = _clack_prompts.spinner();
501
- return {
502
- start: (text) => spinner.start(text),
503
- message: (text) => spinner.message(text),
504
- clear: () => spinner.clear(),
505
- stop: (text) => spinner.stop(text),
506
- error: (text) => spinner.error(text)
507
- };
508
- },
509
- progress(max) {
510
- const bar = _clack_prompts.progress({
511
- style: "block",
512
- max,
513
- size: 30
514
- });
515
- return {
516
- start: (text) => bar.start(text),
517
- advance: (text) => bar.advance(1, text),
518
- stop: (text) => bar.stop(text)
519
- };
520
- }
521
- })
522
- };
523
- //#endregion
524
- //#region src/loggers/plainLogger.ts
525
- /**
526
- * Prefixes that stand in for the symbols clack draws, so plain output reads the same way.
527
- */
528
- const SYMBOLS = {
529
- info: "ℹ",
530
- warn: "⚠",
531
- error: "✗",
532
- step: "◇"
533
- };
534
- /** Draws with one `write` call per line. Nothing animates, so a step prints at start and end. */
535
- function createWriter(write) {
536
- return {
537
- group: write,
538
- groupEnd: write,
539
- step: (text) => write(`${SYMBOLS.step} ${text}`),
540
- info: write,
541
- success: write,
542
- warn: write,
543
- error: (text) => write(`${SYMBOLS.error} ${text}`),
544
- block: (lines) => write(lines.join("\n")),
545
- raw: (lines) => write(lines.join("\n")),
546
- diagnostic: (lines) => write(lines.join("\n")),
547
- update: (lines, title) => write([title, ...lines].join("\n")),
548
- spinner: () => ({
549
- start: write,
550
- message: write,
551
- clear: () => {},
552
- stop: (text) => text && write(text),
553
- error: (text) => write(`${SYMBOLS.error} ${text}`)
554
- }),
555
- progress: () => ({
556
- start: write,
557
- advance: write,
558
- stop: (text) => text && write(text)
559
- })
560
- };
561
- }
562
- /** The plain logger writing through `write`, e.g. `console.error` so stdout carries only JSON. */
563
- function createPlainLogger(write) {
564
- return {
565
- name: "plain",
566
- install: createLogger(createWriter(write))
567
- };
568
- }
569
- /**
570
- * Console adapter for non-TTY environments, and for an AI coding agent reading the output. Reports
571
- * the same run in the same order as `clackLogger`, without the animation.
572
- */
573
- const plainLogger = createPlainLogger((line) => console.log(line));
574
- //#endregion
575
- //#region src/loggers/utils.ts
576
- /**
577
- * Display path for a config's input: the string form, or its `path` field when the input is an
578
- * object. Loggers show it alongside `Generation started`.
579
- */
580
- function getInputPath(config) {
581
- const { input } = config;
582
- if (typeof input === "string") return input;
583
- return typeof input?.path === "string" ? input.path : void 0;
584
- }
585
- /**
586
- * Counts a noun, so a message never reads `1 files`.
587
- *
588
- * @example
589
- * `pluralize(1, 'config')` returns `'1 config'`
590
- */
591
- function pluralize(count, noun) {
592
- return `${count} ${count === 1 ? noun : `${noun}s`}`;
593
- }
594
- /**
595
- * Optionally prefix a message with a [HH:MM:SS] timestamp when logLevel >= verbose.
596
- * Shared across all logger adapters to avoid duplication.
597
- */
598
- function formatMessage(message, logLevel) {
599
- if (logLevel >= _kubb_core.logLevel.verbose) {
600
- const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
601
- hour12: false,
602
- hour: "2-digit",
603
- minute: "2-digit",
604
- second: "2-digit"
605
- });
606
- return `${(0, node_util.styleText)("dim", `[${timestamp}]`)} ${message}`;
607
- }
608
- return message;
609
- }
610
- /**
611
- * Renders the versions from a `studio:connected` event as one parenthetical. The runtime is listed
612
- * only when it differs from the host, and Studio's only when it sent one.
613
- */
614
- function formatVersions({ studio, kubb, agent }) {
615
- return [
616
- `v${agent}`,
617
- kubb !== agent ? `runtime v${kubb}` : void 0,
618
- studio ? `Studio v${studio}` : void 0
619
- ].filter(Boolean).join(", ");
620
- }
621
- /**
622
- * Extracts the `.cause` of an `Error` as an `Error`, or `undefined` when absent or not an `Error`.
623
- */
624
- function toCause(error) {
625
- return error.cause instanceof Error ? error.cause : void 0;
626
- }
627
- /**
628
- * First stack frames for verbose error output, including an optional `cause` chain.
629
- */
630
- function formatErrorFrames(error) {
631
- if (!error.stack) return null;
632
- const frames = error.stack.split("\n").slice(1, 4).map((frame) => frame.trim());
633
- const caused = toCause(error);
634
- if (!caused?.stack) return { frames };
635
- return {
636
- frames,
637
- cause: {
638
- header: `└─ caused by ${caused.message}`,
639
- frames: caused.stack.split("\n").slice(1, 4).map((frame) => frame.trim())
640
- }
641
- };
642
- }
643
- /**
644
- * Build the progress summary line shown by the clack logger.
645
- * Returns null when there is nothing to display.
646
- */
647
- function buildProgressLine(state) {
648
- const parts = [];
649
- const duration = require_output.formatMs(require_output.getElapsedMs(state.hrStart));
650
- if (state.totalPlugins > 0) {
651
- const pluginStr = state.failedPlugins > 0 ? `Plugins ${(0, node_util.styleText)("green", state.completedPlugins.toString())}/${state.totalPlugins} ${(0, node_util.styleText)("red", `(${state.failedPlugins} failed)`)}` : `Plugins ${(0, node_util.styleText)("green", state.completedPlugins.toString())}/${state.totalPlugins}`;
652
- parts.push(pluginStr);
653
- }
654
- if (state.totalFiles > 0) parts.push(`Files ${(0, node_util.styleText)("green", state.processedFiles.toString())}/${state.totalFiles}`);
655
- if (parts.length === 0) return null;
656
- parts.push(`${(0, node_util.styleText)("green", duration)} elapsed`);
657
- return parts.join((0, node_util.styleText)("dim", " | "));
658
- }
659
- /**
660
- * Creates the per-run progress counters used by the clack logger.
661
- */
662
- function createProgressCounters() {
663
- return {
664
- totalPlugins: 0,
665
- completedPlugins: 0,
666
- failedPlugins: 0,
667
- totalFiles: 0,
668
- processedFiles: 0,
669
- hrStart: node_process.default.hrtime()
670
- };
671
- }
672
- /**
673
- * Resets the progress counters in place at the start/end of a generation run.
674
- */
675
- function resetProgressCounters(state) {
676
- state.totalPlugins = 0;
677
- state.completedPlugins = 0;
678
- state.failedPlugins = 0;
679
- state.totalFiles = 0;
680
- state.processedFiles = 0;
681
- state.hrStart = node_process.default.hrtime();
682
- }
683
- /**
684
- * Records a finished plugin against the progress counters.
685
- */
686
- function recordPluginResult(state, success) {
687
- if (success) state.completedPlugins++;
688
- else state.failedPlugins++;
689
- }
690
- /**
691
- * Join a command and its optional args into a single display string.
692
- *
693
- * @example With args
694
- * `formatCommandWithArgs('prettier', ['--write', '.'])` returns `'prettier --write .'`
695
- */
696
- function formatCommandWithArgs(command, args) {
697
- return args?.length ? `${command} ${args.join(" ")}` : command;
698
- }
699
- /**
700
- * Bridges a {@link Reporter} onto the run's hook emitter: calls `report` with each config's
701
- * {@link GenerationResult} on `kubb:generation:end`. The reporter never touches the emitter.
702
- */
703
- function installReporter(context, reporter, ctx) {
704
- const pluginFiles = reporter.needsPluginFiles ? /* @__PURE__ */ new Map() : void 0;
705
- if (pluginFiles) context.hook("kubb:plugin:end", ({ config, plugin, files }) => {
706
- const filesByPlugin = pluginFiles.get(config) ?? /* @__PURE__ */ new Map();
707
- const seen = new Set([...filesByPlugin.values()].flatMap((paths) => [...paths]));
708
- const added = files.filter((file) => !seen.has(file.path)).map((file) => file.path);
709
- if (added.length) filesByPlugin.set(plugin.name, /* @__PURE__ */ new Set([...filesByPlugin.get(plugin.name) ?? [], ...added]));
710
- pluginFiles.set(config, filesByPlugin);
711
- });
712
- context.hook("kubb:generation:end", async ({ config, diagnostics = [], filesCreated = 0, status = "success", hrStart = node_process.default.hrtime() }) => {
713
- const files = pluginFiles?.get(config);
714
- const grouped = files && [...files].map(([plugin, paths]) => ({
715
- plugin,
716
- files: [...paths]
717
- }));
718
- await reporter.report({
719
- config,
720
- diagnostics,
721
- filesCreated,
722
- status,
723
- hrStart,
724
- pluginFiles: grouped
725
- }, ctx);
726
- pluginFiles?.delete(config);
727
- });
728
- if (reporter.drain) context.hook("kubb:lifecycle:end", () => reporter.drain?.(ctx));
729
- }
730
- /**
731
- * Installs the live logger (the TUI view) and the given reporters (the output). The reporters are
732
- * already selected by the caller (the CLI maps `--reporter` to names via `selectReporters`). This
733
- * only wires them. Loggers receive hook subprocess output through `kubb:hook:line` and the
734
- * `stdout`/`stderr` on `kubb:hook:end`, so nothing is returned here.
735
- *
736
- * Loggers and reporters are independent, except for `cli`: it both installs the live logger view
737
- * here and registers a reporter that renders the per-config summary through that logger, so the
738
- * summary lands inside the group the logger opened. The `json` reporter owns stdout, so the whole
739
- * `cli` reporter (live logger and summary) is skipped whenever `json` is among the reporters, even
740
- * if `cli` is also listed.
741
- */
742
- async function setupReporters(context, { logLevel, reporters, logger: forcedLogger }) {
743
- let cliHandle;
744
- const hasJson = reporters.some((reporter) => reporter.name === "json");
745
- const ctx = { logLevel };
746
- for (const reporter of reporters) {
747
- if (reporter.name !== "cli") {
748
- installReporter(context, reporter, ctx);
749
- continue;
750
- }
751
- if (hasJson) continue;
752
- const handle = await (forcedLogger ?? (require_output.canUseTTY() && !require_agent.getAgentName() ? clackLogger : plainLogger)).install(context, { logLevel }) ?? void 0;
753
- cliHandle = handle;
754
- installReporter(context, (0, _kubb_core.createCliReporter)({ render: handle?.renderSummary }), ctx);
755
- }
756
- return cliHandle;
757
- }
758
- /**
759
- * Picks the reporters whose `name` matches one of `names`, in the order the names are given.
760
- * The config carries every available reporter, and the host selects which to activate by name
761
- * (the CLI maps `--reporter` to this). Duplicate names and names without a matching reporter are
762
- * skipped.
763
- */
764
- function selectReporters(reporters, names) {
765
- const seen = /* @__PURE__ */ new Set();
766
- const selected = [];
767
- for (const name of names) {
768
- if (seen.has(name)) continue;
769
- seen.add(name);
770
- const reporter = reporters.find((candidate) => candidate.name === name);
771
- if (reporter) selected.push(reporter);
772
- }
773
- return selected;
774
- }
775
- //#endregion
776
- //#region ../../internals/shared/src/loader.ts
777
- /**
778
- * jiti options for loading Kubb config modules: the automatic JSX runtime pointed at
779
- * `@kubb/renderer-jsx`, and `moduleCache` off so a re-load re-evaluates the file.
780
- */
781
- const JITI_OPTIONS = {
782
- jsx: {
783
- runtime: "automatic",
784
- importSource: "@kubb/renderer-jsx"
785
- },
786
- moduleCache: false
787
- };
788
- /**
789
- * Creates a jiti-based loader for Kubb's TypeScript and JavaScript config modules.
790
- *
791
- * jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly.
792
- *
793
- * @example
794
- * ```ts
795
- * const config = await createModuleLoader().load('/abs/kubb.config.ts', { default: true })
796
- * ```
797
- */
798
- function createModuleLoader() {
799
- const jiti$1 = (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, JITI_OPTIONS);
800
- return { load(filePath, options) {
801
- return options?.default ? jiti$1.import(filePath, { default: true }) : jiti$1.import(filePath);
802
- } };
803
- }
804
- //#endregion
805
- //#region src/runners/generate/utils.ts
806
- const loader = createModuleLoader();
807
- const tsLoader = (configFile) => loader.load(configFile, { default: true });
808
- const MODULE_NAME = "kubb";
809
- const SEARCH_FILES = [
810
- "",
811
- ".config/",
812
- "configs/"
813
- ].flatMap((prefix) => [`${prefix}.${MODULE_NAME}rc`, `${prefix}${MODULE_NAME}.config`]);
814
- const SEARCH_EXTENSIONS = [
815
- "ts",
816
- "mts",
817
- "cts",
818
- "js",
819
- "mjs",
820
- "cjs"
821
- ];
822
- /**
823
- * Discovers the Kubb config and resolves it into a normalized array of configs.
824
- * Every config in the result is guaranteed to have a `plugins` array.
825
- */
826
- async function getConfigs({ configPath, input, watch, logLevel }) {
827
- const abs = configPath ? (0, node_path.resolve)(configPath) : void 0;
828
- const sources = abs ? [{
829
- files: [(0, node_path.basename)(abs)],
830
- extensions: [],
831
- parser: tsLoader
832
- }] : [{
833
- files: SEARCH_FILES,
834
- extensions: SEARCH_EXTENSIONS,
835
- parser: tsLoader
836
- }];
837
- let result;
838
- try {
839
- result = await (0, unconfig.loadConfig)({
840
- cwd: abs ? (0, node_path.dirname)(abs) : node_process.default.cwd(),
841
- sources,
842
- merge: false
843
- });
844
- } catch (error) {
845
- throw new Error("Config failed loading", { cause: error });
846
- }
847
- const [filepath] = result.sources;
848
- if (!result.config || !filepath) throw new Error("Config not defined, create a kubb.config.js or pass through your config with the option --config");
849
- const config = result.config;
850
- const resolved = await (typeof config === "function" ? config({
851
- config: configPath,
852
- input,
853
- watch,
854
- logLevel
855
- }) : config);
856
- return {
857
- configPath: filepath,
858
- configs: (Array.isArray(resolved) ? resolved : [resolved]).map((item) => {
859
- return {
860
- ...item,
861
- plugins: item.plugins ?? []
862
- };
863
- })
864
- };
865
- }
866
- /**
867
- * Returns `true` when `latest` is a newer semver version than `current`. Compares each numeric
868
- * part, so `5.10.0` beats `5.9.0` where a plain string comparison would not. Prerelease
869
- * suffixes are ignored, and a malformed version never reports an update.
870
- *
871
- * @example Double-digit minor
872
- * `isNewerVersion('5.9.0', '5.10.0') // true`
873
- *
874
- * @example String comparison would get this wrong the other way
875
- * `isNewerVersion('5.10.0', '5.9.0') // false`
876
- */
877
- function isNewerVersion(current, latest) {
878
- if (!(0, verkit.isValid)(current) || !(0, verkit.isValid)(latest)) return false;
879
- return (0, verkit.isGreaterThan)((0, verkit.truncate)(latest, "patch"), (0, verkit.truncate)(current, "patch"));
880
- }
881
- /**
882
- * Runs the `output.postGenerate` commands of a Kubb config in sequence and returns each command's
883
- * outcome, so the caller can turn failures into diagnostics.
884
- */
885
- async function runPostGenerate({ commands, hooks }) {
886
- const results = [];
887
- for (const entry of commands) {
888
- const { command, name } = typeof entry === "string" ? {
889
- command: entry,
890
- name: void 0
891
- } : entry;
892
- const [cmd, ...args] = tokenize(command);
893
- if (!cmd) continue;
894
- const hookId = (0, node_crypto.randomUUID)();
895
- const commandWithArgs = [cmd, ...args].join(" ");
896
- await hooks.callHook("kubb:hook:start", {
897
- id: hookId,
898
- command: cmd,
899
- name,
900
- args
901
- });
902
- results.push(await runHook({
903
- id: hookId,
904
- command: cmd,
905
- name,
906
- args,
907
- commandWithArgs,
908
- hooks
909
- }));
910
- }
911
- return results;
912
- }
913
- /**
914
- * Spawns a hook command and returns its outcome, mirroring it through `kubb:hook:end` for the
915
- * loggers. A non-zero exit returns `success: false` rather than throwing, so the caller can turn
916
- * it into a diagnostic. Other spawn errors do the same. Output is streamed through `kubb:hook:line`
917
- * only while a listener is attached.
918
- */
919
- async function runHook({ id, command, name, args, commandWithArgs, hooks }) {
920
- const emitEnd = async (result) => {
921
- await hooks.callHook("kubb:hook:end", {
922
- command,
923
- name,
924
- args,
925
- id,
926
- ...result
927
- });
928
- return result;
929
- };
930
- const stream = hooks.listenerCount("kubb:hook:line") > 0;
931
- try {
932
- const proc = (0, tinyexec.x)(command, [...args ?? []], {
933
- nodeOptions: { detached: node_process.default.platform !== "win32" },
934
- throwOnError: true
935
- });
936
- if (stream) for await (const line of proc) await hooks.callHook("kubb:hook:line", {
937
- id,
938
- line
939
- });
940
- await proc;
941
- await hooks.callHook("kubb:success", { message: `${(0, node_util.styleText)("dim", name ?? commandWithArgs)} successfully executed` });
942
- return emitEnd({
943
- success: true,
944
- error: null
945
- });
946
- } catch (err) {
947
- if (!(err instanceof tinyexec.NonZeroExitError)) return emitEnd({
948
- success: false,
949
- error: require_errors.toError(err)
950
- });
951
- const stderr = err.output?.stderr ?? "";
952
- const stdout = err.output?.stdout ?? "";
953
- return emitEnd({
954
- success: false,
955
- error: /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`),
956
- stdout,
957
- stderr
958
- });
959
- }
960
- }
961
- /**
962
- * Wraps `run` so invocations never overlap: a trigger that lands while a run is in flight
963
- * marks it dirty and runs once more after it finishes, no matter how many triggers arrived.
964
- * Useful for event-driven reruns (a file watcher, a queue drain) where bursts should
965
- * coalesce into a single trailing run.
966
- *
967
- * @example
968
- * ```ts
969
- * const rebuild = createSerialRunner({
970
- * run: () => build(),
971
- * onError: (error) => log.error(error.message),
972
- * })
973
- * watcher.on('change', () => void rebuild())
974
- * ```
975
- */
976
- function createSerialRunner({ run, onError }) {
977
- let running = false;
978
- let dirty = false;
979
- return async () => {
980
- if (running) {
981
- dirty = true;
982
- return;
983
- }
984
- running = true;
985
- do {
986
- dirty = false;
987
- try {
988
- await run();
989
- } catch (error) {
990
- onError(require_errors.toError(error));
991
- }
992
- } while (dirty);
993
- running = false;
994
- };
995
- }
996
- /**
997
- * Starts a file watcher on the given paths and calls `cb` on any change.
998
- * Ignores `.git` and `node_modules` directories. Event bursts (an editor save emits several)
999
- * are debounced into one build, and builds never overlap: changes during a build queue exactly
1000
- * one rebuild.
1001
- */
1002
- async function startWatcher(path, cb, log = {
1003
- info: console.log,
1004
- error: console.log
1005
- }) {
1006
- const { watch } = await import("chokidar");
1007
- const watcher = watch(path, {
1008
- ignorePermissionErrors: true,
1009
- ignored: require_Telemetry.WATCHER_IGNORED_PATHS,
1010
- ignoreInitial: true
1011
- });
1012
- node_process.default.once("SIGINT", () => {
1013
- watcher.close();
1014
- });
1015
- node_process.default.once("SIGTERM", () => {
1016
- watcher.close();
1017
- });
1018
- const runBuild = createSerialRunner({
1019
- run: () => cb(path),
1020
- onError: () => log.error((0, node_util.styleText)("red", "Watcher failed"))
1021
- });
1022
- let debounceTimer = null;
1023
- watcher.on("all", (type, file) => {
1024
- log.info((0, node_util.styleText)("yellow", (0, node_util.styleText)("bold", `Change detected: ${type} ${file}`)));
1025
- if (debounceTimer) clearTimeout(debounceTimer);
1026
- debounceTimer = setTimeout(() => {
1027
- debounceTimer = null;
1028
- runBuild();
1029
- }, 100);
1030
- });
1031
- }
1032
- /**
1033
- * Fetches the body of a remote spec URL, `undefined` when the server does not answer with a
1034
- * readable 2xx body within the timeout. The caller seeds `startUrlWatcher` with the result, so
1035
- * the watcher compares polls against the content the initial build ran on.
1036
- */
1037
- async function fetchUrlBody(url, timeoutMs = require_Telemetry.URL_WATCHER_TIMEOUT_MS) {
1038
- try {
1039
- const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
1040
- if (!response.ok) return void 0;
1041
- return await response.text();
1042
- } catch {
1043
- return;
1044
- }
1045
- }
1046
- /**
1047
- * Polls a remote spec URL and calls `cb` whenever the response body changes. A remote document
1048
- * emits no filesystem events, so this is the URL counterpart of `startWatcher`.
1049
- *
1050
- * `initialBody` seeds the change detection, so an edit landing between the initial build and the
1051
- * first poll still rebuilds. An unreachable server (the API restarting between saves) is reported
1052
- * once per outage and polling continues; after it recovers, a rebuild happens only when the body
1053
- * actually differs from the last one seen, so a plain restart with an unchanged spec stays quiet.
1054
- * Each request is aborted after `timeoutMs`, so a hung response delays at most one poll.
1055
- *
1056
- * Returns a function that stops polling and aborts the in-flight request, so callers (and tests)
1057
- * can shut the watcher down without signaling the process.
1058
- */
1059
- function startUrlWatcher(url, cb, options = {}) {
1060
- const { log = {
1061
- info: console.log,
1062
- error: console.log
1063
- }, intervalMs = require_Telemetry.URL_WATCHER_INTERVAL_MS, timeoutMs = require_Telemetry.URL_WATCHER_TIMEOUT_MS, initialBody } = options;
1064
- let lastBody = initialBody;
1065
- let offline = false;
1066
- let timer = null;
1067
- let controller = null;
1068
- let stopped = false;
1069
- const stop = () => {
1070
- stopped = true;
1071
- if (timer) clearTimeout(timer);
1072
- controller?.abort();
1073
- };
1074
- node_process.default.once("SIGINT", stop);
1075
- node_process.default.once("SIGTERM", stop);
1076
- const runBuild = createSerialRunner({
1077
- run: () => cb([url]),
1078
- onError: () => log.error((0, node_util.styleText)("red", "Watcher failed"))
1079
- });
1080
- const poll = async () => {
1081
- controller = new AbortController();
1082
- try {
1083
- const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(timeoutMs)]);
1084
- const response = await fetch(url, { signal });
1085
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
1086
- const body = await response.text();
1087
- if (offline) {
1088
- offline = false;
1089
- log.info((0, node_util.styleText)("yellow", `${url} is reachable again`));
1090
- }
1091
- const changed = lastBody !== void 0 && body !== lastBody;
1092
- if (changed) log.info((0, node_util.styleText)("yellow", (0, node_util.styleText)("bold", `Change detected: ${url}`)));
1093
- if (changed || lastBody === void 0) runBuild();
1094
- lastBody = body;
1095
- } catch {
1096
- if (!offline && !stopped) {
1097
- offline = true;
1098
- log.error((0, node_util.styleText)("red", `Cannot reach ${url}, polling until it responds again`));
1099
- }
1100
- }
1101
- if (!stopped) timer = setTimeout(() => void poll(), intervalMs);
1102
- };
1103
- poll();
1104
- return stop;
1105
- }
1106
- //#endregion
1107
- Object.defineProperty(exports, "FORMATTER_PREFERENCE", {
1108
- enumerable: true,
1109
- get: function() {
1110
- return FORMATTER_PREFERENCE;
1111
- }
1112
- });
1113
- Object.defineProperty(exports, "LINTER_PREFERENCE", {
1114
- enumerable: true,
1115
- get: function() {
1116
- return LINTER_PREFERENCE;
1117
- }
1118
- });
1119
- Object.defineProperty(exports, "createPlainLogger", {
1120
- enumerable: true,
1121
- get: function() {
1122
- return createPlainLogger;
1123
- }
1124
- });
1125
- Object.defineProperty(exports, "detectTool", {
1126
- enumerable: true,
1127
- get: function() {
1128
- return detectTool;
1129
- }
1130
- });
1131
- Object.defineProperty(exports, "fetchUrlBody", {
1132
- enumerable: true,
1133
- get: function() {
1134
- return fetchUrlBody;
1135
- }
1136
- });
1137
- Object.defineProperty(exports, "formatters", {
1138
- enumerable: true,
1139
- get: function() {
1140
- return formatters;
1141
- }
1142
- });
1143
- Object.defineProperty(exports, "getConfigs", {
1144
- enumerable: true,
1145
- get: function() {
1146
- return getConfigs;
1147
- }
1148
- });
1149
- Object.defineProperty(exports, "isNewerVersion", {
1150
- enumerable: true,
1151
- get: function() {
1152
- return isNewerVersion;
1153
- }
1154
- });
1155
- Object.defineProperty(exports, "linters", {
1156
- enumerable: true,
1157
- get: function() {
1158
- return linters;
1159
- }
1160
- });
1161
- Object.defineProperty(exports, "plainLogger", {
1162
- enumerable: true,
1163
- get: function() {
1164
- return plainLogger;
1165
- }
1166
- });
1167
- Object.defineProperty(exports, "pluralize", {
1168
- enumerable: true,
1169
- get: function() {
1170
- return pluralize;
1171
- }
1172
- });
1173
- Object.defineProperty(exports, "runHook", {
1174
- enumerable: true,
1175
- get: function() {
1176
- return runHook;
1177
- }
1178
- });
1179
- Object.defineProperty(exports, "runPostGenerate", {
1180
- enumerable: true,
1181
- get: function() {
1182
- return runPostGenerate;
1183
- }
1184
- });
1185
- Object.defineProperty(exports, "selectReporters", {
1186
- enumerable: true,
1187
- get: function() {
1188
- return selectReporters;
1189
- }
1190
- });
1191
- Object.defineProperty(exports, "setupReporters", {
1192
- enumerable: true,
1193
- get: function() {
1194
- return setupReporters;
1195
- }
1196
- });
1197
- Object.defineProperty(exports, "startUrlWatcher", {
1198
- enumerable: true,
1199
- get: function() {
1200
- return startUrlWatcher;
1201
- }
1202
- });
1203
- Object.defineProperty(exports, "startWatcher", {
1204
- enumerable: true,
1205
- get: function() {
1206
- return startWatcher;
1207
- }
1208
- });
1209
-
1210
- //# sourceMappingURL=utils-ClRbJfxu.cjs.map