@kubb/core 5.0.0-alpha.16 → 5.0.0-alpha.18
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.cjs
CHANGED
|
@@ -3,13 +3,12 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("./chunk-ByKO4r7w.cjs");
|
|
6
|
-
let _kubb_ast = require("@kubb/ast");
|
|
7
|
-
let node_path = require("node:path");
|
|
8
|
-
node_path = require_chunk.__toESM(node_path);
|
|
9
6
|
let node_events = require("node:events");
|
|
10
|
-
let node_util = require("node:util");
|
|
11
7
|
let node_fs = require("node:fs");
|
|
12
8
|
let node_fs_promises = require("node:fs/promises");
|
|
9
|
+
let node_path = require("node:path");
|
|
10
|
+
node_path = require_chunk.__toESM(node_path);
|
|
11
|
+
let _kubb_ast = require("@kubb/ast");
|
|
13
12
|
let _kubb_react_fabric = require("@kubb/react-fabric");
|
|
14
13
|
let _kubb_react_fabric_parsers = require("@kubb/react-fabric/parsers");
|
|
15
14
|
let _kubb_react_fabric_plugins = require("@kubb/react-fabric/plugins");
|
|
@@ -22,12 +21,23 @@ let remeda = require("remeda");
|
|
|
22
21
|
let empathic_package = require("empathic/package");
|
|
23
22
|
empathic_package = require_chunk.__toESM(empathic_package);
|
|
24
23
|
let semver = require("semver");
|
|
25
|
-
//#region ../../internals/utils/
|
|
26
|
-
/** Thrown when a plugin's configuration or input fails validation.
|
|
24
|
+
//#region ../../internals/utils/src/errors.ts
|
|
25
|
+
/** Thrown when a plugin's configuration or input fails validation.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* throw new ValidationPluginError('Invalid config: "output.path" is required')
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
27
32
|
var ValidationPluginError = class extends Error {};
|
|
28
33
|
/**
|
|
29
34
|
* Thrown when one or more errors occur during a Kubb build.
|
|
30
35
|
* Carries the full list of underlying errors on `errors`.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* throw new BuildError('Build failed', { errors: [err1, err2] })
|
|
40
|
+
* ```
|
|
31
41
|
*/
|
|
32
42
|
var BuildError = class extends Error {
|
|
33
43
|
errors;
|
|
@@ -39,19 +49,34 @@ var BuildError = class extends Error {
|
|
|
39
49
|
};
|
|
40
50
|
/**
|
|
41
51
|
* Coerces an unknown thrown value to an `Error` instance.
|
|
42
|
-
*
|
|
43
|
-
*
|
|
52
|
+
* Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* try { ... } catch(err) {
|
|
57
|
+
* throw new BuildError('Build failed', { cause: toError(err), errors: [] })
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
44
60
|
*/
|
|
45
61
|
function toError(value) {
|
|
46
62
|
return value instanceof Error ? value : new Error(String(value));
|
|
47
63
|
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region ../../internals/utils/src/asyncEventEmitter.ts
|
|
48
66
|
/**
|
|
49
|
-
*
|
|
67
|
+
* Typed `EventEmitter` that awaits all async listeners before resolving.
|
|
50
68
|
* Wraps Node's `EventEmitter` with full TypeScript event-map inference.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
|
|
73
|
+
* emitter.on('build', async (name) => { console.log(name) })
|
|
74
|
+
* await emitter.emit('build', 'petstore') // all listeners awaited
|
|
75
|
+
* ```
|
|
51
76
|
*/
|
|
52
77
|
var AsyncEventEmitter = class {
|
|
53
78
|
/**
|
|
54
|
-
*
|
|
79
|
+
* Maximum number of listeners per event before Node emits a memory-leak warning.
|
|
55
80
|
* @default 10
|
|
56
81
|
*/
|
|
57
82
|
constructor(maxListener = 10) {
|
|
@@ -59,8 +84,13 @@ var AsyncEventEmitter = class {
|
|
|
59
84
|
}
|
|
60
85
|
#emitter = new node_events.EventEmitter();
|
|
61
86
|
/**
|
|
62
|
-
* Emits
|
|
87
|
+
* Emits `eventName` and awaits all registered listeners in parallel.
|
|
63
88
|
* Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* await emitter.emit('build', 'petstore')
|
|
93
|
+
* ```
|
|
64
94
|
*/
|
|
65
95
|
async emit(eventName, ...eventArgs) {
|
|
66
96
|
const listeners = this.#emitter.listeners(eventName);
|
|
@@ -79,11 +109,25 @@ var AsyncEventEmitter = class {
|
|
|
79
109
|
}
|
|
80
110
|
}));
|
|
81
111
|
}
|
|
82
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Registers a persistent listener for `eventName`.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* emitter.on('build', async (name) => { console.log(name) })
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
83
120
|
on(eventName, handler) {
|
|
84
121
|
this.#emitter.on(eventName, handler);
|
|
85
122
|
}
|
|
86
|
-
/**
|
|
123
|
+
/**
|
|
124
|
+
* Registers a one-shot listener that removes itself after the first invocation.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* emitter.onOnce('build', async (name) => { console.log(name) })
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
87
131
|
onOnce(eventName, handler) {
|
|
88
132
|
const wrapper = (...args) => {
|
|
89
133
|
this.off(eventName, wrapper);
|
|
@@ -91,15 +135,31 @@ var AsyncEventEmitter = class {
|
|
|
91
135
|
};
|
|
92
136
|
this.on(eventName, wrapper);
|
|
93
137
|
}
|
|
94
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Removes a previously registered listener.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* emitter.off('build', handler)
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
95
146
|
off(eventName, handler) {
|
|
96
147
|
this.#emitter.off(eventName, handler);
|
|
97
148
|
}
|
|
98
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Removes all listeners from every event channel.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* emitter.removeAll()
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
99
157
|
removeAll() {
|
|
100
158
|
this.#emitter.removeAllListeners();
|
|
101
159
|
}
|
|
102
160
|
};
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region ../../internals/utils/src/casing.ts
|
|
103
163
|
/**
|
|
104
164
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
105
165
|
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
@@ -156,190 +216,18 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
156
216
|
}) : camelCase(part));
|
|
157
217
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
158
218
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return adapter;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Serializes `CommandDefinition[]` to a plain, JSON-serializable structure.
|
|
165
|
-
* Use to expose CLI capabilities to AI agents or MCP tools.
|
|
166
|
-
*/
|
|
167
|
-
function getCommandSchema(defs) {
|
|
168
|
-
return defs.map(serializeCommand);
|
|
169
|
-
}
|
|
170
|
-
function serializeCommand(def) {
|
|
171
|
-
return {
|
|
172
|
-
name: def.name,
|
|
173
|
-
description: def.description,
|
|
174
|
-
arguments: def.arguments,
|
|
175
|
-
options: serializeOptions(def.options ?? {}),
|
|
176
|
-
subCommands: def.subCommands ? def.subCommands.map(serializeCommand) : []
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
function serializeOptions(options) {
|
|
180
|
-
return Object.entries(options).map(([name, opt]) => {
|
|
181
|
-
return {
|
|
182
|
-
name,
|
|
183
|
-
flags: `${opt.short ? `-${opt.short}, ` : ""}--${name}${opt.type === "string" ? ` <${opt.hint ?? name}>` : ""}`,
|
|
184
|
-
type: opt.type,
|
|
185
|
-
description: opt.description,
|
|
186
|
-
...opt.default !== void 0 ? { default: opt.default } : {},
|
|
187
|
-
...opt.hint ? { hint: opt.hint } : {},
|
|
188
|
-
...opt.enum ? { enum: opt.enum } : {},
|
|
189
|
-
...opt.required ? { required: opt.required } : {}
|
|
190
|
-
};
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
/** Prints formatted help output for a command using its `CommandDefinition`. */
|
|
194
|
-
function renderHelp(def, parentName) {
|
|
195
|
-
const schema = getCommandSchema([def])[0];
|
|
196
|
-
const programName = parentName ? `${parentName} ${schema.name}` : schema.name;
|
|
197
|
-
const argsPart = schema.arguments?.length ? ` ${schema.arguments.join(" ")}` : "";
|
|
198
|
-
const subCmdPart = schema.subCommands.length ? " <command>" : "";
|
|
199
|
-
console.log(`\n${(0, node_util.styleText)("bold", "Usage:")} ${programName}${argsPart}${subCmdPart} [options]\n`);
|
|
200
|
-
if (schema.description) console.log(` ${schema.description}\n`);
|
|
201
|
-
if (schema.subCommands.length) {
|
|
202
|
-
console.log((0, node_util.styleText)("bold", "Commands:"));
|
|
203
|
-
for (const sub of schema.subCommands) console.log(` ${(0, node_util.styleText)("cyan", sub.name.padEnd(16))}${sub.description}`);
|
|
204
|
-
console.log();
|
|
205
|
-
}
|
|
206
|
-
const options = [...schema.options, {
|
|
207
|
-
name: "help",
|
|
208
|
-
flags: "-h, --help",
|
|
209
|
-
type: "boolean",
|
|
210
|
-
description: "Show help"
|
|
211
|
-
}];
|
|
212
|
-
console.log((0, node_util.styleText)("bold", "Options:"));
|
|
213
|
-
for (const opt of options) {
|
|
214
|
-
const flags = (0, node_util.styleText)("cyan", opt.flags.padEnd(30));
|
|
215
|
-
const defaultPart = opt.default !== void 0 ? (0, node_util.styleText)("dim", ` (default: ${opt.default})`) : "";
|
|
216
|
-
console.log(` ${flags}${opt.description}${defaultPart}`);
|
|
217
|
-
}
|
|
218
|
-
console.log();
|
|
219
|
-
}
|
|
220
|
-
function buildParseOptions(def) {
|
|
221
|
-
const result = { help: {
|
|
222
|
-
type: "boolean",
|
|
223
|
-
short: "h"
|
|
224
|
-
} };
|
|
225
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) result[name] = {
|
|
226
|
-
type: opt.type,
|
|
227
|
-
...opt.short ? { short: opt.short } : {},
|
|
228
|
-
...opt.default !== void 0 ? { default: opt.default } : {}
|
|
229
|
-
};
|
|
230
|
-
return result;
|
|
231
|
-
}
|
|
232
|
-
async function runCommand(def, argv, parentName) {
|
|
233
|
-
const parseOptions = buildParseOptions(def);
|
|
234
|
-
let parsed;
|
|
235
|
-
try {
|
|
236
|
-
const result = (0, node_util.parseArgs)({
|
|
237
|
-
args: argv,
|
|
238
|
-
options: parseOptions,
|
|
239
|
-
allowPositionals: true,
|
|
240
|
-
strict: false
|
|
241
|
-
});
|
|
242
|
-
parsed = {
|
|
243
|
-
values: result.values,
|
|
244
|
-
positionals: result.positionals
|
|
245
|
-
};
|
|
246
|
-
} catch {
|
|
247
|
-
renderHelp(def, parentName);
|
|
248
|
-
process.exit(1);
|
|
249
|
-
}
|
|
250
|
-
if (parsed.values["help"]) {
|
|
251
|
-
renderHelp(def, parentName);
|
|
252
|
-
process.exit(0);
|
|
253
|
-
}
|
|
254
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) if (opt.required && parsed.values[name] === void 0) {
|
|
255
|
-
console.error((0, node_util.styleText)("red", `Error: --${name} is required`));
|
|
256
|
-
renderHelp(def, parentName);
|
|
257
|
-
process.exit(1);
|
|
258
|
-
}
|
|
259
|
-
if (!def.run) {
|
|
260
|
-
renderHelp(def, parentName);
|
|
261
|
-
process.exit(0);
|
|
262
|
-
}
|
|
263
|
-
try {
|
|
264
|
-
await def.run(parsed);
|
|
265
|
-
} catch (err) {
|
|
266
|
-
console.error((0, node_util.styleText)("red", `Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
267
|
-
renderHelp(def, parentName);
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
function printRootHelp(programName, version, defs) {
|
|
272
|
-
console.log(`\n${(0, node_util.styleText)("bold", "Usage:")} ${programName} <command> [options]\n`);
|
|
273
|
-
console.log(` Kubb generation — v${version}\n`);
|
|
274
|
-
console.log((0, node_util.styleText)("bold", "Commands:"));
|
|
275
|
-
for (const def of defs) console.log(` ${(0, node_util.styleText)("cyan", def.name.padEnd(16))}${def.description}`);
|
|
276
|
-
console.log();
|
|
277
|
-
console.log((0, node_util.styleText)("bold", "Options:"));
|
|
278
|
-
console.log(` ${(0, node_util.styleText)("cyan", "-v, --version".padEnd(30))}Show version number`);
|
|
279
|
-
console.log(` ${(0, node_util.styleText)("cyan", "-h, --help".padEnd(30))}Show help`);
|
|
280
|
-
console.log();
|
|
281
|
-
console.log(`Run ${(0, node_util.styleText)("cyan", `${programName} <command> --help`)} for command-specific help.\n`);
|
|
282
|
-
}
|
|
283
|
-
defineCLIAdapter({
|
|
284
|
-
renderHelp(def, parentName) {
|
|
285
|
-
renderHelp(def, parentName);
|
|
286
|
-
},
|
|
287
|
-
async run(defs, argv, opts) {
|
|
288
|
-
const { programName, defaultCommandName, version } = opts;
|
|
289
|
-
const args = argv.length >= 2 && argv[0]?.includes("node") ? argv.slice(2) : argv;
|
|
290
|
-
if (args[0] === "--version" || args[0] === "-v") {
|
|
291
|
-
console.log(version);
|
|
292
|
-
process.exit(0);
|
|
293
|
-
}
|
|
294
|
-
if (args[0] === "--help" || args[0] === "-h") {
|
|
295
|
-
printRootHelp(programName, version, defs);
|
|
296
|
-
process.exit(0);
|
|
297
|
-
}
|
|
298
|
-
if (args.length === 0) {
|
|
299
|
-
const defaultDef = defs.find((d) => d.name === defaultCommandName);
|
|
300
|
-
if (defaultDef?.run) await runCommand(defaultDef, [], programName);
|
|
301
|
-
else printRootHelp(programName, version, defs);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
const [first, ...rest] = args;
|
|
305
|
-
const isKnownSubcommand = defs.some((d) => d.name === first);
|
|
306
|
-
let def;
|
|
307
|
-
let commandArgv;
|
|
308
|
-
let parentName;
|
|
309
|
-
if (isKnownSubcommand) {
|
|
310
|
-
def = defs.find((d) => d.name === first);
|
|
311
|
-
commandArgv = rest;
|
|
312
|
-
parentName = programName;
|
|
313
|
-
} else {
|
|
314
|
-
def = defs.find((d) => d.name === defaultCommandName);
|
|
315
|
-
commandArgv = args;
|
|
316
|
-
parentName = programName;
|
|
317
|
-
}
|
|
318
|
-
if (!def) {
|
|
319
|
-
console.error(`Unknown command: ${first}`);
|
|
320
|
-
printRootHelp(programName, version, defs);
|
|
321
|
-
process.exit(1);
|
|
322
|
-
}
|
|
323
|
-
if (def.subCommands?.length) {
|
|
324
|
-
const [subName, ...subRest] = commandArgv;
|
|
325
|
-
const subDef = def.subCommands.find((s) => s.name === subName);
|
|
326
|
-
if (subName === "--help" || subName === "-h") {
|
|
327
|
-
renderHelp(def, parentName);
|
|
328
|
-
process.exit(0);
|
|
329
|
-
}
|
|
330
|
-
if (!subDef) {
|
|
331
|
-
renderHelp(def, parentName);
|
|
332
|
-
process.exit(subName ? 1 : 0);
|
|
333
|
-
}
|
|
334
|
-
await runCommand(subDef, subRest, `${parentName} ${def.name}`);
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
await runCommand(def, commandArgv, parentName);
|
|
338
|
-
}
|
|
339
|
-
});
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region ../../internals/utils/src/time.ts
|
|
340
221
|
/**
|
|
341
|
-
* Calculates elapsed time in milliseconds from a high-resolution start time.
|
|
342
|
-
* Rounds to 2 decimal places
|
|
222
|
+
* Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
|
|
223
|
+
* Rounds to 2 decimal places for sub-millisecond precision without noise.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```ts
|
|
227
|
+
* const start = process.hrtime()
|
|
228
|
+
* doWork()
|
|
229
|
+
* getElapsedMs(start) // 42.35
|
|
230
|
+
* ```
|
|
343
231
|
*/
|
|
344
232
|
function getElapsedMs(hrStart) {
|
|
345
233
|
const [seconds, nanoseconds] = process.hrtime(hrStart);
|
|
@@ -347,39 +235,22 @@ function getElapsedMs(hrStart) {
|
|
|
347
235
|
return Math.round(ms * 100) / 100;
|
|
348
236
|
}
|
|
349
237
|
/**
|
|
350
|
-
* Converts a millisecond duration into a human-readable string.
|
|
351
|
-
*
|
|
238
|
+
* Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```ts
|
|
242
|
+
* formatMs(250) // '250ms'
|
|
243
|
+
* formatMs(1500) // '1.50s'
|
|
244
|
+
* formatMs(90000) // '1m 30.0s'
|
|
245
|
+
* ```
|
|
352
246
|
*/
|
|
353
247
|
function formatMs(ms) {
|
|
354
248
|
if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
|
|
355
249
|
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
356
250
|
return `${Math.round(ms)}ms`;
|
|
357
251
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
* Falls back to `255` for any channel that cannot be parsed.
|
|
361
|
-
*/
|
|
362
|
-
function parseHex(color) {
|
|
363
|
-
const int = Number.parseInt(color.replace("#", ""), 16);
|
|
364
|
-
return Number.isNaN(int) ? {
|
|
365
|
-
r: 255,
|
|
366
|
-
g: 255,
|
|
367
|
-
b: 255
|
|
368
|
-
} : {
|
|
369
|
-
r: int >> 16 & 255,
|
|
370
|
-
g: int >> 8 & 255,
|
|
371
|
-
b: int & 255
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Returns a function that wraps a string in a 24-bit ANSI true-color escape sequence
|
|
376
|
-
* for the given hex color.
|
|
377
|
-
*/
|
|
378
|
-
function hex(color) {
|
|
379
|
-
const { r, g, b } = parseHex(color);
|
|
380
|
-
return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
|
|
381
|
-
}
|
|
382
|
-
hex("#F55A17"), hex("#F5A217"), hex("#F58517"), hex("#B45309"), hex("#FFFFFF"), hex("#adadc6"), hex("#FDA4AF");
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region ../../internals/utils/src/fs.ts
|
|
383
254
|
/**
|
|
384
255
|
* Converts all backslashes to forward slashes.
|
|
385
256
|
* Extended-length Windows paths (`\\?\...`) are left unchanged.
|
|
@@ -389,8 +260,14 @@ function toSlash(p) {
|
|
|
389
260
|
return p.replaceAll("\\", "/");
|
|
390
261
|
}
|
|
391
262
|
/**
|
|
392
|
-
* Returns the relative path from `rootDir` to `filePath`, always using
|
|
393
|
-
*
|
|
263
|
+
* Returns the relative path from `rootDir` to `filePath`, always using forward slashes
|
|
264
|
+
* and prefixed with `./` when not already traversing upward.
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```ts
|
|
268
|
+
* getRelativePath('/src/components', '/src/components/Button.tsx') // './Button.tsx'
|
|
269
|
+
* getRelativePath('/src/components', '/src/utils/helpers.ts') // '../utils/helpers.ts'
|
|
270
|
+
* ```
|
|
394
271
|
*/
|
|
395
272
|
function getRelativePath(rootDir, filePath) {
|
|
396
273
|
if (!rootDir || !filePath) throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ""} ${filePath || ""}`);
|
|
@@ -400,35 +277,54 @@ function getRelativePath(rootDir, filePath) {
|
|
|
400
277
|
/**
|
|
401
278
|
* Resolves to `true` when the file or directory at `path` exists.
|
|
402
279
|
* Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```ts
|
|
283
|
+
* if (await exists('./kubb.config.ts')) {
|
|
284
|
+
* const content = await read('./kubb.config.ts')
|
|
285
|
+
* }
|
|
286
|
+
* ```
|
|
403
287
|
*/
|
|
404
288
|
async function exists(path) {
|
|
405
289
|
if (typeof Bun !== "undefined") return Bun.file(path).exists();
|
|
406
290
|
return (0, node_fs_promises.access)(path).then(() => true, () => false);
|
|
407
291
|
}
|
|
408
|
-
/**
|
|
292
|
+
/**
|
|
293
|
+
* Synchronous counterpart of `read`.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* const source = readSync('./src/Pet.ts')
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
409
300
|
function readSync(path) {
|
|
410
301
|
return (0, node_fs.readFileSync)(path, { encoding: "utf8" });
|
|
411
302
|
}
|
|
412
303
|
/**
|
|
413
304
|
* Writes `data` to `path`, trimming leading/trailing whitespace before saving.
|
|
414
|
-
* Skips the write
|
|
415
|
-
* identical to what is already on disk.
|
|
305
|
+
* Skips the write when the trimmed content is empty or identical to what is already on disk.
|
|
416
306
|
* Creates any missing parent directories automatically.
|
|
417
|
-
* When `sanity` is `true`, re-reads the file after writing and throws if the
|
|
418
|
-
*
|
|
307
|
+
* When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```ts
|
|
311
|
+
* await write('./src/Pet.ts', source) // writes and returns trimmed content
|
|
312
|
+
* await write('./src/Pet.ts', source) // null — file unchanged
|
|
313
|
+
* await write('./src/Pet.ts', ' ') // null — empty content skipped
|
|
314
|
+
* ```
|
|
419
315
|
*/
|
|
420
316
|
async function write(path, data, options = {}) {
|
|
421
317
|
const trimmed = data.trim();
|
|
422
|
-
if (trimmed === "") return
|
|
318
|
+
if (trimmed === "") return null;
|
|
423
319
|
const resolved = (0, node_path.resolve)(path);
|
|
424
320
|
if (typeof Bun !== "undefined") {
|
|
425
321
|
const file = Bun.file(resolved);
|
|
426
|
-
if ((await file.exists() ? await file.text() : null) === trimmed) return
|
|
322
|
+
if ((await file.exists() ? await file.text() : null) === trimmed) return null;
|
|
427
323
|
await Bun.write(resolved, trimmed);
|
|
428
324
|
return trimmed;
|
|
429
325
|
}
|
|
430
326
|
try {
|
|
431
|
-
if (await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" }) === trimmed) return
|
|
327
|
+
if (await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" }) === trimmed) return null;
|
|
432
328
|
} catch {}
|
|
433
329
|
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(resolved), { recursive: true });
|
|
434
330
|
await (0, node_fs_promises.writeFile)(resolved, trimmed, { encoding: "utf-8" });
|
|
@@ -439,16 +335,32 @@ async function write(path, data, options = {}) {
|
|
|
439
335
|
}
|
|
440
336
|
return trimmed;
|
|
441
337
|
}
|
|
442
|
-
/**
|
|
338
|
+
/**
|
|
339
|
+
* Recursively removes `path`. Silently succeeds when `path` does not exist.
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```ts
|
|
343
|
+
* await clean('./dist')
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
443
346
|
async function clean(path) {
|
|
444
347
|
return (0, node_fs_promises.rm)(path, {
|
|
445
348
|
recursive: true,
|
|
446
349
|
force: true
|
|
447
350
|
});
|
|
448
351
|
}
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region ../../internals/utils/src/names.ts
|
|
449
354
|
/**
|
|
450
355
|
* Registers `originalName` in `data` without altering the returned name.
|
|
451
|
-
* Use
|
|
356
|
+
* Use when you need to track usage frequency but always emit the original identifier.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```ts
|
|
360
|
+
* const seen: Record<string, number> = {}
|
|
361
|
+
* setUniqueName('Foo', seen) // 'Foo' (seen = { Foo: 1 })
|
|
362
|
+
* setUniqueName('Foo', seen) // 'Foo' (seen = { Foo: 2 })
|
|
363
|
+
* ```
|
|
452
364
|
*/
|
|
453
365
|
function setUniqueName(originalName, data) {
|
|
454
366
|
let used = data[originalName] || 0;
|
|
@@ -459,10 +371,21 @@ function setUniqueName(originalName, data) {
|
|
|
459
371
|
data[originalName] = 1;
|
|
460
372
|
return originalName;
|
|
461
373
|
}
|
|
462
|
-
|
|
374
|
+
//#endregion
|
|
375
|
+
//#region ../../internals/utils/src/promise.ts
|
|
376
|
+
/** Returns `true` when `result` is a rejected `Promise.allSettled` result with a typed `reason`.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```ts
|
|
380
|
+
* const results = await Promise.allSettled([p1, p2])
|
|
381
|
+
* results.filter(isPromiseRejectedResult<Error>).map((r) => r.reason.message)
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
463
384
|
function isPromiseRejectedResult(result) {
|
|
464
385
|
return result.status === "rejected";
|
|
465
386
|
}
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
466
389
|
/**
|
|
467
390
|
* JavaScript and Java reserved words.
|
|
468
391
|
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
@@ -551,8 +474,14 @@ const reservedWords = new Set([
|
|
|
551
474
|
"valueOf"
|
|
552
475
|
]);
|
|
553
476
|
/**
|
|
554
|
-
* Prefixes
|
|
555
|
-
*
|
|
477
|
+
* Prefixes `word` with `_` when it is a reserved JavaScript/Java identifier or starts with a digit.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```ts
|
|
481
|
+
* transformReservedWord('class') // '_class'
|
|
482
|
+
* transformReservedWord('42foo') // '_42foo'
|
|
483
|
+
* transformReservedWord('status') // 'status'
|
|
484
|
+
* ```
|
|
556
485
|
*/
|
|
557
486
|
function transformReservedWord(word) {
|
|
558
487
|
const firstChar = word.charCodeAt(0);
|
|
@@ -561,6 +490,13 @@ function transformReservedWord(word) {
|
|
|
561
490
|
}
|
|
562
491
|
/**
|
|
563
492
|
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* ```ts
|
|
496
|
+
* isValidVarName('status') // true
|
|
497
|
+
* isValidVarName('class') // false (reserved word)
|
|
498
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
499
|
+
* ```
|
|
564
500
|
*/
|
|
565
501
|
function isValidVarName(name) {
|
|
566
502
|
try {
|
|
@@ -570,6 +506,8 @@ function isValidVarName(name) {
|
|
|
570
506
|
}
|
|
571
507
|
return true;
|
|
572
508
|
}
|
|
509
|
+
//#endregion
|
|
510
|
+
//#region ../../internals/utils/src/urlPath.ts
|
|
573
511
|
/**
|
|
574
512
|
* Parses and transforms an OpenAPI/Swagger path string into various URL formats.
|
|
575
513
|
*
|
|
@@ -579,18 +517,33 @@ function isValidVarName(name) {
|
|
|
579
517
|
* p.template // '`/pet/${petId}`'
|
|
580
518
|
*/
|
|
581
519
|
var URLPath = class {
|
|
582
|
-
/**
|
|
520
|
+
/**
|
|
521
|
+
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
522
|
+
*/
|
|
583
523
|
path;
|
|
584
524
|
#options;
|
|
585
525
|
constructor(path, options = {}) {
|
|
586
526
|
this.path = path;
|
|
587
527
|
this.#options = options;
|
|
588
528
|
}
|
|
589
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
529
|
+
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
530
|
+
*
|
|
531
|
+
* @example
|
|
532
|
+
* ```ts
|
|
533
|
+
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
534
|
+
* ```
|
|
535
|
+
*/
|
|
590
536
|
get URL() {
|
|
591
537
|
return this.toURLPath();
|
|
592
538
|
}
|
|
593
|
-
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
539
|
+
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```ts
|
|
543
|
+
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
544
|
+
* new URLPath('/pet/{petId}').isURL // false
|
|
545
|
+
* ```
|
|
546
|
+
*/
|
|
594
547
|
get isURL() {
|
|
595
548
|
try {
|
|
596
549
|
return !!new URL(this.path).href;
|
|
@@ -608,11 +561,25 @@ var URLPath = class {
|
|
|
608
561
|
get template() {
|
|
609
562
|
return this.toTemplateString();
|
|
610
563
|
}
|
|
611
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
564
|
+
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
565
|
+
*
|
|
566
|
+
* @example
|
|
567
|
+
* ```ts
|
|
568
|
+
* new URLPath('/pet/{petId}').object
|
|
569
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
612
572
|
get object() {
|
|
613
573
|
return this.toObject();
|
|
614
574
|
}
|
|
615
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
575
|
+
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
576
|
+
*
|
|
577
|
+
* @example
|
|
578
|
+
* ```ts
|
|
579
|
+
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
580
|
+
* new URLPath('/pet').params // undefined
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
616
583
|
get params() {
|
|
617
584
|
return this.getParams();
|
|
618
585
|
}
|
|
@@ -620,7 +587,9 @@ var URLPath = class {
|
|
|
620
587
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
621
588
|
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
622
589
|
}
|
|
623
|
-
/**
|
|
590
|
+
/**
|
|
591
|
+
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
592
|
+
*/
|
|
624
593
|
#eachParam(fn) {
|
|
625
594
|
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
626
595
|
const raw = match[1];
|
|
@@ -657,6 +626,12 @@ var URLPath = class {
|
|
|
657
626
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
658
627
|
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
659
628
|
* Returns `undefined` when no path parameters are found.
|
|
629
|
+
*
|
|
630
|
+
* @example
|
|
631
|
+
* ```ts
|
|
632
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
633
|
+
* // { petId: 'petId', tagId: 'tagId' }
|
|
634
|
+
* ```
|
|
660
635
|
*/
|
|
661
636
|
getParams(replacer) {
|
|
662
637
|
const params = {};
|
|
@@ -666,7 +641,13 @@ var URLPath = class {
|
|
|
666
641
|
});
|
|
667
642
|
return Object.keys(params).length > 0 ? params : void 0;
|
|
668
643
|
}
|
|
669
|
-
/** Converts the OpenAPI path to Express-style colon syntax
|
|
644
|
+
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
645
|
+
*
|
|
646
|
+
* @example
|
|
647
|
+
* ```ts
|
|
648
|
+
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
649
|
+
* ```
|
|
650
|
+
*/
|
|
670
651
|
toURLPath() {
|
|
671
652
|
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
672
653
|
}
|
|
@@ -684,10 +665,27 @@ function isInputPath(config) {
|
|
|
684
665
|
}
|
|
685
666
|
//#endregion
|
|
686
667
|
//#region src/constants.ts
|
|
668
|
+
/**
|
|
669
|
+
* Base URL for the Kubb Studio web app.
|
|
670
|
+
*/
|
|
687
671
|
const DEFAULT_STUDIO_URL = "https://studio.kubb.dev";
|
|
672
|
+
/**
|
|
673
|
+
* File name used for generated barrel (index) files.
|
|
674
|
+
*/
|
|
688
675
|
const BARREL_FILENAME = "index.ts";
|
|
676
|
+
/**
|
|
677
|
+
* Default banner style written at the top of every generated file.
|
|
678
|
+
*/
|
|
689
679
|
const DEFAULT_BANNER = "simple";
|
|
680
|
+
/**
|
|
681
|
+
* Default file-extension mapping used when no explicit mapping is configured.
|
|
682
|
+
*/
|
|
690
683
|
const DEFAULT_EXTENSION = { ".ts": ".ts" };
|
|
684
|
+
/**
|
|
685
|
+
* Numeric log-level thresholds used internally to compare verbosity.
|
|
686
|
+
*
|
|
687
|
+
* Higher numbers are more verbose.
|
|
688
|
+
*/
|
|
691
689
|
const logLevel = {
|
|
692
690
|
silent: Number.NEGATIVE_INFINITY,
|
|
693
691
|
error: 0,
|
|
@@ -696,6 +694,13 @@ const logLevel = {
|
|
|
696
694
|
verbose: 4,
|
|
697
695
|
debug: 5
|
|
698
696
|
};
|
|
697
|
+
/**
|
|
698
|
+
* CLI command descriptors for each supported linter.
|
|
699
|
+
*
|
|
700
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
701
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
702
|
+
* the linter is not found.
|
|
703
|
+
*/
|
|
699
704
|
const linters = {
|
|
700
705
|
eslint: {
|
|
701
706
|
command: "eslint",
|
|
@@ -717,6 +722,13 @@ const linters = {
|
|
|
717
722
|
errorMessage: "Oxlint not found"
|
|
718
723
|
}
|
|
719
724
|
};
|
|
725
|
+
/**
|
|
726
|
+
* CLI command descriptors for each supported code formatter.
|
|
727
|
+
*
|
|
728
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
729
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
730
|
+
* the formatter is not found.
|
|
731
|
+
*/
|
|
720
732
|
const formatters = {
|
|
721
733
|
prettier: {
|
|
722
734
|
command: "prettier",
|
|
@@ -916,7 +928,11 @@ function validateConcurrency(concurrency) {
|
|
|
916
928
|
//#endregion
|
|
917
929
|
//#region src/utils/executeStrategies.ts
|
|
918
930
|
/**
|
|
919
|
-
*
|
|
931
|
+
* Runs promise functions in sequence, threading each result into the next call.
|
|
932
|
+
*
|
|
933
|
+
* - Each function receives the accumulated state from the previous call.
|
|
934
|
+
* - Skips functions that return a falsy value (acts as a no-op for that step).
|
|
935
|
+
* - Returns an array of all individual results.
|
|
920
936
|
*/
|
|
921
937
|
function hookSeq(promises) {
|
|
922
938
|
return promises.filter(Boolean).reduce((promise, func) => {
|
|
@@ -929,7 +945,10 @@ function hookSeq(promises) {
|
|
|
929
945
|
}, Promise.resolve([]));
|
|
930
946
|
}
|
|
931
947
|
/**
|
|
932
|
-
*
|
|
948
|
+
* Runs promise functions in sequence and returns the first non-null result.
|
|
949
|
+
*
|
|
950
|
+
* - Stops as soon as `nullCheck` passes for a result (default: `!== null`).
|
|
951
|
+
* - Subsequent functions are skipped once a match is found.
|
|
933
952
|
*/
|
|
934
953
|
function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
935
954
|
let promise = Promise.resolve(null);
|
|
@@ -940,7 +959,10 @@ function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
|
940
959
|
return promise;
|
|
941
960
|
}
|
|
942
961
|
/**
|
|
943
|
-
* Runs
|
|
962
|
+
* Runs promise functions concurrently and returns all settled results.
|
|
963
|
+
*
|
|
964
|
+
* - Limits simultaneous executions to `concurrency` (default: unlimited).
|
|
965
|
+
* - Uses `Promise.allSettled` so individual failures do not cancel other tasks.
|
|
944
966
|
*/
|
|
945
967
|
function hookParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
946
968
|
const limit = pLimit(concurrency);
|
|
@@ -1447,11 +1469,14 @@ const fsStorage = createStorage(() => ({
|
|
|
1447
1469
|
}));
|
|
1448
1470
|
//#endregion
|
|
1449
1471
|
//#region package.json
|
|
1450
|
-
var version = "5.0.0-alpha.
|
|
1472
|
+
var version = "5.0.0-alpha.18";
|
|
1451
1473
|
//#endregion
|
|
1452
1474
|
//#region src/utils/diagnostics.ts
|
|
1453
1475
|
/**
|
|
1454
|
-
*
|
|
1476
|
+
* Returns a snapshot of the current runtime environment.
|
|
1477
|
+
*
|
|
1478
|
+
* Useful for attaching context to debug logs and error reports so that
|
|
1479
|
+
* issues can be reproduced without manual information gathering.
|
|
1455
1480
|
*/
|
|
1456
1481
|
function getDiagnosticInfo() {
|
|
1457
1482
|
return {
|
|
@@ -1464,6 +1489,17 @@ function getDiagnosticInfo() {
|
|
|
1464
1489
|
}
|
|
1465
1490
|
//#endregion
|
|
1466
1491
|
//#region src/build.ts
|
|
1492
|
+
/**
|
|
1493
|
+
* Initializes all Kubb infrastructure for a build without executing any plugins.
|
|
1494
|
+
*
|
|
1495
|
+
* - Validates the input path (when applicable).
|
|
1496
|
+
* - Applies config defaults (`root`, `output.*`, `devtools`).
|
|
1497
|
+
* - Creates the Fabric instance and wires storage, format, and lint hooks.
|
|
1498
|
+
* - Runs the adapter (if configured) to produce the universal `RootNode`.
|
|
1499
|
+
*
|
|
1500
|
+
* Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
|
|
1501
|
+
* via the `overrides` argument to reuse the same infrastructure across multiple runs.
|
|
1502
|
+
*/
|
|
1467
1503
|
async function setup(options) {
|
|
1468
1504
|
const { config: userConfig, events = new AsyncEventEmitter() } = options;
|
|
1469
1505
|
const sources = /* @__PURE__ */ new Map();
|
|
@@ -1590,6 +1626,12 @@ async function setup(options) {
|
|
|
1590
1626
|
sources
|
|
1591
1627
|
};
|
|
1592
1628
|
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Runs a full Kubb build and throws on any error or plugin failure.
|
|
1631
|
+
*
|
|
1632
|
+
* Internally delegates to {@link safeBuild} and rethrows collected errors.
|
|
1633
|
+
* Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
|
|
1634
|
+
*/
|
|
1593
1635
|
async function build(options, overrides) {
|
|
1594
1636
|
const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides);
|
|
1595
1637
|
if (error) throw error;
|
|
@@ -1607,6 +1649,16 @@ async function build(options, overrides) {
|
|
|
1607
1649
|
sources
|
|
1608
1650
|
};
|
|
1609
1651
|
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Runs a full Kubb build and captures errors instead of throwing.
|
|
1654
|
+
*
|
|
1655
|
+
* - Installs each plugin in order, recording failures in `failedPlugins`.
|
|
1656
|
+
* - Generates the root barrel file when `output.barrelType` is set.
|
|
1657
|
+
* - Writes all files through Fabric.
|
|
1658
|
+
*
|
|
1659
|
+
* Returns a {@link BuildOutput} even on failure — inspect `error` and
|
|
1660
|
+
* `failedPlugins` to determine whether the build succeeded.
|
|
1661
|
+
*/
|
|
1610
1662
|
async function safeBuild(options, overrides) {
|
|
1611
1663
|
const { fabric, driver, events, sources } = overrides ? overrides : await setup(options);
|
|
1612
1664
|
const failedPlugins = /* @__PURE__ */ new Set();
|
|
@@ -2170,14 +2222,10 @@ var FunctionParams = class FunctionParams {
|
|
|
2170
2222
|
//#endregion
|
|
2171
2223
|
//#region src/utils/formatters.ts
|
|
2172
2224
|
/**
|
|
2173
|
-
*
|
|
2225
|
+
* Returns `true` when the given formatter is installed and callable.
|
|
2174
2226
|
*
|
|
2175
|
-
*
|
|
2176
|
-
*
|
|
2177
|
-
*
|
|
2178
|
-
* @remarks
|
|
2179
|
-
* This function checks availability by running `<formatter> --version` command.
|
|
2180
|
-
* All supported formatters (biome, prettier, oxfmt) implement the --version flag.
|
|
2227
|
+
* Availability is detected by running `<formatter> --version` and checking
|
|
2228
|
+
* that the process exits without error.
|
|
2181
2229
|
*/
|
|
2182
2230
|
async function isFormatterAvailable(formatter) {
|
|
2183
2231
|
try {
|
|
@@ -2188,22 +2236,16 @@ async function isFormatterAvailable(formatter) {
|
|
|
2188
2236
|
}
|
|
2189
2237
|
}
|
|
2190
2238
|
/**
|
|
2191
|
-
*
|
|
2192
|
-
*
|
|
2193
|
-
* @returns Promise that resolves to the first available formatter or undefined if none are found
|
|
2239
|
+
* Detects the first available code formatter on the current system.
|
|
2194
2240
|
*
|
|
2195
|
-
*
|
|
2196
|
-
*
|
|
2197
|
-
* Uses the `--version` flag to detect if each formatter command is available.
|
|
2198
|
-
* This is a reliable method as all supported formatters implement this flag.
|
|
2241
|
+
* - Checks in preference order: `biome`, `oxfmt`, `prettier`.
|
|
2242
|
+
* - Returns `null` when none are found.
|
|
2199
2243
|
*
|
|
2200
2244
|
* @example
|
|
2201
|
-
* ```
|
|
2245
|
+
* ```ts
|
|
2202
2246
|
* const formatter = await detectFormatter()
|
|
2203
2247
|
* if (formatter) {
|
|
2204
2248
|
* console.log(`Using ${formatter} for formatting`)
|
|
2205
|
-
* } else {
|
|
2206
|
-
* console.log('No formatter found')
|
|
2207
2249
|
* }
|
|
2208
2250
|
* ```
|
|
2209
2251
|
*/
|
|
@@ -2214,9 +2256,19 @@ async function detectFormatter() {
|
|
|
2214
2256
|
"prettier"
|
|
2215
2257
|
]);
|
|
2216
2258
|
for (const formatter of formatterNames) if (await isFormatterAvailable(formatter)) return formatter;
|
|
2259
|
+
return null;
|
|
2217
2260
|
}
|
|
2218
2261
|
//#endregion
|
|
2219
2262
|
//#region src/utils/TreeNode.ts
|
|
2263
|
+
/**
|
|
2264
|
+
* Tree structure used to build per-directory barrel (`index.ts`) files from a
|
|
2265
|
+
* flat list of generated {@link KubbFile.File} entries.
|
|
2266
|
+
*
|
|
2267
|
+
* Each node represents either a directory or a file within the output tree.
|
|
2268
|
+
* Use {@link TreeNode.build} to construct a root node from a file list, then
|
|
2269
|
+
* traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
|
|
2270
|
+
* `*Deep` helpers.
|
|
2271
|
+
*/
|
|
2220
2272
|
var TreeNode = class TreeNode {
|
|
2221
2273
|
data;
|
|
2222
2274
|
parent;
|
|
@@ -2232,10 +2284,18 @@ var TreeNode = class TreeNode {
|
|
|
2232
2284
|
this.children.push(child);
|
|
2233
2285
|
return child;
|
|
2234
2286
|
}
|
|
2287
|
+
/**
|
|
2288
|
+
* Returns the root ancestor of this node, walking up via `parent` links.
|
|
2289
|
+
*/
|
|
2235
2290
|
get root() {
|
|
2236
2291
|
if (!this.parent) return this;
|
|
2237
2292
|
return this.parent.root;
|
|
2238
2293
|
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Returns all leaf descendants (nodes with no children) of this node.
|
|
2296
|
+
*
|
|
2297
|
+
* Results are cached after the first traversal.
|
|
2298
|
+
*/
|
|
2239
2299
|
get leaves() {
|
|
2240
2300
|
if (!this.children || this.children.length === 0) return [this];
|
|
2241
2301
|
if (this.#cachedLeaves) return this.#cachedLeaves;
|
|
@@ -2266,6 +2326,12 @@ var TreeNode = class TreeNode {
|
|
|
2266
2326
|
if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
|
|
2267
2327
|
return this.leaves.map(callback);
|
|
2268
2328
|
}
|
|
2329
|
+
/**
|
|
2330
|
+
* Builds a {@link TreeNode} tree from a flat list of files.
|
|
2331
|
+
*
|
|
2332
|
+
* - Filters to files under `root` (when provided) and skips `.json` files.
|
|
2333
|
+
* - Returns `null` when no files match.
|
|
2334
|
+
*/
|
|
2269
2335
|
static build(files, root) {
|
|
2270
2336
|
try {
|
|
2271
2337
|
const filteredTree = buildDirectoryTree(files, root);
|
|
@@ -2381,6 +2447,14 @@ function trimExtName(text) {
|
|
|
2381
2447
|
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
2382
2448
|
return text;
|
|
2383
2449
|
}
|
|
2450
|
+
/**
|
|
2451
|
+
* Generates `index.ts` barrel files for all directories under `root/output.path`.
|
|
2452
|
+
*
|
|
2453
|
+
* - Returns an empty array when `type` is falsy or `'propagate'`.
|
|
2454
|
+
* - Skips generation when the output path itself ends with `index` (already a barrel).
|
|
2455
|
+
* - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
|
|
2456
|
+
* - Attaches `meta` to each barrel file for downstream plugin identification.
|
|
2457
|
+
*/
|
|
2384
2458
|
async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
2385
2459
|
if (!type || type === "propagate") return [];
|
|
2386
2460
|
const pathToBuildFrom = (0, node_path.join)(root, output.path);
|
|
@@ -2407,7 +2481,11 @@ async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
|
2407
2481
|
//#endregion
|
|
2408
2482
|
//#region src/utils/getConfigs.ts
|
|
2409
2483
|
/**
|
|
2410
|
-
*
|
|
2484
|
+
* Resolves a {@link ConfigInput} into a normalized array of {@link Config} objects.
|
|
2485
|
+
*
|
|
2486
|
+
* - Awaits the config when it is a `Promise`.
|
|
2487
|
+
* - Calls the factory function with `args` when the config is a function.
|
|
2488
|
+
* - Wraps a single config object in an array for uniform downstream handling.
|
|
2411
2489
|
*/
|
|
2412
2490
|
async function getConfigs(config, args) {
|
|
2413
2491
|
const resolved = await (typeof config === "function" ? config(args) : config);
|
|
@@ -2426,6 +2504,13 @@ function mergeResolvers(...resolvers) {
|
|
|
2426
2504
|
}
|
|
2427
2505
|
//#endregion
|
|
2428
2506
|
//#region src/utils/getPreset.ts
|
|
2507
|
+
/**
|
|
2508
|
+
* Resolves a named preset into merged resolvers and transformers.
|
|
2509
|
+
*
|
|
2510
|
+
* - Merges the preset's resolvers on top of the first (default) resolver to produce `baseResolver`.
|
|
2511
|
+
* - Merges any additional user-supplied resolvers on top of that to produce the final `resolver`.
|
|
2512
|
+
* - Concatenates preset transformers before user-supplied transformers.
|
|
2513
|
+
*/
|
|
2429
2514
|
function getPreset(params) {
|
|
2430
2515
|
const { preset: presetName, presets, resolvers, transformers: userTransformers } = params;
|
|
2431
2516
|
const [defaultResolver, ...userResolvers] = resolvers;
|
|
@@ -2440,6 +2525,12 @@ function getPreset(params) {
|
|
|
2440
2525
|
}
|
|
2441
2526
|
//#endregion
|
|
2442
2527
|
//#region src/utils/linters.ts
|
|
2528
|
+
/**
|
|
2529
|
+
* Returns `true` when the given linter is installed and callable.
|
|
2530
|
+
*
|
|
2531
|
+
* Availability is detected by running `<linter> --version` and checking
|
|
2532
|
+
* that the process exits without error.
|
|
2533
|
+
*/
|
|
2443
2534
|
async function isLinterAvailable(linter) {
|
|
2444
2535
|
try {
|
|
2445
2536
|
await (0, tinyexec.x)(linter, ["--version"], { nodeOptions: { stdio: "ignore" } });
|
|
@@ -2448,6 +2539,20 @@ async function isLinterAvailable(linter) {
|
|
|
2448
2539
|
return false;
|
|
2449
2540
|
}
|
|
2450
2541
|
}
|
|
2542
|
+
/**
|
|
2543
|
+
* Detects the first available linter on the current system.
|
|
2544
|
+
*
|
|
2545
|
+
* - Checks in preference order: `biome`, `oxlint`, `eslint`.
|
|
2546
|
+
* - Returns `null` when none are found.
|
|
2547
|
+
*
|
|
2548
|
+
* @example
|
|
2549
|
+
* ```ts
|
|
2550
|
+
* const linter = await detectLinter()
|
|
2551
|
+
* if (linter) {
|
|
2552
|
+
* console.log(`Using ${linter} for linting`)
|
|
2553
|
+
* }
|
|
2554
|
+
* ```
|
|
2555
|
+
*/
|
|
2451
2556
|
async function detectLinter() {
|
|
2452
2557
|
const linterNames = new Set([
|
|
2453
2558
|
"biome",
|
|
@@ -2455,12 +2560,13 @@ async function detectLinter() {
|
|
|
2455
2560
|
"eslint"
|
|
2456
2561
|
]);
|
|
2457
2562
|
for (const linter of linterNames) if (await isLinterAvailable(linter)) return linter;
|
|
2563
|
+
return null;
|
|
2458
2564
|
}
|
|
2459
2565
|
//#endregion
|
|
2460
2566
|
//#region src/utils/packageJSON.ts
|
|
2461
2567
|
function getPackageJSONSync(cwd) {
|
|
2462
2568
|
const pkgPath = empathic_package.up({ cwd });
|
|
2463
|
-
if (!pkgPath) return;
|
|
2569
|
+
if (!pkgPath) return null;
|
|
2464
2570
|
return JSON.parse(readSync(pkgPath));
|
|
2465
2571
|
}
|
|
2466
2572
|
function match(packageJSON, dependency) {
|
|
@@ -2470,12 +2576,27 @@ function match(packageJSON, dependency) {
|
|
|
2470
2576
|
};
|
|
2471
2577
|
if (typeof dependency === "string" && dependencies[dependency]) return dependencies[dependency];
|
|
2472
2578
|
const matched = Object.keys(dependencies).find((dep) => dep.match(dependency));
|
|
2473
|
-
return matched ? dependencies[matched] :
|
|
2579
|
+
return matched ? dependencies[matched] ?? null : null;
|
|
2474
2580
|
}
|
|
2475
2581
|
function getVersionSync(dependency, cwd) {
|
|
2476
2582
|
const packageJSON = getPackageJSONSync(cwd);
|
|
2477
|
-
return packageJSON ? match(packageJSON, dependency) :
|
|
2583
|
+
return packageJSON ? match(packageJSON, dependency) : null;
|
|
2478
2584
|
}
|
|
2585
|
+
/**
|
|
2586
|
+
* Returns `true` when the nearest `package.json` declares a dependency that
|
|
2587
|
+
* satisfies the given semver range.
|
|
2588
|
+
*
|
|
2589
|
+
* - Searches both `dependencies` and `devDependencies`.
|
|
2590
|
+
* - Accepts a string package name or a `RegExp` to match scoped/pattern packages.
|
|
2591
|
+
* - Uses `semver.satisfies` for range comparison; returns `false` when the
|
|
2592
|
+
* version string cannot be coerced into a valid semver.
|
|
2593
|
+
*
|
|
2594
|
+
* @example
|
|
2595
|
+
* ```ts
|
|
2596
|
+
* satisfiesDependency('react', '>=18') // true when react@18.x is installed
|
|
2597
|
+
* satisfiesDependency(/^@tanstack\//, '>=5') // true when any @tanstack/* >=5 is found
|
|
2598
|
+
* ```
|
|
2599
|
+
*/
|
|
2479
2600
|
function satisfiesDependency(dependency, version, cwd) {
|
|
2480
2601
|
const packageVersion = getVersionSync(dependency, cwd);
|
|
2481
2602
|
if (!packageVersion) return false;
|
|
@@ -2485,8 +2606,10 @@ function satisfiesDependency(dependency, version, cwd) {
|
|
|
2485
2606
|
return (0, semver.satisfies)(semVer, version);
|
|
2486
2607
|
}
|
|
2487
2608
|
//#endregion
|
|
2609
|
+
exports.AsyncEventEmitter = AsyncEventEmitter;
|
|
2488
2610
|
exports.FunctionParams = FunctionParams;
|
|
2489
2611
|
exports.PluginDriver = PluginDriver;
|
|
2612
|
+
exports.URLPath = URLPath;
|
|
2490
2613
|
exports.build = build;
|
|
2491
2614
|
exports.createAdapter = createAdapter;
|
|
2492
2615
|
exports.createPlugin = createPlugin;
|