@kubb/core 5.0.0-alpha.2 → 5.0.0-alpha.20
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/{types-B7eZvqwD.d.ts → PluginDriver-BkSenc-R.d.ts} +521 -299
- package/dist/hooks.cjs +101 -8
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +83 -4
- package/dist/hooks.js +99 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +850 -536
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +438 -89
- package/dist/index.js +839 -532
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/Kubb.ts +37 -55
- package/src/{PluginManager.ts → PluginDriver.ts} +51 -40
- package/src/build.ts +74 -29
- package/src/config.ts +9 -8
- package/src/constants.ts +44 -1
- package/src/createAdapter.ts +25 -0
- package/src/createPlugin.ts +28 -0
- package/src/createStorage.ts +58 -0
- package/src/defineGenerator.ts +134 -0
- package/src/defineLogger.ts +13 -3
- package/src/definePreset.ts +23 -0
- package/src/definePresets.ts +16 -0
- package/src/defineResolver.ts +131 -0
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useKubb.ts +160 -0
- package/src/hooks/useMode.ts +5 -2
- package/src/hooks/usePlugin.ts +5 -2
- package/src/hooks/usePluginDriver.ts +11 -0
- package/src/index.ts +12 -6
- package/src/renderNode.tsx +108 -0
- package/src/storages/fsStorage.ts +2 -2
- package/src/storages/memoryStorage.ts +2 -2
- package/src/types.ts +150 -38
- package/src/utils/FunctionParams.ts +2 -2
- package/src/utils/TreeNode.ts +24 -1
- package/src/utils/diagnostics.ts +4 -1
- package/src/utils/executeStrategies.ts +23 -10
- package/src/utils/formatters.ts +10 -21
- package/src/utils/getBarrelFiles.ts +79 -9
- package/src/utils/getConfigs.ts +8 -22
- package/src/utils/getPreset.ts +41 -0
- package/src/utils/linters.ts +23 -3
- package/src/utils/mergeResolvers.ts +8 -0
- package/src/utils/packageJSON.ts +76 -0
- package/src/BarrelManager.ts +0 -74
- package/src/PackageManager.ts +0 -180
- package/src/PromiseManager.ts +0 -40
- package/src/defineAdapter.ts +0 -22
- package/src/definePlugin.ts +0 -12
- package/src/defineStorage.ts +0 -56
- package/src/errors.ts +0 -1
- package/src/hooks/usePluginManager.ts +0 -8
- package/src/utils/getPlugins.ts +0 -23
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,6 @@ Object.defineProperties(exports, {
|
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("./chunk-ByKO4r7w.cjs");
|
|
6
6
|
let node_events = require("node:events");
|
|
7
|
-
let node_util = require("node:util");
|
|
8
7
|
let node_fs = require("node:fs");
|
|
9
8
|
let node_fs_promises = require("node:fs/promises");
|
|
10
9
|
let node_path = require("node:path");
|
|
@@ -17,21 +16,28 @@ let node_perf_hooks = require("node:perf_hooks");
|
|
|
17
16
|
let fflate = require("fflate");
|
|
18
17
|
let tinyexec = require("tinyexec");
|
|
19
18
|
let node_process = require("node:process");
|
|
20
|
-
let
|
|
21
|
-
|
|
22
|
-
let node_os = require("node:os");
|
|
23
|
-
node_os = require_chunk.__toESM(node_os);
|
|
24
|
-
let node_url = require("node:url");
|
|
19
|
+
let _kubb_react_fabric_jsx_runtime = require("@kubb/react-fabric/jsx-runtime");
|
|
20
|
+
let remeda = require("remeda");
|
|
25
21
|
let empathic_package = require("empathic/package");
|
|
26
22
|
empathic_package = require_chunk.__toESM(empathic_package);
|
|
27
23
|
let semver = require("semver");
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
+
*/
|
|
31
32
|
var ValidationPluginError = class extends Error {};
|
|
32
33
|
/**
|
|
33
34
|
* Thrown when one or more errors occur during a Kubb build.
|
|
34
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
|
+
* ```
|
|
35
41
|
*/
|
|
36
42
|
var BuildError = class extends Error {
|
|
37
43
|
errors;
|
|
@@ -43,19 +49,34 @@ var BuildError = class extends Error {
|
|
|
43
49
|
};
|
|
44
50
|
/**
|
|
45
51
|
* Coerces an unknown thrown value to an `Error` instance.
|
|
46
|
-
*
|
|
47
|
-
*
|
|
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
|
+
* ```
|
|
48
60
|
*/
|
|
49
61
|
function toError(value) {
|
|
50
62
|
return value instanceof Error ? value : new Error(String(value));
|
|
51
63
|
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region ../../internals/utils/src/asyncEventEmitter.ts
|
|
52
66
|
/**
|
|
53
|
-
*
|
|
67
|
+
* Typed `EventEmitter` that awaits all async listeners before resolving.
|
|
54
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
|
+
* ```
|
|
55
76
|
*/
|
|
56
77
|
var AsyncEventEmitter = class {
|
|
57
78
|
/**
|
|
58
|
-
*
|
|
79
|
+
* Maximum number of listeners per event before Node emits a memory-leak warning.
|
|
59
80
|
* @default 10
|
|
60
81
|
*/
|
|
61
82
|
constructor(maxListener = 10) {
|
|
@@ -63,8 +84,13 @@ var AsyncEventEmitter = class {
|
|
|
63
84
|
}
|
|
64
85
|
#emitter = new node_events.EventEmitter();
|
|
65
86
|
/**
|
|
66
|
-
* Emits
|
|
87
|
+
* Emits `eventName` and awaits all registered listeners in parallel.
|
|
67
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
|
+
* ```
|
|
68
94
|
*/
|
|
69
95
|
async emit(eventName, ...eventArgs) {
|
|
70
96
|
const listeners = this.#emitter.listeners(eventName);
|
|
@@ -83,11 +109,25 @@ var AsyncEventEmitter = class {
|
|
|
83
109
|
}
|
|
84
110
|
}));
|
|
85
111
|
}
|
|
86
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Registers a persistent listener for `eventName`.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* emitter.on('build', async (name) => { console.log(name) })
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
87
120
|
on(eventName, handler) {
|
|
88
121
|
this.#emitter.on(eventName, handler);
|
|
89
122
|
}
|
|
90
|
-
/**
|
|
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
|
+
*/
|
|
91
131
|
onOnce(eventName, handler) {
|
|
92
132
|
const wrapper = (...args) => {
|
|
93
133
|
this.off(eventName, wrapper);
|
|
@@ -95,15 +135,31 @@ var AsyncEventEmitter = class {
|
|
|
95
135
|
};
|
|
96
136
|
this.on(eventName, wrapper);
|
|
97
137
|
}
|
|
98
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Removes a previously registered listener.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* emitter.off('build', handler)
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
99
146
|
off(eventName, handler) {
|
|
100
147
|
this.#emitter.off(eventName, handler);
|
|
101
148
|
}
|
|
102
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Removes all listeners from every event channel.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* emitter.removeAll()
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
103
157
|
removeAll() {
|
|
104
158
|
this.#emitter.removeAllListeners();
|
|
105
159
|
}
|
|
106
160
|
};
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region ../../internals/utils/src/casing.ts
|
|
107
163
|
/**
|
|
108
164
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
109
165
|
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
@@ -122,9 +178,12 @@ function toCamelOrPascal(text, pascal) {
|
|
|
122
178
|
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
123
179
|
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
124
180
|
* Segments are joined with `/` to form a file path.
|
|
181
|
+
*
|
|
182
|
+
* Only splits on dots followed by a letter so that version numbers
|
|
183
|
+
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
125
184
|
*/
|
|
126
185
|
function applyToFileParts(text, transformPart) {
|
|
127
|
-
const parts = text.split(
|
|
186
|
+
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
128
187
|
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
129
188
|
}
|
|
130
189
|
/**
|
|
@@ -142,190 +201,33 @@ function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
142
201
|
} : {}));
|
|
143
202
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
144
203
|
}
|
|
145
|
-
/** Returns a `CLIAdapter` with type inference. Pass a different adapter to `createCLI` to swap the CLI engine. */
|
|
146
|
-
function defineCLIAdapter(adapter) {
|
|
147
|
-
return adapter;
|
|
148
|
-
}
|
|
149
204
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
205
|
+
* Converts `text` to PascalCase.
|
|
206
|
+
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* pascalCase('hello-world') // 'HelloWorld'
|
|
210
|
+
* pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
|
|
152
211
|
*/
|
|
153
|
-
function
|
|
154
|
-
return
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
description: def.description,
|
|
160
|
-
arguments: def.arguments,
|
|
161
|
-
options: serializeOptions(def.options ?? {}),
|
|
162
|
-
subCommands: def.subCommands ? def.subCommands.map(serializeCommand) : []
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
function serializeOptions(options) {
|
|
166
|
-
return Object.entries(options).map(([name, opt]) => {
|
|
167
|
-
return {
|
|
168
|
-
name,
|
|
169
|
-
flags: `${opt.short ? `-${opt.short}, ` : ""}--${name}${opt.type === "string" ? ` <${opt.hint ?? name}>` : ""}`,
|
|
170
|
-
type: opt.type,
|
|
171
|
-
description: opt.description,
|
|
172
|
-
...opt.default !== void 0 ? { default: opt.default } : {},
|
|
173
|
-
...opt.hint ? { hint: opt.hint } : {},
|
|
174
|
-
...opt.enum ? { enum: opt.enum } : {},
|
|
175
|
-
...opt.required ? { required: opt.required } : {}
|
|
176
|
-
};
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
/** Prints formatted help output for a command using its `CommandDefinition`. */
|
|
180
|
-
function renderHelp(def, parentName) {
|
|
181
|
-
const schema = getCommandSchema([def])[0];
|
|
182
|
-
const programName = parentName ? `${parentName} ${schema.name}` : schema.name;
|
|
183
|
-
const argsPart = schema.arguments?.length ? ` ${schema.arguments.join(" ")}` : "";
|
|
184
|
-
const subCmdPart = schema.subCommands.length ? " <command>" : "";
|
|
185
|
-
console.log(`\n${(0, node_util.styleText)("bold", "Usage:")} ${programName}${argsPart}${subCmdPart} [options]\n`);
|
|
186
|
-
if (schema.description) console.log(` ${schema.description}\n`);
|
|
187
|
-
if (schema.subCommands.length) {
|
|
188
|
-
console.log((0, node_util.styleText)("bold", "Commands:"));
|
|
189
|
-
for (const sub of schema.subCommands) console.log(` ${(0, node_util.styleText)("cyan", sub.name.padEnd(16))}${sub.description}`);
|
|
190
|
-
console.log();
|
|
191
|
-
}
|
|
192
|
-
const options = [...schema.options, {
|
|
193
|
-
name: "help",
|
|
194
|
-
flags: "-h, --help",
|
|
195
|
-
type: "boolean",
|
|
196
|
-
description: "Show help"
|
|
197
|
-
}];
|
|
198
|
-
console.log((0, node_util.styleText)("bold", "Options:"));
|
|
199
|
-
for (const opt of options) {
|
|
200
|
-
const flags = (0, node_util.styleText)("cyan", opt.flags.padEnd(30));
|
|
201
|
-
const defaultPart = opt.default !== void 0 ? (0, node_util.styleText)("dim", ` (default: ${opt.default})`) : "";
|
|
202
|
-
console.log(` ${flags}${opt.description}${defaultPart}`);
|
|
203
|
-
}
|
|
204
|
-
console.log();
|
|
205
|
-
}
|
|
206
|
-
function buildParseOptions(def) {
|
|
207
|
-
const result = { help: {
|
|
208
|
-
type: "boolean",
|
|
209
|
-
short: "h"
|
|
210
|
-
} };
|
|
211
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) result[name] = {
|
|
212
|
-
type: opt.type,
|
|
213
|
-
...opt.short ? { short: opt.short } : {},
|
|
214
|
-
...opt.default !== void 0 ? { default: opt.default } : {}
|
|
215
|
-
};
|
|
216
|
-
return result;
|
|
212
|
+
function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
213
|
+
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
214
|
+
prefix,
|
|
215
|
+
suffix
|
|
216
|
+
}) : camelCase(part));
|
|
217
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
217
218
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
let parsed;
|
|
221
|
-
try {
|
|
222
|
-
const result = (0, node_util.parseArgs)({
|
|
223
|
-
args: argv,
|
|
224
|
-
options: parseOptions,
|
|
225
|
-
allowPositionals: true,
|
|
226
|
-
strict: false
|
|
227
|
-
});
|
|
228
|
-
parsed = {
|
|
229
|
-
values: result.values,
|
|
230
|
-
positionals: result.positionals
|
|
231
|
-
};
|
|
232
|
-
} catch {
|
|
233
|
-
renderHelp(def, parentName);
|
|
234
|
-
process.exit(1);
|
|
235
|
-
}
|
|
236
|
-
if (parsed.values["help"]) {
|
|
237
|
-
renderHelp(def, parentName);
|
|
238
|
-
process.exit(0);
|
|
239
|
-
}
|
|
240
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) if (opt.required && parsed.values[name] === void 0) {
|
|
241
|
-
console.error((0, node_util.styleText)("red", `Error: --${name} is required`));
|
|
242
|
-
renderHelp(def, parentName);
|
|
243
|
-
process.exit(1);
|
|
244
|
-
}
|
|
245
|
-
if (!def.run) {
|
|
246
|
-
renderHelp(def, parentName);
|
|
247
|
-
process.exit(0);
|
|
248
|
-
}
|
|
249
|
-
try {
|
|
250
|
-
await def.run(parsed);
|
|
251
|
-
} catch (err) {
|
|
252
|
-
console.error((0, node_util.styleText)("red", `Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
253
|
-
renderHelp(def, parentName);
|
|
254
|
-
process.exit(1);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
function printRootHelp(programName, version, defs) {
|
|
258
|
-
console.log(`\n${(0, node_util.styleText)("bold", "Usage:")} ${programName} <command> [options]\n`);
|
|
259
|
-
console.log(` Kubb generation — v${version}\n`);
|
|
260
|
-
console.log((0, node_util.styleText)("bold", "Commands:"));
|
|
261
|
-
for (const def of defs) console.log(` ${(0, node_util.styleText)("cyan", def.name.padEnd(16))}${def.description}`);
|
|
262
|
-
console.log();
|
|
263
|
-
console.log((0, node_util.styleText)("bold", "Options:"));
|
|
264
|
-
console.log(` ${(0, node_util.styleText)("cyan", "-v, --version".padEnd(30))}Show version number`);
|
|
265
|
-
console.log(` ${(0, node_util.styleText)("cyan", "-h, --help".padEnd(30))}Show help`);
|
|
266
|
-
console.log();
|
|
267
|
-
console.log(`Run ${(0, node_util.styleText)("cyan", `${programName} <command> --help`)} for command-specific help.\n`);
|
|
268
|
-
}
|
|
269
|
-
defineCLIAdapter({
|
|
270
|
-
renderHelp(def, parentName) {
|
|
271
|
-
renderHelp(def, parentName);
|
|
272
|
-
},
|
|
273
|
-
async run(defs, argv, opts) {
|
|
274
|
-
const { programName, defaultCommandName, version } = opts;
|
|
275
|
-
const args = argv.length >= 2 && argv[0]?.includes("node") ? argv.slice(2) : argv;
|
|
276
|
-
if (args[0] === "--version" || args[0] === "-v") {
|
|
277
|
-
console.log(version);
|
|
278
|
-
process.exit(0);
|
|
279
|
-
}
|
|
280
|
-
if (args[0] === "--help" || args[0] === "-h") {
|
|
281
|
-
printRootHelp(programName, version, defs);
|
|
282
|
-
process.exit(0);
|
|
283
|
-
}
|
|
284
|
-
if (args.length === 0) {
|
|
285
|
-
const defaultDef = defs.find((d) => d.name === defaultCommandName);
|
|
286
|
-
if (defaultDef?.run) await runCommand(defaultDef, [], programName);
|
|
287
|
-
else printRootHelp(programName, version, defs);
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
const [first, ...rest] = args;
|
|
291
|
-
const isKnownSubcommand = defs.some((d) => d.name === first);
|
|
292
|
-
let def;
|
|
293
|
-
let commandArgv;
|
|
294
|
-
let parentName;
|
|
295
|
-
if (isKnownSubcommand) {
|
|
296
|
-
def = defs.find((d) => d.name === first);
|
|
297
|
-
commandArgv = rest;
|
|
298
|
-
parentName = programName;
|
|
299
|
-
} else {
|
|
300
|
-
def = defs.find((d) => d.name === defaultCommandName);
|
|
301
|
-
commandArgv = args;
|
|
302
|
-
parentName = programName;
|
|
303
|
-
}
|
|
304
|
-
if (!def) {
|
|
305
|
-
console.error(`Unknown command: ${first}`);
|
|
306
|
-
printRootHelp(programName, version, defs);
|
|
307
|
-
process.exit(1);
|
|
308
|
-
}
|
|
309
|
-
if (def.subCommands?.length) {
|
|
310
|
-
const [subName, ...subRest] = commandArgv;
|
|
311
|
-
const subDef = def.subCommands.find((s) => s.name === subName);
|
|
312
|
-
if (subName === "--help" || subName === "-h") {
|
|
313
|
-
renderHelp(def, parentName);
|
|
314
|
-
process.exit(0);
|
|
315
|
-
}
|
|
316
|
-
if (!subDef) {
|
|
317
|
-
renderHelp(def, parentName);
|
|
318
|
-
process.exit(subName ? 1 : 0);
|
|
319
|
-
}
|
|
320
|
-
await runCommand(subDef, subRest, `${parentName} ${def.name}`);
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
await runCommand(def, commandArgv, parentName);
|
|
324
|
-
}
|
|
325
|
-
});
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region ../../internals/utils/src/time.ts
|
|
326
221
|
/**
|
|
327
|
-
* Calculates elapsed time in milliseconds from a high-resolution start time.
|
|
328
|
-
* 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
|
+
* ```
|
|
329
231
|
*/
|
|
330
232
|
function getElapsedMs(hrStart) {
|
|
331
233
|
const [seconds, nanoseconds] = process.hrtime(hrStart);
|
|
@@ -333,39 +235,22 @@ function getElapsedMs(hrStart) {
|
|
|
333
235
|
return Math.round(ms * 100) / 100;
|
|
334
236
|
}
|
|
335
237
|
/**
|
|
336
|
-
* Converts a millisecond duration into a human-readable string.
|
|
337
|
-
*
|
|
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
|
+
* ```
|
|
338
246
|
*/
|
|
339
247
|
function formatMs(ms) {
|
|
340
248
|
if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
|
|
341
249
|
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
342
250
|
return `${Math.round(ms)}ms`;
|
|
343
251
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
* Falls back to `255` for any channel that cannot be parsed.
|
|
347
|
-
*/
|
|
348
|
-
function parseHex(color) {
|
|
349
|
-
const int = Number.parseInt(color.replace("#", ""), 16);
|
|
350
|
-
return Number.isNaN(int) ? {
|
|
351
|
-
r: 255,
|
|
352
|
-
g: 255,
|
|
353
|
-
b: 255
|
|
354
|
-
} : {
|
|
355
|
-
r: int >> 16 & 255,
|
|
356
|
-
g: int >> 8 & 255,
|
|
357
|
-
b: int & 255
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Returns a function that wraps a string in a 24-bit ANSI true-color escape sequence
|
|
362
|
-
* for the given hex color.
|
|
363
|
-
*/
|
|
364
|
-
function hex(color) {
|
|
365
|
-
const { r, g, b } = parseHex(color);
|
|
366
|
-
return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
|
|
367
|
-
}
|
|
368
|
-
hex("#F55A17"), hex("#F5A217"), hex("#F58517"), hex("#B45309"), hex("#FFFFFF"), hex("#adadc6"), hex("#FDA4AF");
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region ../../internals/utils/src/fs.ts
|
|
369
254
|
/**
|
|
370
255
|
* Converts all backslashes to forward slashes.
|
|
371
256
|
* Extended-length Windows paths (`\\?\...`) are left unchanged.
|
|
@@ -375,8 +260,14 @@ function toSlash(p) {
|
|
|
375
260
|
return p.replaceAll("\\", "/");
|
|
376
261
|
}
|
|
377
262
|
/**
|
|
378
|
-
* Returns the relative path from `rootDir` to `filePath`, always using
|
|
379
|
-
*
|
|
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
|
+
* ```
|
|
380
271
|
*/
|
|
381
272
|
function getRelativePath(rootDir, filePath) {
|
|
382
273
|
if (!rootDir || !filePath) throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ""} ${filePath || ""}`);
|
|
@@ -386,43 +277,54 @@ function getRelativePath(rootDir, filePath) {
|
|
|
386
277
|
/**
|
|
387
278
|
* Resolves to `true` when the file or directory at `path` exists.
|
|
388
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
|
+
* ```
|
|
389
287
|
*/
|
|
390
288
|
async function exists(path) {
|
|
391
289
|
if (typeof Bun !== "undefined") return Bun.file(path).exists();
|
|
392
290
|
return (0, node_fs_promises.access)(path).then(() => true, () => false);
|
|
393
291
|
}
|
|
394
292
|
/**
|
|
395
|
-
*
|
|
396
|
-
*
|
|
293
|
+
* Synchronous counterpart of `read`.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* const source = readSync('./src/Pet.ts')
|
|
298
|
+
* ```
|
|
397
299
|
*/
|
|
398
|
-
async function read(path) {
|
|
399
|
-
if (typeof Bun !== "undefined") return Bun.file(path).text();
|
|
400
|
-
return (0, node_fs_promises.readFile)(path, { encoding: "utf8" });
|
|
401
|
-
}
|
|
402
|
-
/** Synchronous counterpart of `read`. */
|
|
403
300
|
function readSync(path) {
|
|
404
301
|
return (0, node_fs.readFileSync)(path, { encoding: "utf8" });
|
|
405
302
|
}
|
|
406
303
|
/**
|
|
407
304
|
* Writes `data` to `path`, trimming leading/trailing whitespace before saving.
|
|
408
|
-
* Skips the write
|
|
409
|
-
* 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.
|
|
410
306
|
* Creates any missing parent directories automatically.
|
|
411
|
-
* When `sanity` is `true`, re-reads the file after writing and throws if the
|
|
412
|
-
*
|
|
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
|
+
* ```
|
|
413
315
|
*/
|
|
414
316
|
async function write(path, data, options = {}) {
|
|
415
317
|
const trimmed = data.trim();
|
|
416
|
-
if (trimmed === "") return
|
|
318
|
+
if (trimmed === "") return null;
|
|
417
319
|
const resolved = (0, node_path.resolve)(path);
|
|
418
320
|
if (typeof Bun !== "undefined") {
|
|
419
321
|
const file = Bun.file(resolved);
|
|
420
|
-
if ((await file.exists() ? await file.text() : null) === trimmed) return
|
|
322
|
+
if ((await file.exists() ? await file.text() : null) === trimmed) return null;
|
|
421
323
|
await Bun.write(resolved, trimmed);
|
|
422
324
|
return trimmed;
|
|
423
325
|
}
|
|
424
326
|
try {
|
|
425
|
-
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;
|
|
426
328
|
} catch {}
|
|
427
329
|
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(resolved), { recursive: true });
|
|
428
330
|
await (0, node_fs_promises.writeFile)(resolved, trimmed, { encoding: "utf-8" });
|
|
@@ -433,16 +335,32 @@ async function write(path, data, options = {}) {
|
|
|
433
335
|
}
|
|
434
336
|
return trimmed;
|
|
435
337
|
}
|
|
436
|
-
/**
|
|
338
|
+
/**
|
|
339
|
+
* Recursively removes `path`. Silently succeeds when `path` does not exist.
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```ts
|
|
343
|
+
* await clean('./dist')
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
437
346
|
async function clean(path) {
|
|
438
347
|
return (0, node_fs_promises.rm)(path, {
|
|
439
348
|
recursive: true,
|
|
440
349
|
force: true
|
|
441
350
|
});
|
|
442
351
|
}
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region ../../internals/utils/src/names.ts
|
|
443
354
|
/**
|
|
444
355
|
* Registers `originalName` in `data` without altering the returned name.
|
|
445
|
-
* 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
|
+
* ```
|
|
446
364
|
*/
|
|
447
365
|
function setUniqueName(originalName, data) {
|
|
448
366
|
let used = data[originalName] || 0;
|
|
@@ -453,11 +371,26 @@ function setUniqueName(originalName, data) {
|
|
|
453
371
|
data[originalName] = 1;
|
|
454
372
|
return originalName;
|
|
455
373
|
}
|
|
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
|
+
*/
|
|
384
|
+
function isPromiseRejectedResult(result) {
|
|
385
|
+
return result.status === "rejected";
|
|
386
|
+
}
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
456
389
|
/**
|
|
457
390
|
* JavaScript and Java reserved words.
|
|
458
391
|
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
459
392
|
*/
|
|
460
|
-
const reservedWords = [
|
|
393
|
+
const reservedWords = new Set([
|
|
461
394
|
"abstract",
|
|
462
395
|
"arguments",
|
|
463
396
|
"boolean",
|
|
@@ -539,18 +472,31 @@ const reservedWords = [
|
|
|
539
472
|
"toString",
|
|
540
473
|
"undefined",
|
|
541
474
|
"valueOf"
|
|
542
|
-
];
|
|
475
|
+
]);
|
|
543
476
|
/**
|
|
544
|
-
* Prefixes
|
|
545
|
-
*
|
|
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
|
+
* ```
|
|
546
485
|
*/
|
|
547
486
|
function transformReservedWord(word) {
|
|
548
487
|
const firstChar = word.charCodeAt(0);
|
|
549
|
-
if (word && (reservedWords.
|
|
488
|
+
if (word && (reservedWords.has(word) || firstChar >= 48 && firstChar <= 57)) return `_${word}`;
|
|
550
489
|
return word;
|
|
551
490
|
}
|
|
552
491
|
/**
|
|
553
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
|
+
* ```
|
|
554
500
|
*/
|
|
555
501
|
function isValidVarName(name) {
|
|
556
502
|
try {
|
|
@@ -560,6 +506,8 @@ function isValidVarName(name) {
|
|
|
560
506
|
}
|
|
561
507
|
return true;
|
|
562
508
|
}
|
|
509
|
+
//#endregion
|
|
510
|
+
//#region ../../internals/utils/src/urlPath.ts
|
|
563
511
|
/**
|
|
564
512
|
* Parses and transforms an OpenAPI/Swagger path string into various URL formats.
|
|
565
513
|
*
|
|
@@ -569,18 +517,33 @@ function isValidVarName(name) {
|
|
|
569
517
|
* p.template // '`/pet/${petId}`'
|
|
570
518
|
*/
|
|
571
519
|
var URLPath = class {
|
|
572
|
-
/**
|
|
520
|
+
/**
|
|
521
|
+
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
522
|
+
*/
|
|
573
523
|
path;
|
|
574
524
|
#options;
|
|
575
525
|
constructor(path, options = {}) {
|
|
576
526
|
this.path = path;
|
|
577
527
|
this.#options = options;
|
|
578
528
|
}
|
|
579
|
-
/** 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
|
+
*/
|
|
580
536
|
get URL() {
|
|
581
537
|
return this.toURLPath();
|
|
582
538
|
}
|
|
583
|
-
/** 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
|
+
*/
|
|
584
547
|
get isURL() {
|
|
585
548
|
try {
|
|
586
549
|
return !!new URL(this.path).href;
|
|
@@ -598,11 +561,25 @@ var URLPath = class {
|
|
|
598
561
|
get template() {
|
|
599
562
|
return this.toTemplateString();
|
|
600
563
|
}
|
|
601
|
-
/** 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
|
+
*/
|
|
602
572
|
get object() {
|
|
603
573
|
return this.toObject();
|
|
604
574
|
}
|
|
605
|
-
/** 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
|
+
*/
|
|
606
583
|
get params() {
|
|
607
584
|
return this.getParams();
|
|
608
585
|
}
|
|
@@ -610,7 +587,9 @@ var URLPath = class {
|
|
|
610
587
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
611
588
|
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
612
589
|
}
|
|
613
|
-
/**
|
|
590
|
+
/**
|
|
591
|
+
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
592
|
+
*/
|
|
614
593
|
#eachParam(fn) {
|
|
615
594
|
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
616
595
|
const raw = match[1];
|
|
@@ -647,6 +626,12 @@ var URLPath = class {
|
|
|
647
626
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
648
627
|
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
649
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
|
+
* ```
|
|
650
635
|
*/
|
|
651
636
|
getParams(replacer) {
|
|
652
637
|
const params = {};
|
|
@@ -656,7 +641,13 @@ var URLPath = class {
|
|
|
656
641
|
});
|
|
657
642
|
return Object.keys(params).length > 0 ? params : void 0;
|
|
658
643
|
}
|
|
659
|
-
/** 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
|
+
*/
|
|
660
651
|
toURLPath() {
|
|
661
652
|
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
662
653
|
}
|
|
@@ -674,11 +665,27 @@ function isInputPath(config) {
|
|
|
674
665
|
}
|
|
675
666
|
//#endregion
|
|
676
667
|
//#region src/constants.ts
|
|
668
|
+
/**
|
|
669
|
+
* Base URL for the Kubb Studio web app.
|
|
670
|
+
*/
|
|
677
671
|
const DEFAULT_STUDIO_URL = "https://studio.kubb.dev";
|
|
672
|
+
/**
|
|
673
|
+
* File name used for generated barrel (index) files.
|
|
674
|
+
*/
|
|
678
675
|
const BARREL_FILENAME = "index.ts";
|
|
676
|
+
/**
|
|
677
|
+
* Default banner style written at the top of every generated file.
|
|
678
|
+
*/
|
|
679
679
|
const DEFAULT_BANNER = "simple";
|
|
680
|
+
/**
|
|
681
|
+
* Default file-extension mapping used when no explicit mapping is configured.
|
|
682
|
+
*/
|
|
680
683
|
const DEFAULT_EXTENSION = { ".ts": ".ts" };
|
|
681
|
-
|
|
684
|
+
/**
|
|
685
|
+
* Numeric log-level thresholds used internally to compare verbosity.
|
|
686
|
+
*
|
|
687
|
+
* Higher numbers are more verbose.
|
|
688
|
+
*/
|
|
682
689
|
const logLevel = {
|
|
683
690
|
silent: Number.NEGATIVE_INFINITY,
|
|
684
691
|
error: 0,
|
|
@@ -687,6 +694,13 @@ const logLevel = {
|
|
|
687
694
|
verbose: 4,
|
|
688
695
|
debug: 5
|
|
689
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
|
+
*/
|
|
690
704
|
const linters = {
|
|
691
705
|
eslint: {
|
|
692
706
|
command: "eslint",
|
|
@@ -708,6 +722,13 @@ const linters = {
|
|
|
708
722
|
errorMessage: "Oxlint not found"
|
|
709
723
|
}
|
|
710
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
|
+
*/
|
|
711
732
|
const formatters = {
|
|
712
733
|
prettier: {
|
|
713
734
|
command: "prettier",
|
|
@@ -907,7 +928,11 @@ function validateConcurrency(concurrency) {
|
|
|
907
928
|
//#endregion
|
|
908
929
|
//#region src/utils/executeStrategies.ts
|
|
909
930
|
/**
|
|
910
|
-
*
|
|
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.
|
|
911
936
|
*/
|
|
912
937
|
function hookSeq(promises) {
|
|
913
938
|
return promises.filter(Boolean).reduce((promise, func) => {
|
|
@@ -920,7 +945,10 @@ function hookSeq(promises) {
|
|
|
920
945
|
}, Promise.resolve([]));
|
|
921
946
|
}
|
|
922
947
|
/**
|
|
923
|
-
*
|
|
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.
|
|
924
952
|
*/
|
|
925
953
|
function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
926
954
|
let promise = Promise.resolve(null);
|
|
@@ -931,7 +959,10 @@ function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
|
931
959
|
return promise;
|
|
932
960
|
}
|
|
933
961
|
/**
|
|
934
|
-
* 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.
|
|
935
966
|
*/
|
|
936
967
|
function hookParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
937
968
|
const limit = pLimit(concurrency);
|
|
@@ -939,29 +970,13 @@ function hookParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
|
939
970
|
return Promise.allSettled(tasks);
|
|
940
971
|
}
|
|
941
972
|
//#endregion
|
|
942
|
-
//#region src/
|
|
943
|
-
var PromiseManager = class {
|
|
944
|
-
#options = {};
|
|
945
|
-
constructor(options = {}) {
|
|
946
|
-
this.#options = options;
|
|
947
|
-
}
|
|
948
|
-
run(strategy, promises, { concurrency = Number.POSITIVE_INFINITY } = {}) {
|
|
949
|
-
if (strategy === "seq") return hookSeq(promises);
|
|
950
|
-
if (strategy === "first") return hookFirst(promises, this.#options.nullCheck);
|
|
951
|
-
if (strategy === "parallel") return hookParallel(promises, concurrency);
|
|
952
|
-
throw new Error(`${strategy} not implemented`);
|
|
953
|
-
}
|
|
954
|
-
};
|
|
955
|
-
function isPromiseRejectedResult(result) {
|
|
956
|
-
return result.status === "rejected";
|
|
957
|
-
}
|
|
958
|
-
//#endregion
|
|
959
|
-
//#region src/PluginManager.ts
|
|
973
|
+
//#region src/PluginDriver.ts
|
|
960
974
|
function getMode(fileOrFolder) {
|
|
961
975
|
if (!fileOrFolder) return "split";
|
|
962
|
-
return node_path.
|
|
976
|
+
return (0, node_path.extname)(fileOrFolder) ? "single" : "split";
|
|
963
977
|
}
|
|
964
|
-
|
|
978
|
+
const hookFirstNullCheck = (state) => !!state?.result;
|
|
979
|
+
var PluginDriver = class {
|
|
965
980
|
config;
|
|
966
981
|
options;
|
|
967
982
|
/**
|
|
@@ -969,14 +984,13 @@ var PluginManager = class {
|
|
|
969
984
|
* the build pipeline after the adapter's `parse()` resolves.
|
|
970
985
|
*/
|
|
971
986
|
rootNode = void 0;
|
|
987
|
+
adapter = void 0;
|
|
972
988
|
#studioIsOpen = false;
|
|
973
989
|
#plugins = /* @__PURE__ */ new Set();
|
|
974
990
|
#usedPluginNames = {};
|
|
975
|
-
#promiseManager;
|
|
976
991
|
constructor(config, options) {
|
|
977
992
|
this.config = config;
|
|
978
993
|
this.options = options;
|
|
979
|
-
this.#promiseManager = new PromiseManager({ nullCheck: (state) => !!state?.result });
|
|
980
994
|
[...config.plugins || []].forEach((plugin) => {
|
|
981
995
|
const parsedPlugin = this.#parse(plugin);
|
|
982
996
|
this.#plugins.add(parsedPlugin);
|
|
@@ -987,14 +1001,14 @@ var PluginManager = class {
|
|
|
987
1001
|
}
|
|
988
1002
|
getContext(plugin) {
|
|
989
1003
|
const plugins = [...this.#plugins];
|
|
990
|
-
const
|
|
1004
|
+
const driver = this;
|
|
991
1005
|
const baseContext = {
|
|
992
1006
|
fabric: this.options.fabric,
|
|
993
1007
|
config: this.config,
|
|
994
1008
|
plugin,
|
|
995
1009
|
events: this.options.events,
|
|
996
|
-
|
|
997
|
-
mode: getMode(node_path.
|
|
1010
|
+
driver: this,
|
|
1011
|
+
mode: getMode((0, node_path.resolve)(this.config.root, this.config.output.path)),
|
|
998
1012
|
addFile: async (...files) => {
|
|
999
1013
|
await this.options.fabric.addFile(...files);
|
|
1000
1014
|
},
|
|
@@ -1002,15 +1016,18 @@ var PluginManager = class {
|
|
|
1002
1016
|
await this.options.fabric.upsertFile(...files);
|
|
1003
1017
|
},
|
|
1004
1018
|
get rootNode() {
|
|
1005
|
-
return
|
|
1019
|
+
return driver.rootNode;
|
|
1020
|
+
},
|
|
1021
|
+
get adapter() {
|
|
1022
|
+
return driver.adapter;
|
|
1006
1023
|
},
|
|
1007
1024
|
openInStudio(options) {
|
|
1008
|
-
if (
|
|
1009
|
-
if (
|
|
1010
|
-
if (
|
|
1011
|
-
|
|
1012
|
-
const studioUrl =
|
|
1013
|
-
return openInStudio(
|
|
1025
|
+
if (!driver.config.devtools || driver.#studioIsOpen) return;
|
|
1026
|
+
if (typeof driver.config.devtools !== "object") throw new Error("Devtools must be an object");
|
|
1027
|
+
if (!driver.rootNode || !driver.adapter) throw new Error("adapter is not defined, make sure you have set the parser in kubb.config.ts");
|
|
1028
|
+
driver.#studioIsOpen = true;
|
|
1029
|
+
const studioUrl = driver.config.devtools?.studioUrl ?? "https://studio.kubb.dev";
|
|
1030
|
+
return openInStudio(driver.rootNode, studioUrl, options);
|
|
1014
1031
|
}
|
|
1015
1032
|
};
|
|
1016
1033
|
const mergedExtras = {};
|
|
@@ -1027,17 +1044,21 @@ var PluginManager = class {
|
|
|
1027
1044
|
return this.#getSortedPlugins();
|
|
1028
1045
|
}
|
|
1029
1046
|
getFile({ name, mode, extname, pluginName, options }) {
|
|
1030
|
-
const
|
|
1047
|
+
const resolvedName = mode ? mode === "single" ? "" : this.resolveName({
|
|
1048
|
+
name,
|
|
1049
|
+
pluginName,
|
|
1050
|
+
type: "file"
|
|
1051
|
+
}) : name;
|
|
1031
1052
|
const path = this.resolvePath({
|
|
1032
|
-
baseName
|
|
1053
|
+
baseName: `${resolvedName}${extname}`,
|
|
1033
1054
|
mode,
|
|
1034
1055
|
pluginName,
|
|
1035
1056
|
options
|
|
1036
1057
|
});
|
|
1037
|
-
if (!path) throw new Error(`Filepath should be defined for resolvedName "${
|
|
1058
|
+
if (!path) throw new Error(`Filepath should be defined for resolvedName "${resolvedName}" and pluginName "${pluginName}"`);
|
|
1038
1059
|
return {
|
|
1039
1060
|
path,
|
|
1040
|
-
baseName,
|
|
1061
|
+
baseName: (0, node_path.basename)(path),
|
|
1041
1062
|
meta: { pluginName },
|
|
1042
1063
|
sources: [],
|
|
1043
1064
|
imports: [],
|
|
@@ -1045,8 +1066,7 @@ var PluginManager = class {
|
|
|
1045
1066
|
};
|
|
1046
1067
|
}
|
|
1047
1068
|
resolvePath = (params) => {
|
|
1048
|
-
const
|
|
1049
|
-
const defaultPath = node_path.default.resolve(root, params.baseName);
|
|
1069
|
+
const defaultPath = (0, node_path.resolve)((0, node_path.resolve)(this.config.root, this.config.output.path), params.baseName);
|
|
1050
1070
|
if (params.pluginName) return this.hookForPluginSync({
|
|
1051
1071
|
pluginName: params.pluginName,
|
|
1052
1072
|
hookName: "resolvePath",
|
|
@@ -1126,7 +1146,7 @@ var PluginManager = class {
|
|
|
1126
1146
|
hookName,
|
|
1127
1147
|
plugins
|
|
1128
1148
|
});
|
|
1129
|
-
const
|
|
1149
|
+
const result = await hookFirst(plugins.map((plugin) => {
|
|
1130
1150
|
return async () => {
|
|
1131
1151
|
const value = await this.#execute({
|
|
1132
1152
|
strategy: "hookFirst",
|
|
@@ -1139,8 +1159,7 @@ var PluginManager = class {
|
|
|
1139
1159
|
result: value
|
|
1140
1160
|
});
|
|
1141
1161
|
};
|
|
1142
|
-
});
|
|
1143
|
-
const result = await this.#promiseManager.run("first", promises);
|
|
1162
|
+
}), hookFirstNullCheck);
|
|
1144
1163
|
this.events.emit("plugins:hook:progress:end", { hookName });
|
|
1145
1164
|
return result;
|
|
1146
1165
|
}
|
|
@@ -1176,7 +1195,7 @@ var PluginManager = class {
|
|
|
1176
1195
|
plugins
|
|
1177
1196
|
});
|
|
1178
1197
|
const pluginStartTimes = /* @__PURE__ */ new Map();
|
|
1179
|
-
const
|
|
1198
|
+
const results = await hookParallel(plugins.map((plugin) => {
|
|
1180
1199
|
return () => {
|
|
1181
1200
|
pluginStartTimes.set(plugin, node_perf_hooks.performance.now());
|
|
1182
1201
|
return this.#execute({
|
|
@@ -1186,8 +1205,7 @@ var PluginManager = class {
|
|
|
1186
1205
|
plugin
|
|
1187
1206
|
});
|
|
1188
1207
|
};
|
|
1189
|
-
});
|
|
1190
|
-
const results = await this.#promiseManager.run("parallel", promises, { concurrency: this.options.concurrency });
|
|
1208
|
+
}), this.options.concurrency);
|
|
1191
1209
|
results.forEach((result, index) => {
|
|
1192
1210
|
if (isPromiseRejectedResult(result)) {
|
|
1193
1211
|
const plugin = this.#getSortedPlugins(hookName)[index];
|
|
@@ -1218,15 +1236,14 @@ var PluginManager = class {
|
|
|
1218
1236
|
hookName,
|
|
1219
1237
|
plugins
|
|
1220
1238
|
});
|
|
1221
|
-
|
|
1239
|
+
await hookSeq(plugins.map((plugin) => {
|
|
1222
1240
|
return () => this.#execute({
|
|
1223
1241
|
strategy: "hookSeq",
|
|
1224
1242
|
hookName,
|
|
1225
1243
|
parameters,
|
|
1226
1244
|
plugin
|
|
1227
1245
|
});
|
|
1228
|
-
});
|
|
1229
|
-
await this.#promiseManager.run("seq", promises);
|
|
1246
|
+
}));
|
|
1230
1247
|
this.events.emit("plugins:hook:progress:end", { hookName });
|
|
1231
1248
|
}
|
|
1232
1249
|
#getSortedPlugins(hookName) {
|
|
@@ -1234,7 +1251,8 @@ var PluginManager = class {
|
|
|
1234
1251
|
if (hookName) return plugins.filter((plugin) => hookName in plugin);
|
|
1235
1252
|
return plugins.map((plugin) => {
|
|
1236
1253
|
if (plugin.pre) {
|
|
1237
|
-
|
|
1254
|
+
let missingPlugins = plugin.pre.filter((pluginName) => !plugins.find((pluginToFind) => pluginToFind.name === pluginName));
|
|
1255
|
+
if (missingPlugins.includes("plugin-oas") && this.adapter) missingPlugins = missingPlugins.filter((pluginName) => pluginName !== "plugin-oas");
|
|
1238
1256
|
if (missingPlugins.length > 0) throw new ValidationPluginError(`The plugin '${plugin.name}' has a pre set that references missing plugins for '${missingPlugins.join(", ")}'`);
|
|
1239
1257
|
}
|
|
1240
1258
|
return plugin;
|
|
@@ -1354,19 +1372,12 @@ var PluginManager = class {
|
|
|
1354
1372
|
}
|
|
1355
1373
|
};
|
|
1356
1374
|
//#endregion
|
|
1357
|
-
//#region src/
|
|
1375
|
+
//#region src/createStorage.ts
|
|
1358
1376
|
/**
|
|
1359
|
-
*
|
|
1360
|
-
* same factory pattern as `definePlugin`, `defineLogger`, and `defineAdapter`.
|
|
1361
|
-
*
|
|
1362
|
-
* The builder receives the resolved options object and must return a
|
|
1363
|
-
* `DefineStorage`-compatible object that includes a `name` string.
|
|
1377
|
+
* Creates a storage factory. Call the returned function with optional options to get the storage instance.
|
|
1364
1378
|
*
|
|
1365
1379
|
* @example
|
|
1366
|
-
*
|
|
1367
|
-
* import { defineStorage } from '@kubb/core'
|
|
1368
|
-
*
|
|
1369
|
-
* export const memoryStorage = defineStorage((_options) => {
|
|
1380
|
+
* export const memoryStorage = createStorage(() => {
|
|
1370
1381
|
* const store = new Map<string, string>()
|
|
1371
1382
|
* return {
|
|
1372
1383
|
* name: 'memory',
|
|
@@ -1374,13 +1385,15 @@ var PluginManager = class {
|
|
|
1374
1385
|
* async getItem(key) { return store.get(key) ?? null },
|
|
1375
1386
|
* async setItem(key, value) { store.set(key, value) },
|
|
1376
1387
|
* async removeItem(key) { store.delete(key) },
|
|
1377
|
-
* async getKeys() {
|
|
1378
|
-
*
|
|
1388
|
+
* async getKeys(base) {
|
|
1389
|
+
* const keys = [...store.keys()]
|
|
1390
|
+
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
1391
|
+
* },
|
|
1392
|
+
* async clear(base) { if (!base) store.clear() },
|
|
1379
1393
|
* }
|
|
1380
1394
|
* })
|
|
1381
|
-
* ```
|
|
1382
1395
|
*/
|
|
1383
|
-
function
|
|
1396
|
+
function createStorage(build) {
|
|
1384
1397
|
return (options) => build(options ?? {});
|
|
1385
1398
|
}
|
|
1386
1399
|
//#endregion
|
|
@@ -1408,7 +1421,7 @@ function defineStorage(build) {
|
|
|
1408
1421
|
* })
|
|
1409
1422
|
* ```
|
|
1410
1423
|
*/
|
|
1411
|
-
const fsStorage =
|
|
1424
|
+
const fsStorage = createStorage(() => ({
|
|
1412
1425
|
name: "fs",
|
|
1413
1426
|
async hasItem(key) {
|
|
1414
1427
|
try {
|
|
@@ -1456,11 +1469,14 @@ const fsStorage = defineStorage(() => ({
|
|
|
1456
1469
|
}));
|
|
1457
1470
|
//#endregion
|
|
1458
1471
|
//#region package.json
|
|
1459
|
-
var version = "5.0.0-alpha.
|
|
1472
|
+
var version = "5.0.0-alpha.20";
|
|
1460
1473
|
//#endregion
|
|
1461
1474
|
//#region src/utils/diagnostics.ts
|
|
1462
1475
|
/**
|
|
1463
|
-
*
|
|
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.
|
|
1464
1480
|
*/
|
|
1465
1481
|
function getDiagnosticInfo() {
|
|
1466
1482
|
return {
|
|
@@ -1473,6 +1489,17 @@ function getDiagnosticInfo() {
|
|
|
1473
1489
|
}
|
|
1474
1490
|
//#endregion
|
|
1475
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
|
+
*/
|
|
1476
1503
|
async function setup(options) {
|
|
1477
1504
|
const { config: userConfig, events = new AsyncEventEmitter() } = options;
|
|
1478
1505
|
const sources = /* @__PURE__ */ new Map();
|
|
@@ -1570,7 +1597,7 @@ async function setup(options) {
|
|
|
1570
1597
|
` • Barrel type: ${definedConfig.output.barrelType || "none"}`
|
|
1571
1598
|
]
|
|
1572
1599
|
});
|
|
1573
|
-
const
|
|
1600
|
+
const pluginDriver = new PluginDriver(definedConfig, {
|
|
1574
1601
|
fabric,
|
|
1575
1602
|
events,
|
|
1576
1603
|
concurrency: 15
|
|
@@ -1581,25 +1608,32 @@ async function setup(options) {
|
|
|
1581
1608
|
date: /* @__PURE__ */ new Date(),
|
|
1582
1609
|
logs: [`Running adapter: ${definedConfig.adapter.name}`]
|
|
1583
1610
|
});
|
|
1584
|
-
|
|
1611
|
+
pluginDriver.adapter = definedConfig.adapter;
|
|
1612
|
+
pluginDriver.rootNode = await definedConfig.adapter.parse(source);
|
|
1585
1613
|
await events.emit("debug", {
|
|
1586
1614
|
date: /* @__PURE__ */ new Date(),
|
|
1587
1615
|
logs: [
|
|
1588
1616
|
`✓ Adapter '${definedConfig.adapter.name}' resolved RootNode`,
|
|
1589
|
-
` • Schemas: ${
|
|
1590
|
-
` • Operations: ${
|
|
1617
|
+
` • Schemas: ${pluginDriver.rootNode.schemas.length}`,
|
|
1618
|
+
` • Operations: ${pluginDriver.rootNode.operations.length}`
|
|
1591
1619
|
]
|
|
1592
1620
|
});
|
|
1593
1621
|
}
|
|
1594
1622
|
return {
|
|
1595
1623
|
events,
|
|
1596
1624
|
fabric,
|
|
1597
|
-
|
|
1625
|
+
driver: pluginDriver,
|
|
1598
1626
|
sources
|
|
1599
1627
|
};
|
|
1600
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
|
+
*/
|
|
1601
1635
|
async function build(options, overrides) {
|
|
1602
|
-
const { fabric, files,
|
|
1636
|
+
const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides);
|
|
1603
1637
|
if (error) throw error;
|
|
1604
1638
|
if (failedPlugins.size > 0) {
|
|
1605
1639
|
const errors = [...failedPlugins].map(({ error }) => error);
|
|
@@ -1609,20 +1643,30 @@ async function build(options, overrides) {
|
|
|
1609
1643
|
failedPlugins,
|
|
1610
1644
|
fabric,
|
|
1611
1645
|
files,
|
|
1612
|
-
|
|
1646
|
+
driver,
|
|
1613
1647
|
pluginTimings,
|
|
1614
1648
|
error: void 0,
|
|
1615
1649
|
sources
|
|
1616
1650
|
};
|
|
1617
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
|
+
*/
|
|
1618
1662
|
async function safeBuild(options, overrides) {
|
|
1619
|
-
const { fabric,
|
|
1663
|
+
const { fabric, driver, events, sources } = overrides ? overrides : await setup(options);
|
|
1620
1664
|
const failedPlugins = /* @__PURE__ */ new Set();
|
|
1621
1665
|
const pluginTimings = /* @__PURE__ */ new Map();
|
|
1622
|
-
const config =
|
|
1666
|
+
const config = driver.config;
|
|
1623
1667
|
try {
|
|
1624
|
-
for (const plugin of
|
|
1625
|
-
const context =
|
|
1668
|
+
for (const plugin of driver.plugins) {
|
|
1669
|
+
const context = driver.getContext(plugin);
|
|
1626
1670
|
const hrStart = process.hrtime();
|
|
1627
1671
|
const installer = plugin.install.bind(context);
|
|
1628
1672
|
try {
|
|
@@ -1695,7 +1739,7 @@ async function safeBuild(options, overrides) {
|
|
|
1695
1739
|
rootDir,
|
|
1696
1740
|
existingExports: new Set(existingBarrel?.exports?.flatMap((e) => Array.isArray(e.name) ? e.name : [e.name]).filter((n) => Boolean(n)) ?? []),
|
|
1697
1741
|
config,
|
|
1698
|
-
|
|
1742
|
+
driver
|
|
1699
1743
|
}),
|
|
1700
1744
|
sources: [],
|
|
1701
1745
|
imports: [],
|
|
@@ -1713,7 +1757,7 @@ async function safeBuild(options, overrides) {
|
|
|
1713
1757
|
failedPlugins,
|
|
1714
1758
|
fabric,
|
|
1715
1759
|
files,
|
|
1716
|
-
|
|
1760
|
+
driver,
|
|
1717
1761
|
pluginTimings,
|
|
1718
1762
|
sources
|
|
1719
1763
|
};
|
|
@@ -1722,16 +1766,16 @@ async function safeBuild(options, overrides) {
|
|
|
1722
1766
|
failedPlugins,
|
|
1723
1767
|
fabric,
|
|
1724
1768
|
files: [],
|
|
1725
|
-
|
|
1769
|
+
driver,
|
|
1726
1770
|
pluginTimings,
|
|
1727
1771
|
error,
|
|
1728
1772
|
sources
|
|
1729
1773
|
};
|
|
1730
1774
|
}
|
|
1731
1775
|
}
|
|
1732
|
-
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config,
|
|
1776
|
+
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }) {
|
|
1733
1777
|
const pluginNameMap = /* @__PURE__ */ new Map();
|
|
1734
|
-
for (const plugin of
|
|
1778
|
+
for (const plugin of driver.plugins) pluginNameMap.set(plugin.name, plugin);
|
|
1735
1779
|
return barrelFiles.flatMap((file) => {
|
|
1736
1780
|
const containsOnlyTypes = file.sources?.every((source) => source.isTypeOnly);
|
|
1737
1781
|
return (file.sources ?? []).flatMap((source) => {
|
|
@@ -1756,133 +1800,301 @@ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, plu
|
|
|
1756
1800
|
function inputToAdapterSource(config) {
|
|
1757
1801
|
if (Array.isArray(config.input)) return {
|
|
1758
1802
|
type: "paths",
|
|
1759
|
-
paths: config.input.map((i) => (0, node_path.resolve)(config.root, i.path))
|
|
1803
|
+
paths: config.input.map((i) => new URLPath(i.path).isURL ? i.path : (0, node_path.resolve)(config.root, i.path))
|
|
1760
1804
|
};
|
|
1761
1805
|
if ("data" in config.input) return {
|
|
1762
1806
|
type: "data",
|
|
1763
1807
|
data: config.input.data
|
|
1764
1808
|
};
|
|
1809
|
+
if (new URLPath(config.input.path).isURL) return {
|
|
1810
|
+
type: "path",
|
|
1811
|
+
path: config.input.path
|
|
1812
|
+
};
|
|
1765
1813
|
return {
|
|
1766
1814
|
type: "path",
|
|
1767
1815
|
path: (0, node_path.resolve)(config.root, config.input.path)
|
|
1768
1816
|
};
|
|
1769
1817
|
}
|
|
1770
1818
|
//#endregion
|
|
1771
|
-
//#region src/
|
|
1819
|
+
//#region src/createAdapter.ts
|
|
1772
1820
|
/**
|
|
1773
|
-
*
|
|
1821
|
+
* Creates an adapter factory. Call the returned function with optional options to get the adapter instance.
|
|
1774
1822
|
*
|
|
1775
1823
|
* @example
|
|
1776
|
-
*
|
|
1777
|
-
* export const adapterOas = defineAdapter<OasAdapter>((options) => {
|
|
1778
|
-
* const { validate = true, dateType = 'string' } = options
|
|
1824
|
+
* export const myAdapter = createAdapter<MyAdapter>((options) => {
|
|
1779
1825
|
* return {
|
|
1780
|
-
* name:
|
|
1781
|
-
* options
|
|
1782
|
-
* parse(source) { ... },
|
|
1826
|
+
* name: 'my-adapter',
|
|
1827
|
+
* options,
|
|
1828
|
+
* async parse(source) { ... },
|
|
1783
1829
|
* }
|
|
1784
1830
|
* })
|
|
1785
|
-
*
|
|
1831
|
+
*
|
|
1832
|
+
* // instantiate
|
|
1833
|
+
* const adapter = myAdapter({ validate: true })
|
|
1834
|
+
*/
|
|
1835
|
+
function createAdapter(build) {
|
|
1836
|
+
return (options) => build(options ?? {});
|
|
1837
|
+
}
|
|
1838
|
+
//#endregion
|
|
1839
|
+
//#region src/createPlugin.ts
|
|
1840
|
+
/**
|
|
1841
|
+
* Creates a plugin factory. Call the returned function with optional options to get the plugin instance.
|
|
1842
|
+
*
|
|
1843
|
+
* @example
|
|
1844
|
+
* export const myPlugin = createPlugin<MyPlugin>((options) => {
|
|
1845
|
+
* return {
|
|
1846
|
+
* name: 'my-plugin',
|
|
1847
|
+
* options,
|
|
1848
|
+
* resolvePath(baseName) { ... },
|
|
1849
|
+
* resolveName(name, type) { ... },
|
|
1850
|
+
* }
|
|
1851
|
+
* })
|
|
1852
|
+
*
|
|
1853
|
+
* // instantiate
|
|
1854
|
+
* const plugin = myPlugin({ output: { path: 'src/gen' } })
|
|
1786
1855
|
*/
|
|
1787
|
-
function
|
|
1856
|
+
function createPlugin(build) {
|
|
1788
1857
|
return (options) => build(options ?? {});
|
|
1789
1858
|
}
|
|
1790
1859
|
//#endregion
|
|
1860
|
+
//#region src/defineGenerator.ts
|
|
1861
|
+
function defineGenerator(generator) {
|
|
1862
|
+
if (generator.type === "react") return {
|
|
1863
|
+
version: "2",
|
|
1864
|
+
Operations() {
|
|
1865
|
+
return null;
|
|
1866
|
+
},
|
|
1867
|
+
Operation() {
|
|
1868
|
+
return null;
|
|
1869
|
+
},
|
|
1870
|
+
Schema() {
|
|
1871
|
+
return null;
|
|
1872
|
+
},
|
|
1873
|
+
...generator
|
|
1874
|
+
};
|
|
1875
|
+
return {
|
|
1876
|
+
version: "2",
|
|
1877
|
+
async operations() {
|
|
1878
|
+
return [];
|
|
1879
|
+
},
|
|
1880
|
+
async operation() {
|
|
1881
|
+
return [];
|
|
1882
|
+
},
|
|
1883
|
+
async schema() {
|
|
1884
|
+
return [];
|
|
1885
|
+
},
|
|
1886
|
+
...generator
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
//#endregion
|
|
1791
1890
|
//#region src/defineLogger.ts
|
|
1891
|
+
/**
|
|
1892
|
+
* Wraps a logger definition into a typed {@link Logger}.
|
|
1893
|
+
*
|
|
1894
|
+
* @example
|
|
1895
|
+
* export const myLogger = defineLogger({
|
|
1896
|
+
* name: 'my-logger',
|
|
1897
|
+
* install(context, options) {
|
|
1898
|
+
* context.on('info', (message) => console.log('ℹ', message))
|
|
1899
|
+
* context.on('error', (error) => console.error('✗', error.message))
|
|
1900
|
+
* },
|
|
1901
|
+
* })
|
|
1902
|
+
*/
|
|
1792
1903
|
function defineLogger(logger) {
|
|
1793
|
-
return
|
|
1904
|
+
return logger;
|
|
1794
1905
|
}
|
|
1795
1906
|
//#endregion
|
|
1796
|
-
//#region src/
|
|
1907
|
+
//#region src/definePreset.ts
|
|
1797
1908
|
/**
|
|
1798
|
-
*
|
|
1909
|
+
* Creates a typed preset object that bundles a name, resolvers, and optional
|
|
1910
|
+
* transformers — the building block for composable plugin presets.
|
|
1911
|
+
*
|
|
1912
|
+
* @example
|
|
1913
|
+
* import { definePreset } from '@kubb/core'
|
|
1914
|
+
* import { resolverTsLegacy } from '@kubb/plugin-ts'
|
|
1915
|
+
*
|
|
1916
|
+
* export const myPreset = definePreset('myPreset', { resolvers: [resolverTsLegacy] })
|
|
1917
|
+
*
|
|
1918
|
+
* @example
|
|
1919
|
+
* // With custom transformers
|
|
1920
|
+
* export const myPreset = definePreset('myPreset', { resolvers: [resolverTsLegacy], transformers: [myTransformer] })
|
|
1799
1921
|
*/
|
|
1800
|
-
function
|
|
1801
|
-
return
|
|
1922
|
+
function definePreset(name, { resolvers, transformers }) {
|
|
1923
|
+
return {
|
|
1924
|
+
name,
|
|
1925
|
+
resolvers,
|
|
1926
|
+
transformers
|
|
1927
|
+
};
|
|
1802
1928
|
}
|
|
1803
1929
|
//#endregion
|
|
1804
|
-
//#region src/
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1930
|
+
//#region src/definePresets.ts
|
|
1931
|
+
/**
|
|
1932
|
+
* Creates a typed presets registry object — a named collection of {@link Preset} entries.
|
|
1933
|
+
*
|
|
1934
|
+
* @example
|
|
1935
|
+
* import { definePreset, definePresets } from '@kubb/core'
|
|
1936
|
+
* import { resolverTsLegacy } from '@kubb/plugin-ts'
|
|
1937
|
+
*
|
|
1938
|
+
* export const myPresets = definePresets({
|
|
1939
|
+
* kubbV4: definePreset('kubbV4', { resolvers: [resolverTsLegacy] }),
|
|
1940
|
+
* })
|
|
1941
|
+
*/
|
|
1942
|
+
function definePresets(presets) {
|
|
1943
|
+
return presets;
|
|
1944
|
+
}
|
|
1945
|
+
//#endregion
|
|
1946
|
+
//#region src/defineResolver.ts
|
|
1947
|
+
/**
|
|
1948
|
+
* Checks if an operation matches a pattern for a given filter type (`tag`, `operationId`, `path`, `method`).
|
|
1949
|
+
*/
|
|
1950
|
+
function matchesOperationPattern(node, type, pattern) {
|
|
1951
|
+
switch (type) {
|
|
1952
|
+
case "tag": return node.tags.some((tag) => !!tag.match(pattern));
|
|
1953
|
+
case "operationId": return !!node.operationId.match(pattern);
|
|
1954
|
+
case "path": return !!node.path.match(pattern);
|
|
1955
|
+
case "method": return !!node.method.toLowerCase().match(pattern);
|
|
1956
|
+
default: return false;
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* Checks if a schema matches a pattern for a given filter type (`schemaName`).
|
|
1961
|
+
* Returns `null` when the filter type doesn't apply to schemas.
|
|
1962
|
+
*/
|
|
1963
|
+
function matchesSchemaPattern(node, type, pattern) {
|
|
1964
|
+
switch (type) {
|
|
1965
|
+
case "schemaName": return node.name ? !!node.name.match(pattern) : false;
|
|
1966
|
+
default: return null;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* Default name resolver — `camelCase` for most types, `PascalCase` for `type`.
|
|
1971
|
+
*/
|
|
1972
|
+
function defaultResolver(name, type) {
|
|
1973
|
+
let resolvedName = camelCase(name);
|
|
1974
|
+
if (type === "file" || type === "function") resolvedName = camelCase(name, { isFile: type === "file" });
|
|
1975
|
+
if (type === "type") resolvedName = pascalCase(name);
|
|
1976
|
+
return resolvedName;
|
|
1977
|
+
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Default option resolver — applies include/exclude filters and merges any matching override options.
|
|
1980
|
+
* Returns `null` when the node is filtered out.
|
|
1981
|
+
*/
|
|
1982
|
+
function defaultResolveOptions(node, { options, exclude = [], include, override = [] }) {
|
|
1983
|
+
if ((0, _kubb_ast.isOperationNode)(node)) {
|
|
1984
|
+
if (exclude.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
1985
|
+
if (include && !include.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
1986
|
+
const overrideOptions = override.find(({ type, pattern }) => matchesOperationPattern(node, type, pattern))?.options;
|
|
1987
|
+
return {
|
|
1988
|
+
...options,
|
|
1989
|
+
...overrideOptions
|
|
1852
1990
|
};
|
|
1853
|
-
if (typeof dependency === "string" && dependencies[dependency]) return dependencies[dependency];
|
|
1854
|
-
const matchedDependency = Object.keys(dependencies).find((dep) => dep.match(dependency));
|
|
1855
|
-
return matchedDependency ? dependencies[matchedDependency] : void 0;
|
|
1856
|
-
}
|
|
1857
|
-
async getVersion(dependency) {
|
|
1858
|
-
if (typeof dependency === "string" && PackageManager.#cache[dependency]) return PackageManager.#cache[dependency];
|
|
1859
|
-
const packageJSON = await this.getPackageJSON();
|
|
1860
|
-
if (!packageJSON) return;
|
|
1861
|
-
return this.#match(packageJSON, dependency);
|
|
1862
|
-
}
|
|
1863
|
-
getVersionSync(dependency) {
|
|
1864
|
-
if (typeof dependency === "string" && PackageManager.#cache[dependency]) return PackageManager.#cache[dependency];
|
|
1865
|
-
const packageJSON = this.getPackageJSONSync();
|
|
1866
|
-
if (!packageJSON) return;
|
|
1867
|
-
return this.#match(packageJSON, dependency);
|
|
1868
|
-
}
|
|
1869
|
-
async isValid(dependency, version) {
|
|
1870
|
-
const packageVersion = await this.getVersion(dependency);
|
|
1871
|
-
if (!packageVersion) return false;
|
|
1872
|
-
if (packageVersion === version) return true;
|
|
1873
|
-
const semVer = (0, semver.coerce)(packageVersion);
|
|
1874
|
-
if (!semVer) return false;
|
|
1875
|
-
return (0, semver.satisfies)(semVer, version);
|
|
1876
|
-
}
|
|
1877
|
-
isValidSync(dependency, version) {
|
|
1878
|
-
const packageVersion = this.getVersionSync(dependency);
|
|
1879
|
-
if (!packageVersion) return false;
|
|
1880
|
-
if (packageVersion === version) return true;
|
|
1881
|
-
const semVer = (0, semver.coerce)(packageVersion);
|
|
1882
|
-
if (!semVer) return false;
|
|
1883
|
-
return (0, semver.satisfies)(semVer, version);
|
|
1884
1991
|
}
|
|
1885
|
-
|
|
1992
|
+
if ((0, _kubb_ast.isSchemaNode)(node)) {
|
|
1993
|
+
if (exclude.some(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)) return null;
|
|
1994
|
+
if (include) {
|
|
1995
|
+
const applicable = include.map(({ type, pattern }) => matchesSchemaPattern(node, type, pattern)).filter((r) => r !== null);
|
|
1996
|
+
if (applicable.length > 0 && !applicable.includes(true)) return null;
|
|
1997
|
+
}
|
|
1998
|
+
const overrideOptions = override.find(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)?.options;
|
|
1999
|
+
return {
|
|
2000
|
+
...options,
|
|
2001
|
+
...overrideOptions
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
return options;
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* Defines a resolver for a plugin, with built-in defaults for name casing and include/exclude/override filtering.
|
|
2008
|
+
* Override `default` or `resolveOptions` in the builder to customize the behavior.
|
|
2009
|
+
*
|
|
2010
|
+
* @example
|
|
2011
|
+
* export const resolver = defineResolver<PluginTs>(() => ({
|
|
2012
|
+
* name: 'default',
|
|
2013
|
+
* resolveName(name) {
|
|
2014
|
+
* return this.default(name, 'function')
|
|
2015
|
+
* },
|
|
2016
|
+
* resolveTypedName(name) {
|
|
2017
|
+
* return this.default(name, 'type')
|
|
2018
|
+
* },
|
|
2019
|
+
* resolveParamName(node, param) {
|
|
2020
|
+
* return this.resolveName(`${node.operationId} ${param.in} ${param.name}`)
|
|
2021
|
+
* },
|
|
2022
|
+
* }))
|
|
2023
|
+
*/
|
|
2024
|
+
function defineResolver(build) {
|
|
2025
|
+
return {
|
|
2026
|
+
default: defaultResolver,
|
|
2027
|
+
resolveOptions: defaultResolveOptions,
|
|
2028
|
+
...build()
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
//#endregion
|
|
2032
|
+
//#region src/renderNode.tsx
|
|
2033
|
+
/**
|
|
2034
|
+
* Renders a React component for a list of operation nodes (V2 generators).
|
|
2035
|
+
*/
|
|
2036
|
+
async function renderOperations(nodes, options) {
|
|
2037
|
+
const { config, fabric, plugin, Component, adapter } = options;
|
|
2038
|
+
if (!Component) return;
|
|
2039
|
+
const fabricChild = (0, _kubb_react_fabric.createReactFabric)();
|
|
2040
|
+
await fabricChild.render(/* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(_kubb_react_fabric.Fabric, {
|
|
2041
|
+
meta: { plugin },
|
|
2042
|
+
children: /* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(Component, {
|
|
2043
|
+
config,
|
|
2044
|
+
adapter,
|
|
2045
|
+
nodes,
|
|
2046
|
+
options: options.options
|
|
2047
|
+
})
|
|
2048
|
+
}));
|
|
2049
|
+
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
2050
|
+
fabricChild.unmount();
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Renders a React component for a single operation node (V2 generators).
|
|
2054
|
+
*/
|
|
2055
|
+
async function renderOperation(node, options) {
|
|
2056
|
+
const { config, fabric, plugin, Component, adapter, driver, mode } = options;
|
|
2057
|
+
if (!Component) return;
|
|
2058
|
+
const fabricChild = (0, _kubb_react_fabric.createReactFabric)();
|
|
2059
|
+
await fabricChild.render(/* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(_kubb_react_fabric.Fabric, {
|
|
2060
|
+
meta: {
|
|
2061
|
+
plugin,
|
|
2062
|
+
driver,
|
|
2063
|
+
mode
|
|
2064
|
+
},
|
|
2065
|
+
children: /* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(Component, {
|
|
2066
|
+
config,
|
|
2067
|
+
adapter,
|
|
2068
|
+
node,
|
|
2069
|
+
options: options.options
|
|
2070
|
+
})
|
|
2071
|
+
}));
|
|
2072
|
+
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
2073
|
+
fabricChild.unmount();
|
|
2074
|
+
}
|
|
2075
|
+
/**
|
|
2076
|
+
* Renders a React component for a single schema node (V2 generators).
|
|
2077
|
+
*/
|
|
2078
|
+
async function renderSchema(node, options) {
|
|
2079
|
+
const { config, fabric, plugin, Component, adapter, driver, mode } = options;
|
|
2080
|
+
if (!Component) return;
|
|
2081
|
+
const fabricChild = (0, _kubb_react_fabric.createReactFabric)();
|
|
2082
|
+
await fabricChild.render(/* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(_kubb_react_fabric.Fabric, {
|
|
2083
|
+
meta: {
|
|
2084
|
+
plugin,
|
|
2085
|
+
driver,
|
|
2086
|
+
mode
|
|
2087
|
+
},
|
|
2088
|
+
children: /* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(Component, {
|
|
2089
|
+
config,
|
|
2090
|
+
adapter,
|
|
2091
|
+
node,
|
|
2092
|
+
options: options.options
|
|
2093
|
+
})
|
|
2094
|
+
}));
|
|
2095
|
+
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
2096
|
+
fabricChild.unmount();
|
|
2097
|
+
}
|
|
1886
2098
|
//#endregion
|
|
1887
2099
|
//#region src/storages/memoryStorage.ts
|
|
1888
2100
|
/**
|
|
@@ -1902,7 +2114,7 @@ var PackageManager = class PackageManager {
|
|
|
1902
2114
|
* })
|
|
1903
2115
|
* ```
|
|
1904
2116
|
*/
|
|
1905
|
-
const memoryStorage =
|
|
2117
|
+
const memoryStorage = createStorage(() => {
|
|
1906
2118
|
const store = /* @__PURE__ */ new Map();
|
|
1907
2119
|
return {
|
|
1908
2120
|
name: "memory",
|
|
@@ -1934,7 +2146,7 @@ const memoryStorage = defineStorage(() => {
|
|
|
1934
2146
|
//#endregion
|
|
1935
2147
|
//#region src/utils/FunctionParams.ts
|
|
1936
2148
|
/**
|
|
1937
|
-
* @deprecated
|
|
2149
|
+
* @deprecated use ast package instead
|
|
1938
2150
|
*/
|
|
1939
2151
|
var FunctionParams = class FunctionParams {
|
|
1940
2152
|
#items = [];
|
|
@@ -2010,14 +2222,10 @@ var FunctionParams = class FunctionParams {
|
|
|
2010
2222
|
//#endregion
|
|
2011
2223
|
//#region src/utils/formatters.ts
|
|
2012
2224
|
/**
|
|
2013
|
-
*
|
|
2225
|
+
* Returns `true` when the given formatter is installed and callable.
|
|
2014
2226
|
*
|
|
2015
|
-
*
|
|
2016
|
-
*
|
|
2017
|
-
*
|
|
2018
|
-
* @remarks
|
|
2019
|
-
* This function checks availability by running `<formatter> --version` command.
|
|
2020
|
-
* 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.
|
|
2021
2229
|
*/
|
|
2022
2230
|
async function isFormatterAvailable(formatter) {
|
|
2023
2231
|
try {
|
|
@@ -2028,34 +2236,39 @@ async function isFormatterAvailable(formatter) {
|
|
|
2028
2236
|
}
|
|
2029
2237
|
}
|
|
2030
2238
|
/**
|
|
2031
|
-
*
|
|
2032
|
-
*
|
|
2033
|
-
* @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.
|
|
2034
2240
|
*
|
|
2035
|
-
*
|
|
2036
|
-
*
|
|
2037
|
-
* Uses the `--version` flag to detect if each formatter command is available.
|
|
2038
|
-
* 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.
|
|
2039
2243
|
*
|
|
2040
2244
|
* @example
|
|
2041
|
-
* ```
|
|
2245
|
+
* ```ts
|
|
2042
2246
|
* const formatter = await detectFormatter()
|
|
2043
2247
|
* if (formatter) {
|
|
2044
2248
|
* console.log(`Using ${formatter} for formatting`)
|
|
2045
|
-
* } else {
|
|
2046
|
-
* console.log('No formatter found')
|
|
2047
2249
|
* }
|
|
2048
2250
|
* ```
|
|
2049
2251
|
*/
|
|
2050
2252
|
async function detectFormatter() {
|
|
2051
|
-
|
|
2253
|
+
const formatterNames = new Set([
|
|
2052
2254
|
"biome",
|
|
2053
2255
|
"oxfmt",
|
|
2054
2256
|
"prettier"
|
|
2055
|
-
])
|
|
2257
|
+
]);
|
|
2258
|
+
for (const formatter of formatterNames) if (await isFormatterAvailable(formatter)) return formatter;
|
|
2259
|
+
return null;
|
|
2056
2260
|
}
|
|
2057
2261
|
//#endregion
|
|
2058
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
|
+
*/
|
|
2059
2272
|
var TreeNode = class TreeNode {
|
|
2060
2273
|
data;
|
|
2061
2274
|
parent;
|
|
@@ -2071,10 +2284,18 @@ var TreeNode = class TreeNode {
|
|
|
2071
2284
|
this.children.push(child);
|
|
2072
2285
|
return child;
|
|
2073
2286
|
}
|
|
2287
|
+
/**
|
|
2288
|
+
* Returns the root ancestor of this node, walking up via `parent` links.
|
|
2289
|
+
*/
|
|
2074
2290
|
get root() {
|
|
2075
2291
|
if (!this.parent) return this;
|
|
2076
2292
|
return this.parent.root;
|
|
2077
2293
|
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Returns all leaf descendants (nodes with no children) of this node.
|
|
2296
|
+
*
|
|
2297
|
+
* Results are cached after the first traversal.
|
|
2298
|
+
*/
|
|
2078
2299
|
get leaves() {
|
|
2079
2300
|
if (!this.children || this.children.length === 0) return [this];
|
|
2080
2301
|
if (this.#cachedLeaves) return this.#cachedLeaves;
|
|
@@ -2105,6 +2326,12 @@ var TreeNode = class TreeNode {
|
|
|
2105
2326
|
if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
|
|
2106
2327
|
return this.leaves.map(callback);
|
|
2107
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
|
+
*/
|
|
2108
2335
|
static build(files, root) {
|
|
2109
2336
|
try {
|
|
2110
2337
|
const filteredTree = buildDirectoryTree(files, root);
|
|
@@ -2175,65 +2402,64 @@ function buildDirectoryTree(files, rootFolder = "") {
|
|
|
2175
2402
|
return root;
|
|
2176
2403
|
}
|
|
2177
2404
|
//#endregion
|
|
2178
|
-
//#region src/
|
|
2405
|
+
//#region src/utils/getBarrelFiles.ts
|
|
2179
2406
|
/** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
(item.data.file?.
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
isIndexable: false
|
|
2209
|
-
});
|
|
2407
|
+
function getBarrelFilesByRoot(root, files) {
|
|
2408
|
+
const cachedFiles = /* @__PURE__ */ new Map();
|
|
2409
|
+
TreeNode.build(files, root)?.forEach((treeNode) => {
|
|
2410
|
+
if (!treeNode || !treeNode.children || !treeNode.parent?.data.path) return;
|
|
2411
|
+
const barrelFile = {
|
|
2412
|
+
path: (0, node_path.join)(treeNode.parent?.data.path, "index.ts"),
|
|
2413
|
+
baseName: "index.ts",
|
|
2414
|
+
exports: [],
|
|
2415
|
+
imports: [],
|
|
2416
|
+
sources: []
|
|
2417
|
+
};
|
|
2418
|
+
const previousBarrelFile = cachedFiles.get(barrelFile.path);
|
|
2419
|
+
treeNode.leaves.forEach((item) => {
|
|
2420
|
+
if (!item.data.name) return;
|
|
2421
|
+
(item.data.file?.sources || []).forEach((source) => {
|
|
2422
|
+
if (!item.data.file?.path || !source.isIndexable || !source.name) return;
|
|
2423
|
+
if (previousBarrelFile?.sources.some((item) => item.name === source.name && item.isTypeOnly === source.isTypeOnly)) return;
|
|
2424
|
+
barrelFile.exports.push({
|
|
2425
|
+
name: [source.name],
|
|
2426
|
+
path: getRelativePath(treeNode.parent?.data.path, item.data.path),
|
|
2427
|
+
isTypeOnly: source.isTypeOnly
|
|
2428
|
+
});
|
|
2429
|
+
barrelFile.sources.push({
|
|
2430
|
+
name: source.name,
|
|
2431
|
+
isTypeOnly: source.isTypeOnly,
|
|
2432
|
+
value: "",
|
|
2433
|
+
isExportable: false,
|
|
2434
|
+
isIndexable: false
|
|
2210
2435
|
});
|
|
2211
2436
|
});
|
|
2212
|
-
if (previousBarrelFile) {
|
|
2213
|
-
previousBarrelFile.sources.push(...barrelFile.sources);
|
|
2214
|
-
previousBarrelFile.exports?.push(...barrelFile.exports || []);
|
|
2215
|
-
} else cachedFiles.set(barrelFile.path, barrelFile);
|
|
2216
2437
|
});
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2438
|
+
if (previousBarrelFile) {
|
|
2439
|
+
previousBarrelFile.sources.push(...barrelFile.sources);
|
|
2440
|
+
previousBarrelFile.exports?.push(...barrelFile.exports || []);
|
|
2441
|
+
} else cachedFiles.set(barrelFile.path, barrelFile);
|
|
2442
|
+
});
|
|
2443
|
+
return [...cachedFiles.values()];
|
|
2444
|
+
}
|
|
2222
2445
|
function trimExtName(text) {
|
|
2223
2446
|
const dotIndex = text.lastIndexOf(".");
|
|
2224
2447
|
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
2225
2448
|
return text;
|
|
2226
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
|
+
*/
|
|
2227
2458
|
async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
2228
2459
|
if (!type || type === "propagate") return [];
|
|
2229
|
-
const barrelManager = new BarrelManager();
|
|
2230
2460
|
const pathToBuildFrom = (0, node_path.join)(root, output.path);
|
|
2231
2461
|
if (trimExtName(pathToBuildFrom).endsWith("index")) return [];
|
|
2232
|
-
const barrelFiles =
|
|
2233
|
-
files,
|
|
2234
|
-
root: pathToBuildFrom,
|
|
2235
|
-
meta
|
|
2236
|
-
});
|
|
2462
|
+
const barrelFiles = getBarrelFilesByRoot(pathToBuildFrom, files);
|
|
2237
2463
|
if (type === "all") return barrelFiles.map((file) => {
|
|
2238
2464
|
return {
|
|
2239
2465
|
...file,
|
|
@@ -2253,38 +2479,58 @@ async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
|
2253
2479
|
});
|
|
2254
2480
|
}
|
|
2255
2481
|
//#endregion
|
|
2256
|
-
//#region src/utils/
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2482
|
+
//#region src/utils/getConfigs.ts
|
|
2483
|
+
/**
|
|
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.
|
|
2489
|
+
*/
|
|
2490
|
+
async function getConfigs(config, args) {
|
|
2491
|
+
const resolved = await (typeof config === "function" ? config(args) : config);
|
|
2492
|
+
return (Array.isArray(resolved) ? resolved : [resolved]).map((item) => ({ ...item }));
|
|
2262
2493
|
}
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2494
|
+
//#endregion
|
|
2495
|
+
//#region src/utils/mergeResolvers.ts
|
|
2496
|
+
/**
|
|
2497
|
+
* Merges an array of resolvers into a single resolver. Later entries override earlier ones (last wins).
|
|
2498
|
+
*/
|
|
2499
|
+
function mergeResolvers(...resolvers) {
|
|
2500
|
+
return resolvers.reduce((acc, curr) => ({
|
|
2501
|
+
...acc,
|
|
2502
|
+
...curr
|
|
2503
|
+
}), resolvers[0]);
|
|
2267
2504
|
}
|
|
2268
2505
|
//#endregion
|
|
2269
|
-
//#region src/utils/
|
|
2506
|
+
//#region src/utils/getPreset.ts
|
|
2270
2507
|
/**
|
|
2271
|
-
*
|
|
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.
|
|
2272
2513
|
*/
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
const
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
}
|
|
2284
|
-
return results;
|
|
2514
|
+
function getPreset(params) {
|
|
2515
|
+
const { preset: presetName, presets, resolvers, transformers: userTransformers } = params;
|
|
2516
|
+
const [defaultResolver, ...userResolvers] = resolvers;
|
|
2517
|
+
const preset = presets[presetName];
|
|
2518
|
+
const baseResolver = mergeResolvers(defaultResolver, ...preset?.resolvers ?? []);
|
|
2519
|
+
return {
|
|
2520
|
+
baseResolver,
|
|
2521
|
+
resolver: mergeResolvers(baseResolver, ...userResolvers ?? []),
|
|
2522
|
+
transformers: [...preset?.transformers ?? [], ...userTransformers ?? []],
|
|
2523
|
+
preset
|
|
2524
|
+
};
|
|
2285
2525
|
}
|
|
2286
2526
|
//#endregion
|
|
2287
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
|
+
*/
|
|
2288
2534
|
async function isLinterAvailable(linter) {
|
|
2289
2535
|
try {
|
|
2290
2536
|
await (0, tinyexec.x)(linter, ["--version"], { nodeOptions: { stdio: "ignore" } });
|
|
@@ -2293,33 +2539,95 @@ async function isLinterAvailable(linter) {
|
|
|
2293
2539
|
return false;
|
|
2294
2540
|
}
|
|
2295
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
|
+
*/
|
|
2296
2556
|
async function detectLinter() {
|
|
2297
|
-
|
|
2557
|
+
const linterNames = new Set([
|
|
2298
2558
|
"biome",
|
|
2299
2559
|
"oxlint",
|
|
2300
2560
|
"eslint"
|
|
2301
|
-
])
|
|
2561
|
+
]);
|
|
2562
|
+
for (const linter of linterNames) if (await isLinterAvailable(linter)) return linter;
|
|
2563
|
+
return null;
|
|
2564
|
+
}
|
|
2565
|
+
//#endregion
|
|
2566
|
+
//#region src/utils/packageJSON.ts
|
|
2567
|
+
function getPackageJSONSync(cwd) {
|
|
2568
|
+
const pkgPath = empathic_package.up({ cwd });
|
|
2569
|
+
if (!pkgPath) return null;
|
|
2570
|
+
return JSON.parse(readSync(pkgPath));
|
|
2571
|
+
}
|
|
2572
|
+
function match(packageJSON, dependency) {
|
|
2573
|
+
const dependencies = {
|
|
2574
|
+
...packageJSON.dependencies || {},
|
|
2575
|
+
...packageJSON.devDependencies || {}
|
|
2576
|
+
};
|
|
2577
|
+
if (typeof dependency === "string" && dependencies[dependency]) return dependencies[dependency];
|
|
2578
|
+
const matched = Object.keys(dependencies).find((dep) => dep.match(dependency));
|
|
2579
|
+
return matched ? dependencies[matched] ?? null : null;
|
|
2580
|
+
}
|
|
2581
|
+
function getVersionSync(dependency, cwd) {
|
|
2582
|
+
const packageJSON = getPackageJSONSync(cwd);
|
|
2583
|
+
return packageJSON ? match(packageJSON, dependency) : null;
|
|
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
|
+
*/
|
|
2600
|
+
function satisfiesDependency(dependency, version, cwd) {
|
|
2601
|
+
const packageVersion = getVersionSync(dependency, cwd);
|
|
2602
|
+
if (!packageVersion) return false;
|
|
2603
|
+
if (packageVersion === version) return true;
|
|
2604
|
+
const semVer = (0, semver.coerce)(packageVersion);
|
|
2605
|
+
if (!semVer) return false;
|
|
2606
|
+
return (0, semver.satisfies)(semVer, version);
|
|
2302
2607
|
}
|
|
2303
2608
|
//#endregion
|
|
2304
2609
|
exports.AsyncEventEmitter = AsyncEventEmitter;
|
|
2305
2610
|
exports.FunctionParams = FunctionParams;
|
|
2306
|
-
exports.
|
|
2307
|
-
exports.PluginManager = PluginManager;
|
|
2308
|
-
exports.PromiseManager = PromiseManager;
|
|
2611
|
+
exports.PluginDriver = PluginDriver;
|
|
2309
2612
|
exports.URLPath = URLPath;
|
|
2310
2613
|
exports.build = build;
|
|
2614
|
+
exports.createAdapter = createAdapter;
|
|
2615
|
+
exports.createPlugin = createPlugin;
|
|
2616
|
+
exports.createStorage = createStorage;
|
|
2311
2617
|
exports.default = build;
|
|
2312
|
-
exports.
|
|
2618
|
+
exports.defaultResolveOptions = defaultResolveOptions;
|
|
2313
2619
|
exports.defineConfig = defineConfig;
|
|
2620
|
+
exports.defineGenerator = defineGenerator;
|
|
2314
2621
|
exports.defineLogger = defineLogger;
|
|
2315
|
-
exports.
|
|
2622
|
+
exports.definePreset = definePreset;
|
|
2623
|
+
exports.definePresets = definePresets;
|
|
2316
2624
|
Object.defineProperty(exports, "definePrinter", {
|
|
2317
2625
|
enumerable: true,
|
|
2318
2626
|
get: function() {
|
|
2319
2627
|
return _kubb_ast.definePrinter;
|
|
2320
2628
|
}
|
|
2321
2629
|
});
|
|
2322
|
-
exports.
|
|
2630
|
+
exports.defineResolver = defineResolver;
|
|
2323
2631
|
exports.detectFormatter = detectFormatter;
|
|
2324
2632
|
exports.detectLinter = detectLinter;
|
|
2325
2633
|
exports.formatters = formatters;
|
|
@@ -2327,11 +2635,17 @@ exports.fsStorage = fsStorage;
|
|
|
2327
2635
|
exports.getBarrelFiles = getBarrelFiles;
|
|
2328
2636
|
exports.getConfigs = getConfigs;
|
|
2329
2637
|
exports.getMode = getMode;
|
|
2638
|
+
exports.getPreset = getPreset;
|
|
2330
2639
|
exports.isInputPath = isInputPath;
|
|
2331
2640
|
exports.linters = linters;
|
|
2332
2641
|
exports.logLevel = logLevel;
|
|
2333
2642
|
exports.memoryStorage = memoryStorage;
|
|
2643
|
+
exports.mergeResolvers = mergeResolvers;
|
|
2644
|
+
exports.renderOperation = renderOperation;
|
|
2645
|
+
exports.renderOperations = renderOperations;
|
|
2646
|
+
exports.renderSchema = renderSchema;
|
|
2334
2647
|
exports.safeBuild = safeBuild;
|
|
2648
|
+
exports.satisfiesDependency = satisfiesDependency;
|
|
2335
2649
|
exports.setup = setup;
|
|
2336
2650
|
|
|
2337
2651
|
//# sourceMappingURL=index.cjs.map
|