@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.js
CHANGED
|
@@ -1,29 +1,37 @@
|
|
|
1
1
|
import "./chunk--u3MIqq1.js";
|
|
2
|
-
import mod from "node:module";
|
|
3
2
|
import { EventEmitter } from "node:events";
|
|
4
|
-
import { parseArgs, styleText } from "node:util";
|
|
5
3
|
import { readFileSync } from "node:fs";
|
|
6
4
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
7
|
-
import path, { dirname, join, posix, relative, resolve } from "node:path";
|
|
8
|
-
import { definePrinter } from "@kubb/ast";
|
|
9
|
-
import { createFabric } from "@kubb/react-fabric";
|
|
5
|
+
import path, { basename, dirname, extname, join, posix, relative, resolve } from "node:path";
|
|
6
|
+
import { definePrinter, isOperationNode, isSchemaNode } from "@kubb/ast";
|
|
7
|
+
import { Fabric, createFabric, createReactFabric } from "@kubb/react-fabric";
|
|
10
8
|
import { typescriptParser } from "@kubb/react-fabric/parsers";
|
|
11
9
|
import { fsPlugin } from "@kubb/react-fabric/plugins";
|
|
12
10
|
import { performance } from "node:perf_hooks";
|
|
13
11
|
import { deflateSync } from "fflate";
|
|
14
12
|
import { x } from "tinyexec";
|
|
15
13
|
import { version } from "node:process";
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
14
|
+
import { jsx } from "@kubb/react-fabric/jsx-runtime";
|
|
15
|
+
import { sortBy } from "remeda";
|
|
18
16
|
import * as pkg from "empathic/package";
|
|
19
17
|
import { coerce, satisfies } from "semver";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
//#region ../../internals/utils/src/errors.ts
|
|
19
|
+
/** Thrown when a plugin's configuration or input fails validation.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* throw new ValidationPluginError('Invalid config: "output.path" is required')
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
23
26
|
var ValidationPluginError = class extends Error {};
|
|
24
27
|
/**
|
|
25
28
|
* Thrown when one or more errors occur during a Kubb build.
|
|
26
29
|
* Carries the full list of underlying errors on `errors`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* throw new BuildError('Build failed', { errors: [err1, err2] })
|
|
34
|
+
* ```
|
|
27
35
|
*/
|
|
28
36
|
var BuildError = class extends Error {
|
|
29
37
|
errors;
|
|
@@ -35,19 +43,34 @@ var BuildError = class extends Error {
|
|
|
35
43
|
};
|
|
36
44
|
/**
|
|
37
45
|
* Coerces an unknown thrown value to an `Error` instance.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
46
|
+
* Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* try { ... } catch(err) {
|
|
51
|
+
* throw new BuildError('Build failed', { cause: toError(err), errors: [] })
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
40
54
|
*/
|
|
41
55
|
function toError(value) {
|
|
42
56
|
return value instanceof Error ? value : new Error(String(value));
|
|
43
57
|
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region ../../internals/utils/src/asyncEventEmitter.ts
|
|
44
60
|
/**
|
|
45
|
-
*
|
|
61
|
+
* Typed `EventEmitter` that awaits all async listeners before resolving.
|
|
46
62
|
* Wraps Node's `EventEmitter` with full TypeScript event-map inference.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
|
|
67
|
+
* emitter.on('build', async (name) => { console.log(name) })
|
|
68
|
+
* await emitter.emit('build', 'petstore') // all listeners awaited
|
|
69
|
+
* ```
|
|
47
70
|
*/
|
|
48
71
|
var AsyncEventEmitter = class {
|
|
49
72
|
/**
|
|
50
|
-
*
|
|
73
|
+
* Maximum number of listeners per event before Node emits a memory-leak warning.
|
|
51
74
|
* @default 10
|
|
52
75
|
*/
|
|
53
76
|
constructor(maxListener = 10) {
|
|
@@ -55,8 +78,13 @@ var AsyncEventEmitter = class {
|
|
|
55
78
|
}
|
|
56
79
|
#emitter = new EventEmitter();
|
|
57
80
|
/**
|
|
58
|
-
* Emits
|
|
81
|
+
* Emits `eventName` and awaits all registered listeners in parallel.
|
|
59
82
|
* Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* await emitter.emit('build', 'petstore')
|
|
87
|
+
* ```
|
|
60
88
|
*/
|
|
61
89
|
async emit(eventName, ...eventArgs) {
|
|
62
90
|
const listeners = this.#emitter.listeners(eventName);
|
|
@@ -75,11 +103,25 @@ var AsyncEventEmitter = class {
|
|
|
75
103
|
}
|
|
76
104
|
}));
|
|
77
105
|
}
|
|
78
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* Registers a persistent listener for `eventName`.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* emitter.on('build', async (name) => { console.log(name) })
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
79
114
|
on(eventName, handler) {
|
|
80
115
|
this.#emitter.on(eventName, handler);
|
|
81
116
|
}
|
|
82
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Registers a one-shot listener that removes itself after the first invocation.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* emitter.onOnce('build', async (name) => { console.log(name) })
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
83
125
|
onOnce(eventName, handler) {
|
|
84
126
|
const wrapper = (...args) => {
|
|
85
127
|
this.off(eventName, wrapper);
|
|
@@ -87,15 +129,31 @@ var AsyncEventEmitter = class {
|
|
|
87
129
|
};
|
|
88
130
|
this.on(eventName, wrapper);
|
|
89
131
|
}
|
|
90
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Removes a previously registered listener.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* emitter.off('build', handler)
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
91
140
|
off(eventName, handler) {
|
|
92
141
|
this.#emitter.off(eventName, handler);
|
|
93
142
|
}
|
|
94
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* Removes all listeners from every event channel.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* emitter.removeAll()
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
95
151
|
removeAll() {
|
|
96
152
|
this.#emitter.removeAllListeners();
|
|
97
153
|
}
|
|
98
154
|
};
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region ../../internals/utils/src/casing.ts
|
|
99
157
|
/**
|
|
100
158
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
101
159
|
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
@@ -114,9 +172,12 @@ function toCamelOrPascal(text, pascal) {
|
|
|
114
172
|
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
115
173
|
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
116
174
|
* Segments are joined with `/` to form a file path.
|
|
175
|
+
*
|
|
176
|
+
* Only splits on dots followed by a letter so that version numbers
|
|
177
|
+
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
117
178
|
*/
|
|
118
179
|
function applyToFileParts(text, transformPart) {
|
|
119
|
-
const parts = text.split(
|
|
180
|
+
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
120
181
|
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
121
182
|
}
|
|
122
183
|
/**
|
|
@@ -134,190 +195,33 @@ function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
134
195
|
} : {}));
|
|
135
196
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
136
197
|
}
|
|
137
|
-
/** Returns a `CLIAdapter` with type inference. Pass a different adapter to `createCLI` to swap the CLI engine. */
|
|
138
|
-
function defineCLIAdapter(adapter) {
|
|
139
|
-
return adapter;
|
|
140
|
-
}
|
|
141
198
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
199
|
+
* Converts `text` to PascalCase.
|
|
200
|
+
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* pascalCase('hello-world') // 'HelloWorld'
|
|
204
|
+
* pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
|
|
144
205
|
*/
|
|
145
|
-
function
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
description: def.description,
|
|
152
|
-
arguments: def.arguments,
|
|
153
|
-
options: serializeOptions(def.options ?? {}),
|
|
154
|
-
subCommands: def.subCommands ? def.subCommands.map(serializeCommand) : []
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
function serializeOptions(options) {
|
|
158
|
-
return Object.entries(options).map(([name, opt]) => {
|
|
159
|
-
return {
|
|
160
|
-
name,
|
|
161
|
-
flags: `${opt.short ? `-${opt.short}, ` : ""}--${name}${opt.type === "string" ? ` <${opt.hint ?? name}>` : ""}`,
|
|
162
|
-
type: opt.type,
|
|
163
|
-
description: opt.description,
|
|
164
|
-
...opt.default !== void 0 ? { default: opt.default } : {},
|
|
165
|
-
...opt.hint ? { hint: opt.hint } : {},
|
|
166
|
-
...opt.enum ? { enum: opt.enum } : {},
|
|
167
|
-
...opt.required ? { required: opt.required } : {}
|
|
168
|
-
};
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
/** Prints formatted help output for a command using its `CommandDefinition`. */
|
|
172
|
-
function renderHelp(def, parentName) {
|
|
173
|
-
const schema = getCommandSchema([def])[0];
|
|
174
|
-
const programName = parentName ? `${parentName} ${schema.name}` : schema.name;
|
|
175
|
-
const argsPart = schema.arguments?.length ? ` ${schema.arguments.join(" ")}` : "";
|
|
176
|
-
const subCmdPart = schema.subCommands.length ? " <command>" : "";
|
|
177
|
-
console.log(`\n${styleText("bold", "Usage:")} ${programName}${argsPart}${subCmdPart} [options]\n`);
|
|
178
|
-
if (schema.description) console.log(` ${schema.description}\n`);
|
|
179
|
-
if (schema.subCommands.length) {
|
|
180
|
-
console.log(styleText("bold", "Commands:"));
|
|
181
|
-
for (const sub of schema.subCommands) console.log(` ${styleText("cyan", sub.name.padEnd(16))}${sub.description}`);
|
|
182
|
-
console.log();
|
|
183
|
-
}
|
|
184
|
-
const options = [...schema.options, {
|
|
185
|
-
name: "help",
|
|
186
|
-
flags: "-h, --help",
|
|
187
|
-
type: "boolean",
|
|
188
|
-
description: "Show help"
|
|
189
|
-
}];
|
|
190
|
-
console.log(styleText("bold", "Options:"));
|
|
191
|
-
for (const opt of options) {
|
|
192
|
-
const flags = styleText("cyan", opt.flags.padEnd(30));
|
|
193
|
-
const defaultPart = opt.default !== void 0 ? styleText("dim", ` (default: ${opt.default})`) : "";
|
|
194
|
-
console.log(` ${flags}${opt.description}${defaultPart}`);
|
|
195
|
-
}
|
|
196
|
-
console.log();
|
|
197
|
-
}
|
|
198
|
-
function buildParseOptions(def) {
|
|
199
|
-
const result = { help: {
|
|
200
|
-
type: "boolean",
|
|
201
|
-
short: "h"
|
|
202
|
-
} };
|
|
203
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) result[name] = {
|
|
204
|
-
type: opt.type,
|
|
205
|
-
...opt.short ? { short: opt.short } : {},
|
|
206
|
-
...opt.default !== void 0 ? { default: opt.default } : {}
|
|
207
|
-
};
|
|
208
|
-
return result;
|
|
206
|
+
function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
207
|
+
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
208
|
+
prefix,
|
|
209
|
+
suffix
|
|
210
|
+
}) : camelCase(part));
|
|
211
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
209
212
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
let parsed;
|
|
213
|
-
try {
|
|
214
|
-
const result = parseArgs({
|
|
215
|
-
args: argv,
|
|
216
|
-
options: parseOptions,
|
|
217
|
-
allowPositionals: true,
|
|
218
|
-
strict: false
|
|
219
|
-
});
|
|
220
|
-
parsed = {
|
|
221
|
-
values: result.values,
|
|
222
|
-
positionals: result.positionals
|
|
223
|
-
};
|
|
224
|
-
} catch {
|
|
225
|
-
renderHelp(def, parentName);
|
|
226
|
-
process.exit(1);
|
|
227
|
-
}
|
|
228
|
-
if (parsed.values["help"]) {
|
|
229
|
-
renderHelp(def, parentName);
|
|
230
|
-
process.exit(0);
|
|
231
|
-
}
|
|
232
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) if (opt.required && parsed.values[name] === void 0) {
|
|
233
|
-
console.error(styleText("red", `Error: --${name} is required`));
|
|
234
|
-
renderHelp(def, parentName);
|
|
235
|
-
process.exit(1);
|
|
236
|
-
}
|
|
237
|
-
if (!def.run) {
|
|
238
|
-
renderHelp(def, parentName);
|
|
239
|
-
process.exit(0);
|
|
240
|
-
}
|
|
241
|
-
try {
|
|
242
|
-
await def.run(parsed);
|
|
243
|
-
} catch (err) {
|
|
244
|
-
console.error(styleText("red", `Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
245
|
-
renderHelp(def, parentName);
|
|
246
|
-
process.exit(1);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
function printRootHelp(programName, version, defs) {
|
|
250
|
-
console.log(`\n${styleText("bold", "Usage:")} ${programName} <command> [options]\n`);
|
|
251
|
-
console.log(` Kubb generation — v${version}\n`);
|
|
252
|
-
console.log(styleText("bold", "Commands:"));
|
|
253
|
-
for (const def of defs) console.log(` ${styleText("cyan", def.name.padEnd(16))}${def.description}`);
|
|
254
|
-
console.log();
|
|
255
|
-
console.log(styleText("bold", "Options:"));
|
|
256
|
-
console.log(` ${styleText("cyan", "-v, --version".padEnd(30))}Show version number`);
|
|
257
|
-
console.log(` ${styleText("cyan", "-h, --help".padEnd(30))}Show help`);
|
|
258
|
-
console.log();
|
|
259
|
-
console.log(`Run ${styleText("cyan", `${programName} <command> --help`)} for command-specific help.\n`);
|
|
260
|
-
}
|
|
261
|
-
defineCLIAdapter({
|
|
262
|
-
renderHelp(def, parentName) {
|
|
263
|
-
renderHelp(def, parentName);
|
|
264
|
-
},
|
|
265
|
-
async run(defs, argv, opts) {
|
|
266
|
-
const { programName, defaultCommandName, version } = opts;
|
|
267
|
-
const args = argv.length >= 2 && argv[0]?.includes("node") ? argv.slice(2) : argv;
|
|
268
|
-
if (args[0] === "--version" || args[0] === "-v") {
|
|
269
|
-
console.log(version);
|
|
270
|
-
process.exit(0);
|
|
271
|
-
}
|
|
272
|
-
if (args[0] === "--help" || args[0] === "-h") {
|
|
273
|
-
printRootHelp(programName, version, defs);
|
|
274
|
-
process.exit(0);
|
|
275
|
-
}
|
|
276
|
-
if (args.length === 0) {
|
|
277
|
-
const defaultDef = defs.find((d) => d.name === defaultCommandName);
|
|
278
|
-
if (defaultDef?.run) await runCommand(defaultDef, [], programName);
|
|
279
|
-
else printRootHelp(programName, version, defs);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const [first, ...rest] = args;
|
|
283
|
-
const isKnownSubcommand = defs.some((d) => d.name === first);
|
|
284
|
-
let def;
|
|
285
|
-
let commandArgv;
|
|
286
|
-
let parentName;
|
|
287
|
-
if (isKnownSubcommand) {
|
|
288
|
-
def = defs.find((d) => d.name === first);
|
|
289
|
-
commandArgv = rest;
|
|
290
|
-
parentName = programName;
|
|
291
|
-
} else {
|
|
292
|
-
def = defs.find((d) => d.name === defaultCommandName);
|
|
293
|
-
commandArgv = args;
|
|
294
|
-
parentName = programName;
|
|
295
|
-
}
|
|
296
|
-
if (!def) {
|
|
297
|
-
console.error(`Unknown command: ${first}`);
|
|
298
|
-
printRootHelp(programName, version, defs);
|
|
299
|
-
process.exit(1);
|
|
300
|
-
}
|
|
301
|
-
if (def.subCommands?.length) {
|
|
302
|
-
const [subName, ...subRest] = commandArgv;
|
|
303
|
-
const subDef = def.subCommands.find((s) => s.name === subName);
|
|
304
|
-
if (subName === "--help" || subName === "-h") {
|
|
305
|
-
renderHelp(def, parentName);
|
|
306
|
-
process.exit(0);
|
|
307
|
-
}
|
|
308
|
-
if (!subDef) {
|
|
309
|
-
renderHelp(def, parentName);
|
|
310
|
-
process.exit(subName ? 1 : 0);
|
|
311
|
-
}
|
|
312
|
-
await runCommand(subDef, subRest, `${parentName} ${def.name}`);
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
await runCommand(def, commandArgv, parentName);
|
|
316
|
-
}
|
|
317
|
-
});
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region ../../internals/utils/src/time.ts
|
|
318
215
|
/**
|
|
319
|
-
* Calculates elapsed time in milliseconds from a high-resolution start time.
|
|
320
|
-
* Rounds to 2 decimal places
|
|
216
|
+
* Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
|
|
217
|
+
* Rounds to 2 decimal places for sub-millisecond precision without noise.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* const start = process.hrtime()
|
|
222
|
+
* doWork()
|
|
223
|
+
* getElapsedMs(start) // 42.35
|
|
224
|
+
* ```
|
|
321
225
|
*/
|
|
322
226
|
function getElapsedMs(hrStart) {
|
|
323
227
|
const [seconds, nanoseconds] = process.hrtime(hrStart);
|
|
@@ -325,39 +229,22 @@ function getElapsedMs(hrStart) {
|
|
|
325
229
|
return Math.round(ms * 100) / 100;
|
|
326
230
|
}
|
|
327
231
|
/**
|
|
328
|
-
* Converts a millisecond duration into a human-readable string.
|
|
329
|
-
*
|
|
232
|
+
* Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* formatMs(250) // '250ms'
|
|
237
|
+
* formatMs(1500) // '1.50s'
|
|
238
|
+
* formatMs(90000) // '1m 30.0s'
|
|
239
|
+
* ```
|
|
330
240
|
*/
|
|
331
241
|
function formatMs(ms) {
|
|
332
242
|
if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
|
|
333
243
|
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
334
244
|
return `${Math.round(ms)}ms`;
|
|
335
245
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
* Falls back to `255` for any channel that cannot be parsed.
|
|
339
|
-
*/
|
|
340
|
-
function parseHex(color) {
|
|
341
|
-
const int = Number.parseInt(color.replace("#", ""), 16);
|
|
342
|
-
return Number.isNaN(int) ? {
|
|
343
|
-
r: 255,
|
|
344
|
-
g: 255,
|
|
345
|
-
b: 255
|
|
346
|
-
} : {
|
|
347
|
-
r: int >> 16 & 255,
|
|
348
|
-
g: int >> 8 & 255,
|
|
349
|
-
b: int & 255
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Returns a function that wraps a string in a 24-bit ANSI true-color escape sequence
|
|
354
|
-
* for the given hex color.
|
|
355
|
-
*/
|
|
356
|
-
function hex(color) {
|
|
357
|
-
const { r, g, b } = parseHex(color);
|
|
358
|
-
return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
|
|
359
|
-
}
|
|
360
|
-
hex("#F55A17"), hex("#F5A217"), hex("#F58517"), hex("#B45309"), hex("#FFFFFF"), hex("#adadc6"), hex("#FDA4AF");
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region ../../internals/utils/src/fs.ts
|
|
361
248
|
/**
|
|
362
249
|
* Converts all backslashes to forward slashes.
|
|
363
250
|
* Extended-length Windows paths (`\\?\...`) are left unchanged.
|
|
@@ -367,8 +254,14 @@ function toSlash(p) {
|
|
|
367
254
|
return p.replaceAll("\\", "/");
|
|
368
255
|
}
|
|
369
256
|
/**
|
|
370
|
-
* Returns the relative path from `rootDir` to `filePath`, always using
|
|
371
|
-
*
|
|
257
|
+
* Returns the relative path from `rootDir` to `filePath`, always using forward slashes
|
|
258
|
+
* and prefixed with `./` when not already traversing upward.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```ts
|
|
262
|
+
* getRelativePath('/src/components', '/src/components/Button.tsx') // './Button.tsx'
|
|
263
|
+
* getRelativePath('/src/components', '/src/utils/helpers.ts') // '../utils/helpers.ts'
|
|
264
|
+
* ```
|
|
372
265
|
*/
|
|
373
266
|
function getRelativePath(rootDir, filePath) {
|
|
374
267
|
if (!rootDir || !filePath) throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ""} ${filePath || ""}`);
|
|
@@ -378,43 +271,54 @@ function getRelativePath(rootDir, filePath) {
|
|
|
378
271
|
/**
|
|
379
272
|
* Resolves to `true` when the file or directory at `path` exists.
|
|
380
273
|
* Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* if (await exists('./kubb.config.ts')) {
|
|
278
|
+
* const content = await read('./kubb.config.ts')
|
|
279
|
+
* }
|
|
280
|
+
* ```
|
|
381
281
|
*/
|
|
382
282
|
async function exists(path) {
|
|
383
283
|
if (typeof Bun !== "undefined") return Bun.file(path).exists();
|
|
384
284
|
return access(path).then(() => true, () => false);
|
|
385
285
|
}
|
|
386
286
|
/**
|
|
387
|
-
*
|
|
388
|
-
*
|
|
287
|
+
* Synchronous counterpart of `read`.
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```ts
|
|
291
|
+
* const source = readSync('./src/Pet.ts')
|
|
292
|
+
* ```
|
|
389
293
|
*/
|
|
390
|
-
async function read(path) {
|
|
391
|
-
if (typeof Bun !== "undefined") return Bun.file(path).text();
|
|
392
|
-
return readFile(path, { encoding: "utf8" });
|
|
393
|
-
}
|
|
394
|
-
/** Synchronous counterpart of `read`. */
|
|
395
294
|
function readSync(path) {
|
|
396
295
|
return readFileSync(path, { encoding: "utf8" });
|
|
397
296
|
}
|
|
398
297
|
/**
|
|
399
298
|
* Writes `data` to `path`, trimming leading/trailing whitespace before saving.
|
|
400
|
-
* Skips the write
|
|
401
|
-
* identical to what is already on disk.
|
|
299
|
+
* Skips the write when the trimmed content is empty or identical to what is already on disk.
|
|
402
300
|
* Creates any missing parent directories automatically.
|
|
403
|
-
* When `sanity` is `true`, re-reads the file after writing and throws if the
|
|
404
|
-
*
|
|
301
|
+
* When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* ```ts
|
|
305
|
+
* await write('./src/Pet.ts', source) // writes and returns trimmed content
|
|
306
|
+
* await write('./src/Pet.ts', source) // null — file unchanged
|
|
307
|
+
* await write('./src/Pet.ts', ' ') // null — empty content skipped
|
|
308
|
+
* ```
|
|
405
309
|
*/
|
|
406
310
|
async function write(path, data, options = {}) {
|
|
407
311
|
const trimmed = data.trim();
|
|
408
|
-
if (trimmed === "") return
|
|
312
|
+
if (trimmed === "") return null;
|
|
409
313
|
const resolved = resolve(path);
|
|
410
314
|
if (typeof Bun !== "undefined") {
|
|
411
315
|
const file = Bun.file(resolved);
|
|
412
|
-
if ((await file.exists() ? await file.text() : null) === trimmed) return
|
|
316
|
+
if ((await file.exists() ? await file.text() : null) === trimmed) return null;
|
|
413
317
|
await Bun.write(resolved, trimmed);
|
|
414
318
|
return trimmed;
|
|
415
319
|
}
|
|
416
320
|
try {
|
|
417
|
-
if (await readFile(resolved, { encoding: "utf-8" }) === trimmed) return
|
|
321
|
+
if (await readFile(resolved, { encoding: "utf-8" }) === trimmed) return null;
|
|
418
322
|
} catch {}
|
|
419
323
|
await mkdir(dirname(resolved), { recursive: true });
|
|
420
324
|
await writeFile(resolved, trimmed, { encoding: "utf-8" });
|
|
@@ -425,16 +329,32 @@ async function write(path, data, options = {}) {
|
|
|
425
329
|
}
|
|
426
330
|
return trimmed;
|
|
427
331
|
}
|
|
428
|
-
/**
|
|
332
|
+
/**
|
|
333
|
+
* Recursively removes `path`. Silently succeeds when `path` does not exist.
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```ts
|
|
337
|
+
* await clean('./dist')
|
|
338
|
+
* ```
|
|
339
|
+
*/
|
|
429
340
|
async function clean(path) {
|
|
430
341
|
return rm(path, {
|
|
431
342
|
recursive: true,
|
|
432
343
|
force: true
|
|
433
344
|
});
|
|
434
345
|
}
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region ../../internals/utils/src/names.ts
|
|
435
348
|
/**
|
|
436
349
|
* Registers `originalName` in `data` without altering the returned name.
|
|
437
|
-
* Use
|
|
350
|
+
* Use when you need to track usage frequency but always emit the original identifier.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* const seen: Record<string, number> = {}
|
|
355
|
+
* setUniqueName('Foo', seen) // 'Foo' (seen = { Foo: 1 })
|
|
356
|
+
* setUniqueName('Foo', seen) // 'Foo' (seen = { Foo: 2 })
|
|
357
|
+
* ```
|
|
438
358
|
*/
|
|
439
359
|
function setUniqueName(originalName, data) {
|
|
440
360
|
let used = data[originalName] || 0;
|
|
@@ -445,11 +365,26 @@ function setUniqueName(originalName, data) {
|
|
|
445
365
|
data[originalName] = 1;
|
|
446
366
|
return originalName;
|
|
447
367
|
}
|
|
368
|
+
//#endregion
|
|
369
|
+
//#region ../../internals/utils/src/promise.ts
|
|
370
|
+
/** Returns `true` when `result` is a rejected `Promise.allSettled` result with a typed `reason`.
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```ts
|
|
374
|
+
* const results = await Promise.allSettled([p1, p2])
|
|
375
|
+
* results.filter(isPromiseRejectedResult<Error>).map((r) => r.reason.message)
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
function isPromiseRejectedResult(result) {
|
|
379
|
+
return result.status === "rejected";
|
|
380
|
+
}
|
|
381
|
+
//#endregion
|
|
382
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
448
383
|
/**
|
|
449
384
|
* JavaScript and Java reserved words.
|
|
450
385
|
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
451
386
|
*/
|
|
452
|
-
const reservedWords = [
|
|
387
|
+
const reservedWords = new Set([
|
|
453
388
|
"abstract",
|
|
454
389
|
"arguments",
|
|
455
390
|
"boolean",
|
|
@@ -531,18 +466,31 @@ const reservedWords = [
|
|
|
531
466
|
"toString",
|
|
532
467
|
"undefined",
|
|
533
468
|
"valueOf"
|
|
534
|
-
];
|
|
469
|
+
]);
|
|
535
470
|
/**
|
|
536
|
-
* Prefixes
|
|
537
|
-
*
|
|
471
|
+
* Prefixes `word` with `_` when it is a reserved JavaScript/Java identifier or starts with a digit.
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```ts
|
|
475
|
+
* transformReservedWord('class') // '_class'
|
|
476
|
+
* transformReservedWord('42foo') // '_42foo'
|
|
477
|
+
* transformReservedWord('status') // 'status'
|
|
478
|
+
* ```
|
|
538
479
|
*/
|
|
539
480
|
function transformReservedWord(word) {
|
|
540
481
|
const firstChar = word.charCodeAt(0);
|
|
541
|
-
if (word && (reservedWords.
|
|
482
|
+
if (word && (reservedWords.has(word) || firstChar >= 48 && firstChar <= 57)) return `_${word}`;
|
|
542
483
|
return word;
|
|
543
484
|
}
|
|
544
485
|
/**
|
|
545
486
|
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```ts
|
|
490
|
+
* isValidVarName('status') // true
|
|
491
|
+
* isValidVarName('class') // false (reserved word)
|
|
492
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
493
|
+
* ```
|
|
546
494
|
*/
|
|
547
495
|
function isValidVarName(name) {
|
|
548
496
|
try {
|
|
@@ -552,6 +500,8 @@ function isValidVarName(name) {
|
|
|
552
500
|
}
|
|
553
501
|
return true;
|
|
554
502
|
}
|
|
503
|
+
//#endregion
|
|
504
|
+
//#region ../../internals/utils/src/urlPath.ts
|
|
555
505
|
/**
|
|
556
506
|
* Parses and transforms an OpenAPI/Swagger path string into various URL formats.
|
|
557
507
|
*
|
|
@@ -561,18 +511,33 @@ function isValidVarName(name) {
|
|
|
561
511
|
* p.template // '`/pet/${petId}`'
|
|
562
512
|
*/
|
|
563
513
|
var URLPath = class {
|
|
564
|
-
/**
|
|
514
|
+
/**
|
|
515
|
+
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
516
|
+
*/
|
|
565
517
|
path;
|
|
566
518
|
#options;
|
|
567
519
|
constructor(path, options = {}) {
|
|
568
520
|
this.path = path;
|
|
569
521
|
this.#options = options;
|
|
570
522
|
}
|
|
571
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
523
|
+
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* ```ts
|
|
527
|
+
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
528
|
+
* ```
|
|
529
|
+
*/
|
|
572
530
|
get URL() {
|
|
573
531
|
return this.toURLPath();
|
|
574
532
|
}
|
|
575
|
-
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
533
|
+
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
534
|
+
*
|
|
535
|
+
* @example
|
|
536
|
+
* ```ts
|
|
537
|
+
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
538
|
+
* new URLPath('/pet/{petId}').isURL // false
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
576
541
|
get isURL() {
|
|
577
542
|
try {
|
|
578
543
|
return !!new URL(this.path).href;
|
|
@@ -590,11 +555,25 @@ var URLPath = class {
|
|
|
590
555
|
get template() {
|
|
591
556
|
return this.toTemplateString();
|
|
592
557
|
}
|
|
593
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
558
|
+
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```ts
|
|
562
|
+
* new URLPath('/pet/{petId}').object
|
|
563
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
564
|
+
* ```
|
|
565
|
+
*/
|
|
594
566
|
get object() {
|
|
595
567
|
return this.toObject();
|
|
596
568
|
}
|
|
597
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
569
|
+
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
570
|
+
*
|
|
571
|
+
* @example
|
|
572
|
+
* ```ts
|
|
573
|
+
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
574
|
+
* new URLPath('/pet').params // undefined
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
598
577
|
get params() {
|
|
599
578
|
return this.getParams();
|
|
600
579
|
}
|
|
@@ -602,7 +581,9 @@ var URLPath = class {
|
|
|
602
581
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
603
582
|
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
604
583
|
}
|
|
605
|
-
/**
|
|
584
|
+
/**
|
|
585
|
+
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
586
|
+
*/
|
|
606
587
|
#eachParam(fn) {
|
|
607
588
|
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
608
589
|
const raw = match[1];
|
|
@@ -639,6 +620,12 @@ var URLPath = class {
|
|
|
639
620
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
640
621
|
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
641
622
|
* Returns `undefined` when no path parameters are found.
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```ts
|
|
626
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
627
|
+
* // { petId: 'petId', tagId: 'tagId' }
|
|
628
|
+
* ```
|
|
642
629
|
*/
|
|
643
630
|
getParams(replacer) {
|
|
644
631
|
const params = {};
|
|
@@ -648,7 +635,13 @@ var URLPath = class {
|
|
|
648
635
|
});
|
|
649
636
|
return Object.keys(params).length > 0 ? params : void 0;
|
|
650
637
|
}
|
|
651
|
-
/** Converts the OpenAPI path to Express-style colon syntax
|
|
638
|
+
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
639
|
+
*
|
|
640
|
+
* @example
|
|
641
|
+
* ```ts
|
|
642
|
+
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
643
|
+
* ```
|
|
644
|
+
*/
|
|
652
645
|
toURLPath() {
|
|
653
646
|
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
654
647
|
}
|
|
@@ -666,11 +659,27 @@ function isInputPath(config) {
|
|
|
666
659
|
}
|
|
667
660
|
//#endregion
|
|
668
661
|
//#region src/constants.ts
|
|
662
|
+
/**
|
|
663
|
+
* Base URL for the Kubb Studio web app.
|
|
664
|
+
*/
|
|
669
665
|
const DEFAULT_STUDIO_URL = "https://studio.kubb.dev";
|
|
666
|
+
/**
|
|
667
|
+
* File name used for generated barrel (index) files.
|
|
668
|
+
*/
|
|
670
669
|
const BARREL_FILENAME = "index.ts";
|
|
670
|
+
/**
|
|
671
|
+
* Default banner style written at the top of every generated file.
|
|
672
|
+
*/
|
|
671
673
|
const DEFAULT_BANNER = "simple";
|
|
674
|
+
/**
|
|
675
|
+
* Default file-extension mapping used when no explicit mapping is configured.
|
|
676
|
+
*/
|
|
672
677
|
const DEFAULT_EXTENSION = { ".ts": ".ts" };
|
|
673
|
-
|
|
678
|
+
/**
|
|
679
|
+
* Numeric log-level thresholds used internally to compare verbosity.
|
|
680
|
+
*
|
|
681
|
+
* Higher numbers are more verbose.
|
|
682
|
+
*/
|
|
674
683
|
const logLevel = {
|
|
675
684
|
silent: Number.NEGATIVE_INFINITY,
|
|
676
685
|
error: 0,
|
|
@@ -679,6 +688,13 @@ const logLevel = {
|
|
|
679
688
|
verbose: 4,
|
|
680
689
|
debug: 5
|
|
681
690
|
};
|
|
691
|
+
/**
|
|
692
|
+
* CLI command descriptors for each supported linter.
|
|
693
|
+
*
|
|
694
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
695
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
696
|
+
* the linter is not found.
|
|
697
|
+
*/
|
|
682
698
|
const linters = {
|
|
683
699
|
eslint: {
|
|
684
700
|
command: "eslint",
|
|
@@ -700,6 +716,13 @@ const linters = {
|
|
|
700
716
|
errorMessage: "Oxlint not found"
|
|
701
717
|
}
|
|
702
718
|
};
|
|
719
|
+
/**
|
|
720
|
+
* CLI command descriptors for each supported code formatter.
|
|
721
|
+
*
|
|
722
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
723
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
724
|
+
* the formatter is not found.
|
|
725
|
+
*/
|
|
703
726
|
const formatters = {
|
|
704
727
|
prettier: {
|
|
705
728
|
command: "prettier",
|
|
@@ -899,7 +922,11 @@ function validateConcurrency(concurrency) {
|
|
|
899
922
|
//#endregion
|
|
900
923
|
//#region src/utils/executeStrategies.ts
|
|
901
924
|
/**
|
|
902
|
-
*
|
|
925
|
+
* Runs promise functions in sequence, threading each result into the next call.
|
|
926
|
+
*
|
|
927
|
+
* - Each function receives the accumulated state from the previous call.
|
|
928
|
+
* - Skips functions that return a falsy value (acts as a no-op for that step).
|
|
929
|
+
* - Returns an array of all individual results.
|
|
903
930
|
*/
|
|
904
931
|
function hookSeq(promises) {
|
|
905
932
|
return promises.filter(Boolean).reduce((promise, func) => {
|
|
@@ -912,7 +939,10 @@ function hookSeq(promises) {
|
|
|
912
939
|
}, Promise.resolve([]));
|
|
913
940
|
}
|
|
914
941
|
/**
|
|
915
|
-
*
|
|
942
|
+
* Runs promise functions in sequence and returns the first non-null result.
|
|
943
|
+
*
|
|
944
|
+
* - Stops as soon as `nullCheck` passes for a result (default: `!== null`).
|
|
945
|
+
* - Subsequent functions are skipped once a match is found.
|
|
916
946
|
*/
|
|
917
947
|
function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
918
948
|
let promise = Promise.resolve(null);
|
|
@@ -923,7 +953,10 @@ function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
|
923
953
|
return promise;
|
|
924
954
|
}
|
|
925
955
|
/**
|
|
926
|
-
* Runs
|
|
956
|
+
* Runs promise functions concurrently and returns all settled results.
|
|
957
|
+
*
|
|
958
|
+
* - Limits simultaneous executions to `concurrency` (default: unlimited).
|
|
959
|
+
* - Uses `Promise.allSettled` so individual failures do not cancel other tasks.
|
|
927
960
|
*/
|
|
928
961
|
function hookParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
929
962
|
const limit = pLimit(concurrency);
|
|
@@ -931,29 +964,13 @@ function hookParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
|
931
964
|
return Promise.allSettled(tasks);
|
|
932
965
|
}
|
|
933
966
|
//#endregion
|
|
934
|
-
//#region src/
|
|
935
|
-
var PromiseManager = class {
|
|
936
|
-
#options = {};
|
|
937
|
-
constructor(options = {}) {
|
|
938
|
-
this.#options = options;
|
|
939
|
-
}
|
|
940
|
-
run(strategy, promises, { concurrency = Number.POSITIVE_INFINITY } = {}) {
|
|
941
|
-
if (strategy === "seq") return hookSeq(promises);
|
|
942
|
-
if (strategy === "first") return hookFirst(promises, this.#options.nullCheck);
|
|
943
|
-
if (strategy === "parallel") return hookParallel(promises, concurrency);
|
|
944
|
-
throw new Error(`${strategy} not implemented`);
|
|
945
|
-
}
|
|
946
|
-
};
|
|
947
|
-
function isPromiseRejectedResult(result) {
|
|
948
|
-
return result.status === "rejected";
|
|
949
|
-
}
|
|
950
|
-
//#endregion
|
|
951
|
-
//#region src/PluginManager.ts
|
|
967
|
+
//#region src/PluginDriver.ts
|
|
952
968
|
function getMode(fileOrFolder) {
|
|
953
969
|
if (!fileOrFolder) return "split";
|
|
954
|
-
return
|
|
970
|
+
return extname(fileOrFolder) ? "single" : "split";
|
|
955
971
|
}
|
|
956
|
-
|
|
972
|
+
const hookFirstNullCheck = (state) => !!state?.result;
|
|
973
|
+
var PluginDriver = class {
|
|
957
974
|
config;
|
|
958
975
|
options;
|
|
959
976
|
/**
|
|
@@ -961,14 +978,13 @@ var PluginManager = class {
|
|
|
961
978
|
* the build pipeline after the adapter's `parse()` resolves.
|
|
962
979
|
*/
|
|
963
980
|
rootNode = void 0;
|
|
981
|
+
adapter = void 0;
|
|
964
982
|
#studioIsOpen = false;
|
|
965
983
|
#plugins = /* @__PURE__ */ new Set();
|
|
966
984
|
#usedPluginNames = {};
|
|
967
|
-
#promiseManager;
|
|
968
985
|
constructor(config, options) {
|
|
969
986
|
this.config = config;
|
|
970
987
|
this.options = options;
|
|
971
|
-
this.#promiseManager = new PromiseManager({ nullCheck: (state) => !!state?.result });
|
|
972
988
|
[...config.plugins || []].forEach((plugin) => {
|
|
973
989
|
const parsedPlugin = this.#parse(plugin);
|
|
974
990
|
this.#plugins.add(parsedPlugin);
|
|
@@ -979,14 +995,14 @@ var PluginManager = class {
|
|
|
979
995
|
}
|
|
980
996
|
getContext(plugin) {
|
|
981
997
|
const plugins = [...this.#plugins];
|
|
982
|
-
const
|
|
998
|
+
const driver = this;
|
|
983
999
|
const baseContext = {
|
|
984
1000
|
fabric: this.options.fabric,
|
|
985
1001
|
config: this.config,
|
|
986
1002
|
plugin,
|
|
987
1003
|
events: this.options.events,
|
|
988
|
-
|
|
989
|
-
mode: getMode(
|
|
1004
|
+
driver: this,
|
|
1005
|
+
mode: getMode(resolve(this.config.root, this.config.output.path)),
|
|
990
1006
|
addFile: async (...files) => {
|
|
991
1007
|
await this.options.fabric.addFile(...files);
|
|
992
1008
|
},
|
|
@@ -994,15 +1010,18 @@ var PluginManager = class {
|
|
|
994
1010
|
await this.options.fabric.upsertFile(...files);
|
|
995
1011
|
},
|
|
996
1012
|
get rootNode() {
|
|
997
|
-
return
|
|
1013
|
+
return driver.rootNode;
|
|
1014
|
+
},
|
|
1015
|
+
get adapter() {
|
|
1016
|
+
return driver.adapter;
|
|
998
1017
|
},
|
|
999
1018
|
openInStudio(options) {
|
|
1000
|
-
if (
|
|
1001
|
-
if (
|
|
1002
|
-
if (
|
|
1003
|
-
|
|
1004
|
-
const studioUrl =
|
|
1005
|
-
return openInStudio(
|
|
1019
|
+
if (!driver.config.devtools || driver.#studioIsOpen) return;
|
|
1020
|
+
if (typeof driver.config.devtools !== "object") throw new Error("Devtools must be an object");
|
|
1021
|
+
if (!driver.rootNode || !driver.adapter) throw new Error("adapter is not defined, make sure you have set the parser in kubb.config.ts");
|
|
1022
|
+
driver.#studioIsOpen = true;
|
|
1023
|
+
const studioUrl = driver.config.devtools?.studioUrl ?? "https://studio.kubb.dev";
|
|
1024
|
+
return openInStudio(driver.rootNode, studioUrl, options);
|
|
1006
1025
|
}
|
|
1007
1026
|
};
|
|
1008
1027
|
const mergedExtras = {};
|
|
@@ -1019,17 +1038,21 @@ var PluginManager = class {
|
|
|
1019
1038
|
return this.#getSortedPlugins();
|
|
1020
1039
|
}
|
|
1021
1040
|
getFile({ name, mode, extname, pluginName, options }) {
|
|
1022
|
-
const
|
|
1041
|
+
const resolvedName = mode ? mode === "single" ? "" : this.resolveName({
|
|
1042
|
+
name,
|
|
1043
|
+
pluginName,
|
|
1044
|
+
type: "file"
|
|
1045
|
+
}) : name;
|
|
1023
1046
|
const path = this.resolvePath({
|
|
1024
|
-
baseName
|
|
1047
|
+
baseName: `${resolvedName}${extname}`,
|
|
1025
1048
|
mode,
|
|
1026
1049
|
pluginName,
|
|
1027
1050
|
options
|
|
1028
1051
|
});
|
|
1029
|
-
if (!path) throw new Error(`Filepath should be defined for resolvedName "${
|
|
1052
|
+
if (!path) throw new Error(`Filepath should be defined for resolvedName "${resolvedName}" and pluginName "${pluginName}"`);
|
|
1030
1053
|
return {
|
|
1031
1054
|
path,
|
|
1032
|
-
baseName,
|
|
1055
|
+
baseName: basename(path),
|
|
1033
1056
|
meta: { pluginName },
|
|
1034
1057
|
sources: [],
|
|
1035
1058
|
imports: [],
|
|
@@ -1037,8 +1060,7 @@ var PluginManager = class {
|
|
|
1037
1060
|
};
|
|
1038
1061
|
}
|
|
1039
1062
|
resolvePath = (params) => {
|
|
1040
|
-
const
|
|
1041
|
-
const defaultPath = path.resolve(root, params.baseName);
|
|
1063
|
+
const defaultPath = resolve(resolve(this.config.root, this.config.output.path), params.baseName);
|
|
1042
1064
|
if (params.pluginName) return this.hookForPluginSync({
|
|
1043
1065
|
pluginName: params.pluginName,
|
|
1044
1066
|
hookName: "resolvePath",
|
|
@@ -1118,7 +1140,7 @@ var PluginManager = class {
|
|
|
1118
1140
|
hookName,
|
|
1119
1141
|
plugins
|
|
1120
1142
|
});
|
|
1121
|
-
const
|
|
1143
|
+
const result = await hookFirst(plugins.map((plugin) => {
|
|
1122
1144
|
return async () => {
|
|
1123
1145
|
const value = await this.#execute({
|
|
1124
1146
|
strategy: "hookFirst",
|
|
@@ -1131,8 +1153,7 @@ var PluginManager = class {
|
|
|
1131
1153
|
result: value
|
|
1132
1154
|
});
|
|
1133
1155
|
};
|
|
1134
|
-
});
|
|
1135
|
-
const result = await this.#promiseManager.run("first", promises);
|
|
1156
|
+
}), hookFirstNullCheck);
|
|
1136
1157
|
this.events.emit("plugins:hook:progress:end", { hookName });
|
|
1137
1158
|
return result;
|
|
1138
1159
|
}
|
|
@@ -1168,7 +1189,7 @@ var PluginManager = class {
|
|
|
1168
1189
|
plugins
|
|
1169
1190
|
});
|
|
1170
1191
|
const pluginStartTimes = /* @__PURE__ */ new Map();
|
|
1171
|
-
const
|
|
1192
|
+
const results = await hookParallel(plugins.map((plugin) => {
|
|
1172
1193
|
return () => {
|
|
1173
1194
|
pluginStartTimes.set(plugin, performance.now());
|
|
1174
1195
|
return this.#execute({
|
|
@@ -1178,8 +1199,7 @@ var PluginManager = class {
|
|
|
1178
1199
|
plugin
|
|
1179
1200
|
});
|
|
1180
1201
|
};
|
|
1181
|
-
});
|
|
1182
|
-
const results = await this.#promiseManager.run("parallel", promises, { concurrency: this.options.concurrency });
|
|
1202
|
+
}), this.options.concurrency);
|
|
1183
1203
|
results.forEach((result, index) => {
|
|
1184
1204
|
if (isPromiseRejectedResult(result)) {
|
|
1185
1205
|
const plugin = this.#getSortedPlugins(hookName)[index];
|
|
@@ -1210,15 +1230,14 @@ var PluginManager = class {
|
|
|
1210
1230
|
hookName,
|
|
1211
1231
|
plugins
|
|
1212
1232
|
});
|
|
1213
|
-
|
|
1233
|
+
await hookSeq(plugins.map((plugin) => {
|
|
1214
1234
|
return () => this.#execute({
|
|
1215
1235
|
strategy: "hookSeq",
|
|
1216
1236
|
hookName,
|
|
1217
1237
|
parameters,
|
|
1218
1238
|
plugin
|
|
1219
1239
|
});
|
|
1220
|
-
});
|
|
1221
|
-
await this.#promiseManager.run("seq", promises);
|
|
1240
|
+
}));
|
|
1222
1241
|
this.events.emit("plugins:hook:progress:end", { hookName });
|
|
1223
1242
|
}
|
|
1224
1243
|
#getSortedPlugins(hookName) {
|
|
@@ -1226,7 +1245,8 @@ var PluginManager = class {
|
|
|
1226
1245
|
if (hookName) return plugins.filter((plugin) => hookName in plugin);
|
|
1227
1246
|
return plugins.map((plugin) => {
|
|
1228
1247
|
if (plugin.pre) {
|
|
1229
|
-
|
|
1248
|
+
let missingPlugins = plugin.pre.filter((pluginName) => !plugins.find((pluginToFind) => pluginToFind.name === pluginName));
|
|
1249
|
+
if (missingPlugins.includes("plugin-oas") && this.adapter) missingPlugins = missingPlugins.filter((pluginName) => pluginName !== "plugin-oas");
|
|
1230
1250
|
if (missingPlugins.length > 0) throw new ValidationPluginError(`The plugin '${plugin.name}' has a pre set that references missing plugins for '${missingPlugins.join(", ")}'`);
|
|
1231
1251
|
}
|
|
1232
1252
|
return plugin;
|
|
@@ -1346,19 +1366,12 @@ var PluginManager = class {
|
|
|
1346
1366
|
}
|
|
1347
1367
|
};
|
|
1348
1368
|
//#endregion
|
|
1349
|
-
//#region src/
|
|
1369
|
+
//#region src/createStorage.ts
|
|
1350
1370
|
/**
|
|
1351
|
-
*
|
|
1352
|
-
* same factory pattern as `definePlugin`, `defineLogger`, and `defineAdapter`.
|
|
1353
|
-
*
|
|
1354
|
-
* The builder receives the resolved options object and must return a
|
|
1355
|
-
* `DefineStorage`-compatible object that includes a `name` string.
|
|
1371
|
+
* Creates a storage factory. Call the returned function with optional options to get the storage instance.
|
|
1356
1372
|
*
|
|
1357
1373
|
* @example
|
|
1358
|
-
*
|
|
1359
|
-
* import { defineStorage } from '@kubb/core'
|
|
1360
|
-
*
|
|
1361
|
-
* export const memoryStorage = defineStorage((_options) => {
|
|
1374
|
+
* export const memoryStorage = createStorage(() => {
|
|
1362
1375
|
* const store = new Map<string, string>()
|
|
1363
1376
|
* return {
|
|
1364
1377
|
* name: 'memory',
|
|
@@ -1366,13 +1379,15 @@ var PluginManager = class {
|
|
|
1366
1379
|
* async getItem(key) { return store.get(key) ?? null },
|
|
1367
1380
|
* async setItem(key, value) { store.set(key, value) },
|
|
1368
1381
|
* async removeItem(key) { store.delete(key) },
|
|
1369
|
-
* async getKeys() {
|
|
1370
|
-
*
|
|
1382
|
+
* async getKeys(base) {
|
|
1383
|
+
* const keys = [...store.keys()]
|
|
1384
|
+
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
1385
|
+
* },
|
|
1386
|
+
* async clear(base) { if (!base) store.clear() },
|
|
1371
1387
|
* }
|
|
1372
1388
|
* })
|
|
1373
|
-
* ```
|
|
1374
1389
|
*/
|
|
1375
|
-
function
|
|
1390
|
+
function createStorage(build) {
|
|
1376
1391
|
return (options) => build(options ?? {});
|
|
1377
1392
|
}
|
|
1378
1393
|
//#endregion
|
|
@@ -1400,7 +1415,7 @@ function defineStorage(build) {
|
|
|
1400
1415
|
* })
|
|
1401
1416
|
* ```
|
|
1402
1417
|
*/
|
|
1403
|
-
const fsStorage =
|
|
1418
|
+
const fsStorage = createStorage(() => ({
|
|
1404
1419
|
name: "fs",
|
|
1405
1420
|
async hasItem(key) {
|
|
1406
1421
|
try {
|
|
@@ -1448,11 +1463,14 @@ const fsStorage = defineStorage(() => ({
|
|
|
1448
1463
|
}));
|
|
1449
1464
|
//#endregion
|
|
1450
1465
|
//#region package.json
|
|
1451
|
-
var version$1 = "5.0.0-alpha.
|
|
1466
|
+
var version$1 = "5.0.0-alpha.20";
|
|
1452
1467
|
//#endregion
|
|
1453
1468
|
//#region src/utils/diagnostics.ts
|
|
1454
1469
|
/**
|
|
1455
|
-
*
|
|
1470
|
+
* Returns a snapshot of the current runtime environment.
|
|
1471
|
+
*
|
|
1472
|
+
* Useful for attaching context to debug logs and error reports so that
|
|
1473
|
+
* issues can be reproduced without manual information gathering.
|
|
1456
1474
|
*/
|
|
1457
1475
|
function getDiagnosticInfo() {
|
|
1458
1476
|
return {
|
|
@@ -1465,6 +1483,17 @@ function getDiagnosticInfo() {
|
|
|
1465
1483
|
}
|
|
1466
1484
|
//#endregion
|
|
1467
1485
|
//#region src/build.ts
|
|
1486
|
+
/**
|
|
1487
|
+
* Initializes all Kubb infrastructure for a build without executing any plugins.
|
|
1488
|
+
*
|
|
1489
|
+
* - Validates the input path (when applicable).
|
|
1490
|
+
* - Applies config defaults (`root`, `output.*`, `devtools`).
|
|
1491
|
+
* - Creates the Fabric instance and wires storage, format, and lint hooks.
|
|
1492
|
+
* - Runs the adapter (if configured) to produce the universal `RootNode`.
|
|
1493
|
+
*
|
|
1494
|
+
* Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
|
|
1495
|
+
* via the `overrides` argument to reuse the same infrastructure across multiple runs.
|
|
1496
|
+
*/
|
|
1468
1497
|
async function setup(options) {
|
|
1469
1498
|
const { config: userConfig, events = new AsyncEventEmitter() } = options;
|
|
1470
1499
|
const sources = /* @__PURE__ */ new Map();
|
|
@@ -1562,7 +1591,7 @@ async function setup(options) {
|
|
|
1562
1591
|
` • Barrel type: ${definedConfig.output.barrelType || "none"}`
|
|
1563
1592
|
]
|
|
1564
1593
|
});
|
|
1565
|
-
const
|
|
1594
|
+
const pluginDriver = new PluginDriver(definedConfig, {
|
|
1566
1595
|
fabric,
|
|
1567
1596
|
events,
|
|
1568
1597
|
concurrency: 15
|
|
@@ -1573,25 +1602,32 @@ async function setup(options) {
|
|
|
1573
1602
|
date: /* @__PURE__ */ new Date(),
|
|
1574
1603
|
logs: [`Running adapter: ${definedConfig.adapter.name}`]
|
|
1575
1604
|
});
|
|
1576
|
-
|
|
1605
|
+
pluginDriver.adapter = definedConfig.adapter;
|
|
1606
|
+
pluginDriver.rootNode = await definedConfig.adapter.parse(source);
|
|
1577
1607
|
await events.emit("debug", {
|
|
1578
1608
|
date: /* @__PURE__ */ new Date(),
|
|
1579
1609
|
logs: [
|
|
1580
1610
|
`✓ Adapter '${definedConfig.adapter.name}' resolved RootNode`,
|
|
1581
|
-
` • Schemas: ${
|
|
1582
|
-
` • Operations: ${
|
|
1611
|
+
` • Schemas: ${pluginDriver.rootNode.schemas.length}`,
|
|
1612
|
+
` • Operations: ${pluginDriver.rootNode.operations.length}`
|
|
1583
1613
|
]
|
|
1584
1614
|
});
|
|
1585
1615
|
}
|
|
1586
1616
|
return {
|
|
1587
1617
|
events,
|
|
1588
1618
|
fabric,
|
|
1589
|
-
|
|
1619
|
+
driver: pluginDriver,
|
|
1590
1620
|
sources
|
|
1591
1621
|
};
|
|
1592
1622
|
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Runs a full Kubb build and throws on any error or plugin failure.
|
|
1625
|
+
*
|
|
1626
|
+
* Internally delegates to {@link safeBuild} and rethrows collected errors.
|
|
1627
|
+
* Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
|
|
1628
|
+
*/
|
|
1593
1629
|
async function build(options, overrides) {
|
|
1594
|
-
const { fabric, files,
|
|
1630
|
+
const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides);
|
|
1595
1631
|
if (error) throw error;
|
|
1596
1632
|
if (failedPlugins.size > 0) {
|
|
1597
1633
|
const errors = [...failedPlugins].map(({ error }) => error);
|
|
@@ -1601,20 +1637,30 @@ async function build(options, overrides) {
|
|
|
1601
1637
|
failedPlugins,
|
|
1602
1638
|
fabric,
|
|
1603
1639
|
files,
|
|
1604
|
-
|
|
1640
|
+
driver,
|
|
1605
1641
|
pluginTimings,
|
|
1606
1642
|
error: void 0,
|
|
1607
1643
|
sources
|
|
1608
1644
|
};
|
|
1609
1645
|
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Runs a full Kubb build and captures errors instead of throwing.
|
|
1648
|
+
*
|
|
1649
|
+
* - Installs each plugin in order, recording failures in `failedPlugins`.
|
|
1650
|
+
* - Generates the root barrel file when `output.barrelType` is set.
|
|
1651
|
+
* - Writes all files through Fabric.
|
|
1652
|
+
*
|
|
1653
|
+
* Returns a {@link BuildOutput} even on failure — inspect `error` and
|
|
1654
|
+
* `failedPlugins` to determine whether the build succeeded.
|
|
1655
|
+
*/
|
|
1610
1656
|
async function safeBuild(options, overrides) {
|
|
1611
|
-
const { fabric,
|
|
1657
|
+
const { fabric, driver, events, sources } = overrides ? overrides : await setup(options);
|
|
1612
1658
|
const failedPlugins = /* @__PURE__ */ new Set();
|
|
1613
1659
|
const pluginTimings = /* @__PURE__ */ new Map();
|
|
1614
|
-
const config =
|
|
1660
|
+
const config = driver.config;
|
|
1615
1661
|
try {
|
|
1616
|
-
for (const plugin of
|
|
1617
|
-
const context =
|
|
1662
|
+
for (const plugin of driver.plugins) {
|
|
1663
|
+
const context = driver.getContext(plugin);
|
|
1618
1664
|
const hrStart = process.hrtime();
|
|
1619
1665
|
const installer = plugin.install.bind(context);
|
|
1620
1666
|
try {
|
|
@@ -1687,7 +1733,7 @@ async function safeBuild(options, overrides) {
|
|
|
1687
1733
|
rootDir,
|
|
1688
1734
|
existingExports: new Set(existingBarrel?.exports?.flatMap((e) => Array.isArray(e.name) ? e.name : [e.name]).filter((n) => Boolean(n)) ?? []),
|
|
1689
1735
|
config,
|
|
1690
|
-
|
|
1736
|
+
driver
|
|
1691
1737
|
}),
|
|
1692
1738
|
sources: [],
|
|
1693
1739
|
imports: [],
|
|
@@ -1705,7 +1751,7 @@ async function safeBuild(options, overrides) {
|
|
|
1705
1751
|
failedPlugins,
|
|
1706
1752
|
fabric,
|
|
1707
1753
|
files,
|
|
1708
|
-
|
|
1754
|
+
driver,
|
|
1709
1755
|
pluginTimings,
|
|
1710
1756
|
sources
|
|
1711
1757
|
};
|
|
@@ -1714,16 +1760,16 @@ async function safeBuild(options, overrides) {
|
|
|
1714
1760
|
failedPlugins,
|
|
1715
1761
|
fabric,
|
|
1716
1762
|
files: [],
|
|
1717
|
-
|
|
1763
|
+
driver,
|
|
1718
1764
|
pluginTimings,
|
|
1719
1765
|
error,
|
|
1720
1766
|
sources
|
|
1721
1767
|
};
|
|
1722
1768
|
}
|
|
1723
1769
|
}
|
|
1724
|
-
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config,
|
|
1770
|
+
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }) {
|
|
1725
1771
|
const pluginNameMap = /* @__PURE__ */ new Map();
|
|
1726
|
-
for (const plugin of
|
|
1772
|
+
for (const plugin of driver.plugins) pluginNameMap.set(plugin.name, plugin);
|
|
1727
1773
|
return barrelFiles.flatMap((file) => {
|
|
1728
1774
|
const containsOnlyTypes = file.sources?.every((source) => source.isTypeOnly);
|
|
1729
1775
|
return (file.sources ?? []).flatMap((source) => {
|
|
@@ -1748,133 +1794,301 @@ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, plu
|
|
|
1748
1794
|
function inputToAdapterSource(config) {
|
|
1749
1795
|
if (Array.isArray(config.input)) return {
|
|
1750
1796
|
type: "paths",
|
|
1751
|
-
paths: config.input.map((i) => resolve(config.root, i.path))
|
|
1797
|
+
paths: config.input.map((i) => new URLPath(i.path).isURL ? i.path : resolve(config.root, i.path))
|
|
1752
1798
|
};
|
|
1753
1799
|
if ("data" in config.input) return {
|
|
1754
1800
|
type: "data",
|
|
1755
1801
|
data: config.input.data
|
|
1756
1802
|
};
|
|
1803
|
+
if (new URLPath(config.input.path).isURL) return {
|
|
1804
|
+
type: "path",
|
|
1805
|
+
path: config.input.path
|
|
1806
|
+
};
|
|
1757
1807
|
return {
|
|
1758
1808
|
type: "path",
|
|
1759
1809
|
path: resolve(config.root, config.input.path)
|
|
1760
1810
|
};
|
|
1761
1811
|
}
|
|
1762
1812
|
//#endregion
|
|
1763
|
-
//#region src/
|
|
1813
|
+
//#region src/createAdapter.ts
|
|
1764
1814
|
/**
|
|
1765
|
-
*
|
|
1815
|
+
* Creates an adapter factory. Call the returned function with optional options to get the adapter instance.
|
|
1766
1816
|
*
|
|
1767
1817
|
* @example
|
|
1768
|
-
*
|
|
1769
|
-
* export const adapterOas = defineAdapter<OasAdapter>((options) => {
|
|
1770
|
-
* const { validate = true, dateType = 'string' } = options
|
|
1818
|
+
* export const myAdapter = createAdapter<MyAdapter>((options) => {
|
|
1771
1819
|
* return {
|
|
1772
|
-
* name:
|
|
1773
|
-
* options
|
|
1774
|
-
* parse(source) { ... },
|
|
1820
|
+
* name: 'my-adapter',
|
|
1821
|
+
* options,
|
|
1822
|
+
* async parse(source) { ... },
|
|
1775
1823
|
* }
|
|
1776
1824
|
* })
|
|
1777
|
-
*
|
|
1825
|
+
*
|
|
1826
|
+
* // instantiate
|
|
1827
|
+
* const adapter = myAdapter({ validate: true })
|
|
1828
|
+
*/
|
|
1829
|
+
function createAdapter(build) {
|
|
1830
|
+
return (options) => build(options ?? {});
|
|
1831
|
+
}
|
|
1832
|
+
//#endregion
|
|
1833
|
+
//#region src/createPlugin.ts
|
|
1834
|
+
/**
|
|
1835
|
+
* Creates a plugin factory. Call the returned function with optional options to get the plugin instance.
|
|
1836
|
+
*
|
|
1837
|
+
* @example
|
|
1838
|
+
* export const myPlugin = createPlugin<MyPlugin>((options) => {
|
|
1839
|
+
* return {
|
|
1840
|
+
* name: 'my-plugin',
|
|
1841
|
+
* options,
|
|
1842
|
+
* resolvePath(baseName) { ... },
|
|
1843
|
+
* resolveName(name, type) { ... },
|
|
1844
|
+
* }
|
|
1845
|
+
* })
|
|
1846
|
+
*
|
|
1847
|
+
* // instantiate
|
|
1848
|
+
* const plugin = myPlugin({ output: { path: 'src/gen' } })
|
|
1778
1849
|
*/
|
|
1779
|
-
function
|
|
1850
|
+
function createPlugin(build) {
|
|
1780
1851
|
return (options) => build(options ?? {});
|
|
1781
1852
|
}
|
|
1782
1853
|
//#endregion
|
|
1854
|
+
//#region src/defineGenerator.ts
|
|
1855
|
+
function defineGenerator(generator) {
|
|
1856
|
+
if (generator.type === "react") return {
|
|
1857
|
+
version: "2",
|
|
1858
|
+
Operations() {
|
|
1859
|
+
return null;
|
|
1860
|
+
},
|
|
1861
|
+
Operation() {
|
|
1862
|
+
return null;
|
|
1863
|
+
},
|
|
1864
|
+
Schema() {
|
|
1865
|
+
return null;
|
|
1866
|
+
},
|
|
1867
|
+
...generator
|
|
1868
|
+
};
|
|
1869
|
+
return {
|
|
1870
|
+
version: "2",
|
|
1871
|
+
async operations() {
|
|
1872
|
+
return [];
|
|
1873
|
+
},
|
|
1874
|
+
async operation() {
|
|
1875
|
+
return [];
|
|
1876
|
+
},
|
|
1877
|
+
async schema() {
|
|
1878
|
+
return [];
|
|
1879
|
+
},
|
|
1880
|
+
...generator
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
//#endregion
|
|
1783
1884
|
//#region src/defineLogger.ts
|
|
1885
|
+
/**
|
|
1886
|
+
* Wraps a logger definition into a typed {@link Logger}.
|
|
1887
|
+
*
|
|
1888
|
+
* @example
|
|
1889
|
+
* export const myLogger = defineLogger({
|
|
1890
|
+
* name: 'my-logger',
|
|
1891
|
+
* install(context, options) {
|
|
1892
|
+
* context.on('info', (message) => console.log('ℹ', message))
|
|
1893
|
+
* context.on('error', (error) => console.error('✗', error.message))
|
|
1894
|
+
* },
|
|
1895
|
+
* })
|
|
1896
|
+
*/
|
|
1784
1897
|
function defineLogger(logger) {
|
|
1785
|
-
return
|
|
1898
|
+
return logger;
|
|
1786
1899
|
}
|
|
1787
1900
|
//#endregion
|
|
1788
|
-
//#region src/
|
|
1901
|
+
//#region src/definePreset.ts
|
|
1789
1902
|
/**
|
|
1790
|
-
*
|
|
1903
|
+
* Creates a typed preset object that bundles a name, resolvers, and optional
|
|
1904
|
+
* transformers — the building block for composable plugin presets.
|
|
1905
|
+
*
|
|
1906
|
+
* @example
|
|
1907
|
+
* import { definePreset } from '@kubb/core'
|
|
1908
|
+
* import { resolverTsLegacy } from '@kubb/plugin-ts'
|
|
1909
|
+
*
|
|
1910
|
+
* export const myPreset = definePreset('myPreset', { resolvers: [resolverTsLegacy] })
|
|
1911
|
+
*
|
|
1912
|
+
* @example
|
|
1913
|
+
* // With custom transformers
|
|
1914
|
+
* export const myPreset = definePreset('myPreset', { resolvers: [resolverTsLegacy], transformers: [myTransformer] })
|
|
1791
1915
|
*/
|
|
1792
|
-
function
|
|
1793
|
-
return
|
|
1916
|
+
function definePreset(name, { resolvers, transformers }) {
|
|
1917
|
+
return {
|
|
1918
|
+
name,
|
|
1919
|
+
resolvers,
|
|
1920
|
+
transformers
|
|
1921
|
+
};
|
|
1794
1922
|
}
|
|
1795
1923
|
//#endregion
|
|
1796
|
-
//#region src/
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
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
|
-
|
|
1924
|
+
//#region src/definePresets.ts
|
|
1925
|
+
/**
|
|
1926
|
+
* Creates a typed presets registry object — a named collection of {@link Preset} entries.
|
|
1927
|
+
*
|
|
1928
|
+
* @example
|
|
1929
|
+
* import { definePreset, definePresets } from '@kubb/core'
|
|
1930
|
+
* import { resolverTsLegacy } from '@kubb/plugin-ts'
|
|
1931
|
+
*
|
|
1932
|
+
* export const myPresets = definePresets({
|
|
1933
|
+
* kubbV4: definePreset('kubbV4', { resolvers: [resolverTsLegacy] }),
|
|
1934
|
+
* })
|
|
1935
|
+
*/
|
|
1936
|
+
function definePresets(presets) {
|
|
1937
|
+
return presets;
|
|
1938
|
+
}
|
|
1939
|
+
//#endregion
|
|
1940
|
+
//#region src/defineResolver.ts
|
|
1941
|
+
/**
|
|
1942
|
+
* Checks if an operation matches a pattern for a given filter type (`tag`, `operationId`, `path`, `method`).
|
|
1943
|
+
*/
|
|
1944
|
+
function matchesOperationPattern(node, type, pattern) {
|
|
1945
|
+
switch (type) {
|
|
1946
|
+
case "tag": return node.tags.some((tag) => !!tag.match(pattern));
|
|
1947
|
+
case "operationId": return !!node.operationId.match(pattern);
|
|
1948
|
+
case "path": return !!node.path.match(pattern);
|
|
1949
|
+
case "method": return !!node.method.toLowerCase().match(pattern);
|
|
1950
|
+
default: return false;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
/**
|
|
1954
|
+
* Checks if a schema matches a pattern for a given filter type (`schemaName`).
|
|
1955
|
+
* Returns `null` when the filter type doesn't apply to schemas.
|
|
1956
|
+
*/
|
|
1957
|
+
function matchesSchemaPattern(node, type, pattern) {
|
|
1958
|
+
switch (type) {
|
|
1959
|
+
case "schemaName": return node.name ? !!node.name.match(pattern) : false;
|
|
1960
|
+
default: return null;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* Default name resolver — `camelCase` for most types, `PascalCase` for `type`.
|
|
1965
|
+
*/
|
|
1966
|
+
function defaultResolver(name, type) {
|
|
1967
|
+
let resolvedName = camelCase(name);
|
|
1968
|
+
if (type === "file" || type === "function") resolvedName = camelCase(name, { isFile: type === "file" });
|
|
1969
|
+
if (type === "type") resolvedName = pascalCase(name);
|
|
1970
|
+
return resolvedName;
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Default option resolver — applies include/exclude filters and merges any matching override options.
|
|
1974
|
+
* Returns `null` when the node is filtered out.
|
|
1975
|
+
*/
|
|
1976
|
+
function defaultResolveOptions(node, { options, exclude = [], include, override = [] }) {
|
|
1977
|
+
if (isOperationNode(node)) {
|
|
1978
|
+
if (exclude.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
1979
|
+
if (include && !include.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
1980
|
+
const overrideOptions = override.find(({ type, pattern }) => matchesOperationPattern(node, type, pattern))?.options;
|
|
1981
|
+
return {
|
|
1982
|
+
...options,
|
|
1983
|
+
...overrideOptions
|
|
1844
1984
|
};
|
|
1845
|
-
if (typeof dependency === "string" && dependencies[dependency]) return dependencies[dependency];
|
|
1846
|
-
const matchedDependency = Object.keys(dependencies).find((dep) => dep.match(dependency));
|
|
1847
|
-
return matchedDependency ? dependencies[matchedDependency] : void 0;
|
|
1848
|
-
}
|
|
1849
|
-
async getVersion(dependency) {
|
|
1850
|
-
if (typeof dependency === "string" && PackageManager.#cache[dependency]) return PackageManager.#cache[dependency];
|
|
1851
|
-
const packageJSON = await this.getPackageJSON();
|
|
1852
|
-
if (!packageJSON) return;
|
|
1853
|
-
return this.#match(packageJSON, dependency);
|
|
1854
|
-
}
|
|
1855
|
-
getVersionSync(dependency) {
|
|
1856
|
-
if (typeof dependency === "string" && PackageManager.#cache[dependency]) return PackageManager.#cache[dependency];
|
|
1857
|
-
const packageJSON = this.getPackageJSONSync();
|
|
1858
|
-
if (!packageJSON) return;
|
|
1859
|
-
return this.#match(packageJSON, dependency);
|
|
1860
|
-
}
|
|
1861
|
-
async isValid(dependency, version) {
|
|
1862
|
-
const packageVersion = await this.getVersion(dependency);
|
|
1863
|
-
if (!packageVersion) return false;
|
|
1864
|
-
if (packageVersion === version) return true;
|
|
1865
|
-
const semVer = coerce(packageVersion);
|
|
1866
|
-
if (!semVer) return false;
|
|
1867
|
-
return satisfies(semVer, version);
|
|
1868
|
-
}
|
|
1869
|
-
isValidSync(dependency, version) {
|
|
1870
|
-
const packageVersion = this.getVersionSync(dependency);
|
|
1871
|
-
if (!packageVersion) return false;
|
|
1872
|
-
if (packageVersion === version) return true;
|
|
1873
|
-
const semVer = coerce(packageVersion);
|
|
1874
|
-
if (!semVer) return false;
|
|
1875
|
-
return satisfies(semVer, version);
|
|
1876
1985
|
}
|
|
1877
|
-
|
|
1986
|
+
if (isSchemaNode(node)) {
|
|
1987
|
+
if (exclude.some(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)) return null;
|
|
1988
|
+
if (include) {
|
|
1989
|
+
const applicable = include.map(({ type, pattern }) => matchesSchemaPattern(node, type, pattern)).filter((r) => r !== null);
|
|
1990
|
+
if (applicable.length > 0 && !applicable.includes(true)) return null;
|
|
1991
|
+
}
|
|
1992
|
+
const overrideOptions = override.find(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)?.options;
|
|
1993
|
+
return {
|
|
1994
|
+
...options,
|
|
1995
|
+
...overrideOptions
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
return options;
|
|
1999
|
+
}
|
|
2000
|
+
/**
|
|
2001
|
+
* Defines a resolver for a plugin, with built-in defaults for name casing and include/exclude/override filtering.
|
|
2002
|
+
* Override `default` or `resolveOptions` in the builder to customize the behavior.
|
|
2003
|
+
*
|
|
2004
|
+
* @example
|
|
2005
|
+
* export const resolver = defineResolver<PluginTs>(() => ({
|
|
2006
|
+
* name: 'default',
|
|
2007
|
+
* resolveName(name) {
|
|
2008
|
+
* return this.default(name, 'function')
|
|
2009
|
+
* },
|
|
2010
|
+
* resolveTypedName(name) {
|
|
2011
|
+
* return this.default(name, 'type')
|
|
2012
|
+
* },
|
|
2013
|
+
* resolveParamName(node, param) {
|
|
2014
|
+
* return this.resolveName(`${node.operationId} ${param.in} ${param.name}`)
|
|
2015
|
+
* },
|
|
2016
|
+
* }))
|
|
2017
|
+
*/
|
|
2018
|
+
function defineResolver(build) {
|
|
2019
|
+
return {
|
|
2020
|
+
default: defaultResolver,
|
|
2021
|
+
resolveOptions: defaultResolveOptions,
|
|
2022
|
+
...build()
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
//#endregion
|
|
2026
|
+
//#region src/renderNode.tsx
|
|
2027
|
+
/**
|
|
2028
|
+
* Renders a React component for a list of operation nodes (V2 generators).
|
|
2029
|
+
*/
|
|
2030
|
+
async function renderOperations(nodes, options) {
|
|
2031
|
+
const { config, fabric, plugin, Component, adapter } = options;
|
|
2032
|
+
if (!Component) return;
|
|
2033
|
+
const fabricChild = createReactFabric();
|
|
2034
|
+
await fabricChild.render(/* @__PURE__ */ jsx(Fabric, {
|
|
2035
|
+
meta: { plugin },
|
|
2036
|
+
children: /* @__PURE__ */ jsx(Component, {
|
|
2037
|
+
config,
|
|
2038
|
+
adapter,
|
|
2039
|
+
nodes,
|
|
2040
|
+
options: options.options
|
|
2041
|
+
})
|
|
2042
|
+
}));
|
|
2043
|
+
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
2044
|
+
fabricChild.unmount();
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Renders a React component for a single operation node (V2 generators).
|
|
2048
|
+
*/
|
|
2049
|
+
async function renderOperation(node, options) {
|
|
2050
|
+
const { config, fabric, plugin, Component, adapter, driver, mode } = options;
|
|
2051
|
+
if (!Component) return;
|
|
2052
|
+
const fabricChild = createReactFabric();
|
|
2053
|
+
await fabricChild.render(/* @__PURE__ */ jsx(Fabric, {
|
|
2054
|
+
meta: {
|
|
2055
|
+
plugin,
|
|
2056
|
+
driver,
|
|
2057
|
+
mode
|
|
2058
|
+
},
|
|
2059
|
+
children: /* @__PURE__ */ jsx(Component, {
|
|
2060
|
+
config,
|
|
2061
|
+
adapter,
|
|
2062
|
+
node,
|
|
2063
|
+
options: options.options
|
|
2064
|
+
})
|
|
2065
|
+
}));
|
|
2066
|
+
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
2067
|
+
fabricChild.unmount();
|
|
2068
|
+
}
|
|
2069
|
+
/**
|
|
2070
|
+
* Renders a React component for a single schema node (V2 generators).
|
|
2071
|
+
*/
|
|
2072
|
+
async function renderSchema(node, options) {
|
|
2073
|
+
const { config, fabric, plugin, Component, adapter, driver, mode } = options;
|
|
2074
|
+
if (!Component) return;
|
|
2075
|
+
const fabricChild = createReactFabric();
|
|
2076
|
+
await fabricChild.render(/* @__PURE__ */ jsx(Fabric, {
|
|
2077
|
+
meta: {
|
|
2078
|
+
plugin,
|
|
2079
|
+
driver,
|
|
2080
|
+
mode
|
|
2081
|
+
},
|
|
2082
|
+
children: /* @__PURE__ */ jsx(Component, {
|
|
2083
|
+
config,
|
|
2084
|
+
adapter,
|
|
2085
|
+
node,
|
|
2086
|
+
options: options.options
|
|
2087
|
+
})
|
|
2088
|
+
}));
|
|
2089
|
+
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
2090
|
+
fabricChild.unmount();
|
|
2091
|
+
}
|
|
1878
2092
|
//#endregion
|
|
1879
2093
|
//#region src/storages/memoryStorage.ts
|
|
1880
2094
|
/**
|
|
@@ -1894,7 +2108,7 @@ var PackageManager = class PackageManager {
|
|
|
1894
2108
|
* })
|
|
1895
2109
|
* ```
|
|
1896
2110
|
*/
|
|
1897
|
-
const memoryStorage =
|
|
2111
|
+
const memoryStorage = createStorage(() => {
|
|
1898
2112
|
const store = /* @__PURE__ */ new Map();
|
|
1899
2113
|
return {
|
|
1900
2114
|
name: "memory",
|
|
@@ -1926,7 +2140,7 @@ const memoryStorage = defineStorage(() => {
|
|
|
1926
2140
|
//#endregion
|
|
1927
2141
|
//#region src/utils/FunctionParams.ts
|
|
1928
2142
|
/**
|
|
1929
|
-
* @deprecated
|
|
2143
|
+
* @deprecated use ast package instead
|
|
1930
2144
|
*/
|
|
1931
2145
|
var FunctionParams = class FunctionParams {
|
|
1932
2146
|
#items = [];
|
|
@@ -2002,14 +2216,10 @@ var FunctionParams = class FunctionParams {
|
|
|
2002
2216
|
//#endregion
|
|
2003
2217
|
//#region src/utils/formatters.ts
|
|
2004
2218
|
/**
|
|
2005
|
-
*
|
|
2219
|
+
* Returns `true` when the given formatter is installed and callable.
|
|
2006
2220
|
*
|
|
2007
|
-
*
|
|
2008
|
-
*
|
|
2009
|
-
*
|
|
2010
|
-
* @remarks
|
|
2011
|
-
* This function checks availability by running `<formatter> --version` command.
|
|
2012
|
-
* All supported formatters (biome, prettier, oxfmt) implement the --version flag.
|
|
2221
|
+
* Availability is detected by running `<formatter> --version` and checking
|
|
2222
|
+
* that the process exits without error.
|
|
2013
2223
|
*/
|
|
2014
2224
|
async function isFormatterAvailable(formatter) {
|
|
2015
2225
|
try {
|
|
@@ -2020,34 +2230,39 @@ async function isFormatterAvailable(formatter) {
|
|
|
2020
2230
|
}
|
|
2021
2231
|
}
|
|
2022
2232
|
/**
|
|
2023
|
-
*
|
|
2024
|
-
*
|
|
2025
|
-
* @returns Promise that resolves to the first available formatter or undefined if none are found
|
|
2233
|
+
* Detects the first available code formatter on the current system.
|
|
2026
2234
|
*
|
|
2027
|
-
*
|
|
2028
|
-
*
|
|
2029
|
-
* Uses the `--version` flag to detect if each formatter command is available.
|
|
2030
|
-
* This is a reliable method as all supported formatters implement this flag.
|
|
2235
|
+
* - Checks in preference order: `biome`, `oxfmt`, `prettier`.
|
|
2236
|
+
* - Returns `null` when none are found.
|
|
2031
2237
|
*
|
|
2032
2238
|
* @example
|
|
2033
|
-
* ```
|
|
2239
|
+
* ```ts
|
|
2034
2240
|
* const formatter = await detectFormatter()
|
|
2035
2241
|
* if (formatter) {
|
|
2036
2242
|
* console.log(`Using ${formatter} for formatting`)
|
|
2037
|
-
* } else {
|
|
2038
|
-
* console.log('No formatter found')
|
|
2039
2243
|
* }
|
|
2040
2244
|
* ```
|
|
2041
2245
|
*/
|
|
2042
2246
|
async function detectFormatter() {
|
|
2043
|
-
|
|
2247
|
+
const formatterNames = new Set([
|
|
2044
2248
|
"biome",
|
|
2045
2249
|
"oxfmt",
|
|
2046
2250
|
"prettier"
|
|
2047
|
-
])
|
|
2251
|
+
]);
|
|
2252
|
+
for (const formatter of formatterNames) if (await isFormatterAvailable(formatter)) return formatter;
|
|
2253
|
+
return null;
|
|
2048
2254
|
}
|
|
2049
2255
|
//#endregion
|
|
2050
2256
|
//#region src/utils/TreeNode.ts
|
|
2257
|
+
/**
|
|
2258
|
+
* Tree structure used to build per-directory barrel (`index.ts`) files from a
|
|
2259
|
+
* flat list of generated {@link KubbFile.File} entries.
|
|
2260
|
+
*
|
|
2261
|
+
* Each node represents either a directory or a file within the output tree.
|
|
2262
|
+
* Use {@link TreeNode.build} to construct a root node from a file list, then
|
|
2263
|
+
* traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
|
|
2264
|
+
* `*Deep` helpers.
|
|
2265
|
+
*/
|
|
2051
2266
|
var TreeNode = class TreeNode {
|
|
2052
2267
|
data;
|
|
2053
2268
|
parent;
|
|
@@ -2063,10 +2278,18 @@ var TreeNode = class TreeNode {
|
|
|
2063
2278
|
this.children.push(child);
|
|
2064
2279
|
return child;
|
|
2065
2280
|
}
|
|
2281
|
+
/**
|
|
2282
|
+
* Returns the root ancestor of this node, walking up via `parent` links.
|
|
2283
|
+
*/
|
|
2066
2284
|
get root() {
|
|
2067
2285
|
if (!this.parent) return this;
|
|
2068
2286
|
return this.parent.root;
|
|
2069
2287
|
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Returns all leaf descendants (nodes with no children) of this node.
|
|
2290
|
+
*
|
|
2291
|
+
* Results are cached after the first traversal.
|
|
2292
|
+
*/
|
|
2070
2293
|
get leaves() {
|
|
2071
2294
|
if (!this.children || this.children.length === 0) return [this];
|
|
2072
2295
|
if (this.#cachedLeaves) return this.#cachedLeaves;
|
|
@@ -2097,6 +2320,12 @@ var TreeNode = class TreeNode {
|
|
|
2097
2320
|
if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
|
|
2098
2321
|
return this.leaves.map(callback);
|
|
2099
2322
|
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Builds a {@link TreeNode} tree from a flat list of files.
|
|
2325
|
+
*
|
|
2326
|
+
* - Filters to files under `root` (when provided) and skips `.json` files.
|
|
2327
|
+
* - Returns `null` when no files match.
|
|
2328
|
+
*/
|
|
2100
2329
|
static build(files, root) {
|
|
2101
2330
|
try {
|
|
2102
2331
|
const filteredTree = buildDirectoryTree(files, root);
|
|
@@ -2167,65 +2396,64 @@ function buildDirectoryTree(files, rootFolder = "") {
|
|
|
2167
2396
|
return root;
|
|
2168
2397
|
}
|
|
2169
2398
|
//#endregion
|
|
2170
|
-
//#region src/
|
|
2399
|
+
//#region src/utils/getBarrelFiles.ts
|
|
2171
2400
|
/** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
(item.data.file?.
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
isIndexable: false
|
|
2201
|
-
});
|
|
2401
|
+
function getBarrelFilesByRoot(root, files) {
|
|
2402
|
+
const cachedFiles = /* @__PURE__ */ new Map();
|
|
2403
|
+
TreeNode.build(files, root)?.forEach((treeNode) => {
|
|
2404
|
+
if (!treeNode || !treeNode.children || !treeNode.parent?.data.path) return;
|
|
2405
|
+
const barrelFile = {
|
|
2406
|
+
path: join(treeNode.parent?.data.path, "index.ts"),
|
|
2407
|
+
baseName: "index.ts",
|
|
2408
|
+
exports: [],
|
|
2409
|
+
imports: [],
|
|
2410
|
+
sources: []
|
|
2411
|
+
};
|
|
2412
|
+
const previousBarrelFile = cachedFiles.get(barrelFile.path);
|
|
2413
|
+
treeNode.leaves.forEach((item) => {
|
|
2414
|
+
if (!item.data.name) return;
|
|
2415
|
+
(item.data.file?.sources || []).forEach((source) => {
|
|
2416
|
+
if (!item.data.file?.path || !source.isIndexable || !source.name) return;
|
|
2417
|
+
if (previousBarrelFile?.sources.some((item) => item.name === source.name && item.isTypeOnly === source.isTypeOnly)) return;
|
|
2418
|
+
barrelFile.exports.push({
|
|
2419
|
+
name: [source.name],
|
|
2420
|
+
path: getRelativePath(treeNode.parent?.data.path, item.data.path),
|
|
2421
|
+
isTypeOnly: source.isTypeOnly
|
|
2422
|
+
});
|
|
2423
|
+
barrelFile.sources.push({
|
|
2424
|
+
name: source.name,
|
|
2425
|
+
isTypeOnly: source.isTypeOnly,
|
|
2426
|
+
value: "",
|
|
2427
|
+
isExportable: false,
|
|
2428
|
+
isIndexable: false
|
|
2202
2429
|
});
|
|
2203
2430
|
});
|
|
2204
|
-
if (previousBarrelFile) {
|
|
2205
|
-
previousBarrelFile.sources.push(...barrelFile.sources);
|
|
2206
|
-
previousBarrelFile.exports?.push(...barrelFile.exports || []);
|
|
2207
|
-
} else cachedFiles.set(barrelFile.path, barrelFile);
|
|
2208
2431
|
});
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2432
|
+
if (previousBarrelFile) {
|
|
2433
|
+
previousBarrelFile.sources.push(...barrelFile.sources);
|
|
2434
|
+
previousBarrelFile.exports?.push(...barrelFile.exports || []);
|
|
2435
|
+
} else cachedFiles.set(barrelFile.path, barrelFile);
|
|
2436
|
+
});
|
|
2437
|
+
return [...cachedFiles.values()];
|
|
2438
|
+
}
|
|
2214
2439
|
function trimExtName(text) {
|
|
2215
2440
|
const dotIndex = text.lastIndexOf(".");
|
|
2216
2441
|
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
2217
2442
|
return text;
|
|
2218
2443
|
}
|
|
2444
|
+
/**
|
|
2445
|
+
* Generates `index.ts` barrel files for all directories under `root/output.path`.
|
|
2446
|
+
*
|
|
2447
|
+
* - Returns an empty array when `type` is falsy or `'propagate'`.
|
|
2448
|
+
* - Skips generation when the output path itself ends with `index` (already a barrel).
|
|
2449
|
+
* - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
|
|
2450
|
+
* - Attaches `meta` to each barrel file for downstream plugin identification.
|
|
2451
|
+
*/
|
|
2219
2452
|
async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
2220
2453
|
if (!type || type === "propagate") return [];
|
|
2221
|
-
const barrelManager = new BarrelManager();
|
|
2222
2454
|
const pathToBuildFrom = join(root, output.path);
|
|
2223
2455
|
if (trimExtName(pathToBuildFrom).endsWith("index")) return [];
|
|
2224
|
-
const barrelFiles =
|
|
2225
|
-
files,
|
|
2226
|
-
root: pathToBuildFrom,
|
|
2227
|
-
meta
|
|
2228
|
-
});
|
|
2456
|
+
const barrelFiles = getBarrelFilesByRoot(pathToBuildFrom, files);
|
|
2229
2457
|
if (type === "all") return barrelFiles.map((file) => {
|
|
2230
2458
|
return {
|
|
2231
2459
|
...file,
|
|
@@ -2245,38 +2473,58 @@ async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
|
2245
2473
|
});
|
|
2246
2474
|
}
|
|
2247
2475
|
//#endregion
|
|
2248
|
-
//#region src/utils/
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2476
|
+
//#region src/utils/getConfigs.ts
|
|
2477
|
+
/**
|
|
2478
|
+
* Resolves a {@link ConfigInput} into a normalized array of {@link Config} objects.
|
|
2479
|
+
*
|
|
2480
|
+
* - Awaits the config when it is a `Promise`.
|
|
2481
|
+
* - Calls the factory function with `args` when the config is a function.
|
|
2482
|
+
* - Wraps a single config object in an array for uniform downstream handling.
|
|
2483
|
+
*/
|
|
2484
|
+
async function getConfigs(config, args) {
|
|
2485
|
+
const resolved = await (typeof config === "function" ? config(args) : config);
|
|
2486
|
+
return (Array.isArray(resolved) ? resolved : [resolved]).map((item) => ({ ...item }));
|
|
2254
2487
|
}
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2488
|
+
//#endregion
|
|
2489
|
+
//#region src/utils/mergeResolvers.ts
|
|
2490
|
+
/**
|
|
2491
|
+
* Merges an array of resolvers into a single resolver. Later entries override earlier ones (last wins).
|
|
2492
|
+
*/
|
|
2493
|
+
function mergeResolvers(...resolvers) {
|
|
2494
|
+
return resolvers.reduce((acc, curr) => ({
|
|
2495
|
+
...acc,
|
|
2496
|
+
...curr
|
|
2497
|
+
}), resolvers[0]);
|
|
2259
2498
|
}
|
|
2260
2499
|
//#endregion
|
|
2261
|
-
//#region src/utils/
|
|
2500
|
+
//#region src/utils/getPreset.ts
|
|
2262
2501
|
/**
|
|
2263
|
-
*
|
|
2502
|
+
* Resolves a named preset into merged resolvers and transformers.
|
|
2503
|
+
*
|
|
2504
|
+
* - Merges the preset's resolvers on top of the first (default) resolver to produce `baseResolver`.
|
|
2505
|
+
* - Merges any additional user-supplied resolvers on top of that to produce the final `resolver`.
|
|
2506
|
+
* - Concatenates preset transformers before user-supplied transformers.
|
|
2264
2507
|
*/
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
const
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
}
|
|
2276
|
-
return results;
|
|
2508
|
+
function getPreset(params) {
|
|
2509
|
+
const { preset: presetName, presets, resolvers, transformers: userTransformers } = params;
|
|
2510
|
+
const [defaultResolver, ...userResolvers] = resolvers;
|
|
2511
|
+
const preset = presets[presetName];
|
|
2512
|
+
const baseResolver = mergeResolvers(defaultResolver, ...preset?.resolvers ?? []);
|
|
2513
|
+
return {
|
|
2514
|
+
baseResolver,
|
|
2515
|
+
resolver: mergeResolvers(baseResolver, ...userResolvers ?? []),
|
|
2516
|
+
transformers: [...preset?.transformers ?? [], ...userTransformers ?? []],
|
|
2517
|
+
preset
|
|
2518
|
+
};
|
|
2277
2519
|
}
|
|
2278
2520
|
//#endregion
|
|
2279
2521
|
//#region src/utils/linters.ts
|
|
2522
|
+
/**
|
|
2523
|
+
* Returns `true` when the given linter is installed and callable.
|
|
2524
|
+
*
|
|
2525
|
+
* Availability is detected by running `<linter> --version` and checking
|
|
2526
|
+
* that the process exits without error.
|
|
2527
|
+
*/
|
|
2280
2528
|
async function isLinterAvailable(linter) {
|
|
2281
2529
|
try {
|
|
2282
2530
|
await x(linter, ["--version"], { nodeOptions: { stdio: "ignore" } });
|
|
@@ -2285,14 +2533,73 @@ async function isLinterAvailable(linter) {
|
|
|
2285
2533
|
return false;
|
|
2286
2534
|
}
|
|
2287
2535
|
}
|
|
2536
|
+
/**
|
|
2537
|
+
* Detects the first available linter on the current system.
|
|
2538
|
+
*
|
|
2539
|
+
* - Checks in preference order: `biome`, `oxlint`, `eslint`.
|
|
2540
|
+
* - Returns `null` when none are found.
|
|
2541
|
+
*
|
|
2542
|
+
* @example
|
|
2543
|
+
* ```ts
|
|
2544
|
+
* const linter = await detectLinter()
|
|
2545
|
+
* if (linter) {
|
|
2546
|
+
* console.log(`Using ${linter} for linting`)
|
|
2547
|
+
* }
|
|
2548
|
+
* ```
|
|
2549
|
+
*/
|
|
2288
2550
|
async function detectLinter() {
|
|
2289
|
-
|
|
2551
|
+
const linterNames = new Set([
|
|
2290
2552
|
"biome",
|
|
2291
2553
|
"oxlint",
|
|
2292
2554
|
"eslint"
|
|
2293
|
-
])
|
|
2555
|
+
]);
|
|
2556
|
+
for (const linter of linterNames) if (await isLinterAvailable(linter)) return linter;
|
|
2557
|
+
return null;
|
|
2558
|
+
}
|
|
2559
|
+
//#endregion
|
|
2560
|
+
//#region src/utils/packageJSON.ts
|
|
2561
|
+
function getPackageJSONSync(cwd) {
|
|
2562
|
+
const pkgPath = pkg.up({ cwd });
|
|
2563
|
+
if (!pkgPath) return null;
|
|
2564
|
+
return JSON.parse(readSync(pkgPath));
|
|
2565
|
+
}
|
|
2566
|
+
function match(packageJSON, dependency) {
|
|
2567
|
+
const dependencies = {
|
|
2568
|
+
...packageJSON.dependencies || {},
|
|
2569
|
+
...packageJSON.devDependencies || {}
|
|
2570
|
+
};
|
|
2571
|
+
if (typeof dependency === "string" && dependencies[dependency]) return dependencies[dependency];
|
|
2572
|
+
const matched = Object.keys(dependencies).find((dep) => dep.match(dependency));
|
|
2573
|
+
return matched ? dependencies[matched] ?? null : null;
|
|
2574
|
+
}
|
|
2575
|
+
function getVersionSync(dependency, cwd) {
|
|
2576
|
+
const packageJSON = getPackageJSONSync(cwd);
|
|
2577
|
+
return packageJSON ? match(packageJSON, dependency) : null;
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Returns `true` when the nearest `package.json` declares a dependency that
|
|
2581
|
+
* satisfies the given semver range.
|
|
2582
|
+
*
|
|
2583
|
+
* - Searches both `dependencies` and `devDependencies`.
|
|
2584
|
+
* - Accepts a string package name or a `RegExp` to match scoped/pattern packages.
|
|
2585
|
+
* - Uses `semver.satisfies` for range comparison; returns `false` when the
|
|
2586
|
+
* version string cannot be coerced into a valid semver.
|
|
2587
|
+
*
|
|
2588
|
+
* @example
|
|
2589
|
+
* ```ts
|
|
2590
|
+
* satisfiesDependency('react', '>=18') // true when react@18.x is installed
|
|
2591
|
+
* satisfiesDependency(/^@tanstack\//, '>=5') // true when any @tanstack/* >=5 is found
|
|
2592
|
+
* ```
|
|
2593
|
+
*/
|
|
2594
|
+
function satisfiesDependency(dependency, version, cwd) {
|
|
2595
|
+
const packageVersion = getVersionSync(dependency, cwd);
|
|
2596
|
+
if (!packageVersion) return false;
|
|
2597
|
+
if (packageVersion === version) return true;
|
|
2598
|
+
const semVer = coerce(packageVersion);
|
|
2599
|
+
if (!semVer) return false;
|
|
2600
|
+
return satisfies(semVer, version);
|
|
2294
2601
|
}
|
|
2295
2602
|
//#endregion
|
|
2296
|
-
export { AsyncEventEmitter, FunctionParams,
|
|
2603
|
+
export { AsyncEventEmitter, FunctionParams, PluginDriver, URLPath, build, build as default, createAdapter, createPlugin, createStorage, defaultResolveOptions, defineConfig, defineGenerator, defineLogger, definePreset, definePresets, definePrinter, defineResolver, detectFormatter, detectLinter, formatters, fsStorage, getBarrelFiles, getConfigs, getMode, getPreset, isInputPath, linters, logLevel, memoryStorage, mergeResolvers, renderOperation, renderOperations, renderSchema, safeBuild, satisfiesDependency, setup };
|
|
2297
2604
|
|
|
2298
2605
|
//# sourceMappingURL=index.js.map
|