@kubb/core 5.0.0-alpha.17 → 5.0.0-alpha.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{PluginDriver-CNKhDf-w.d.ts → PluginDriver-BRTrzfiD.d.ts} +135 -27
- package/dist/hooks.cjs +16 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +17 -1
- package/dist/hooks.js +16 -0
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +396 -273
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +246 -19
- package/dist/index.js +394 -273
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/Kubb.ts +15 -5
- package/src/build.ts +42 -0
- package/src/config.ts +9 -8
- package/src/constants.ts +43 -0
- package/src/hooks/useKubb.ts +16 -0
- package/src/index.ts +1 -0
- package/src/types.ts +9 -3
- package/src/utils/TreeNode.ts +23 -0
- package/src/utils/diagnostics.ts +4 -1
- package/src/utils/executeStrategies.ts +16 -3
- package/src/utils/formatters.ts +9 -20
- package/src/utils/getBarrelFiles.ts +8 -0
- package/src/utils/getConfigs.ts +5 -1
- package/src/utils/getPreset.ts +7 -0
- package/src/utils/linters.ts +22 -2
- package/src/utils/packageJSON.ts +21 -6
package/dist/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import "./chunk--u3MIqq1.js";
|
|
2
|
-
import { definePrinter, isOperationNode, isSchemaNode } from "@kubb/ast";
|
|
3
|
-
import path, { basename, dirname, extname, join, posix, relative, resolve } from "node:path";
|
|
4
2
|
import { EventEmitter } from "node:events";
|
|
5
|
-
import { parseArgs, styleText } from "node:util";
|
|
6
3
|
import { readFileSync } from "node:fs";
|
|
7
4
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
import path, { basename, dirname, extname, join, posix, relative, resolve } from "node:path";
|
|
6
|
+
import { definePrinter, isOperationNode, isSchemaNode } from "@kubb/ast";
|
|
8
7
|
import { Fabric, createFabric, createReactFabric } from "@kubb/react-fabric";
|
|
9
8
|
import { typescriptParser } from "@kubb/react-fabric/parsers";
|
|
10
9
|
import { fsPlugin } from "@kubb/react-fabric/plugins";
|
|
@@ -16,12 +15,23 @@ import { jsx } from "@kubb/react-fabric/jsx-runtime";
|
|
|
16
15
|
import { sortBy } from "remeda";
|
|
17
16
|
import * as pkg from "empathic/package";
|
|
18
17
|
import { coerce, satisfies } from "semver";
|
|
19
|
-
//#region ../../internals/utils/
|
|
20
|
-
/** Thrown when a plugin's configuration or input fails validation.
|
|
18
|
+
//#region ../../internals/utils/src/errors.ts
|
|
19
|
+
/** Thrown when a plugin's configuration or input fails validation.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* throw new ValidationPluginError('Invalid config: "output.path" is required')
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
21
26
|
var ValidationPluginError = class extends Error {};
|
|
22
27
|
/**
|
|
23
28
|
* Thrown when one or more errors occur during a Kubb build.
|
|
24
29
|
* Carries the full list of underlying errors on `errors`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* throw new BuildError('Build failed', { errors: [err1, err2] })
|
|
34
|
+
* ```
|
|
25
35
|
*/
|
|
26
36
|
var BuildError = class extends Error {
|
|
27
37
|
errors;
|
|
@@ -33,19 +43,34 @@ var BuildError = class extends Error {
|
|
|
33
43
|
};
|
|
34
44
|
/**
|
|
35
45
|
* Coerces an unknown thrown value to an `Error` instance.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
46
|
+
* Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* try { ... } catch(err) {
|
|
51
|
+
* throw new BuildError('Build failed', { cause: toError(err), errors: [] })
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
38
54
|
*/
|
|
39
55
|
function toError(value) {
|
|
40
56
|
return value instanceof Error ? value : new Error(String(value));
|
|
41
57
|
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region ../../internals/utils/src/asyncEventEmitter.ts
|
|
42
60
|
/**
|
|
43
|
-
*
|
|
61
|
+
* Typed `EventEmitter` that awaits all async listeners before resolving.
|
|
44
62
|
* Wraps Node's `EventEmitter` with full TypeScript event-map inference.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
|
|
67
|
+
* emitter.on('build', async (name) => { console.log(name) })
|
|
68
|
+
* await emitter.emit('build', 'petstore') // all listeners awaited
|
|
69
|
+
* ```
|
|
45
70
|
*/
|
|
46
71
|
var AsyncEventEmitter = class {
|
|
47
72
|
/**
|
|
48
|
-
*
|
|
73
|
+
* Maximum number of listeners per event before Node emits a memory-leak warning.
|
|
49
74
|
* @default 10
|
|
50
75
|
*/
|
|
51
76
|
constructor(maxListener = 10) {
|
|
@@ -53,8 +78,13 @@ var AsyncEventEmitter = class {
|
|
|
53
78
|
}
|
|
54
79
|
#emitter = new EventEmitter();
|
|
55
80
|
/**
|
|
56
|
-
* Emits
|
|
81
|
+
* Emits `eventName` and awaits all registered listeners in parallel.
|
|
57
82
|
* Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* await emitter.emit('build', 'petstore')
|
|
87
|
+
* ```
|
|
58
88
|
*/
|
|
59
89
|
async emit(eventName, ...eventArgs) {
|
|
60
90
|
const listeners = this.#emitter.listeners(eventName);
|
|
@@ -73,11 +103,25 @@ var AsyncEventEmitter = class {
|
|
|
73
103
|
}
|
|
74
104
|
}));
|
|
75
105
|
}
|
|
76
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* Registers a persistent listener for `eventName`.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* emitter.on('build', async (name) => { console.log(name) })
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
77
114
|
on(eventName, handler) {
|
|
78
115
|
this.#emitter.on(eventName, handler);
|
|
79
116
|
}
|
|
80
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Registers a one-shot listener that removes itself after the first invocation.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* emitter.onOnce('build', async (name) => { console.log(name) })
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
81
125
|
onOnce(eventName, handler) {
|
|
82
126
|
const wrapper = (...args) => {
|
|
83
127
|
this.off(eventName, wrapper);
|
|
@@ -85,15 +129,31 @@ var AsyncEventEmitter = class {
|
|
|
85
129
|
};
|
|
86
130
|
this.on(eventName, wrapper);
|
|
87
131
|
}
|
|
88
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Removes a previously registered listener.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* emitter.off('build', handler)
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
89
140
|
off(eventName, handler) {
|
|
90
141
|
this.#emitter.off(eventName, handler);
|
|
91
142
|
}
|
|
92
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* Removes all listeners from every event channel.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* emitter.removeAll()
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
93
151
|
removeAll() {
|
|
94
152
|
this.#emitter.removeAllListeners();
|
|
95
153
|
}
|
|
96
154
|
};
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region ../../internals/utils/src/casing.ts
|
|
97
157
|
/**
|
|
98
158
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
99
159
|
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
@@ -150,190 +210,18 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
150
210
|
}) : camelCase(part));
|
|
151
211
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
152
212
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return adapter;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Serializes `CommandDefinition[]` to a plain, JSON-serializable structure.
|
|
159
|
-
* Use to expose CLI capabilities to AI agents or MCP tools.
|
|
160
|
-
*/
|
|
161
|
-
function getCommandSchema(defs) {
|
|
162
|
-
return defs.map(serializeCommand);
|
|
163
|
-
}
|
|
164
|
-
function serializeCommand(def) {
|
|
165
|
-
return {
|
|
166
|
-
name: def.name,
|
|
167
|
-
description: def.description,
|
|
168
|
-
arguments: def.arguments,
|
|
169
|
-
options: serializeOptions(def.options ?? {}),
|
|
170
|
-
subCommands: def.subCommands ? def.subCommands.map(serializeCommand) : []
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
function serializeOptions(options) {
|
|
174
|
-
return Object.entries(options).map(([name, opt]) => {
|
|
175
|
-
return {
|
|
176
|
-
name,
|
|
177
|
-
flags: `${opt.short ? `-${opt.short}, ` : ""}--${name}${opt.type === "string" ? ` <${opt.hint ?? name}>` : ""}`,
|
|
178
|
-
type: opt.type,
|
|
179
|
-
description: opt.description,
|
|
180
|
-
...opt.default !== void 0 ? { default: opt.default } : {},
|
|
181
|
-
...opt.hint ? { hint: opt.hint } : {},
|
|
182
|
-
...opt.enum ? { enum: opt.enum } : {},
|
|
183
|
-
...opt.required ? { required: opt.required } : {}
|
|
184
|
-
};
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
/** Prints formatted help output for a command using its `CommandDefinition`. */
|
|
188
|
-
function renderHelp(def, parentName) {
|
|
189
|
-
const schema = getCommandSchema([def])[0];
|
|
190
|
-
const programName = parentName ? `${parentName} ${schema.name}` : schema.name;
|
|
191
|
-
const argsPart = schema.arguments?.length ? ` ${schema.arguments.join(" ")}` : "";
|
|
192
|
-
const subCmdPart = schema.subCommands.length ? " <command>" : "";
|
|
193
|
-
console.log(`\n${styleText("bold", "Usage:")} ${programName}${argsPart}${subCmdPart} [options]\n`);
|
|
194
|
-
if (schema.description) console.log(` ${schema.description}\n`);
|
|
195
|
-
if (schema.subCommands.length) {
|
|
196
|
-
console.log(styleText("bold", "Commands:"));
|
|
197
|
-
for (const sub of schema.subCommands) console.log(` ${styleText("cyan", sub.name.padEnd(16))}${sub.description}`);
|
|
198
|
-
console.log();
|
|
199
|
-
}
|
|
200
|
-
const options = [...schema.options, {
|
|
201
|
-
name: "help",
|
|
202
|
-
flags: "-h, --help",
|
|
203
|
-
type: "boolean",
|
|
204
|
-
description: "Show help"
|
|
205
|
-
}];
|
|
206
|
-
console.log(styleText("bold", "Options:"));
|
|
207
|
-
for (const opt of options) {
|
|
208
|
-
const flags = styleText("cyan", opt.flags.padEnd(30));
|
|
209
|
-
const defaultPart = opt.default !== void 0 ? styleText("dim", ` (default: ${opt.default})`) : "";
|
|
210
|
-
console.log(` ${flags}${opt.description}${defaultPart}`);
|
|
211
|
-
}
|
|
212
|
-
console.log();
|
|
213
|
-
}
|
|
214
|
-
function buildParseOptions(def) {
|
|
215
|
-
const result = { help: {
|
|
216
|
-
type: "boolean",
|
|
217
|
-
short: "h"
|
|
218
|
-
} };
|
|
219
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) result[name] = {
|
|
220
|
-
type: opt.type,
|
|
221
|
-
...opt.short ? { short: opt.short } : {},
|
|
222
|
-
...opt.default !== void 0 ? { default: opt.default } : {}
|
|
223
|
-
};
|
|
224
|
-
return result;
|
|
225
|
-
}
|
|
226
|
-
async function runCommand(def, argv, parentName) {
|
|
227
|
-
const parseOptions = buildParseOptions(def);
|
|
228
|
-
let parsed;
|
|
229
|
-
try {
|
|
230
|
-
const result = parseArgs({
|
|
231
|
-
args: argv,
|
|
232
|
-
options: parseOptions,
|
|
233
|
-
allowPositionals: true,
|
|
234
|
-
strict: false
|
|
235
|
-
});
|
|
236
|
-
parsed = {
|
|
237
|
-
values: result.values,
|
|
238
|
-
positionals: result.positionals
|
|
239
|
-
};
|
|
240
|
-
} catch {
|
|
241
|
-
renderHelp(def, parentName);
|
|
242
|
-
process.exit(1);
|
|
243
|
-
}
|
|
244
|
-
if (parsed.values["help"]) {
|
|
245
|
-
renderHelp(def, parentName);
|
|
246
|
-
process.exit(0);
|
|
247
|
-
}
|
|
248
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) if (opt.required && parsed.values[name] === void 0) {
|
|
249
|
-
console.error(styleText("red", `Error: --${name} is required`));
|
|
250
|
-
renderHelp(def, parentName);
|
|
251
|
-
process.exit(1);
|
|
252
|
-
}
|
|
253
|
-
if (!def.run) {
|
|
254
|
-
renderHelp(def, parentName);
|
|
255
|
-
process.exit(0);
|
|
256
|
-
}
|
|
257
|
-
try {
|
|
258
|
-
await def.run(parsed);
|
|
259
|
-
} catch (err) {
|
|
260
|
-
console.error(styleText("red", `Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
261
|
-
renderHelp(def, parentName);
|
|
262
|
-
process.exit(1);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
function printRootHelp(programName, version, defs) {
|
|
266
|
-
console.log(`\n${styleText("bold", "Usage:")} ${programName} <command> [options]\n`);
|
|
267
|
-
console.log(` Kubb generation — v${version}\n`);
|
|
268
|
-
console.log(styleText("bold", "Commands:"));
|
|
269
|
-
for (const def of defs) console.log(` ${styleText("cyan", def.name.padEnd(16))}${def.description}`);
|
|
270
|
-
console.log();
|
|
271
|
-
console.log(styleText("bold", "Options:"));
|
|
272
|
-
console.log(` ${styleText("cyan", "-v, --version".padEnd(30))}Show version number`);
|
|
273
|
-
console.log(` ${styleText("cyan", "-h, --help".padEnd(30))}Show help`);
|
|
274
|
-
console.log();
|
|
275
|
-
console.log(`Run ${styleText("cyan", `${programName} <command> --help`)} for command-specific help.\n`);
|
|
276
|
-
}
|
|
277
|
-
defineCLIAdapter({
|
|
278
|
-
renderHelp(def, parentName) {
|
|
279
|
-
renderHelp(def, parentName);
|
|
280
|
-
},
|
|
281
|
-
async run(defs, argv, opts) {
|
|
282
|
-
const { programName, defaultCommandName, version } = opts;
|
|
283
|
-
const args = argv.length >= 2 && argv[0]?.includes("node") ? argv.slice(2) : argv;
|
|
284
|
-
if (args[0] === "--version" || args[0] === "-v") {
|
|
285
|
-
console.log(version);
|
|
286
|
-
process.exit(0);
|
|
287
|
-
}
|
|
288
|
-
if (args[0] === "--help" || args[0] === "-h") {
|
|
289
|
-
printRootHelp(programName, version, defs);
|
|
290
|
-
process.exit(0);
|
|
291
|
-
}
|
|
292
|
-
if (args.length === 0) {
|
|
293
|
-
const defaultDef = defs.find((d) => d.name === defaultCommandName);
|
|
294
|
-
if (defaultDef?.run) await runCommand(defaultDef, [], programName);
|
|
295
|
-
else printRootHelp(programName, version, defs);
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
const [first, ...rest] = args;
|
|
299
|
-
const isKnownSubcommand = defs.some((d) => d.name === first);
|
|
300
|
-
let def;
|
|
301
|
-
let commandArgv;
|
|
302
|
-
let parentName;
|
|
303
|
-
if (isKnownSubcommand) {
|
|
304
|
-
def = defs.find((d) => d.name === first);
|
|
305
|
-
commandArgv = rest;
|
|
306
|
-
parentName = programName;
|
|
307
|
-
} else {
|
|
308
|
-
def = defs.find((d) => d.name === defaultCommandName);
|
|
309
|
-
commandArgv = args;
|
|
310
|
-
parentName = programName;
|
|
311
|
-
}
|
|
312
|
-
if (!def) {
|
|
313
|
-
console.error(`Unknown command: ${first}`);
|
|
314
|
-
printRootHelp(programName, version, defs);
|
|
315
|
-
process.exit(1);
|
|
316
|
-
}
|
|
317
|
-
if (def.subCommands?.length) {
|
|
318
|
-
const [subName, ...subRest] = commandArgv;
|
|
319
|
-
const subDef = def.subCommands.find((s) => s.name === subName);
|
|
320
|
-
if (subName === "--help" || subName === "-h") {
|
|
321
|
-
renderHelp(def, parentName);
|
|
322
|
-
process.exit(0);
|
|
323
|
-
}
|
|
324
|
-
if (!subDef) {
|
|
325
|
-
renderHelp(def, parentName);
|
|
326
|
-
process.exit(subName ? 1 : 0);
|
|
327
|
-
}
|
|
328
|
-
await runCommand(subDef, subRest, `${parentName} ${def.name}`);
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
await runCommand(def, commandArgv, parentName);
|
|
332
|
-
}
|
|
333
|
-
});
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region ../../internals/utils/src/time.ts
|
|
334
215
|
/**
|
|
335
|
-
* Calculates elapsed time in milliseconds from a high-resolution start time.
|
|
336
|
-
* Rounds to 2 decimal places
|
|
216
|
+
* Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
|
|
217
|
+
* Rounds to 2 decimal places for sub-millisecond precision without noise.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* const start = process.hrtime()
|
|
222
|
+
* doWork()
|
|
223
|
+
* getElapsedMs(start) // 42.35
|
|
224
|
+
* ```
|
|
337
225
|
*/
|
|
338
226
|
function getElapsedMs(hrStart) {
|
|
339
227
|
const [seconds, nanoseconds] = process.hrtime(hrStart);
|
|
@@ -341,39 +229,22 @@ function getElapsedMs(hrStart) {
|
|
|
341
229
|
return Math.round(ms * 100) / 100;
|
|
342
230
|
}
|
|
343
231
|
/**
|
|
344
|
-
* Converts a millisecond duration into a human-readable string.
|
|
345
|
-
*
|
|
232
|
+
* Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* formatMs(250) // '250ms'
|
|
237
|
+
* formatMs(1500) // '1.50s'
|
|
238
|
+
* formatMs(90000) // '1m 30.0s'
|
|
239
|
+
* ```
|
|
346
240
|
*/
|
|
347
241
|
function formatMs(ms) {
|
|
348
242
|
if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
|
|
349
243
|
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
350
244
|
return `${Math.round(ms)}ms`;
|
|
351
245
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
* Falls back to `255` for any channel that cannot be parsed.
|
|
355
|
-
*/
|
|
356
|
-
function parseHex(color) {
|
|
357
|
-
const int = Number.parseInt(color.replace("#", ""), 16);
|
|
358
|
-
return Number.isNaN(int) ? {
|
|
359
|
-
r: 255,
|
|
360
|
-
g: 255,
|
|
361
|
-
b: 255
|
|
362
|
-
} : {
|
|
363
|
-
r: int >> 16 & 255,
|
|
364
|
-
g: int >> 8 & 255,
|
|
365
|
-
b: int & 255
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Returns a function that wraps a string in a 24-bit ANSI true-color escape sequence
|
|
370
|
-
* for the given hex color.
|
|
371
|
-
*/
|
|
372
|
-
function hex(color) {
|
|
373
|
-
const { r, g, b } = parseHex(color);
|
|
374
|
-
return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
|
|
375
|
-
}
|
|
376
|
-
hex("#F55A17"), hex("#F5A217"), hex("#F58517"), hex("#B45309"), hex("#FFFFFF"), hex("#adadc6"), hex("#FDA4AF");
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region ../../internals/utils/src/fs.ts
|
|
377
248
|
/**
|
|
378
249
|
* Converts all backslashes to forward slashes.
|
|
379
250
|
* Extended-length Windows paths (`\\?\...`) are left unchanged.
|
|
@@ -383,8 +254,14 @@ function toSlash(p) {
|
|
|
383
254
|
return p.replaceAll("\\", "/");
|
|
384
255
|
}
|
|
385
256
|
/**
|
|
386
|
-
* Returns the relative path from `rootDir` to `filePath`, always using
|
|
387
|
-
*
|
|
257
|
+
* Returns the relative path from `rootDir` to `filePath`, always using forward slashes
|
|
258
|
+
* and prefixed with `./` when not already traversing upward.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```ts
|
|
262
|
+
* getRelativePath('/src/components', '/src/components/Button.tsx') // './Button.tsx'
|
|
263
|
+
* getRelativePath('/src/components', '/src/utils/helpers.ts') // '../utils/helpers.ts'
|
|
264
|
+
* ```
|
|
388
265
|
*/
|
|
389
266
|
function getRelativePath(rootDir, filePath) {
|
|
390
267
|
if (!rootDir || !filePath) throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ""} ${filePath || ""}`);
|
|
@@ -394,35 +271,54 @@ function getRelativePath(rootDir, filePath) {
|
|
|
394
271
|
/**
|
|
395
272
|
* Resolves to `true` when the file or directory at `path` exists.
|
|
396
273
|
* Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* if (await exists('./kubb.config.ts')) {
|
|
278
|
+
* const content = await read('./kubb.config.ts')
|
|
279
|
+
* }
|
|
280
|
+
* ```
|
|
397
281
|
*/
|
|
398
282
|
async function exists(path) {
|
|
399
283
|
if (typeof Bun !== "undefined") return Bun.file(path).exists();
|
|
400
284
|
return access(path).then(() => true, () => false);
|
|
401
285
|
}
|
|
402
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* Synchronous counterpart of `read`.
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```ts
|
|
291
|
+
* const source = readSync('./src/Pet.ts')
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
403
294
|
function readSync(path) {
|
|
404
295
|
return readFileSync(path, { encoding: "utf8" });
|
|
405
296
|
}
|
|
406
297
|
/**
|
|
407
298
|
* Writes `data` to `path`, trimming leading/trailing whitespace before saving.
|
|
408
|
-
* Skips the write
|
|
409
|
-
* identical to what is already on disk.
|
|
299
|
+
* Skips the write when the trimmed content is empty or identical to what is already on disk.
|
|
410
300
|
* Creates any missing parent directories automatically.
|
|
411
|
-
* When `sanity` is `true`, re-reads the file after writing and throws if the
|
|
412
|
-
*
|
|
301
|
+
* When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* ```ts
|
|
305
|
+
* await write('./src/Pet.ts', source) // writes and returns trimmed content
|
|
306
|
+
* await write('./src/Pet.ts', source) // null — file unchanged
|
|
307
|
+
* await write('./src/Pet.ts', ' ') // null — empty content skipped
|
|
308
|
+
* ```
|
|
413
309
|
*/
|
|
414
310
|
async function write(path, data, options = {}) {
|
|
415
311
|
const trimmed = data.trim();
|
|
416
|
-
if (trimmed === "") return
|
|
312
|
+
if (trimmed === "") return null;
|
|
417
313
|
const resolved = resolve(path);
|
|
418
314
|
if (typeof Bun !== "undefined") {
|
|
419
315
|
const file = Bun.file(resolved);
|
|
420
|
-
if ((await file.exists() ? await file.text() : null) === trimmed) return
|
|
316
|
+
if ((await file.exists() ? await file.text() : null) === trimmed) return null;
|
|
421
317
|
await Bun.write(resolved, trimmed);
|
|
422
318
|
return trimmed;
|
|
423
319
|
}
|
|
424
320
|
try {
|
|
425
|
-
if (await readFile(resolved, { encoding: "utf-8" }) === trimmed) return
|
|
321
|
+
if (await readFile(resolved, { encoding: "utf-8" }) === trimmed) return null;
|
|
426
322
|
} catch {}
|
|
427
323
|
await mkdir(dirname(resolved), { recursive: true });
|
|
428
324
|
await writeFile(resolved, trimmed, { encoding: "utf-8" });
|
|
@@ -433,16 +329,32 @@ async function write(path, data, options = {}) {
|
|
|
433
329
|
}
|
|
434
330
|
return trimmed;
|
|
435
331
|
}
|
|
436
|
-
/**
|
|
332
|
+
/**
|
|
333
|
+
* Recursively removes `path`. Silently succeeds when `path` does not exist.
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```ts
|
|
337
|
+
* await clean('./dist')
|
|
338
|
+
* ```
|
|
339
|
+
*/
|
|
437
340
|
async function clean(path) {
|
|
438
341
|
return rm(path, {
|
|
439
342
|
recursive: true,
|
|
440
343
|
force: true
|
|
441
344
|
});
|
|
442
345
|
}
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region ../../internals/utils/src/names.ts
|
|
443
348
|
/**
|
|
444
349
|
* Registers `originalName` in `data` without altering the returned name.
|
|
445
|
-
* Use
|
|
350
|
+
* Use when you need to track usage frequency but always emit the original identifier.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* const seen: Record<string, number> = {}
|
|
355
|
+
* setUniqueName('Foo', seen) // 'Foo' (seen = { Foo: 1 })
|
|
356
|
+
* setUniqueName('Foo', seen) // 'Foo' (seen = { Foo: 2 })
|
|
357
|
+
* ```
|
|
446
358
|
*/
|
|
447
359
|
function setUniqueName(originalName, data) {
|
|
448
360
|
let used = data[originalName] || 0;
|
|
@@ -453,10 +365,21 @@ function setUniqueName(originalName, data) {
|
|
|
453
365
|
data[originalName] = 1;
|
|
454
366
|
return originalName;
|
|
455
367
|
}
|
|
456
|
-
|
|
368
|
+
//#endregion
|
|
369
|
+
//#region ../../internals/utils/src/promise.ts
|
|
370
|
+
/** Returns `true` when `result` is a rejected `Promise.allSettled` result with a typed `reason`.
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```ts
|
|
374
|
+
* const results = await Promise.allSettled([p1, p2])
|
|
375
|
+
* results.filter(isPromiseRejectedResult<Error>).map((r) => r.reason.message)
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
457
378
|
function isPromiseRejectedResult(result) {
|
|
458
379
|
return result.status === "rejected";
|
|
459
380
|
}
|
|
381
|
+
//#endregion
|
|
382
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
460
383
|
/**
|
|
461
384
|
* JavaScript and Java reserved words.
|
|
462
385
|
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
@@ -545,8 +468,14 @@ const reservedWords = new Set([
|
|
|
545
468
|
"valueOf"
|
|
546
469
|
]);
|
|
547
470
|
/**
|
|
548
|
-
* Prefixes
|
|
549
|
-
*
|
|
471
|
+
* Prefixes `word` with `_` when it is a reserved JavaScript/Java identifier or starts with a digit.
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```ts
|
|
475
|
+
* transformReservedWord('class') // '_class'
|
|
476
|
+
* transformReservedWord('42foo') // '_42foo'
|
|
477
|
+
* transformReservedWord('status') // 'status'
|
|
478
|
+
* ```
|
|
550
479
|
*/
|
|
551
480
|
function transformReservedWord(word) {
|
|
552
481
|
const firstChar = word.charCodeAt(0);
|
|
@@ -555,6 +484,13 @@ function transformReservedWord(word) {
|
|
|
555
484
|
}
|
|
556
485
|
/**
|
|
557
486
|
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```ts
|
|
490
|
+
* isValidVarName('status') // true
|
|
491
|
+
* isValidVarName('class') // false (reserved word)
|
|
492
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
493
|
+
* ```
|
|
558
494
|
*/
|
|
559
495
|
function isValidVarName(name) {
|
|
560
496
|
try {
|
|
@@ -564,6 +500,8 @@ function isValidVarName(name) {
|
|
|
564
500
|
}
|
|
565
501
|
return true;
|
|
566
502
|
}
|
|
503
|
+
//#endregion
|
|
504
|
+
//#region ../../internals/utils/src/urlPath.ts
|
|
567
505
|
/**
|
|
568
506
|
* Parses and transforms an OpenAPI/Swagger path string into various URL formats.
|
|
569
507
|
*
|
|
@@ -573,18 +511,33 @@ function isValidVarName(name) {
|
|
|
573
511
|
* p.template // '`/pet/${petId}`'
|
|
574
512
|
*/
|
|
575
513
|
var URLPath = class {
|
|
576
|
-
/**
|
|
514
|
+
/**
|
|
515
|
+
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
516
|
+
*/
|
|
577
517
|
path;
|
|
578
518
|
#options;
|
|
579
519
|
constructor(path, options = {}) {
|
|
580
520
|
this.path = path;
|
|
581
521
|
this.#options = options;
|
|
582
522
|
}
|
|
583
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
523
|
+
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* ```ts
|
|
527
|
+
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
528
|
+
* ```
|
|
529
|
+
*/
|
|
584
530
|
get URL() {
|
|
585
531
|
return this.toURLPath();
|
|
586
532
|
}
|
|
587
|
-
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
533
|
+
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
534
|
+
*
|
|
535
|
+
* @example
|
|
536
|
+
* ```ts
|
|
537
|
+
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
538
|
+
* new URLPath('/pet/{petId}').isURL // false
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
588
541
|
get isURL() {
|
|
589
542
|
try {
|
|
590
543
|
return !!new URL(this.path).href;
|
|
@@ -602,11 +555,25 @@ var URLPath = class {
|
|
|
602
555
|
get template() {
|
|
603
556
|
return this.toTemplateString();
|
|
604
557
|
}
|
|
605
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
558
|
+
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```ts
|
|
562
|
+
* new URLPath('/pet/{petId}').object
|
|
563
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
564
|
+
* ```
|
|
565
|
+
*/
|
|
606
566
|
get object() {
|
|
607
567
|
return this.toObject();
|
|
608
568
|
}
|
|
609
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
569
|
+
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
570
|
+
*
|
|
571
|
+
* @example
|
|
572
|
+
* ```ts
|
|
573
|
+
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
574
|
+
* new URLPath('/pet').params // undefined
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
610
577
|
get params() {
|
|
611
578
|
return this.getParams();
|
|
612
579
|
}
|
|
@@ -614,7 +581,9 @@ var URLPath = class {
|
|
|
614
581
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
615
582
|
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
616
583
|
}
|
|
617
|
-
/**
|
|
584
|
+
/**
|
|
585
|
+
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
586
|
+
*/
|
|
618
587
|
#eachParam(fn) {
|
|
619
588
|
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
620
589
|
const raw = match[1];
|
|
@@ -651,6 +620,12 @@ var URLPath = class {
|
|
|
651
620
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
652
621
|
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
653
622
|
* Returns `undefined` when no path parameters are found.
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```ts
|
|
626
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
627
|
+
* // { petId: 'petId', tagId: 'tagId' }
|
|
628
|
+
* ```
|
|
654
629
|
*/
|
|
655
630
|
getParams(replacer) {
|
|
656
631
|
const params = {};
|
|
@@ -660,7 +635,13 @@ var URLPath = class {
|
|
|
660
635
|
});
|
|
661
636
|
return Object.keys(params).length > 0 ? params : void 0;
|
|
662
637
|
}
|
|
663
|
-
/** Converts the OpenAPI path to Express-style colon syntax
|
|
638
|
+
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
639
|
+
*
|
|
640
|
+
* @example
|
|
641
|
+
* ```ts
|
|
642
|
+
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
643
|
+
* ```
|
|
644
|
+
*/
|
|
664
645
|
toURLPath() {
|
|
665
646
|
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
666
647
|
}
|
|
@@ -678,10 +659,27 @@ function isInputPath(config) {
|
|
|
678
659
|
}
|
|
679
660
|
//#endregion
|
|
680
661
|
//#region src/constants.ts
|
|
662
|
+
/**
|
|
663
|
+
* Base URL for the Kubb Studio web app.
|
|
664
|
+
*/
|
|
681
665
|
const DEFAULT_STUDIO_URL = "https://studio.kubb.dev";
|
|
666
|
+
/**
|
|
667
|
+
* File name used for generated barrel (index) files.
|
|
668
|
+
*/
|
|
682
669
|
const BARREL_FILENAME = "index.ts";
|
|
670
|
+
/**
|
|
671
|
+
* Default banner style written at the top of every generated file.
|
|
672
|
+
*/
|
|
683
673
|
const DEFAULT_BANNER = "simple";
|
|
674
|
+
/**
|
|
675
|
+
* Default file-extension mapping used when no explicit mapping is configured.
|
|
676
|
+
*/
|
|
684
677
|
const DEFAULT_EXTENSION = { ".ts": ".ts" };
|
|
678
|
+
/**
|
|
679
|
+
* Numeric log-level thresholds used internally to compare verbosity.
|
|
680
|
+
*
|
|
681
|
+
* Higher numbers are more verbose.
|
|
682
|
+
*/
|
|
685
683
|
const logLevel = {
|
|
686
684
|
silent: Number.NEGATIVE_INFINITY,
|
|
687
685
|
error: 0,
|
|
@@ -690,6 +688,13 @@ const logLevel = {
|
|
|
690
688
|
verbose: 4,
|
|
691
689
|
debug: 5
|
|
692
690
|
};
|
|
691
|
+
/**
|
|
692
|
+
* CLI command descriptors for each supported linter.
|
|
693
|
+
*
|
|
694
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
695
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
696
|
+
* the linter is not found.
|
|
697
|
+
*/
|
|
693
698
|
const linters = {
|
|
694
699
|
eslint: {
|
|
695
700
|
command: "eslint",
|
|
@@ -711,6 +716,13 @@ const linters = {
|
|
|
711
716
|
errorMessage: "Oxlint not found"
|
|
712
717
|
}
|
|
713
718
|
};
|
|
719
|
+
/**
|
|
720
|
+
* CLI command descriptors for each supported code formatter.
|
|
721
|
+
*
|
|
722
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
723
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
724
|
+
* the formatter is not found.
|
|
725
|
+
*/
|
|
714
726
|
const formatters = {
|
|
715
727
|
prettier: {
|
|
716
728
|
command: "prettier",
|
|
@@ -910,7 +922,11 @@ function validateConcurrency(concurrency) {
|
|
|
910
922
|
//#endregion
|
|
911
923
|
//#region src/utils/executeStrategies.ts
|
|
912
924
|
/**
|
|
913
|
-
*
|
|
925
|
+
* Runs promise functions in sequence, threading each result into the next call.
|
|
926
|
+
*
|
|
927
|
+
* - Each function receives the accumulated state from the previous call.
|
|
928
|
+
* - Skips functions that return a falsy value (acts as a no-op for that step).
|
|
929
|
+
* - Returns an array of all individual results.
|
|
914
930
|
*/
|
|
915
931
|
function hookSeq(promises) {
|
|
916
932
|
return promises.filter(Boolean).reduce((promise, func) => {
|
|
@@ -923,7 +939,10 @@ function hookSeq(promises) {
|
|
|
923
939
|
}, Promise.resolve([]));
|
|
924
940
|
}
|
|
925
941
|
/**
|
|
926
|
-
*
|
|
942
|
+
* Runs promise functions in sequence and returns the first non-null result.
|
|
943
|
+
*
|
|
944
|
+
* - Stops as soon as `nullCheck` passes for a result (default: `!== null`).
|
|
945
|
+
* - Subsequent functions are skipped once a match is found.
|
|
927
946
|
*/
|
|
928
947
|
function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
929
948
|
let promise = Promise.resolve(null);
|
|
@@ -934,7 +953,10 @@ function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
|
934
953
|
return promise;
|
|
935
954
|
}
|
|
936
955
|
/**
|
|
937
|
-
* Runs
|
|
956
|
+
* Runs promise functions concurrently and returns all settled results.
|
|
957
|
+
*
|
|
958
|
+
* - Limits simultaneous executions to `concurrency` (default: unlimited).
|
|
959
|
+
* - Uses `Promise.allSettled` so individual failures do not cancel other tasks.
|
|
938
960
|
*/
|
|
939
961
|
function hookParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
940
962
|
const limit = pLimit(concurrency);
|
|
@@ -1441,11 +1463,14 @@ const fsStorage = createStorage(() => ({
|
|
|
1441
1463
|
}));
|
|
1442
1464
|
//#endregion
|
|
1443
1465
|
//#region package.json
|
|
1444
|
-
var version$1 = "5.0.0-alpha.
|
|
1466
|
+
var version$1 = "5.0.0-alpha.19";
|
|
1445
1467
|
//#endregion
|
|
1446
1468
|
//#region src/utils/diagnostics.ts
|
|
1447
1469
|
/**
|
|
1448
|
-
*
|
|
1470
|
+
* Returns a snapshot of the current runtime environment.
|
|
1471
|
+
*
|
|
1472
|
+
* Useful for attaching context to debug logs and error reports so that
|
|
1473
|
+
* issues can be reproduced without manual information gathering.
|
|
1449
1474
|
*/
|
|
1450
1475
|
function getDiagnosticInfo() {
|
|
1451
1476
|
return {
|
|
@@ -1458,6 +1483,17 @@ function getDiagnosticInfo() {
|
|
|
1458
1483
|
}
|
|
1459
1484
|
//#endregion
|
|
1460
1485
|
//#region src/build.ts
|
|
1486
|
+
/**
|
|
1487
|
+
* Initializes all Kubb infrastructure for a build without executing any plugins.
|
|
1488
|
+
*
|
|
1489
|
+
* - Validates the input path (when applicable).
|
|
1490
|
+
* - Applies config defaults (`root`, `output.*`, `devtools`).
|
|
1491
|
+
* - Creates the Fabric instance and wires storage, format, and lint hooks.
|
|
1492
|
+
* - Runs the adapter (if configured) to produce the universal `RootNode`.
|
|
1493
|
+
*
|
|
1494
|
+
* Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
|
|
1495
|
+
* via the `overrides` argument to reuse the same infrastructure across multiple runs.
|
|
1496
|
+
*/
|
|
1461
1497
|
async function setup(options) {
|
|
1462
1498
|
const { config: userConfig, events = new AsyncEventEmitter() } = options;
|
|
1463
1499
|
const sources = /* @__PURE__ */ new Map();
|
|
@@ -1584,6 +1620,12 @@ async function setup(options) {
|
|
|
1584
1620
|
sources
|
|
1585
1621
|
};
|
|
1586
1622
|
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Runs a full Kubb build and throws on any error or plugin failure.
|
|
1625
|
+
*
|
|
1626
|
+
* Internally delegates to {@link safeBuild} and rethrows collected errors.
|
|
1627
|
+
* Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
|
|
1628
|
+
*/
|
|
1587
1629
|
async function build(options, overrides) {
|
|
1588
1630
|
const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides);
|
|
1589
1631
|
if (error) throw error;
|
|
@@ -1601,6 +1643,16 @@ async function build(options, overrides) {
|
|
|
1601
1643
|
sources
|
|
1602
1644
|
};
|
|
1603
1645
|
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Runs a full Kubb build and captures errors instead of throwing.
|
|
1648
|
+
*
|
|
1649
|
+
* - Installs each plugin in order, recording failures in `failedPlugins`.
|
|
1650
|
+
* - Generates the root barrel file when `output.barrelType` is set.
|
|
1651
|
+
* - Writes all files through Fabric.
|
|
1652
|
+
*
|
|
1653
|
+
* Returns a {@link BuildOutput} even on failure — inspect `error` and
|
|
1654
|
+
* `failedPlugins` to determine whether the build succeeded.
|
|
1655
|
+
*/
|
|
1604
1656
|
async function safeBuild(options, overrides) {
|
|
1605
1657
|
const { fabric, driver, events, sources } = overrides ? overrides : await setup(options);
|
|
1606
1658
|
const failedPlugins = /* @__PURE__ */ new Set();
|
|
@@ -2164,14 +2216,10 @@ var FunctionParams = class FunctionParams {
|
|
|
2164
2216
|
//#endregion
|
|
2165
2217
|
//#region src/utils/formatters.ts
|
|
2166
2218
|
/**
|
|
2167
|
-
*
|
|
2219
|
+
* Returns `true` when the given formatter is installed and callable.
|
|
2168
2220
|
*
|
|
2169
|
-
*
|
|
2170
|
-
*
|
|
2171
|
-
*
|
|
2172
|
-
* @remarks
|
|
2173
|
-
* This function checks availability by running `<formatter> --version` command.
|
|
2174
|
-
* All supported formatters (biome, prettier, oxfmt) implement the --version flag.
|
|
2221
|
+
* Availability is detected by running `<formatter> --version` and checking
|
|
2222
|
+
* that the process exits without error.
|
|
2175
2223
|
*/
|
|
2176
2224
|
async function isFormatterAvailable(formatter) {
|
|
2177
2225
|
try {
|
|
@@ -2182,22 +2230,16 @@ async function isFormatterAvailable(formatter) {
|
|
|
2182
2230
|
}
|
|
2183
2231
|
}
|
|
2184
2232
|
/**
|
|
2185
|
-
*
|
|
2186
|
-
*
|
|
2187
|
-
* @returns Promise that resolves to the first available formatter or undefined if none are found
|
|
2233
|
+
* Detects the first available code formatter on the current system.
|
|
2188
2234
|
*
|
|
2189
|
-
*
|
|
2190
|
-
*
|
|
2191
|
-
* Uses the `--version` flag to detect if each formatter command is available.
|
|
2192
|
-
* This is a reliable method as all supported formatters implement this flag.
|
|
2235
|
+
* - Checks in preference order: `biome`, `oxfmt`, `prettier`.
|
|
2236
|
+
* - Returns `null` when none are found.
|
|
2193
2237
|
*
|
|
2194
2238
|
* @example
|
|
2195
|
-
* ```
|
|
2239
|
+
* ```ts
|
|
2196
2240
|
* const formatter = await detectFormatter()
|
|
2197
2241
|
* if (formatter) {
|
|
2198
2242
|
* console.log(`Using ${formatter} for formatting`)
|
|
2199
|
-
* } else {
|
|
2200
|
-
* console.log('No formatter found')
|
|
2201
2243
|
* }
|
|
2202
2244
|
* ```
|
|
2203
2245
|
*/
|
|
@@ -2208,9 +2250,19 @@ async function detectFormatter() {
|
|
|
2208
2250
|
"prettier"
|
|
2209
2251
|
]);
|
|
2210
2252
|
for (const formatter of formatterNames) if (await isFormatterAvailable(formatter)) return formatter;
|
|
2253
|
+
return null;
|
|
2211
2254
|
}
|
|
2212
2255
|
//#endregion
|
|
2213
2256
|
//#region src/utils/TreeNode.ts
|
|
2257
|
+
/**
|
|
2258
|
+
* Tree structure used to build per-directory barrel (`index.ts`) files from a
|
|
2259
|
+
* flat list of generated {@link KubbFile.File} entries.
|
|
2260
|
+
*
|
|
2261
|
+
* Each node represents either a directory or a file within the output tree.
|
|
2262
|
+
* Use {@link TreeNode.build} to construct a root node from a file list, then
|
|
2263
|
+
* traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
|
|
2264
|
+
* `*Deep` helpers.
|
|
2265
|
+
*/
|
|
2214
2266
|
var TreeNode = class TreeNode {
|
|
2215
2267
|
data;
|
|
2216
2268
|
parent;
|
|
@@ -2226,10 +2278,18 @@ var TreeNode = class TreeNode {
|
|
|
2226
2278
|
this.children.push(child);
|
|
2227
2279
|
return child;
|
|
2228
2280
|
}
|
|
2281
|
+
/**
|
|
2282
|
+
* Returns the root ancestor of this node, walking up via `parent` links.
|
|
2283
|
+
*/
|
|
2229
2284
|
get root() {
|
|
2230
2285
|
if (!this.parent) return this;
|
|
2231
2286
|
return this.parent.root;
|
|
2232
2287
|
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Returns all leaf descendants (nodes with no children) of this node.
|
|
2290
|
+
*
|
|
2291
|
+
* Results are cached after the first traversal.
|
|
2292
|
+
*/
|
|
2233
2293
|
get leaves() {
|
|
2234
2294
|
if (!this.children || this.children.length === 0) return [this];
|
|
2235
2295
|
if (this.#cachedLeaves) return this.#cachedLeaves;
|
|
@@ -2260,6 +2320,12 @@ var TreeNode = class TreeNode {
|
|
|
2260
2320
|
if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
|
|
2261
2321
|
return this.leaves.map(callback);
|
|
2262
2322
|
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Builds a {@link TreeNode} tree from a flat list of files.
|
|
2325
|
+
*
|
|
2326
|
+
* - Filters to files under `root` (when provided) and skips `.json` files.
|
|
2327
|
+
* - Returns `null` when no files match.
|
|
2328
|
+
*/
|
|
2263
2329
|
static build(files, root) {
|
|
2264
2330
|
try {
|
|
2265
2331
|
const filteredTree = buildDirectoryTree(files, root);
|
|
@@ -2375,6 +2441,14 @@ function trimExtName(text) {
|
|
|
2375
2441
|
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
2376
2442
|
return text;
|
|
2377
2443
|
}
|
|
2444
|
+
/**
|
|
2445
|
+
* Generates `index.ts` barrel files for all directories under `root/output.path`.
|
|
2446
|
+
*
|
|
2447
|
+
* - Returns an empty array when `type` is falsy or `'propagate'`.
|
|
2448
|
+
* - Skips generation when the output path itself ends with `index` (already a barrel).
|
|
2449
|
+
* - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
|
|
2450
|
+
* - Attaches `meta` to each barrel file for downstream plugin identification.
|
|
2451
|
+
*/
|
|
2378
2452
|
async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
2379
2453
|
if (!type || type === "propagate") return [];
|
|
2380
2454
|
const pathToBuildFrom = join(root, output.path);
|
|
@@ -2401,7 +2475,11 @@ async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
|
2401
2475
|
//#endregion
|
|
2402
2476
|
//#region src/utils/getConfigs.ts
|
|
2403
2477
|
/**
|
|
2404
|
-
*
|
|
2478
|
+
* Resolves a {@link ConfigInput} into a normalized array of {@link Config} objects.
|
|
2479
|
+
*
|
|
2480
|
+
* - Awaits the config when it is a `Promise`.
|
|
2481
|
+
* - Calls the factory function with `args` when the config is a function.
|
|
2482
|
+
* - Wraps a single config object in an array for uniform downstream handling.
|
|
2405
2483
|
*/
|
|
2406
2484
|
async function getConfigs(config, args) {
|
|
2407
2485
|
const resolved = await (typeof config === "function" ? config(args) : config);
|
|
@@ -2420,6 +2498,13 @@ function mergeResolvers(...resolvers) {
|
|
|
2420
2498
|
}
|
|
2421
2499
|
//#endregion
|
|
2422
2500
|
//#region src/utils/getPreset.ts
|
|
2501
|
+
/**
|
|
2502
|
+
* Resolves a named preset into merged resolvers and transformers.
|
|
2503
|
+
*
|
|
2504
|
+
* - Merges the preset's resolvers on top of the first (default) resolver to produce `baseResolver`.
|
|
2505
|
+
* - Merges any additional user-supplied resolvers on top of that to produce the final `resolver`.
|
|
2506
|
+
* - Concatenates preset transformers before user-supplied transformers.
|
|
2507
|
+
*/
|
|
2423
2508
|
function getPreset(params) {
|
|
2424
2509
|
const { preset: presetName, presets, resolvers, transformers: userTransformers } = params;
|
|
2425
2510
|
const [defaultResolver, ...userResolvers] = resolvers;
|
|
@@ -2434,6 +2519,12 @@ function getPreset(params) {
|
|
|
2434
2519
|
}
|
|
2435
2520
|
//#endregion
|
|
2436
2521
|
//#region src/utils/linters.ts
|
|
2522
|
+
/**
|
|
2523
|
+
* Returns `true` when the given linter is installed and callable.
|
|
2524
|
+
*
|
|
2525
|
+
* Availability is detected by running `<linter> --version` and checking
|
|
2526
|
+
* that the process exits without error.
|
|
2527
|
+
*/
|
|
2437
2528
|
async function isLinterAvailable(linter) {
|
|
2438
2529
|
try {
|
|
2439
2530
|
await x(linter, ["--version"], { nodeOptions: { stdio: "ignore" } });
|
|
@@ -2442,6 +2533,20 @@ async function isLinterAvailable(linter) {
|
|
|
2442
2533
|
return false;
|
|
2443
2534
|
}
|
|
2444
2535
|
}
|
|
2536
|
+
/**
|
|
2537
|
+
* Detects the first available linter on the current system.
|
|
2538
|
+
*
|
|
2539
|
+
* - Checks in preference order: `biome`, `oxlint`, `eslint`.
|
|
2540
|
+
* - Returns `null` when none are found.
|
|
2541
|
+
*
|
|
2542
|
+
* @example
|
|
2543
|
+
* ```ts
|
|
2544
|
+
* const linter = await detectLinter()
|
|
2545
|
+
* if (linter) {
|
|
2546
|
+
* console.log(`Using ${linter} for linting`)
|
|
2547
|
+
* }
|
|
2548
|
+
* ```
|
|
2549
|
+
*/
|
|
2445
2550
|
async function detectLinter() {
|
|
2446
2551
|
const linterNames = new Set([
|
|
2447
2552
|
"biome",
|
|
@@ -2449,12 +2554,13 @@ async function detectLinter() {
|
|
|
2449
2554
|
"eslint"
|
|
2450
2555
|
]);
|
|
2451
2556
|
for (const linter of linterNames) if (await isLinterAvailable(linter)) return linter;
|
|
2557
|
+
return null;
|
|
2452
2558
|
}
|
|
2453
2559
|
//#endregion
|
|
2454
2560
|
//#region src/utils/packageJSON.ts
|
|
2455
2561
|
function getPackageJSONSync(cwd) {
|
|
2456
2562
|
const pkgPath = pkg.up({ cwd });
|
|
2457
|
-
if (!pkgPath) return;
|
|
2563
|
+
if (!pkgPath) return null;
|
|
2458
2564
|
return JSON.parse(readSync(pkgPath));
|
|
2459
2565
|
}
|
|
2460
2566
|
function match(packageJSON, dependency) {
|
|
@@ -2464,12 +2570,27 @@ function match(packageJSON, dependency) {
|
|
|
2464
2570
|
};
|
|
2465
2571
|
if (typeof dependency === "string" && dependencies[dependency]) return dependencies[dependency];
|
|
2466
2572
|
const matched = Object.keys(dependencies).find((dep) => dep.match(dependency));
|
|
2467
|
-
return matched ? dependencies[matched] :
|
|
2573
|
+
return matched ? dependencies[matched] ?? null : null;
|
|
2468
2574
|
}
|
|
2469
2575
|
function getVersionSync(dependency, cwd) {
|
|
2470
2576
|
const packageJSON = getPackageJSONSync(cwd);
|
|
2471
|
-
return packageJSON ? match(packageJSON, dependency) :
|
|
2577
|
+
return packageJSON ? match(packageJSON, dependency) : null;
|
|
2472
2578
|
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Returns `true` when the nearest `package.json` declares a dependency that
|
|
2581
|
+
* satisfies the given semver range.
|
|
2582
|
+
*
|
|
2583
|
+
* - Searches both `dependencies` and `devDependencies`.
|
|
2584
|
+
* - Accepts a string package name or a `RegExp` to match scoped/pattern packages.
|
|
2585
|
+
* - Uses `semver.satisfies` for range comparison; returns `false` when the
|
|
2586
|
+
* version string cannot be coerced into a valid semver.
|
|
2587
|
+
*
|
|
2588
|
+
* @example
|
|
2589
|
+
* ```ts
|
|
2590
|
+
* satisfiesDependency('react', '>=18') // true when react@18.x is installed
|
|
2591
|
+
* satisfiesDependency(/^@tanstack\//, '>=5') // true when any @tanstack/* >=5 is found
|
|
2592
|
+
* ```
|
|
2593
|
+
*/
|
|
2473
2594
|
function satisfiesDependency(dependency, version, cwd) {
|
|
2474
2595
|
const packageVersion = getVersionSync(dependency, cwd);
|
|
2475
2596
|
if (!packageVersion) return false;
|
|
@@ -2479,6 +2600,6 @@ function satisfiesDependency(dependency, version, cwd) {
|
|
|
2479
2600
|
return satisfies(semVer, version);
|
|
2480
2601
|
}
|
|
2481
2602
|
//#endregion
|
|
2482
|
-
export { FunctionParams, PluginDriver, build, build as default, createAdapter, createPlugin, createStorage, defaultResolveOptions, defineConfig, defineGenerator, defineLogger, definePreset, definePresets, definePrinter, defineResolver, detectFormatter, detectLinter, formatters, fsStorage, getBarrelFiles, getConfigs, getMode, getPreset, isInputPath, linters, logLevel, memoryStorage, mergeResolvers, renderOperation, renderOperations, renderSchema, safeBuild, satisfiesDependency, setup };
|
|
2603
|
+
export { AsyncEventEmitter, FunctionParams, PluginDriver, URLPath, build, build as default, createAdapter, createPlugin, createStorage, defaultResolveOptions, defineConfig, defineGenerator, defineLogger, definePreset, definePresets, definePrinter, defineResolver, detectFormatter, detectLinter, formatters, fsStorage, getBarrelFiles, getConfigs, getMode, getPreset, isInputPath, linters, logLevel, memoryStorage, mergeResolvers, renderOperation, renderOperations, renderSchema, safeBuild, satisfiesDependency, setup };
|
|
2483
2604
|
|
|
2484
2605
|
//# sourceMappingURL=index.js.map
|