@kubb/core 5.0.0-alpha.4 → 5.0.0-alpha.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PluginDriver-BQwm8hDd.cjs +1729 -0
- package/dist/PluginDriver-BQwm8hDd.cjs.map +1 -0
- package/dist/PluginDriver-CgXFtmNP.js +1617 -0
- package/dist/PluginDriver-CgXFtmNP.js.map +1 -0
- package/dist/index.cjs +918 -1898
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +267 -264
- package/dist/index.js +897 -1860
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +164 -0
- package/dist/mocks.cjs.map +1 -0
- package/dist/mocks.d.ts +74 -0
- package/dist/mocks.js +159 -0
- package/dist/mocks.js.map +1 -0
- package/dist/types-PLXOrxov.d.ts +2151 -0
- package/package.json +11 -14
- package/src/FileManager.ts +131 -0
- package/src/FileProcessor.ts +84 -0
- package/src/Kubb.ts +174 -85
- package/src/PluginDriver.ts +941 -0
- package/src/constants.ts +33 -43
- package/src/createAdapter.ts +25 -0
- package/src/createKubb.ts +607 -0
- package/src/createPlugin.ts +31 -0
- package/src/createRenderer.ts +57 -0
- package/src/createStorage.ts +58 -0
- package/src/defineGenerator.ts +88 -100
- package/src/defineLogger.ts +13 -3
- package/src/defineParser.ts +45 -0
- package/src/definePlugin.ts +90 -7
- package/src/defineResolver.ts +453 -0
- package/src/devtools.ts +14 -14
- package/src/index.ts +12 -17
- package/src/mocks.ts +234 -0
- package/src/renderNode.ts +35 -0
- package/src/storages/fsStorage.ts +29 -9
- package/src/storages/memoryStorage.ts +2 -2
- package/src/types.ts +821 -152
- package/src/utils/TreeNode.ts +47 -9
- package/src/utils/diagnostics.ts +4 -1
- package/src/utils/executeStrategies.ts +16 -13
- package/src/utils/getBarrelFiles.ts +88 -15
- package/src/utils/isInputPath.ts +8 -0
- package/src/utils/packageJSON.ts +75 -0
- package/dist/chunk-ByKO4r7w.cjs +0 -38
- package/dist/hooks.cjs +0 -50
- package/dist/hooks.cjs.map +0 -1
- package/dist/hooks.d.ts +0 -49
- package/dist/hooks.js +0 -46
- package/dist/hooks.js.map +0 -1
- package/dist/types-Bbh1o0yW.d.ts +0 -1057
- package/src/BarrelManager.ts +0 -74
- package/src/PackageManager.ts +0 -180
- package/src/PluginManager.ts +0 -668
- package/src/PromiseManager.ts +0 -40
- package/src/build.ts +0 -420
- package/src/config.ts +0 -56
- package/src/defineAdapter.ts +0 -22
- package/src/defineStorage.ts +0 -56
- package/src/errors.ts +0 -1
- package/src/hooks/index.ts +0 -8
- package/src/hooks/useKubb.ts +0 -22
- package/src/hooks/useMode.ts +0 -11
- package/src/hooks/usePlugin.ts +0 -11
- package/src/hooks/usePluginManager.ts +0 -11
- package/src/utils/FunctionParams.ts +0 -155
- package/src/utils/formatters.ts +0 -56
- package/src/utils/getConfigs.ts +0 -30
- package/src/utils/getPlugins.ts +0 -23
- package/src/utils/linters.ts +0 -25
- package/src/utils/resolveOptions.ts +0 -93
package/dist/index.js
CHANGED
|
@@ -1,29 +1,20 @@
|
|
|
1
1
|
import "./chunk--u3MIqq1.js";
|
|
2
|
-
import
|
|
2
|
+
import { a as definePlugin, c as DEFAULT_BANNER, d as logLevel, f as isValidVarName, i as defineResolver, l as DEFAULT_EXTENSION, n as applyHookResult, o as pLimit, p as camelCase, r as FileManager, s as BARREL_FILENAME, t as PluginDriver, u as DEFAULT_STUDIO_URL } from "./PluginDriver-CgXFtmNP.js";
|
|
3
3
|
import { EventEmitter } from "node:events";
|
|
4
|
-
import { parseArgs, styleText } from "node:util";
|
|
5
|
-
import { readFileSync } from "node:fs";
|
|
6
4
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
7
|
-
import path, {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import { typescriptParser } from "@kubb/react-fabric/parsers";
|
|
11
|
-
import { fsPlugin } from "@kubb/react-fabric/plugins";
|
|
12
|
-
import { performance } from "node:perf_hooks";
|
|
13
|
-
import { deflateSync } from "fflate";
|
|
14
|
-
import { x } from "tinyexec";
|
|
5
|
+
import path, { dirname, join, posix, resolve } from "node:path";
|
|
6
|
+
import * as ast from "@kubb/ast";
|
|
7
|
+
import { createExport, createFile, createSource, extractStringsFromNodes, transform, walk } from "@kubb/ast";
|
|
15
8
|
import { version } from "node:process";
|
|
16
|
-
|
|
17
|
-
import { pathToFileURL } from "node:url";
|
|
18
|
-
import * as pkg from "empathic/package";
|
|
19
|
-
import { coerce, satisfies } from "semver";
|
|
20
|
-
import { sortBy } from "remeda";
|
|
21
|
-
//#region ../../internals/utils/dist/index.js
|
|
22
|
-
/** Thrown when a plugin's configuration or input fails validation. */
|
|
23
|
-
var ValidationPluginError = class extends Error {};
|
|
9
|
+
//#region ../../internals/utils/src/errors.ts
|
|
24
10
|
/**
|
|
25
11
|
* Thrown when one or more errors occur during a Kubb build.
|
|
26
12
|
* Carries the full list of underlying errors on `errors`.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* throw new BuildError('Build failed', { errors: [err1, err2] })
|
|
17
|
+
* ```
|
|
27
18
|
*/
|
|
28
19
|
var BuildError = class extends Error {
|
|
29
20
|
errors;
|
|
@@ -35,19 +26,34 @@ var BuildError = class extends Error {
|
|
|
35
26
|
};
|
|
36
27
|
/**
|
|
37
28
|
* Coerces an unknown thrown value to an `Error` instance.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
29
|
+
* Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* try { ... } catch(err) {
|
|
34
|
+
* throw new BuildError('Build failed', { cause: toError(err), errors: [] })
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
40
37
|
*/
|
|
41
38
|
function toError(value) {
|
|
42
39
|
return value instanceof Error ? value : new Error(String(value));
|
|
43
40
|
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region ../../internals/utils/src/asyncEventEmitter.ts
|
|
44
43
|
/**
|
|
45
|
-
*
|
|
44
|
+
* Typed `EventEmitter` that awaits all async listeners before resolving.
|
|
46
45
|
* Wraps Node's `EventEmitter` with full TypeScript event-map inference.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
|
|
50
|
+
* emitter.on('build', async (name) => { console.log(name) })
|
|
51
|
+
* await emitter.emit('build', 'petstore') // all listeners awaited
|
|
52
|
+
* ```
|
|
47
53
|
*/
|
|
48
54
|
var AsyncEventEmitter = class {
|
|
49
55
|
/**
|
|
50
|
-
*
|
|
56
|
+
* Maximum number of listeners per event before Node emits a memory-leak warning.
|
|
51
57
|
* @default 10
|
|
52
58
|
*/
|
|
53
59
|
constructor(maxListener = 10) {
|
|
@@ -55,31 +61,48 @@ var AsyncEventEmitter = class {
|
|
|
55
61
|
}
|
|
56
62
|
#emitter = new EventEmitter();
|
|
57
63
|
/**
|
|
58
|
-
* Emits
|
|
64
|
+
* Emits `eventName` and awaits all registered listeners sequentially.
|
|
59
65
|
* Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* await emitter.emit('build', 'petstore')
|
|
70
|
+
* ```
|
|
60
71
|
*/
|
|
61
72
|
async emit(eventName, ...eventArgs) {
|
|
62
73
|
const listeners = this.#emitter.listeners(eventName);
|
|
63
74
|
if (listeners.length === 0) return;
|
|
64
|
-
|
|
75
|
+
for (const listener of listeners) try {
|
|
76
|
+
await listener(...eventArgs);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
let serializedArgs;
|
|
65
79
|
try {
|
|
66
|
-
|
|
67
|
-
} catch
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
serializedArgs = JSON.stringify(eventArgs);
|
|
71
|
-
} catch {
|
|
72
|
-
serializedArgs = String(eventArgs);
|
|
73
|
-
}
|
|
74
|
-
throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
|
|
80
|
+
serializedArgs = JSON.stringify(eventArgs);
|
|
81
|
+
} catch {
|
|
82
|
+
serializedArgs = String(eventArgs);
|
|
75
83
|
}
|
|
76
|
-
|
|
84
|
+
throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
|
|
85
|
+
}
|
|
77
86
|
}
|
|
78
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* Registers a persistent listener for `eventName`.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* emitter.on('build', async (name) => { console.log(name) })
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
79
95
|
on(eventName, handler) {
|
|
80
96
|
this.#emitter.on(eventName, handler);
|
|
81
97
|
}
|
|
82
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* Registers a one-shot listener that removes itself after the first invocation.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* emitter.onOnce('build', async (name) => { console.log(name) })
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
83
106
|
onOnce(eventName, handler) {
|
|
84
107
|
const wrapper = (...args) => {
|
|
85
108
|
this.off(eventName, wrapper);
|
|
@@ -87,237 +110,53 @@ var AsyncEventEmitter = class {
|
|
|
87
110
|
};
|
|
88
111
|
this.on(eventName, wrapper);
|
|
89
112
|
}
|
|
90
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* Removes a previously registered listener.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* emitter.off('build', handler)
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
91
121
|
off(eventName, handler) {
|
|
92
122
|
this.#emitter.off(eventName, handler);
|
|
93
123
|
}
|
|
94
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Returns the number of listeners registered for `eventName`.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* emitter.on('build', handler)
|
|
130
|
+
* emitter.listenerCount('build') // 1
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
listenerCount(eventName) {
|
|
134
|
+
return this.#emitter.listenerCount(eventName);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Removes all listeners from every event channel.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* emitter.removeAll()
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
95
144
|
removeAll() {
|
|
96
145
|
this.#emitter.removeAllListeners();
|
|
97
146
|
}
|
|
98
147
|
};
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region ../../internals/utils/src/time.ts
|
|
99
150
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* and capitalizes each word according to `pascal`.
|
|
103
|
-
*
|
|
104
|
-
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
105
|
-
*/
|
|
106
|
-
function toCamelOrPascal(text, pascal) {
|
|
107
|
-
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
108
|
-
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
109
|
-
if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
|
|
110
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
111
|
-
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
115
|
-
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
116
|
-
* Segments are joined with `/` to form a file path.
|
|
117
|
-
*/
|
|
118
|
-
function applyToFileParts(text, transformPart) {
|
|
119
|
-
const parts = text.split(".");
|
|
120
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Converts `text` to camelCase.
|
|
124
|
-
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
151
|
+
* Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
|
|
152
|
+
* Rounds to 2 decimal places for sub-millisecond precision without noise.
|
|
125
153
|
*
|
|
126
154
|
* @example
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
prefix,
|
|
133
|
-
suffix
|
|
134
|
-
} : {}));
|
|
135
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
136
|
-
}
|
|
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
|
-
/**
|
|
142
|
-
* Serializes `CommandDefinition[]` to a plain, JSON-serializable structure.
|
|
143
|
-
* Use to expose CLI capabilities to AI agents or MCP tools.
|
|
144
|
-
*/
|
|
145
|
-
function getCommandSchema(defs) {
|
|
146
|
-
return defs.map(serializeCommand);
|
|
147
|
-
}
|
|
148
|
-
function serializeCommand(def) {
|
|
149
|
-
return {
|
|
150
|
-
name: def.name,
|
|
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;
|
|
209
|
-
}
|
|
210
|
-
async function runCommand(def, argv, parentName) {
|
|
211
|
-
const parseOptions = buildParseOptions(def);
|
|
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
|
-
});
|
|
318
|
-
/**
|
|
319
|
-
* Calculates elapsed time in milliseconds from a high-resolution start time.
|
|
320
|
-
* Rounds to 2 decimal places to provide sub-millisecond precision without noise.
|
|
155
|
+
* ```ts
|
|
156
|
+
* const start = process.hrtime()
|
|
157
|
+
* doWork()
|
|
158
|
+
* getElapsedMs(start) // 42.35
|
|
159
|
+
* ```
|
|
321
160
|
*/
|
|
322
161
|
function getElapsedMs(hrStart) {
|
|
323
162
|
const [seconds, nanoseconds] = process.hrtime(hrStart);
|
|
@@ -325,39 +164,22 @@ function getElapsedMs(hrStart) {
|
|
|
325
164
|
return Math.round(ms * 100) / 100;
|
|
326
165
|
}
|
|
327
166
|
/**
|
|
328
|
-
* Converts a millisecond duration into a human-readable string.
|
|
329
|
-
*
|
|
167
|
+
* Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* formatMs(250) // '250ms'
|
|
172
|
+
* formatMs(1500) // '1.50s'
|
|
173
|
+
* formatMs(90000) // '1m 30.0s'
|
|
174
|
+
* ```
|
|
330
175
|
*/
|
|
331
176
|
function formatMs(ms) {
|
|
332
177
|
if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
|
|
333
178
|
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
334
179
|
return `${Math.round(ms)}ms`;
|
|
335
180
|
}
|
|
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");
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region ../../internals/utils/src/fs.ts
|
|
361
183
|
/**
|
|
362
184
|
* Converts all backslashes to forward slashes.
|
|
363
185
|
* Extended-length Windows paths (`\\?\...`) are left unchanged.
|
|
@@ -367,8 +189,14 @@ function toSlash(p) {
|
|
|
367
189
|
return p.replaceAll("\\", "/");
|
|
368
190
|
}
|
|
369
191
|
/**
|
|
370
|
-
* Returns the relative path from `rootDir` to `filePath`, always using
|
|
371
|
-
*
|
|
192
|
+
* Returns the relative path from `rootDir` to `filePath`, always using forward slashes
|
|
193
|
+
* and prefixed with `./` when not already traversing upward.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* getRelativePath('/src/components', '/src/components/Button.tsx') // './Button.tsx'
|
|
198
|
+
* getRelativePath('/src/components', '/src/utils/helpers.ts') // '../utils/helpers.ts'
|
|
199
|
+
* ```
|
|
372
200
|
*/
|
|
373
201
|
function getRelativePath(rootDir, filePath) {
|
|
374
202
|
if (!rootDir || !filePath) throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ""} ${filePath || ""}`);
|
|
@@ -378,43 +206,43 @@ function getRelativePath(rootDir, filePath) {
|
|
|
378
206
|
/**
|
|
379
207
|
* Resolves to `true` when the file or directory at `path` exists.
|
|
380
208
|
* Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```ts
|
|
212
|
+
* if (await exists('./kubb.config.ts')) {
|
|
213
|
+
* const content = await read('./kubb.config.ts')
|
|
214
|
+
* }
|
|
215
|
+
* ```
|
|
381
216
|
*/
|
|
382
217
|
async function exists(path) {
|
|
383
218
|
if (typeof Bun !== "undefined") return Bun.file(path).exists();
|
|
384
219
|
return access(path).then(() => true, () => false);
|
|
385
220
|
}
|
|
386
221
|
/**
|
|
387
|
-
* Reads the file at `path` as a UTF-8 string.
|
|
388
|
-
* Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.
|
|
389
|
-
*/
|
|
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
|
-
function readSync(path) {
|
|
396
|
-
return readFileSync(path, { encoding: "utf8" });
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
222
|
* Writes `data` to `path`, trimming leading/trailing whitespace before saving.
|
|
400
|
-
* Skips the write
|
|
401
|
-
* identical to what is already on disk.
|
|
223
|
+
* Skips the write when the trimmed content is empty or identical to what is already on disk.
|
|
402
224
|
* Creates any missing parent directories automatically.
|
|
403
|
-
* When `sanity` is `true`, re-reads the file after writing and throws if the
|
|
404
|
-
*
|
|
225
|
+
* When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* await write('./src/Pet.ts', source) // writes and returns trimmed content
|
|
230
|
+
* await write('./src/Pet.ts', source) // null — file unchanged
|
|
231
|
+
* await write('./src/Pet.ts', ' ') // null — empty content skipped
|
|
232
|
+
* ```
|
|
405
233
|
*/
|
|
406
234
|
async function write(path, data, options = {}) {
|
|
407
235
|
const trimmed = data.trim();
|
|
408
|
-
if (trimmed === "") return
|
|
236
|
+
if (trimmed === "") return null;
|
|
409
237
|
const resolved = resolve(path);
|
|
410
238
|
if (typeof Bun !== "undefined") {
|
|
411
239
|
const file = Bun.file(resolved);
|
|
412
|
-
if ((await file.exists() ? await file.text() : null) === trimmed) return
|
|
240
|
+
if ((await file.exists() ? await file.text() : null) === trimmed) return null;
|
|
413
241
|
await Bun.write(resolved, trimmed);
|
|
414
242
|
return trimmed;
|
|
415
243
|
}
|
|
416
244
|
try {
|
|
417
|
-
if (await readFile(resolved, { encoding: "utf-8" }) === trimmed) return
|
|
245
|
+
if (await readFile(resolved, { encoding: "utf-8" }) === trimmed) return null;
|
|
418
246
|
} catch {}
|
|
419
247
|
await mkdir(dirname(resolved), { recursive: true });
|
|
420
248
|
await writeFile(resolved, trimmed, { encoding: "utf-8" });
|
|
@@ -425,133 +253,22 @@ async function write(path, data, options = {}) {
|
|
|
425
253
|
}
|
|
426
254
|
return trimmed;
|
|
427
255
|
}
|
|
428
|
-
/**
|
|
256
|
+
/**
|
|
257
|
+
* Recursively removes `path`. Silently succeeds when `path` does not exist.
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* await clean('./dist')
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
429
264
|
async function clean(path) {
|
|
430
265
|
return rm(path, {
|
|
431
266
|
recursive: true,
|
|
432
267
|
force: true
|
|
433
268
|
});
|
|
434
269
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
* Use this when you need to track usage frequency but always emit the original identifier.
|
|
438
|
-
*/
|
|
439
|
-
function setUniqueName(originalName, data) {
|
|
440
|
-
let used = data[originalName] || 0;
|
|
441
|
-
if (used) {
|
|
442
|
-
data[originalName] = ++used;
|
|
443
|
-
return originalName;
|
|
444
|
-
}
|
|
445
|
-
data[originalName] = 1;
|
|
446
|
-
return originalName;
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* JavaScript and Java reserved words.
|
|
450
|
-
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
451
|
-
*/
|
|
452
|
-
const reservedWords = [
|
|
453
|
-
"abstract",
|
|
454
|
-
"arguments",
|
|
455
|
-
"boolean",
|
|
456
|
-
"break",
|
|
457
|
-
"byte",
|
|
458
|
-
"case",
|
|
459
|
-
"catch",
|
|
460
|
-
"char",
|
|
461
|
-
"class",
|
|
462
|
-
"const",
|
|
463
|
-
"continue",
|
|
464
|
-
"debugger",
|
|
465
|
-
"default",
|
|
466
|
-
"delete",
|
|
467
|
-
"do",
|
|
468
|
-
"double",
|
|
469
|
-
"else",
|
|
470
|
-
"enum",
|
|
471
|
-
"eval",
|
|
472
|
-
"export",
|
|
473
|
-
"extends",
|
|
474
|
-
"false",
|
|
475
|
-
"final",
|
|
476
|
-
"finally",
|
|
477
|
-
"float",
|
|
478
|
-
"for",
|
|
479
|
-
"function",
|
|
480
|
-
"goto",
|
|
481
|
-
"if",
|
|
482
|
-
"implements",
|
|
483
|
-
"import",
|
|
484
|
-
"in",
|
|
485
|
-
"instanceof",
|
|
486
|
-
"int",
|
|
487
|
-
"interface",
|
|
488
|
-
"let",
|
|
489
|
-
"long",
|
|
490
|
-
"native",
|
|
491
|
-
"new",
|
|
492
|
-
"null",
|
|
493
|
-
"package",
|
|
494
|
-
"private",
|
|
495
|
-
"protected",
|
|
496
|
-
"public",
|
|
497
|
-
"return",
|
|
498
|
-
"short",
|
|
499
|
-
"static",
|
|
500
|
-
"super",
|
|
501
|
-
"switch",
|
|
502
|
-
"synchronized",
|
|
503
|
-
"this",
|
|
504
|
-
"throw",
|
|
505
|
-
"throws",
|
|
506
|
-
"transient",
|
|
507
|
-
"true",
|
|
508
|
-
"try",
|
|
509
|
-
"typeof",
|
|
510
|
-
"var",
|
|
511
|
-
"void",
|
|
512
|
-
"volatile",
|
|
513
|
-
"while",
|
|
514
|
-
"with",
|
|
515
|
-
"yield",
|
|
516
|
-
"Array",
|
|
517
|
-
"Date",
|
|
518
|
-
"hasOwnProperty",
|
|
519
|
-
"Infinity",
|
|
520
|
-
"isFinite",
|
|
521
|
-
"isNaN",
|
|
522
|
-
"isPrototypeOf",
|
|
523
|
-
"length",
|
|
524
|
-
"Math",
|
|
525
|
-
"name",
|
|
526
|
-
"NaN",
|
|
527
|
-
"Number",
|
|
528
|
-
"Object",
|
|
529
|
-
"prototype",
|
|
530
|
-
"String",
|
|
531
|
-
"toString",
|
|
532
|
-
"undefined",
|
|
533
|
-
"valueOf"
|
|
534
|
-
];
|
|
535
|
-
/**
|
|
536
|
-
* Prefixes a word with `_` when it is a reserved JavaScript/Java identifier
|
|
537
|
-
* or starts with a digit.
|
|
538
|
-
*/
|
|
539
|
-
function transformReservedWord(word) {
|
|
540
|
-
const firstChar = word.charCodeAt(0);
|
|
541
|
-
if (word && (reservedWords.includes(word) || firstChar >= 48 && firstChar <= 57)) return `_${word}`;
|
|
542
|
-
return word;
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
546
|
-
*/
|
|
547
|
-
function isValidVarName(name) {
|
|
548
|
-
try {
|
|
549
|
-
new Function(`var ${name}`);
|
|
550
|
-
} catch {
|
|
551
|
-
return false;
|
|
552
|
-
}
|
|
553
|
-
return true;
|
|
554
|
-
}
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region ../../internals/utils/src/urlPath.ts
|
|
555
272
|
/**
|
|
556
273
|
* Parses and transforms an OpenAPI/Swagger path string into various URL formats.
|
|
557
274
|
*
|
|
@@ -561,18 +278,33 @@ function isValidVarName(name) {
|
|
|
561
278
|
* p.template // '`/pet/${petId}`'
|
|
562
279
|
*/
|
|
563
280
|
var URLPath = class {
|
|
564
|
-
/**
|
|
281
|
+
/**
|
|
282
|
+
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
283
|
+
*/
|
|
565
284
|
path;
|
|
566
285
|
#options;
|
|
567
286
|
constructor(path, options = {}) {
|
|
568
287
|
this.path = path;
|
|
569
288
|
this.#options = options;
|
|
570
289
|
}
|
|
571
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
290
|
+
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
572
297
|
get URL() {
|
|
573
298
|
return this.toURLPath();
|
|
574
299
|
}
|
|
575
|
-
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
300
|
+
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```ts
|
|
304
|
+
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
305
|
+
* new URLPath('/pet/{petId}').isURL // false
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
576
308
|
get isURL() {
|
|
577
309
|
try {
|
|
578
310
|
return !!new URL(this.path).href;
|
|
@@ -590,11 +322,25 @@ var URLPath = class {
|
|
|
590
322
|
get template() {
|
|
591
323
|
return this.toTemplateString();
|
|
592
324
|
}
|
|
593
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
325
|
+
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```ts
|
|
329
|
+
* new URLPath('/pet/{petId}').object
|
|
330
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
594
333
|
get object() {
|
|
595
334
|
return this.toObject();
|
|
596
335
|
}
|
|
597
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
336
|
+
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```ts
|
|
340
|
+
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
341
|
+
* new URLPath('/pet').params // undefined
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
598
344
|
get params() {
|
|
599
345
|
return this.getParams();
|
|
600
346
|
}
|
|
@@ -602,7 +348,9 @@ var URLPath = class {
|
|
|
602
348
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
603
349
|
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
604
350
|
}
|
|
605
|
-
/**
|
|
351
|
+
/**
|
|
352
|
+
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
353
|
+
*/
|
|
606
354
|
#eachParam(fn) {
|
|
607
355
|
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
608
356
|
const raw = match[1];
|
|
@@ -639,6 +387,12 @@ var URLPath = class {
|
|
|
639
387
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
640
388
|
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
641
389
|
* Returns `undefined` when no path parameters are found.
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```ts
|
|
393
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
394
|
+
* // { petId: 'petId', tagId: 'tagId' }
|
|
395
|
+
* ```
|
|
642
396
|
*/
|
|
643
397
|
getParams(replacer) {
|
|
644
398
|
const params = {};
|
|
@@ -648,837 +402,457 @@ var URLPath = class {
|
|
|
648
402
|
});
|
|
649
403
|
return Object.keys(params).length > 0 ? params : void 0;
|
|
650
404
|
}
|
|
651
|
-
/** Converts the OpenAPI path to Express-style colon syntax
|
|
405
|
+
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* ```ts
|
|
409
|
+
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
652
412
|
toURLPath() {
|
|
653
413
|
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
654
414
|
}
|
|
655
415
|
};
|
|
656
416
|
//#endregion
|
|
657
|
-
//#region src/
|
|
658
|
-
function defineConfig(config) {
|
|
659
|
-
return config;
|
|
660
|
-
}
|
|
417
|
+
//#region src/createAdapter.ts
|
|
661
418
|
/**
|
|
662
|
-
*
|
|
419
|
+
* Creates an adapter factory. Call the returned function with optional options to get the adapter instance.
|
|
420
|
+
*
|
|
421
|
+
* @example
|
|
422
|
+
* export const myAdapter = createAdapter<MyAdapter>((options) => {
|
|
423
|
+
* return {
|
|
424
|
+
* name: 'my-adapter',
|
|
425
|
+
* options,
|
|
426
|
+
* async parse(source) { ... },
|
|
427
|
+
* }
|
|
428
|
+
* })
|
|
429
|
+
*
|
|
430
|
+
* // instantiate
|
|
431
|
+
* const adapter = myAdapter({ validate: true })
|
|
663
432
|
*/
|
|
664
|
-
function
|
|
665
|
-
return
|
|
433
|
+
function createAdapter(build) {
|
|
434
|
+
return (options) => build(options ?? {});
|
|
666
435
|
}
|
|
667
436
|
//#endregion
|
|
668
|
-
//#region src/
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
};
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
"--write",
|
|
709
|
-
outputPath
|
|
710
|
-
],
|
|
711
|
-
errorMessage: "Prettier not found"
|
|
712
|
-
},
|
|
713
|
-
biome: {
|
|
714
|
-
command: "biome",
|
|
715
|
-
args: (outputPath) => [
|
|
716
|
-
"format",
|
|
717
|
-
"--write",
|
|
718
|
-
outputPath
|
|
719
|
-
],
|
|
720
|
-
errorMessage: "Biome not found"
|
|
721
|
-
},
|
|
722
|
-
oxfmt: {
|
|
723
|
-
command: "oxfmt",
|
|
724
|
-
args: (outputPath) => [outputPath],
|
|
725
|
-
errorMessage: "Oxfmt not found"
|
|
437
|
+
//#region src/FileProcessor.ts
|
|
438
|
+
function joinSources(file) {
|
|
439
|
+
return file.sources.map((item) => extractStringsFromNodes(item.nodes)).filter(Boolean).join("\n\n");
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Converts a single file to a string using the registered parsers.
|
|
443
|
+
* Falls back to joining source values when no matching parser is found.
|
|
444
|
+
*/
|
|
445
|
+
var FileProcessor = class {
|
|
446
|
+
#limit = pLimit(100);
|
|
447
|
+
async parse(file, { parsers, extension } = {}) {
|
|
448
|
+
const parseExtName = extension?.[file.extname] || void 0;
|
|
449
|
+
if (!parsers || !file.extname) return joinSources(file);
|
|
450
|
+
const parser = parsers.get(file.extname);
|
|
451
|
+
if (!parser) return joinSources(file);
|
|
452
|
+
return parser.parse(file, { extname: parseExtName });
|
|
453
|
+
}
|
|
454
|
+
async run(files, { parsers, mode = "sequential", extension, onStart, onEnd, onUpdate } = {}) {
|
|
455
|
+
await onStart?.(files);
|
|
456
|
+
const total = files.length;
|
|
457
|
+
let processed = 0;
|
|
458
|
+
const processOne = async (file) => {
|
|
459
|
+
const source = await this.parse(file, {
|
|
460
|
+
extension,
|
|
461
|
+
parsers
|
|
462
|
+
});
|
|
463
|
+
const currentProcessed = ++processed;
|
|
464
|
+
const percentage = currentProcessed / total * 100;
|
|
465
|
+
await onUpdate?.({
|
|
466
|
+
file,
|
|
467
|
+
source,
|
|
468
|
+
processed: currentProcessed,
|
|
469
|
+
percentage,
|
|
470
|
+
total
|
|
471
|
+
});
|
|
472
|
+
};
|
|
473
|
+
if (mode === "sequential") for (const file of files) await processOne(file);
|
|
474
|
+
else await Promise.all(files.map((file) => this.#limit(() => processOne(file))));
|
|
475
|
+
await onEnd?.(files);
|
|
476
|
+
return files;
|
|
726
477
|
}
|
|
727
478
|
};
|
|
728
479
|
//#endregion
|
|
729
|
-
//#region src/
|
|
480
|
+
//#region src/createStorage.ts
|
|
730
481
|
/**
|
|
731
|
-
*
|
|
482
|
+
* Creates a storage factory. Call the returned function with optional options to get the storage instance.
|
|
732
483
|
*
|
|
733
|
-
*
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
484
|
+
* @example
|
|
485
|
+
* export const memoryStorage = createStorage(() => {
|
|
486
|
+
* const store = new Map<string, string>()
|
|
487
|
+
* return {
|
|
488
|
+
* name: 'memory',
|
|
489
|
+
* async hasItem(key) { return store.has(key) },
|
|
490
|
+
* async getItem(key) { return store.get(key) ?? null },
|
|
491
|
+
* async setItem(key, value) { store.set(key, value) },
|
|
492
|
+
* async removeItem(key) { store.delete(key) },
|
|
493
|
+
* async getKeys(base) {
|
|
494
|
+
* const keys = [...store.keys()]
|
|
495
|
+
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
496
|
+
* },
|
|
497
|
+
* async clear(base) { if (!base) store.clear() },
|
|
498
|
+
* }
|
|
499
|
+
* })
|
|
738
500
|
*/
|
|
739
|
-
function
|
|
740
|
-
|
|
741
|
-
return Buffer.from(compressed).toString("base64url");
|
|
501
|
+
function createStorage(build) {
|
|
502
|
+
return (options) => build(options ?? {});
|
|
742
503
|
}
|
|
504
|
+
//#endregion
|
|
505
|
+
//#region src/storages/fsStorage.ts
|
|
743
506
|
/**
|
|
744
|
-
*
|
|
745
|
-
* When `options.ast` is `true`, navigates to the AST inspector (`/ast`).
|
|
746
|
-
* The `root` is encoded and attached as the `?root=` query parameter so Studio
|
|
747
|
-
* can decode and render it without a round-trip to any server.
|
|
507
|
+
* Detects the filesystem error used to indicate that a path does not exist.
|
|
748
508
|
*/
|
|
749
|
-
function
|
|
750
|
-
return
|
|
509
|
+
function isMissingPathError(error) {
|
|
510
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
751
511
|
}
|
|
752
512
|
/**
|
|
753
|
-
*
|
|
513
|
+
* Built-in filesystem storage driver.
|
|
514
|
+
*
|
|
515
|
+
* This is the default storage when no `storage` option is configured in `output`.
|
|
516
|
+
* Keys are resolved against `process.cwd()`, so root-relative paths such as
|
|
517
|
+
* `src/gen/api/getPets.ts` are written to the correct location without extra configuration.
|
|
518
|
+
*
|
|
519
|
+
* Internally uses the `write` utility from `@internals/utils`, which:
|
|
520
|
+
* - trims leading/trailing whitespace before writing
|
|
521
|
+
* - skips the write when file content is already identical (deduplication)
|
|
522
|
+
* - creates missing parent directories automatically
|
|
523
|
+
* - supports Bun's native file API when running under Bun
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* ```ts
|
|
527
|
+
* import { defineConfig, fsStorage } from '@kubb/core'
|
|
754
528
|
*
|
|
755
|
-
*
|
|
529
|
+
* export default defineConfig({
|
|
530
|
+
* input: { path: './petStore.yaml' },
|
|
531
|
+
* output: { path: './src/gen', storage: fsStorage() },
|
|
532
|
+
* })
|
|
533
|
+
* ```
|
|
756
534
|
*/
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
try {
|
|
767
|
-
await x(cmd, args);
|
|
768
|
-
} catch {
|
|
769
|
-
console.log(`\n ${url}\n`);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
//#endregion
|
|
773
|
-
//#region ../../node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
|
|
774
|
-
var Node = class {
|
|
775
|
-
value;
|
|
776
|
-
next;
|
|
777
|
-
constructor(value) {
|
|
778
|
-
this.value = value;
|
|
779
|
-
}
|
|
780
|
-
};
|
|
781
|
-
var Queue = class {
|
|
782
|
-
#head;
|
|
783
|
-
#tail;
|
|
784
|
-
#size;
|
|
785
|
-
constructor() {
|
|
786
|
-
this.clear();
|
|
787
|
-
}
|
|
788
|
-
enqueue(value) {
|
|
789
|
-
const node = new Node(value);
|
|
790
|
-
if (this.#head) {
|
|
791
|
-
this.#tail.next = node;
|
|
792
|
-
this.#tail = node;
|
|
793
|
-
} else {
|
|
794
|
-
this.#head = node;
|
|
795
|
-
this.#tail = node;
|
|
796
|
-
}
|
|
797
|
-
this.#size++;
|
|
798
|
-
}
|
|
799
|
-
dequeue() {
|
|
800
|
-
const current = this.#head;
|
|
801
|
-
if (!current) return;
|
|
802
|
-
this.#head = this.#head.next;
|
|
803
|
-
this.#size--;
|
|
804
|
-
if (!this.#head) this.#tail = void 0;
|
|
805
|
-
return current.value;
|
|
806
|
-
}
|
|
807
|
-
peek() {
|
|
808
|
-
if (!this.#head) return;
|
|
809
|
-
return this.#head.value;
|
|
810
|
-
}
|
|
811
|
-
clear() {
|
|
812
|
-
this.#head = void 0;
|
|
813
|
-
this.#tail = void 0;
|
|
814
|
-
this.#size = 0;
|
|
815
|
-
}
|
|
816
|
-
get size() {
|
|
817
|
-
return this.#size;
|
|
818
|
-
}
|
|
819
|
-
*[Symbol.iterator]() {
|
|
820
|
-
let current = this.#head;
|
|
821
|
-
while (current) {
|
|
822
|
-
yield current.value;
|
|
823
|
-
current = current.next;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
*drain() {
|
|
827
|
-
while (this.#head) yield this.dequeue();
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
|
-
//#endregion
|
|
831
|
-
//#region ../../node_modules/.pnpm/p-limit@7.3.0/node_modules/p-limit/index.js
|
|
832
|
-
function pLimit(concurrency) {
|
|
833
|
-
let rejectOnClear = false;
|
|
834
|
-
if (typeof concurrency === "object") ({concurrency, rejectOnClear = false} = concurrency);
|
|
835
|
-
validateConcurrency(concurrency);
|
|
836
|
-
if (typeof rejectOnClear !== "boolean") throw new TypeError("Expected `rejectOnClear` to be a boolean");
|
|
837
|
-
const queue = new Queue();
|
|
838
|
-
let activeCount = 0;
|
|
839
|
-
const resumeNext = () => {
|
|
840
|
-
if (activeCount < concurrency && queue.size > 0) {
|
|
841
|
-
activeCount++;
|
|
842
|
-
queue.dequeue().run();
|
|
535
|
+
const fsStorage = createStorage(() => ({
|
|
536
|
+
name: "fs",
|
|
537
|
+
async hasItem(key) {
|
|
538
|
+
try {
|
|
539
|
+
await access(resolve(key));
|
|
540
|
+
return true;
|
|
541
|
+
} catch (error) {
|
|
542
|
+
if (isMissingPathError(error)) return false;
|
|
543
|
+
throw new Error(`Failed to access storage item "${key}"`, { cause: error });
|
|
843
544
|
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
activeCount--;
|
|
847
|
-
resumeNext();
|
|
848
|
-
};
|
|
849
|
-
const run = async (function_, resolve, arguments_) => {
|
|
850
|
-
const result = (async () => function_(...arguments_))();
|
|
851
|
-
resolve(result);
|
|
545
|
+
},
|
|
546
|
+
async getItem(key) {
|
|
852
547
|
try {
|
|
853
|
-
await
|
|
854
|
-
} catch {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}
|
|
876
|
-
const
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
const promises = Array.from(iterable, (value, index) => this(function_, value, index));
|
|
891
|
-
return Promise.all(promises);
|
|
892
|
-
} }
|
|
893
|
-
});
|
|
894
|
-
return generator;
|
|
895
|
-
}
|
|
896
|
-
function validateConcurrency(concurrency) {
|
|
897
|
-
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
898
|
-
}
|
|
548
|
+
return await readFile(resolve(key), "utf8");
|
|
549
|
+
} catch (error) {
|
|
550
|
+
if (isMissingPathError(error)) return null;
|
|
551
|
+
throw new Error(`Failed to read storage item "${key}"`, { cause: error });
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
async setItem(key, value) {
|
|
555
|
+
await write(resolve(key), value, { sanity: false });
|
|
556
|
+
},
|
|
557
|
+
async removeItem(key) {
|
|
558
|
+
await rm(resolve(key), { force: true });
|
|
559
|
+
},
|
|
560
|
+
async getKeys(base) {
|
|
561
|
+
const keys = [];
|
|
562
|
+
const resolvedBase = resolve(base ?? process.cwd());
|
|
563
|
+
async function walk(dir, prefix) {
|
|
564
|
+
let entries;
|
|
565
|
+
try {
|
|
566
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
567
|
+
} catch (error) {
|
|
568
|
+
if (isMissingPathError(error)) return;
|
|
569
|
+
throw new Error(`Failed to list storage keys under "${resolvedBase}"`, { cause: error });
|
|
570
|
+
}
|
|
571
|
+
for (const entry of entries) {
|
|
572
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
573
|
+
if (entry.isDirectory()) await walk(join(dir, entry.name), rel);
|
|
574
|
+
else keys.push(rel);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
await walk(resolvedBase, "");
|
|
578
|
+
return keys;
|
|
579
|
+
},
|
|
580
|
+
async clear(base) {
|
|
581
|
+
if (!base) return;
|
|
582
|
+
await clean(resolve(base));
|
|
583
|
+
}
|
|
584
|
+
}));
|
|
899
585
|
//#endregion
|
|
900
|
-
//#region
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
function hookSeq(promises) {
|
|
905
|
-
return promises.filter(Boolean).reduce((promise, func) => {
|
|
906
|
-
if (typeof func !== "function") throw new Error("HookSeq needs a function that returns a promise `() => Promise<unknown>`");
|
|
907
|
-
return promise.then((state) => {
|
|
908
|
-
const calledFunc = func(state);
|
|
909
|
-
if (calledFunc) return calledFunc.then(Array.prototype.concat.bind(state));
|
|
910
|
-
return state;
|
|
911
|
-
});
|
|
912
|
-
}, Promise.resolve([]));
|
|
913
|
-
}
|
|
586
|
+
//#region package.json
|
|
587
|
+
var version$1 = "5.0.0-alpha.40";
|
|
588
|
+
//#endregion
|
|
589
|
+
//#region src/utils/diagnostics.ts
|
|
914
590
|
/**
|
|
915
|
-
*
|
|
591
|
+
* Returns a snapshot of the current runtime environment.
|
|
592
|
+
*
|
|
593
|
+
* Useful for attaching context to debug logs and error reports so that
|
|
594
|
+
* issues can be reproduced without manual information gathering.
|
|
916
595
|
*/
|
|
917
|
-
function
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
596
|
+
function getDiagnosticInfo() {
|
|
597
|
+
return {
|
|
598
|
+
nodeVersion: version,
|
|
599
|
+
KubbVersion: version$1,
|
|
600
|
+
platform: process.platform,
|
|
601
|
+
arch: process.arch,
|
|
602
|
+
cwd: process.cwd()
|
|
603
|
+
};
|
|
924
604
|
}
|
|
605
|
+
//#endregion
|
|
606
|
+
//#region src/utils/TreeNode.ts
|
|
925
607
|
/**
|
|
926
|
-
*
|
|
608
|
+
* Tree structure used to build per-directory barrel (`index.ts`) files from a
|
|
609
|
+
* flat list of generated {@link FileNode} entries.
|
|
610
|
+
*
|
|
611
|
+
* Each node represents either a directory or a file within the output tree.
|
|
612
|
+
* Use {@link TreeNode.build} to construct a root node from a file list, then
|
|
613
|
+
* traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
|
|
614
|
+
* `*Deep` helpers.
|
|
927
615
|
*/
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
#options = {};
|
|
937
|
-
constructor(options = {}) {
|
|
938
|
-
this.#options = options;
|
|
616
|
+
var TreeNode = class TreeNode {
|
|
617
|
+
data;
|
|
618
|
+
parent;
|
|
619
|
+
children = [];
|
|
620
|
+
#cachedLeaves = void 0;
|
|
621
|
+
constructor(data, parent) {
|
|
622
|
+
this.data = data;
|
|
623
|
+
this.parent = parent;
|
|
939
624
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
if (
|
|
943
|
-
|
|
944
|
-
|
|
625
|
+
addChild(data) {
|
|
626
|
+
const child = new TreeNode(data, this);
|
|
627
|
+
if (!this.children) this.children = [];
|
|
628
|
+
this.children.push(child);
|
|
629
|
+
return child;
|
|
945
630
|
}
|
|
946
|
-
};
|
|
947
|
-
function isPromiseRejectedResult(result) {
|
|
948
|
-
return result.status === "rejected";
|
|
949
|
-
}
|
|
950
|
-
//#endregion
|
|
951
|
-
//#region src/PluginManager.ts
|
|
952
|
-
function getMode(fileOrFolder) {
|
|
953
|
-
if (!fileOrFolder) return "split";
|
|
954
|
-
return extname(fileOrFolder) ? "single" : "split";
|
|
955
|
-
}
|
|
956
|
-
var PluginManager = class {
|
|
957
|
-
config;
|
|
958
|
-
options;
|
|
959
631
|
/**
|
|
960
|
-
*
|
|
961
|
-
* the build pipeline after the adapter's `parse()` resolves.
|
|
632
|
+
* Returns the root ancestor of this node, walking up via `parent` links.
|
|
962
633
|
*/
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
#plugins = /* @__PURE__ */ new Set();
|
|
967
|
-
#usedPluginNames = {};
|
|
968
|
-
#promiseManager;
|
|
969
|
-
constructor(config, options) {
|
|
970
|
-
this.config = config;
|
|
971
|
-
this.options = options;
|
|
972
|
-
this.#promiseManager = new PromiseManager({ nullCheck: (state) => !!state?.result });
|
|
973
|
-
[...config.plugins || []].forEach((plugin) => {
|
|
974
|
-
const parsedPlugin = this.#parse(plugin);
|
|
975
|
-
this.#plugins.add(parsedPlugin);
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
get events() {
|
|
979
|
-
return this.options.events;
|
|
980
|
-
}
|
|
981
|
-
getContext(plugin) {
|
|
982
|
-
const plugins = [...this.#plugins];
|
|
983
|
-
const pluginManager = this;
|
|
984
|
-
const baseContext = {
|
|
985
|
-
fabric: this.options.fabric,
|
|
986
|
-
config: this.config,
|
|
987
|
-
plugin,
|
|
988
|
-
events: this.options.events,
|
|
989
|
-
pluginManager: this,
|
|
990
|
-
mode: getMode(resolve(this.config.root, this.config.output.path)),
|
|
991
|
-
addFile: async (...files) => {
|
|
992
|
-
await this.options.fabric.addFile(...files);
|
|
993
|
-
},
|
|
994
|
-
upsertFile: async (...files) => {
|
|
995
|
-
await this.options.fabric.upsertFile(...files);
|
|
996
|
-
},
|
|
997
|
-
get rootNode() {
|
|
998
|
-
return pluginManager.rootNode;
|
|
999
|
-
},
|
|
1000
|
-
get adapter() {
|
|
1001
|
-
return pluginManager.adapter;
|
|
1002
|
-
},
|
|
1003
|
-
openInStudio(options) {
|
|
1004
|
-
if (!pluginManager.config.devtools || pluginManager.#studioIsOpen) return;
|
|
1005
|
-
if (typeof pluginManager.config.devtools !== "object") throw new Error("Devtools must be an object");
|
|
1006
|
-
if (!pluginManager.rootNode || !pluginManager.adapter) throw new Error("adapter is not defined, make sure you have set the parser in kubb.config.ts");
|
|
1007
|
-
pluginManager.#studioIsOpen = true;
|
|
1008
|
-
const studioUrl = pluginManager.config.devtools?.studioUrl ?? "https://studio.kubb.dev";
|
|
1009
|
-
return openInStudio(pluginManager.rootNode, studioUrl, options);
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
1012
|
-
const mergedExtras = {};
|
|
1013
|
-
for (const p of plugins) if (typeof p.inject === "function") {
|
|
1014
|
-
const result = p.inject.call(baseContext, baseContext);
|
|
1015
|
-
if (result !== null && typeof result === "object") Object.assign(mergedExtras, result);
|
|
1016
|
-
}
|
|
1017
|
-
return {
|
|
1018
|
-
...baseContext,
|
|
1019
|
-
...mergedExtras
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
get plugins() {
|
|
1023
|
-
return this.#getSortedPlugins();
|
|
1024
|
-
}
|
|
1025
|
-
getFile({ name, mode, extname, pluginName, options }) {
|
|
1026
|
-
const resolvedName = mode ? mode === "single" ? "" : this.resolveName({
|
|
1027
|
-
name,
|
|
1028
|
-
pluginName,
|
|
1029
|
-
type: "file"
|
|
1030
|
-
}) : name;
|
|
1031
|
-
const path = this.resolvePath({
|
|
1032
|
-
baseName: `${resolvedName}${extname}`,
|
|
1033
|
-
mode,
|
|
1034
|
-
pluginName,
|
|
1035
|
-
options
|
|
1036
|
-
});
|
|
1037
|
-
if (!path) throw new Error(`Filepath should be defined for resolvedName "${resolvedName}" and pluginName "${pluginName}"`);
|
|
1038
|
-
return {
|
|
1039
|
-
path,
|
|
1040
|
-
baseName: basename(path),
|
|
1041
|
-
meta: { pluginName },
|
|
1042
|
-
sources: [],
|
|
1043
|
-
imports: [],
|
|
1044
|
-
exports: []
|
|
1045
|
-
};
|
|
634
|
+
get root() {
|
|
635
|
+
if (!this.parent) return this;
|
|
636
|
+
return this.parent.root;
|
|
1046
637
|
}
|
|
1047
|
-
resolvePath = (params) => {
|
|
1048
|
-
const defaultPath = resolve(resolve(this.config.root, this.config.output.path), params.baseName);
|
|
1049
|
-
if (params.pluginName) return this.hookForPluginSync({
|
|
1050
|
-
pluginName: params.pluginName,
|
|
1051
|
-
hookName: "resolvePath",
|
|
1052
|
-
parameters: [
|
|
1053
|
-
params.baseName,
|
|
1054
|
-
params.mode,
|
|
1055
|
-
params.options
|
|
1056
|
-
]
|
|
1057
|
-
})?.at(0) || defaultPath;
|
|
1058
|
-
return this.hookFirstSync({
|
|
1059
|
-
hookName: "resolvePath",
|
|
1060
|
-
parameters: [
|
|
1061
|
-
params.baseName,
|
|
1062
|
-
params.mode,
|
|
1063
|
-
params.options
|
|
1064
|
-
]
|
|
1065
|
-
})?.result || defaultPath;
|
|
1066
|
-
};
|
|
1067
|
-
resolveName = (params) => {
|
|
1068
|
-
if (params.pluginName) {
|
|
1069
|
-
const names = this.hookForPluginSync({
|
|
1070
|
-
pluginName: params.pluginName,
|
|
1071
|
-
hookName: "resolveName",
|
|
1072
|
-
parameters: [params.name.trim(), params.type]
|
|
1073
|
-
});
|
|
1074
|
-
return transformReservedWord([...new Set(names)].at(0) || params.name);
|
|
1075
|
-
}
|
|
1076
|
-
const name = this.hookFirstSync({
|
|
1077
|
-
hookName: "resolveName",
|
|
1078
|
-
parameters: [params.name.trim(), params.type]
|
|
1079
|
-
})?.result;
|
|
1080
|
-
return transformReservedWord(name ?? params.name);
|
|
1081
|
-
};
|
|
1082
638
|
/**
|
|
1083
|
-
*
|
|
639
|
+
* Returns all leaf descendants (nodes with no children) of this node.
|
|
640
|
+
*
|
|
641
|
+
* Results are cached after the first traversal.
|
|
1084
642
|
*/
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
this
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
for (const plugin of plugins) {
|
|
1093
|
-
const result = await this.#execute({
|
|
1094
|
-
strategy: "hookFirst",
|
|
1095
|
-
hookName,
|
|
1096
|
-
parameters,
|
|
1097
|
-
plugin
|
|
1098
|
-
});
|
|
1099
|
-
if (result !== void 0 && result !== null) items.push(result);
|
|
1100
|
-
}
|
|
1101
|
-
this.events.emit("plugins:hook:progress:end", { hookName });
|
|
1102
|
-
return items;
|
|
643
|
+
get leaves() {
|
|
644
|
+
if (!this.children || this.children.length === 0) return [this];
|
|
645
|
+
if (this.#cachedLeaves) return this.#cachedLeaves;
|
|
646
|
+
const leaves = [];
|
|
647
|
+
for (const child of this.children) leaves.push(...child.leaves);
|
|
648
|
+
this.#cachedLeaves = leaves;
|
|
649
|
+
return leaves;
|
|
1103
650
|
}
|
|
1104
651
|
/**
|
|
1105
|
-
*
|
|
652
|
+
* Visits this node and every descendant in depth-first order.
|
|
1106
653
|
*/
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
parameters,
|
|
1113
|
-
plugin
|
|
1114
|
-
});
|
|
1115
|
-
}).filter((x) => x !== null);
|
|
654
|
+
forEach(callback) {
|
|
655
|
+
if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
|
|
656
|
+
callback(this);
|
|
657
|
+
for (const child of this.children) child.forEach(callback);
|
|
658
|
+
return this;
|
|
1116
659
|
}
|
|
1117
660
|
/**
|
|
1118
|
-
*
|
|
661
|
+
* Finds the first leaf that satisfies `predicate`, or `undefined` when none match.
|
|
1119
662
|
*/
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
});
|
|
1124
|
-
this.events.emit("plugins:hook:progress:start", {
|
|
1125
|
-
hookName,
|
|
1126
|
-
plugins
|
|
1127
|
-
});
|
|
1128
|
-
const promises = plugins.map((plugin) => {
|
|
1129
|
-
return async () => {
|
|
1130
|
-
const value = await this.#execute({
|
|
1131
|
-
strategy: "hookFirst",
|
|
1132
|
-
hookName,
|
|
1133
|
-
parameters,
|
|
1134
|
-
plugin
|
|
1135
|
-
});
|
|
1136
|
-
return Promise.resolve({
|
|
1137
|
-
plugin,
|
|
1138
|
-
result: value
|
|
1139
|
-
});
|
|
1140
|
-
};
|
|
1141
|
-
});
|
|
1142
|
-
const result = await this.#promiseManager.run("first", promises);
|
|
1143
|
-
this.events.emit("plugins:hook:progress:end", { hookName });
|
|
1144
|
-
return result;
|
|
663
|
+
findDeep(predicate) {
|
|
664
|
+
if (typeof predicate !== "function") throw new TypeError("find() predicate must be a function");
|
|
665
|
+
return this.leaves.find(predicate);
|
|
1145
666
|
}
|
|
1146
667
|
/**
|
|
1147
|
-
*
|
|
668
|
+
* Calls `callback` for every leaf of this node.
|
|
1148
669
|
*/
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
return skipped ? !skipped.has(plugin) : true;
|
|
1153
|
-
});
|
|
1154
|
-
for (const plugin of plugins) {
|
|
1155
|
-
parseResult = {
|
|
1156
|
-
result: this.#executeSync({
|
|
1157
|
-
strategy: "hookFirst",
|
|
1158
|
-
hookName,
|
|
1159
|
-
parameters,
|
|
1160
|
-
plugin
|
|
1161
|
-
}),
|
|
1162
|
-
plugin
|
|
1163
|
-
};
|
|
1164
|
-
if (parseResult?.result != null) break;
|
|
1165
|
-
}
|
|
1166
|
-
return parseResult;
|
|
670
|
+
forEachDeep(callback) {
|
|
671
|
+
if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
|
|
672
|
+
this.leaves.forEach(callback);
|
|
1167
673
|
}
|
|
1168
674
|
/**
|
|
1169
|
-
*
|
|
675
|
+
* Returns all leaves that satisfy `callback`.
|
|
1170
676
|
*/
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
this.
|
|
1174
|
-
hookName,
|
|
1175
|
-
plugins
|
|
1176
|
-
});
|
|
1177
|
-
const pluginStartTimes = /* @__PURE__ */ new Map();
|
|
1178
|
-
const promises = plugins.map((plugin) => {
|
|
1179
|
-
return () => {
|
|
1180
|
-
pluginStartTimes.set(plugin, performance.now());
|
|
1181
|
-
return this.#execute({
|
|
1182
|
-
strategy: "hookParallel",
|
|
1183
|
-
hookName,
|
|
1184
|
-
parameters,
|
|
1185
|
-
plugin
|
|
1186
|
-
});
|
|
1187
|
-
};
|
|
1188
|
-
});
|
|
1189
|
-
const results = await this.#promiseManager.run("parallel", promises, { concurrency: this.options.concurrency });
|
|
1190
|
-
results.forEach((result, index) => {
|
|
1191
|
-
if (isPromiseRejectedResult(result)) {
|
|
1192
|
-
const plugin = this.#getSortedPlugins(hookName)[index];
|
|
1193
|
-
if (plugin) {
|
|
1194
|
-
const startTime = pluginStartTimes.get(plugin) ?? performance.now();
|
|
1195
|
-
this.events.emit("error", result.reason, {
|
|
1196
|
-
plugin,
|
|
1197
|
-
hookName,
|
|
1198
|
-
strategy: "hookParallel",
|
|
1199
|
-
duration: Math.round(performance.now() - startTime),
|
|
1200
|
-
parameters
|
|
1201
|
-
});
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
});
|
|
1205
|
-
this.events.emit("plugins:hook:progress:end", { hookName });
|
|
1206
|
-
return results.reduce((acc, result) => {
|
|
1207
|
-
if (result.status === "fulfilled") acc.push(result.value);
|
|
1208
|
-
return acc;
|
|
1209
|
-
}, []);
|
|
677
|
+
filterDeep(callback) {
|
|
678
|
+
if (typeof callback !== "function") throw new TypeError("filter() callback must be a function");
|
|
679
|
+
return this.leaves.filter(callback);
|
|
1210
680
|
}
|
|
1211
681
|
/**
|
|
1212
|
-
*
|
|
682
|
+
* Maps every leaf through `callback` and returns the resulting array.
|
|
1213
683
|
*/
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
this.
|
|
1217
|
-
hookName,
|
|
1218
|
-
plugins
|
|
1219
|
-
});
|
|
1220
|
-
const promises = plugins.map((plugin) => {
|
|
1221
|
-
return () => this.#execute({
|
|
1222
|
-
strategy: "hookSeq",
|
|
1223
|
-
hookName,
|
|
1224
|
-
parameters,
|
|
1225
|
-
plugin
|
|
1226
|
-
});
|
|
1227
|
-
});
|
|
1228
|
-
await this.#promiseManager.run("seq", promises);
|
|
1229
|
-
this.events.emit("plugins:hook:progress:end", { hookName });
|
|
1230
|
-
}
|
|
1231
|
-
#getSortedPlugins(hookName) {
|
|
1232
|
-
const plugins = [...this.#plugins];
|
|
1233
|
-
if (hookName) return plugins.filter((plugin) => hookName in plugin);
|
|
1234
|
-
return plugins.map((plugin) => {
|
|
1235
|
-
if (plugin.pre) {
|
|
1236
|
-
let missingPlugins = plugin.pre.filter((pluginName) => !plugins.find((pluginToFind) => pluginToFind.name === pluginName));
|
|
1237
|
-
if (missingPlugins.includes("plugin-oas") && this.adapter) missingPlugins = missingPlugins.filter((pluginName) => pluginName !== "plugin-oas");
|
|
1238
|
-
if (missingPlugins.length > 0) throw new ValidationPluginError(`The plugin '${plugin.name}' has a pre set that references missing plugins for '${missingPlugins.join(", ")}'`);
|
|
1239
|
-
}
|
|
1240
|
-
return plugin;
|
|
1241
|
-
}).sort((a, b) => {
|
|
1242
|
-
if (b.pre?.includes(a.name)) return 1;
|
|
1243
|
-
if (b.post?.includes(a.name)) return -1;
|
|
1244
|
-
return 0;
|
|
1245
|
-
});
|
|
1246
|
-
}
|
|
1247
|
-
getPluginByName(pluginName) {
|
|
1248
|
-
return [...this.#plugins].find((item) => item.name === pluginName);
|
|
1249
|
-
}
|
|
1250
|
-
getPluginsByName(hookName, pluginName) {
|
|
1251
|
-
const plugins = [...this.plugins];
|
|
1252
|
-
const pluginByPluginName = plugins.filter((plugin) => hookName in plugin).filter((item) => item.name === pluginName);
|
|
1253
|
-
if (!pluginByPluginName?.length) {
|
|
1254
|
-
const corePlugin = plugins.find((plugin) => plugin.name === "core" && hookName in plugin);
|
|
1255
|
-
return corePlugin ? [corePlugin] : [];
|
|
1256
|
-
}
|
|
1257
|
-
return pluginByPluginName;
|
|
684
|
+
mapDeep(callback) {
|
|
685
|
+
if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
|
|
686
|
+
return this.leaves.map(callback);
|
|
1258
687
|
}
|
|
1259
688
|
/**
|
|
1260
|
-
*
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1263
|
-
*
|
|
689
|
+
* Builds a {@link TreeNode} tree from a flat list of files.
|
|
690
|
+
*
|
|
691
|
+
* - Filters to files under `root` (when provided) and skips `.json` files.
|
|
692
|
+
* - Returns `null` when no files match.
|
|
1264
693
|
*/
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
parameters,
|
|
1282
|
-
plugin
|
|
1283
|
-
});
|
|
1284
|
-
const startTime = performance.now();
|
|
1285
|
-
return (async () => {
|
|
1286
|
-
try {
|
|
1287
|
-
const output = typeof hook === "function" ? await Promise.resolve(hook.apply(this.getContext(plugin), parameters ?? [])) : hook;
|
|
1288
|
-
this.#emitProcessingEnd({
|
|
1289
|
-
startTime,
|
|
1290
|
-
output,
|
|
1291
|
-
strategy,
|
|
1292
|
-
hookName,
|
|
1293
|
-
plugin,
|
|
1294
|
-
parameters
|
|
694
|
+
static build(files, root) {
|
|
695
|
+
try {
|
|
696
|
+
const filteredTree = buildDirectoryTree(files, root);
|
|
697
|
+
if (!filteredTree) return null;
|
|
698
|
+
const treeNode = new TreeNode({
|
|
699
|
+
name: filteredTree.name,
|
|
700
|
+
path: filteredTree.path,
|
|
701
|
+
file: filteredTree.file,
|
|
702
|
+
type: PluginDriver.getMode(filteredTree.path)
|
|
703
|
+
});
|
|
704
|
+
const recurse = (node, item) => {
|
|
705
|
+
const subNode = node.addChild({
|
|
706
|
+
name: item.name,
|
|
707
|
+
path: item.path,
|
|
708
|
+
file: item.file,
|
|
709
|
+
type: PluginDriver.getMode(item.path)
|
|
1295
710
|
});
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
this.events.emit("error", error, {
|
|
1299
|
-
plugin,
|
|
1300
|
-
hookName,
|
|
1301
|
-
strategy,
|
|
1302
|
-
duration: Math.round(performance.now() - startTime)
|
|
711
|
+
if (item.children?.length) item.children?.forEach((child) => {
|
|
712
|
+
recurse(subNode, child);
|
|
1303
713
|
});
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
}
|
|
1308
|
-
/**
|
|
1309
|
-
* Run a sync plugin hook and return the result.
|
|
1310
|
-
* @param hookName Name of the plugin hook. Must be in `PluginHooks`.
|
|
1311
|
-
* @param args Arguments passed to the plugin hook.
|
|
1312
|
-
* @param plugin The actual plugin
|
|
1313
|
-
*/
|
|
1314
|
-
#executeSync({ strategy, hookName, parameters, plugin }) {
|
|
1315
|
-
const hook = plugin[hookName];
|
|
1316
|
-
if (!hook) return null;
|
|
1317
|
-
this.events.emit("plugins:hook:processing:start", {
|
|
1318
|
-
strategy,
|
|
1319
|
-
hookName,
|
|
1320
|
-
parameters,
|
|
1321
|
-
plugin
|
|
1322
|
-
});
|
|
1323
|
-
const startTime = performance.now();
|
|
1324
|
-
try {
|
|
1325
|
-
const output = typeof hook === "function" ? hook.apply(this.getContext(plugin), parameters) : hook;
|
|
1326
|
-
this.#emitProcessingEnd({
|
|
1327
|
-
startTime,
|
|
1328
|
-
output,
|
|
1329
|
-
strategy,
|
|
1330
|
-
hookName,
|
|
1331
|
-
plugin,
|
|
1332
|
-
parameters
|
|
714
|
+
};
|
|
715
|
+
filteredTree.children?.forEach((child) => {
|
|
716
|
+
recurse(treeNode, child);
|
|
1333
717
|
});
|
|
1334
|
-
return
|
|
718
|
+
return treeNode;
|
|
1335
719
|
} catch (error) {
|
|
1336
|
-
|
|
1337
|
-
plugin,
|
|
1338
|
-
hookName,
|
|
1339
|
-
strategy,
|
|
1340
|
-
duration: Math.round(performance.now() - startTime)
|
|
1341
|
-
});
|
|
1342
|
-
return null;
|
|
720
|
+
throw new Error("Something went wrong with creating barrel files with the TreeNode class", { cause: error });
|
|
1343
721
|
}
|
|
1344
722
|
}
|
|
1345
|
-
#parse(plugin) {
|
|
1346
|
-
const usedPluginNames = this.#usedPluginNames;
|
|
1347
|
-
setUniqueName(plugin.name, usedPluginNames);
|
|
1348
|
-
const usageCount = usedPluginNames[plugin.name];
|
|
1349
|
-
if (usageCount && usageCount > 1) throw new ValidationPluginError(`Duplicate plugin "${plugin.name}" detected. Each plugin can only be used once. Use a different configuration instead of adding multiple instances of the same plugin.`);
|
|
1350
|
-
return {
|
|
1351
|
-
install() {},
|
|
1352
|
-
...plugin
|
|
1353
|
-
};
|
|
1354
|
-
}
|
|
1355
723
|
};
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
724
|
+
const normalizePath = (p) => p.replaceAll("\\", "/");
|
|
725
|
+
function buildDirectoryTree(files, rootFolder = "") {
|
|
726
|
+
const normalizedRootFolder = normalizePath(rootFolder);
|
|
727
|
+
const rootPrefix = normalizedRootFolder.endsWith("/") ? normalizedRootFolder : `${normalizedRootFolder}/`;
|
|
728
|
+
const filteredFiles = files.filter((file) => {
|
|
729
|
+
const normalizedFilePath = normalizePath(file.path);
|
|
730
|
+
return rootFolder ? normalizedFilePath.startsWith(rootPrefix) && !normalizedFilePath.endsWith(".json") : !normalizedFilePath.endsWith(".json");
|
|
731
|
+
});
|
|
732
|
+
if (filteredFiles.length === 0) return null;
|
|
733
|
+
const root = {
|
|
734
|
+
name: rootFolder || "",
|
|
735
|
+
path: rootFolder || "",
|
|
736
|
+
children: []
|
|
737
|
+
};
|
|
738
|
+
filteredFiles.forEach((file) => {
|
|
739
|
+
const parts = file.path.slice(rootFolder.length).split("/").filter(Boolean);
|
|
740
|
+
let currentLevel = root.children;
|
|
741
|
+
let currentPath = normalizePath(rootFolder);
|
|
742
|
+
parts.forEach((part, index) => {
|
|
743
|
+
currentPath = path.posix.join(currentPath, part);
|
|
744
|
+
let existingNode = currentLevel.find((node) => node.name === part);
|
|
745
|
+
if (!existingNode) {
|
|
746
|
+
if (index === parts.length - 1) existingNode = {
|
|
747
|
+
name: part,
|
|
748
|
+
file,
|
|
749
|
+
path: currentPath
|
|
750
|
+
};
|
|
751
|
+
else existingNode = {
|
|
752
|
+
name: part,
|
|
753
|
+
path: currentPath,
|
|
754
|
+
children: []
|
|
755
|
+
};
|
|
756
|
+
currentLevel.push(existingNode);
|
|
757
|
+
}
|
|
758
|
+
if (!existingNode.file) currentLevel = existingNode.children;
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
return root;
|
|
1385
762
|
}
|
|
1386
763
|
//#endregion
|
|
1387
|
-
//#region src/
|
|
764
|
+
//#region src/utils/getBarrelFiles.ts
|
|
765
|
+
/** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */
|
|
766
|
+
function getBarrelFilesByRoot(root, files) {
|
|
767
|
+
const cachedFiles = /* @__PURE__ */ new Map();
|
|
768
|
+
TreeNode.build(files, root)?.forEach((treeNode) => {
|
|
769
|
+
if (!treeNode?.children || !treeNode.parent?.data.path) return;
|
|
770
|
+
const barrelFile = createFile({
|
|
771
|
+
path: join(treeNode.parent?.data.path, "index.ts"),
|
|
772
|
+
baseName: "index.ts",
|
|
773
|
+
exports: [],
|
|
774
|
+
imports: [],
|
|
775
|
+
sources: []
|
|
776
|
+
});
|
|
777
|
+
const previousBarrelFile = cachedFiles.get(barrelFile.path);
|
|
778
|
+
treeNode.leaves.forEach((item) => {
|
|
779
|
+
if (!item.data.name) return;
|
|
780
|
+
(item.data.file?.sources || []).forEach((source) => {
|
|
781
|
+
if (!item.data.file?.path || !source.isIndexable || !source.name) return;
|
|
782
|
+
if (previousBarrelFile?.sources.some((item) => item.name === source.name && item.isTypeOnly === source.isTypeOnly)) return;
|
|
783
|
+
barrelFile.exports.push(createExport({
|
|
784
|
+
name: [source.name],
|
|
785
|
+
path: getRelativePath(treeNode.parent?.data.path, item.data.path),
|
|
786
|
+
isTypeOnly: source.isTypeOnly
|
|
787
|
+
}));
|
|
788
|
+
barrelFile.sources.push(createSource({
|
|
789
|
+
name: source.name,
|
|
790
|
+
isTypeOnly: source.isTypeOnly,
|
|
791
|
+
isExportable: false,
|
|
792
|
+
isIndexable: false
|
|
793
|
+
}));
|
|
794
|
+
});
|
|
795
|
+
});
|
|
796
|
+
if (previousBarrelFile) {
|
|
797
|
+
previousBarrelFile.sources.push(...barrelFile.sources);
|
|
798
|
+
previousBarrelFile.exports.push(...barrelFile.exports);
|
|
799
|
+
} else cachedFiles.set(barrelFile.path, barrelFile);
|
|
800
|
+
});
|
|
801
|
+
return [...cachedFiles.values()];
|
|
802
|
+
}
|
|
803
|
+
function trimExtName(text) {
|
|
804
|
+
const dotIndex = text.lastIndexOf(".");
|
|
805
|
+
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
806
|
+
return text;
|
|
807
|
+
}
|
|
1388
808
|
/**
|
|
1389
|
-
*
|
|
809
|
+
* Generates `index.ts` barrel files for all directories under `root/output.path`.
|
|
1390
810
|
*
|
|
1391
|
-
*
|
|
1392
|
-
*
|
|
1393
|
-
* `
|
|
1394
|
-
*
|
|
1395
|
-
* Internally uses the `write` utility from `@internals/utils`, which:
|
|
1396
|
-
* - trims leading/trailing whitespace before writing
|
|
1397
|
-
* - skips the write when file content is already identical (deduplication)
|
|
1398
|
-
* - creates missing parent directories automatically
|
|
1399
|
-
* - supports Bun's native file API when running under Bun
|
|
1400
|
-
*
|
|
1401
|
-
* @example
|
|
1402
|
-
* ```ts
|
|
1403
|
-
* import { defineConfig, fsStorage } from '@kubb/core'
|
|
1404
|
-
*
|
|
1405
|
-
* export default defineConfig({
|
|
1406
|
-
* input: { path: './petStore.yaml' },
|
|
1407
|
-
* output: { path: './src/gen', storage: fsStorage() },
|
|
1408
|
-
* })
|
|
1409
|
-
* ```
|
|
811
|
+
* - Returns an empty array when `type` is falsy or `'propagate'`.
|
|
812
|
+
* - Skips generation when the output path itself ends with `index` (already a barrel).
|
|
813
|
+
* - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
|
|
814
|
+
* - Attaches `meta` to each barrel file for downstream plugin identification.
|
|
1410
815
|
*/
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
async getKeys(base) {
|
|
1435
|
-
const keys = [];
|
|
1436
|
-
async function walk(dir, prefix) {
|
|
1437
|
-
let entries;
|
|
1438
|
-
try {
|
|
1439
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
1440
|
-
} catch {
|
|
1441
|
-
return;
|
|
1442
|
-
}
|
|
1443
|
-
for (const entry of entries) {
|
|
1444
|
-
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
1445
|
-
if (entry.isDirectory()) await walk(join(dir, entry.name), rel);
|
|
1446
|
-
else keys.push(rel);
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
await walk(resolve(base ?? process.cwd()), "");
|
|
1450
|
-
return keys;
|
|
1451
|
-
},
|
|
1452
|
-
async clear(base) {
|
|
1453
|
-
if (!base) return;
|
|
1454
|
-
await clean(resolve(base));
|
|
1455
|
-
}
|
|
1456
|
-
}));
|
|
1457
|
-
//#endregion
|
|
1458
|
-
//#region package.json
|
|
1459
|
-
var version$1 = "5.0.0-alpha.4";
|
|
816
|
+
async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
817
|
+
if (!type || type === "propagate") return [];
|
|
818
|
+
const pathToBuildFrom = join(root, output.path);
|
|
819
|
+
if (trimExtName(pathToBuildFrom).endsWith("index")) return [];
|
|
820
|
+
const barrelFiles = getBarrelFilesByRoot(pathToBuildFrom, files);
|
|
821
|
+
if (type === "all") return barrelFiles.map((file) => {
|
|
822
|
+
return {
|
|
823
|
+
...file,
|
|
824
|
+
exports: file.exports.map((exportItem) => {
|
|
825
|
+
return {
|
|
826
|
+
...exportItem,
|
|
827
|
+
name: void 0
|
|
828
|
+
};
|
|
829
|
+
})
|
|
830
|
+
};
|
|
831
|
+
});
|
|
832
|
+
return barrelFiles.map((indexFile) => {
|
|
833
|
+
return {
|
|
834
|
+
...indexFile,
|
|
835
|
+
meta
|
|
836
|
+
};
|
|
837
|
+
});
|
|
838
|
+
}
|
|
1460
839
|
//#endregion
|
|
1461
|
-
//#region src/utils/
|
|
840
|
+
//#region src/utils/isInputPath.ts
|
|
1462
841
|
/**
|
|
1463
|
-
*
|
|
842
|
+
* Type guard to check if a given config has an `input.path`.
|
|
1464
843
|
*/
|
|
1465
|
-
function
|
|
1466
|
-
return
|
|
1467
|
-
nodeVersion: version,
|
|
1468
|
-
KubbVersion: version$1,
|
|
1469
|
-
platform: process.platform,
|
|
1470
|
-
arch: process.arch,
|
|
1471
|
-
cwd: process.cwd()
|
|
1472
|
-
};
|
|
844
|
+
function isInputPath(config) {
|
|
845
|
+
return typeof config?.input === "object" && config.input !== null && "path" in config.input;
|
|
1473
846
|
}
|
|
1474
847
|
//#endregion
|
|
1475
|
-
//#region src/
|
|
848
|
+
//#region src/createKubb.ts
|
|
1476
849
|
async function setup(options) {
|
|
1477
|
-
const { config: userConfig
|
|
850
|
+
const { config: userConfig } = options;
|
|
851
|
+
const hooks = options.hooks ?? new AsyncEventEmitter();
|
|
1478
852
|
const sources = /* @__PURE__ */ new Map();
|
|
1479
853
|
const diagnosticInfo = getDiagnosticInfo();
|
|
1480
|
-
if (Array.isArray(userConfig.input)) await
|
|
1481
|
-
await
|
|
854
|
+
if (Array.isArray(userConfig.input)) await hooks.emit("kubb:warn", "This feature is still under development — use with caution");
|
|
855
|
+
await hooks.emit("kubb:debug", {
|
|
1482
856
|
date: /* @__PURE__ */ new Date(),
|
|
1483
857
|
logs: [
|
|
1484
858
|
"Configuration:",
|
|
@@ -1497,7 +871,7 @@ async function setup(options) {
|
|
|
1497
871
|
try {
|
|
1498
872
|
if (isInputPath(userConfig) && !new URLPath(userConfig.input.path).isURL) {
|
|
1499
873
|
await exists(userConfig.input.path);
|
|
1500
|
-
await
|
|
874
|
+
await hooks.emit("kubb:debug", {
|
|
1501
875
|
date: /* @__PURE__ */ new Date(),
|
|
1502
876
|
logs: [`✓ Input file validated: ${userConfig.input.path}`]
|
|
1503
877
|
});
|
|
@@ -1508,9 +882,12 @@ async function setup(options) {
|
|
|
1508
882
|
throw new Error(`Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${userConfig.input.path}`, { cause: error });
|
|
1509
883
|
}
|
|
1510
884
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
885
|
+
if (!userConfig.adapter) throw new Error("Adapter should be defined");
|
|
886
|
+
const config = {
|
|
1513
887
|
...userConfig,
|
|
888
|
+
root: userConfig.root || process.cwd(),
|
|
889
|
+
parsers: userConfig.parsers ?? [],
|
|
890
|
+
adapter: userConfig.adapter,
|
|
1514
891
|
output: {
|
|
1515
892
|
write: true,
|
|
1516
893
|
barrelType: "named",
|
|
@@ -1524,139 +901,174 @@ async function setup(options) {
|
|
|
1524
901
|
} : void 0,
|
|
1525
902
|
plugins: userConfig.plugins
|
|
1526
903
|
};
|
|
1527
|
-
const storage =
|
|
1528
|
-
if (
|
|
1529
|
-
await
|
|
904
|
+
const storage = config.output.write === false ? null : config.output.storage ?? fsStorage();
|
|
905
|
+
if (config.output.clean) {
|
|
906
|
+
await hooks.emit("kubb:debug", {
|
|
1530
907
|
date: /* @__PURE__ */ new Date(),
|
|
1531
|
-
logs: ["Cleaning output directories", ` • Output: ${
|
|
908
|
+
logs: ["Cleaning output directories", ` • Output: ${config.output.path}`]
|
|
1532
909
|
});
|
|
1533
|
-
await storage?.clear(resolve(
|
|
910
|
+
await storage?.clear(resolve(config.root, config.output.path));
|
|
1534
911
|
}
|
|
1535
|
-
const
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
fabric.context.on("files:processing:start", (files) => {
|
|
1539
|
-
events.emit("files:processing:start", files);
|
|
1540
|
-
events.emit("debug", {
|
|
1541
|
-
date: /* @__PURE__ */ new Date(),
|
|
1542
|
-
logs: [`Writing ${files.length} files...`]
|
|
1543
|
-
});
|
|
1544
|
-
});
|
|
1545
|
-
fabric.context.on("file:processing:update", async (params) => {
|
|
1546
|
-
const { file, source } = params;
|
|
1547
|
-
await events.emit("file:processing:update", {
|
|
1548
|
-
...params,
|
|
1549
|
-
config: definedConfig,
|
|
1550
|
-
source
|
|
1551
|
-
});
|
|
1552
|
-
if (source) {
|
|
1553
|
-
const key = relative(resolve(definedConfig.root), file.path);
|
|
1554
|
-
await storage?.setItem(key, source);
|
|
1555
|
-
sources.set(file.path, source);
|
|
1556
|
-
}
|
|
912
|
+
const driver = new PluginDriver(config, {
|
|
913
|
+
hooks,
|
|
914
|
+
concurrency: 15
|
|
1557
915
|
});
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
}
|
|
916
|
+
const adapter = config.adapter;
|
|
917
|
+
if (!adapter) throw new Error("No adapter configured. Please provide an adapter in your kubb.config.ts.");
|
|
918
|
+
const source = inputToAdapterSource(config);
|
|
919
|
+
await hooks.emit("kubb:debug", {
|
|
920
|
+
date: /* @__PURE__ */ new Date(),
|
|
921
|
+
logs: [`Running adapter: ${adapter.name}`]
|
|
1564
922
|
});
|
|
1565
|
-
|
|
923
|
+
driver.adapter = adapter;
|
|
924
|
+
driver.inputNode = await adapter.parse(source);
|
|
925
|
+
await hooks.emit("kubb:debug", {
|
|
1566
926
|
date: /* @__PURE__ */ new Date(),
|
|
1567
927
|
logs: [
|
|
1568
|
-
|
|
1569
|
-
` •
|
|
1570
|
-
` •
|
|
928
|
+
`✓ Adapter '${adapter.name}' resolved InputNode`,
|
|
929
|
+
` • Schemas: ${driver.inputNode.schemas.length}`,
|
|
930
|
+
` • Operations: ${driver.inputNode.operations.length}`
|
|
1571
931
|
]
|
|
1572
932
|
});
|
|
1573
|
-
const pluginManager = new PluginManager(definedConfig, {
|
|
1574
|
-
fabric,
|
|
1575
|
-
events,
|
|
1576
|
-
concurrency: 15
|
|
1577
|
-
});
|
|
1578
|
-
if (definedConfig.adapter) {
|
|
1579
|
-
const source = inputToAdapterSource(definedConfig);
|
|
1580
|
-
await events.emit("debug", {
|
|
1581
|
-
date: /* @__PURE__ */ new Date(),
|
|
1582
|
-
logs: [`Running adapter: ${definedConfig.adapter.name}`]
|
|
1583
|
-
});
|
|
1584
|
-
pluginManager.adapter = definedConfig.adapter;
|
|
1585
|
-
pluginManager.rootNode = await definedConfig.adapter.parse(source);
|
|
1586
|
-
await events.emit("debug", {
|
|
1587
|
-
date: /* @__PURE__ */ new Date(),
|
|
1588
|
-
logs: [
|
|
1589
|
-
`✓ Adapter '${definedConfig.adapter.name}' resolved RootNode`,
|
|
1590
|
-
` • Schemas: ${pluginManager.rootNode.schemas.length}`,
|
|
1591
|
-
` • Operations: ${pluginManager.rootNode.operations.length}`
|
|
1592
|
-
]
|
|
1593
|
-
});
|
|
1594
|
-
}
|
|
1595
933
|
return {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
sources
|
|
934
|
+
config,
|
|
935
|
+
hooks,
|
|
936
|
+
driver,
|
|
937
|
+
sources,
|
|
938
|
+
storage
|
|
1600
939
|
};
|
|
1601
940
|
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
941
|
+
/**
|
|
942
|
+
* Walks the AST and dispatches nodes to a plugin's direct AST hooks
|
|
943
|
+
* (`schema`, `operation`, `operations`).
|
|
944
|
+
*/
|
|
945
|
+
async function runPluginAstHooks(plugin, context) {
|
|
946
|
+
const { adapter, inputNode, resolver, driver } = context;
|
|
947
|
+
const { exclude, include, override } = plugin.options;
|
|
948
|
+
if (!adapter || !inputNode) throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. pluginOas()) before this plugin in your Kubb config.`);
|
|
949
|
+
function resolveRenderer(gen) {
|
|
950
|
+
return gen.renderer === null ? void 0 : gen.renderer ?? plugin.renderer ?? context.config.renderer;
|
|
951
|
+
}
|
|
952
|
+
const generators = plugin.generators ?? [];
|
|
953
|
+
const collectedOperations = [];
|
|
954
|
+
const generatorContext = {
|
|
955
|
+
...context,
|
|
956
|
+
resolver: driver.getResolver(plugin.name)
|
|
1617
957
|
};
|
|
958
|
+
await walk(inputNode, {
|
|
959
|
+
depth: "shallow",
|
|
960
|
+
async schema(node) {
|
|
961
|
+
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node;
|
|
962
|
+
const options = resolver.resolveOptions(transformedNode, {
|
|
963
|
+
options: plugin.options,
|
|
964
|
+
exclude,
|
|
965
|
+
include,
|
|
966
|
+
override
|
|
967
|
+
});
|
|
968
|
+
if (options === null) return;
|
|
969
|
+
const ctx = {
|
|
970
|
+
...generatorContext,
|
|
971
|
+
options
|
|
972
|
+
};
|
|
973
|
+
for (const gen of generators) {
|
|
974
|
+
if (!gen.schema) continue;
|
|
975
|
+
await applyHookResult(await gen.schema(transformedNode, ctx), driver, resolveRenderer(gen));
|
|
976
|
+
}
|
|
977
|
+
await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
|
|
978
|
+
},
|
|
979
|
+
async operation(node) {
|
|
980
|
+
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node;
|
|
981
|
+
const options = resolver.resolveOptions(transformedNode, {
|
|
982
|
+
options: plugin.options,
|
|
983
|
+
exclude,
|
|
984
|
+
include,
|
|
985
|
+
override
|
|
986
|
+
});
|
|
987
|
+
if (options !== null) {
|
|
988
|
+
collectedOperations.push(transformedNode);
|
|
989
|
+
const ctx = {
|
|
990
|
+
...generatorContext,
|
|
991
|
+
options
|
|
992
|
+
};
|
|
993
|
+
for (const gen of generators) {
|
|
994
|
+
if (!gen.operation) continue;
|
|
995
|
+
await applyHookResult(await gen.operation(transformedNode, ctx), driver, resolveRenderer(gen));
|
|
996
|
+
}
|
|
997
|
+
await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
if (collectedOperations.length > 0) {
|
|
1002
|
+
const ctx = {
|
|
1003
|
+
...generatorContext,
|
|
1004
|
+
options: plugin.options
|
|
1005
|
+
};
|
|
1006
|
+
for (const gen of generators) {
|
|
1007
|
+
if (!gen.operations) continue;
|
|
1008
|
+
await applyHookResult(await gen.operations(collectedOperations, ctx), driver, resolveRenderer(gen));
|
|
1009
|
+
}
|
|
1010
|
+
await driver.hooks.emit("kubb:generate:operations", collectedOperations, ctx);
|
|
1011
|
+
}
|
|
1618
1012
|
}
|
|
1619
|
-
async function safeBuild(
|
|
1620
|
-
const {
|
|
1013
|
+
async function safeBuild(setupResult) {
|
|
1014
|
+
const { driver, hooks, sources, storage } = setupResult;
|
|
1621
1015
|
const failedPlugins = /* @__PURE__ */ new Set();
|
|
1622
1016
|
const pluginTimings = /* @__PURE__ */ new Map();
|
|
1623
|
-
const config =
|
|
1017
|
+
const config = driver.config;
|
|
1624
1018
|
try {
|
|
1625
|
-
|
|
1626
|
-
|
|
1019
|
+
await driver.emitSetupHooks();
|
|
1020
|
+
if (driver.adapter && driver.inputNode) await hooks.emit("kubb:build:start", {
|
|
1021
|
+
config,
|
|
1022
|
+
adapter: driver.adapter,
|
|
1023
|
+
inputNode: driver.inputNode,
|
|
1024
|
+
getPlugin: (name) => driver.getPlugin(name)
|
|
1025
|
+
});
|
|
1026
|
+
for (const plugin of driver.plugins.values()) {
|
|
1027
|
+
const context = driver.getContext(plugin);
|
|
1627
1028
|
const hrStart = process.hrtime();
|
|
1628
|
-
const
|
|
1029
|
+
const { output } = plugin.options ?? {};
|
|
1030
|
+
const root = resolve(config.root, config.output.path);
|
|
1629
1031
|
try {
|
|
1630
1032
|
const timestamp = /* @__PURE__ */ new Date();
|
|
1631
|
-
await
|
|
1632
|
-
await
|
|
1033
|
+
await hooks.emit("kubb:plugin:start", plugin);
|
|
1034
|
+
await hooks.emit("kubb:debug", {
|
|
1633
1035
|
date: timestamp,
|
|
1634
|
-
logs: ["
|
|
1036
|
+
logs: ["Starting plugin...", ` • Plugin Name: ${plugin.name}`]
|
|
1635
1037
|
});
|
|
1636
|
-
await
|
|
1038
|
+
await plugin.buildStart.call(context);
|
|
1039
|
+
if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) await runPluginAstHooks(plugin, context);
|
|
1040
|
+
if (output) {
|
|
1041
|
+
const barrelFiles = await getBarrelFiles(driver.fileManager.files, {
|
|
1042
|
+
type: output.barrelType ?? "named",
|
|
1043
|
+
root,
|
|
1044
|
+
output,
|
|
1045
|
+
meta: { pluginName: plugin.name }
|
|
1046
|
+
});
|
|
1047
|
+
await context.upsertFile(...barrelFiles);
|
|
1048
|
+
}
|
|
1637
1049
|
const duration = getElapsedMs(hrStart);
|
|
1638
1050
|
pluginTimings.set(plugin.name, duration);
|
|
1639
|
-
await
|
|
1051
|
+
await hooks.emit("kubb:plugin:end", plugin, {
|
|
1640
1052
|
duration,
|
|
1641
1053
|
success: true
|
|
1642
1054
|
});
|
|
1643
|
-
await
|
|
1055
|
+
await hooks.emit("kubb:debug", {
|
|
1644
1056
|
date: /* @__PURE__ */ new Date(),
|
|
1645
|
-
logs: [`✓ Plugin
|
|
1057
|
+
logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
|
|
1646
1058
|
});
|
|
1647
1059
|
} catch (caughtError) {
|
|
1648
1060
|
const error = caughtError;
|
|
1649
1061
|
const errorTimestamp = /* @__PURE__ */ new Date();
|
|
1650
1062
|
const duration = getElapsedMs(hrStart);
|
|
1651
|
-
await
|
|
1063
|
+
await hooks.emit("kubb:plugin:end", plugin, {
|
|
1652
1064
|
duration,
|
|
1653
1065
|
success: false,
|
|
1654
1066
|
error
|
|
1655
1067
|
});
|
|
1656
|
-
await
|
|
1068
|
+
await hooks.emit("kubb:debug", {
|
|
1657
1069
|
date: errorTimestamp,
|
|
1658
1070
|
logs: [
|
|
1659
|
-
"✗ Plugin
|
|
1071
|
+
"✗ Plugin start failed",
|
|
1660
1072
|
` • Plugin Name: ${plugin.name}`,
|
|
1661
1073
|
` • Error: ${error.constructor.name} - ${error.message}`,
|
|
1662
1074
|
" • Stack Trace:",
|
|
@@ -1672,7 +1084,7 @@ async function safeBuild(options, overrides) {
|
|
|
1672
1084
|
if (config.output.barrelType) {
|
|
1673
1085
|
const rootPath = resolve(resolve(config.root), config.output.path, BARREL_FILENAME);
|
|
1674
1086
|
const rootDir = dirname(rootPath);
|
|
1675
|
-
await
|
|
1087
|
+
await hooks.emit("kubb:debug", {
|
|
1676
1088
|
date: /* @__PURE__ */ new Date(),
|
|
1677
1089
|
logs: [
|
|
1678
1090
|
"Generating barrel file",
|
|
@@ -1680,15 +1092,15 @@ async function safeBuild(options, overrides) {
|
|
|
1680
1092
|
` • Path: ${rootPath}`
|
|
1681
1093
|
]
|
|
1682
1094
|
});
|
|
1683
|
-
const barrelFiles =
|
|
1095
|
+
const barrelFiles = driver.fileManager.files.filter((file) => {
|
|
1684
1096
|
return file.sources.some((source) => source.isIndexable);
|
|
1685
1097
|
});
|
|
1686
|
-
await
|
|
1098
|
+
await hooks.emit("kubb:debug", {
|
|
1687
1099
|
date: /* @__PURE__ */ new Date(),
|
|
1688
1100
|
logs: [`Found ${barrelFiles.length} indexable files for barrel export`]
|
|
1689
1101
|
});
|
|
1690
|
-
const existingBarrel =
|
|
1691
|
-
const rootFile = {
|
|
1102
|
+
const existingBarrel = driver.fileManager.files.find((f) => f.path === rootPath);
|
|
1103
|
+
const rootFile = createFile({
|
|
1692
1104
|
path: rootPath,
|
|
1693
1105
|
baseName: BARREL_FILENAME,
|
|
1694
1106
|
exports: buildBarrelExports({
|
|
@@ -1696,43 +1108,102 @@ async function safeBuild(options, overrides) {
|
|
|
1696
1108
|
rootDir,
|
|
1697
1109
|
existingExports: new Set(existingBarrel?.exports?.flatMap((e) => Array.isArray(e.name) ? e.name : [e.name]).filter((n) => Boolean(n)) ?? []),
|
|
1698
1110
|
config,
|
|
1699
|
-
|
|
1700
|
-
}),
|
|
1111
|
+
driver
|
|
1112
|
+
}).map((e) => createExport(e)),
|
|
1701
1113
|
sources: [],
|
|
1702
1114
|
imports: [],
|
|
1703
1115
|
meta: {}
|
|
1704
|
-
};
|
|
1705
|
-
|
|
1706
|
-
await
|
|
1116
|
+
});
|
|
1117
|
+
driver.fileManager.upsert(rootFile);
|
|
1118
|
+
await hooks.emit("kubb:debug", {
|
|
1707
1119
|
date: /* @__PURE__ */ new Date(),
|
|
1708
1120
|
logs: [`✓ Generated barrel file (${rootFile.exports?.length || 0} exports)`]
|
|
1709
1121
|
});
|
|
1710
1122
|
}
|
|
1711
|
-
const files =
|
|
1712
|
-
|
|
1123
|
+
const files = driver.fileManager.files;
|
|
1124
|
+
const parsersMap = /* @__PURE__ */ new Map();
|
|
1125
|
+
for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
|
|
1126
|
+
const fileProcessor = new FileProcessor();
|
|
1127
|
+
await hooks.emit("kubb:debug", {
|
|
1128
|
+
date: /* @__PURE__ */ new Date(),
|
|
1129
|
+
logs: [`Writing ${files.length} files...`]
|
|
1130
|
+
});
|
|
1131
|
+
await fileProcessor.run(files, {
|
|
1132
|
+
parsers: parsersMap,
|
|
1133
|
+
extension: config.output.extension,
|
|
1134
|
+
onStart: async (processingFiles) => {
|
|
1135
|
+
await hooks.emit("kubb:files:processing:start", processingFiles);
|
|
1136
|
+
},
|
|
1137
|
+
onUpdate: async ({ file, source, processed, total, percentage }) => {
|
|
1138
|
+
await hooks.emit("kubb:file:processing:update", {
|
|
1139
|
+
file,
|
|
1140
|
+
source,
|
|
1141
|
+
processed,
|
|
1142
|
+
total,
|
|
1143
|
+
percentage,
|
|
1144
|
+
config
|
|
1145
|
+
});
|
|
1146
|
+
if (source) {
|
|
1147
|
+
await storage?.setItem(file.path, source);
|
|
1148
|
+
sources.set(file.path, source);
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
onEnd: async (processedFiles) => {
|
|
1152
|
+
await hooks.emit("kubb:files:processing:end", processedFiles);
|
|
1153
|
+
await hooks.emit("kubb:debug", {
|
|
1154
|
+
date: /* @__PURE__ */ new Date(),
|
|
1155
|
+
logs: [`✓ File write process completed for ${processedFiles.length} files`]
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
for (const plugin of driver.plugins.values()) if (plugin.buildEnd) {
|
|
1160
|
+
const context = driver.getContext(plugin);
|
|
1161
|
+
await plugin.buildEnd.call(context);
|
|
1162
|
+
}
|
|
1163
|
+
await hooks.emit("kubb:build:end", {
|
|
1164
|
+
files,
|
|
1165
|
+
config,
|
|
1166
|
+
outputDir: resolve(config.root, config.output.path)
|
|
1167
|
+
});
|
|
1713
1168
|
return {
|
|
1714
1169
|
failedPlugins,
|
|
1715
|
-
fabric,
|
|
1716
1170
|
files,
|
|
1717
|
-
|
|
1171
|
+
driver,
|
|
1718
1172
|
pluginTimings,
|
|
1719
1173
|
sources
|
|
1720
1174
|
};
|
|
1721
1175
|
} catch (error) {
|
|
1722
1176
|
return {
|
|
1723
1177
|
failedPlugins,
|
|
1724
|
-
fabric,
|
|
1725
1178
|
files: [],
|
|
1726
|
-
|
|
1179
|
+
driver,
|
|
1727
1180
|
pluginTimings,
|
|
1728
1181
|
error,
|
|
1729
1182
|
sources
|
|
1730
1183
|
};
|
|
1184
|
+
} finally {
|
|
1185
|
+
driver.dispose();
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
async function build(setupResult) {
|
|
1189
|
+
const { files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(setupResult);
|
|
1190
|
+
if (error) throw error;
|
|
1191
|
+
if (failedPlugins.size > 0) {
|
|
1192
|
+
const errors = [...failedPlugins].map(({ error }) => error);
|
|
1193
|
+
throw new BuildError(`Build Error with ${failedPlugins.size} failed plugins`, { errors });
|
|
1731
1194
|
}
|
|
1195
|
+
return {
|
|
1196
|
+
failedPlugins,
|
|
1197
|
+
files,
|
|
1198
|
+
driver,
|
|
1199
|
+
pluginTimings,
|
|
1200
|
+
error: void 0,
|
|
1201
|
+
sources
|
|
1202
|
+
};
|
|
1732
1203
|
}
|
|
1733
|
-
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config,
|
|
1204
|
+
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }) {
|
|
1734
1205
|
const pluginNameMap = /* @__PURE__ */ new Map();
|
|
1735
|
-
for (const plugin of
|
|
1206
|
+
for (const plugin of driver.plugins.values()) pluginNameMap.set(plugin.name, plugin);
|
|
1736
1207
|
return barrelFiles.flatMap((file) => {
|
|
1737
1208
|
const containsOnlyTypes = file.sources?.every((source) => source.isTypeOnly);
|
|
1738
1209
|
return (file.sources ?? []).flatMap((source) => {
|
|
@@ -1742,179 +1213,169 @@ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, plu
|
|
|
1742
1213
|
if (!pluginOptions || pluginOptions.output?.barrelType === false) return [];
|
|
1743
1214
|
const exportName = config.output.barrelType === "all" ? void 0 : source.name ? [source.name] : void 0;
|
|
1744
1215
|
if (exportName?.some((n) => existingExports.has(n))) return [];
|
|
1745
|
-
return [{
|
|
1216
|
+
return [createExport({
|
|
1746
1217
|
name: exportName,
|
|
1747
1218
|
path: getRelativePath(rootDir, file.path),
|
|
1748
1219
|
isTypeOnly: config.output.barrelType === "all" ? containsOnlyTypes : source.isTypeOnly
|
|
1749
|
-
}];
|
|
1220
|
+
})];
|
|
1750
1221
|
});
|
|
1751
1222
|
});
|
|
1752
1223
|
}
|
|
1753
|
-
/**
|
|
1754
|
-
* Maps the resolved `Config['input']` shape into an `AdapterSource` that
|
|
1755
|
-
* the adapter's `parse()` can consume.
|
|
1756
|
-
*/
|
|
1757
1224
|
function inputToAdapterSource(config) {
|
|
1758
1225
|
if (Array.isArray(config.input)) return {
|
|
1759
1226
|
type: "paths",
|
|
1760
|
-
paths: config.input.map((i) => resolve(config.root, i.path))
|
|
1227
|
+
paths: config.input.map((i) => new URLPath(i.path).isURL ? i.path : resolve(config.root, i.path))
|
|
1761
1228
|
};
|
|
1762
1229
|
if ("data" in config.input) return {
|
|
1763
1230
|
type: "data",
|
|
1764
1231
|
data: config.input.data
|
|
1765
1232
|
};
|
|
1233
|
+
if (new URLPath(config.input.path).isURL) return {
|
|
1234
|
+
type: "path",
|
|
1235
|
+
path: config.input.path
|
|
1236
|
+
};
|
|
1766
1237
|
return {
|
|
1767
1238
|
type: "path",
|
|
1768
1239
|
path: resolve(config.root, config.input.path)
|
|
1769
1240
|
};
|
|
1770
1241
|
}
|
|
1771
|
-
//#endregion
|
|
1772
|
-
//#region src/defineAdapter.ts
|
|
1773
1242
|
/**
|
|
1774
|
-
*
|
|
1243
|
+
* Creates a Kubb instance bound to a single config entry.
|
|
1244
|
+
*
|
|
1245
|
+
* The instance holds shared state (`hooks`, `sources`, `driver`, `config`) across the
|
|
1246
|
+
* `setup → build` lifecycle. Attach event listeners to `kubb.hooks` before
|
|
1247
|
+
* calling `setup()` or `build()`.
|
|
1775
1248
|
*
|
|
1776
1249
|
* @example
|
|
1777
1250
|
* ```ts
|
|
1778
|
-
*
|
|
1779
|
-
*
|
|
1780
|
-
*
|
|
1781
|
-
*
|
|
1782
|
-
* options: { validate, dateType, ... },
|
|
1783
|
-
* parse(source) { ... },
|
|
1784
|
-
* }
|
|
1251
|
+
* const kubb = createKubb({ config })
|
|
1252
|
+
*
|
|
1253
|
+
* kubb.hooks.on('kubb:plugin:end', (plugin, { duration }) => {
|
|
1254
|
+
* console.log(`${plugin.name} completed in ${duration}ms`)
|
|
1785
1255
|
* })
|
|
1256
|
+
*
|
|
1257
|
+
* const { files, failedPlugins } = await kubb.safeBuild()
|
|
1786
1258
|
* ```
|
|
1787
1259
|
*/
|
|
1788
|
-
function
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
if (generator.type === "react") return {
|
|
1795
|
-
version: "2",
|
|
1796
|
-
Operations() {
|
|
1797
|
-
return null;
|
|
1260
|
+
function createKubb(options) {
|
|
1261
|
+
const hooks = options.hooks ?? new AsyncEventEmitter();
|
|
1262
|
+
let setupResult;
|
|
1263
|
+
const instance = {
|
|
1264
|
+
get hooks() {
|
|
1265
|
+
return hooks;
|
|
1798
1266
|
},
|
|
1799
|
-
|
|
1800
|
-
return
|
|
1267
|
+
get sources() {
|
|
1268
|
+
return setupResult?.sources ?? /* @__PURE__ */ new Map();
|
|
1801
1269
|
},
|
|
1802
|
-
|
|
1803
|
-
return
|
|
1270
|
+
get driver() {
|
|
1271
|
+
return setupResult?.driver;
|
|
1804
1272
|
},
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
return {
|
|
1808
|
-
version: "2",
|
|
1809
|
-
async operations() {
|
|
1810
|
-
return [];
|
|
1273
|
+
get config() {
|
|
1274
|
+
return setupResult?.config;
|
|
1811
1275
|
},
|
|
1812
|
-
async
|
|
1813
|
-
|
|
1276
|
+
async setup() {
|
|
1277
|
+
setupResult = await setup({
|
|
1278
|
+
config: options.config,
|
|
1279
|
+
hooks
|
|
1280
|
+
});
|
|
1814
1281
|
},
|
|
1815
|
-
async
|
|
1816
|
-
|
|
1282
|
+
async build() {
|
|
1283
|
+
if (!setupResult) await instance.setup();
|
|
1284
|
+
return build(setupResult);
|
|
1817
1285
|
},
|
|
1818
|
-
|
|
1286
|
+
async safeBuild() {
|
|
1287
|
+
if (!setupResult) await instance.setup();
|
|
1288
|
+
return safeBuild(setupResult);
|
|
1289
|
+
}
|
|
1819
1290
|
};
|
|
1291
|
+
return instance;
|
|
1292
|
+
}
|
|
1293
|
+
//#endregion
|
|
1294
|
+
//#region src/createRenderer.ts
|
|
1295
|
+
/**
|
|
1296
|
+
* Creates a renderer factory for use in generator definitions.
|
|
1297
|
+
*
|
|
1298
|
+
* Wrap your renderer factory function with this helper to register it as the
|
|
1299
|
+
* renderer for a generator. Core will call this factory once per render cycle
|
|
1300
|
+
* to obtain a fresh renderer instance.
|
|
1301
|
+
*
|
|
1302
|
+
* @example
|
|
1303
|
+
* ```ts
|
|
1304
|
+
* // packages/renderer-jsx/src/index.ts
|
|
1305
|
+
* export const jsxRenderer = createRenderer(() => {
|
|
1306
|
+
* const runtime = new Runtime()
|
|
1307
|
+
* return {
|
|
1308
|
+
* async render(element) { await runtime.render(element) },
|
|
1309
|
+
* get files() { return runtime.nodes },
|
|
1310
|
+
* unmount(error) { runtime.unmount(error) },
|
|
1311
|
+
* }
|
|
1312
|
+
* })
|
|
1313
|
+
*
|
|
1314
|
+
* // packages/plugin-zod/src/generators/zodGenerator.tsx
|
|
1315
|
+
* import { jsxRenderer } from '@kubb/renderer-jsx'
|
|
1316
|
+
* export const zodGenerator = defineGenerator<PluginZod>({
|
|
1317
|
+
* name: 'zod',
|
|
1318
|
+
* renderer: jsxRenderer,
|
|
1319
|
+
* schema(node, options) { return <File ...>...</File> },
|
|
1320
|
+
* })
|
|
1321
|
+
* ```
|
|
1322
|
+
*/
|
|
1323
|
+
function createRenderer(factory) {
|
|
1324
|
+
return factory;
|
|
1325
|
+
}
|
|
1326
|
+
//#endregion
|
|
1327
|
+
//#region src/defineGenerator.ts
|
|
1328
|
+
/**
|
|
1329
|
+
* Defines a generator. Returns the object as-is with correct `this` typings.
|
|
1330
|
+
* `applyHookResult` handles renderer elements and `File[]` uniformly using
|
|
1331
|
+
* the generator's declared `renderer` factory.
|
|
1332
|
+
*/
|
|
1333
|
+
function defineGenerator(generator) {
|
|
1334
|
+
return generator;
|
|
1820
1335
|
}
|
|
1821
1336
|
//#endregion
|
|
1822
1337
|
//#region src/defineLogger.ts
|
|
1338
|
+
/**
|
|
1339
|
+
* Wraps a logger definition into a typed {@link Logger}.
|
|
1340
|
+
*
|
|
1341
|
+
* @example
|
|
1342
|
+
* export const myLogger = defineLogger({
|
|
1343
|
+
* name: 'my-logger',
|
|
1344
|
+
* install(context, options) {
|
|
1345
|
+
* context.on('kubb:info', (message) => console.log('ℹ', message))
|
|
1346
|
+
* context.on('kubb:error', (error) => console.error('✗', error.message))
|
|
1347
|
+
* },
|
|
1348
|
+
* })
|
|
1349
|
+
*/
|
|
1823
1350
|
function defineLogger(logger) {
|
|
1824
|
-
return
|
|
1351
|
+
return logger;
|
|
1825
1352
|
}
|
|
1826
1353
|
//#endregion
|
|
1827
|
-
//#region src/
|
|
1354
|
+
//#region src/defineParser.ts
|
|
1828
1355
|
/**
|
|
1829
|
-
*
|
|
1356
|
+
* Defines a parser with type safety.
|
|
1357
|
+
*
|
|
1358
|
+
* Use this function to create parsers that transform generated files to strings
|
|
1359
|
+
* based on their extension.
|
|
1360
|
+
*
|
|
1361
|
+
* @example
|
|
1362
|
+
* ```ts
|
|
1363
|
+
* import { defineParser } from '@kubb/core'
|
|
1364
|
+
*
|
|
1365
|
+
* export const jsonParser = defineParser({
|
|
1366
|
+
* name: 'json',
|
|
1367
|
+
* extNames: ['.json'],
|
|
1368
|
+
* parse(file) {
|
|
1369
|
+
* const { extractStringsFromNodes } = await import('@kubb/ast')
|
|
1370
|
+
* return file.sources.map((s) => extractStringsFromNodes(s.nodes ?? [])).join('\n')
|
|
1371
|
+
* },
|
|
1372
|
+
* })
|
|
1373
|
+
* ```
|
|
1830
1374
|
*/
|
|
1831
|
-
function
|
|
1832
|
-
return
|
|
1375
|
+
function defineParser(parser) {
|
|
1376
|
+
return parser;
|
|
1833
1377
|
}
|
|
1834
1378
|
//#endregion
|
|
1835
|
-
//#region src/PackageManager.ts
|
|
1836
|
-
var PackageManager = class PackageManager {
|
|
1837
|
-
static #cache = {};
|
|
1838
|
-
#cwd;
|
|
1839
|
-
constructor(workspace) {
|
|
1840
|
-
if (workspace) this.#cwd = workspace;
|
|
1841
|
-
}
|
|
1842
|
-
set workspace(workspace) {
|
|
1843
|
-
this.#cwd = workspace;
|
|
1844
|
-
}
|
|
1845
|
-
get workspace() {
|
|
1846
|
-
return this.#cwd;
|
|
1847
|
-
}
|
|
1848
|
-
normalizeDirectory(directory) {
|
|
1849
|
-
const lastChar = directory[directory.length - 1];
|
|
1850
|
-
if (lastChar && !PATH_SEPARATORS.includes(lastChar)) return `${directory}/`;
|
|
1851
|
-
return directory;
|
|
1852
|
-
}
|
|
1853
|
-
getLocation(path) {
|
|
1854
|
-
let location = path;
|
|
1855
|
-
if (this.#cwd) location = mod.createRequire(this.normalizeDirectory(this.#cwd)).resolve(path);
|
|
1856
|
-
return location;
|
|
1857
|
-
}
|
|
1858
|
-
async import(path) {
|
|
1859
|
-
let location = this.getLocation(path);
|
|
1860
|
-
if (os.platform() === "win32") location = pathToFileURL(location).href;
|
|
1861
|
-
const module = await import(location);
|
|
1862
|
-
return module?.default ?? module;
|
|
1863
|
-
}
|
|
1864
|
-
async getPackageJSON() {
|
|
1865
|
-
const pkgPath = pkg.up({ cwd: this.#cwd });
|
|
1866
|
-
if (!pkgPath) return;
|
|
1867
|
-
const json = await read(pkgPath);
|
|
1868
|
-
return JSON.parse(json);
|
|
1869
|
-
}
|
|
1870
|
-
getPackageJSONSync() {
|
|
1871
|
-
const pkgPath = pkg.up({ cwd: this.#cwd });
|
|
1872
|
-
if (!pkgPath) return;
|
|
1873
|
-
const json = readSync(pkgPath);
|
|
1874
|
-
return JSON.parse(json);
|
|
1875
|
-
}
|
|
1876
|
-
static setVersion(dependency, version) {
|
|
1877
|
-
PackageManager.#cache[dependency] = version;
|
|
1878
|
-
}
|
|
1879
|
-
#match(packageJSON, dependency) {
|
|
1880
|
-
const dependencies = {
|
|
1881
|
-
...packageJSON.dependencies || {},
|
|
1882
|
-
...packageJSON.devDependencies || {}
|
|
1883
|
-
};
|
|
1884
|
-
if (typeof dependency === "string" && dependencies[dependency]) return dependencies[dependency];
|
|
1885
|
-
const matchedDependency = Object.keys(dependencies).find((dep) => dep.match(dependency));
|
|
1886
|
-
return matchedDependency ? dependencies[matchedDependency] : void 0;
|
|
1887
|
-
}
|
|
1888
|
-
async getVersion(dependency) {
|
|
1889
|
-
if (typeof dependency === "string" && PackageManager.#cache[dependency]) return PackageManager.#cache[dependency];
|
|
1890
|
-
const packageJSON = await this.getPackageJSON();
|
|
1891
|
-
if (!packageJSON) return;
|
|
1892
|
-
return this.#match(packageJSON, dependency);
|
|
1893
|
-
}
|
|
1894
|
-
getVersionSync(dependency) {
|
|
1895
|
-
if (typeof dependency === "string" && PackageManager.#cache[dependency]) return PackageManager.#cache[dependency];
|
|
1896
|
-
const packageJSON = this.getPackageJSONSync();
|
|
1897
|
-
if (!packageJSON) return;
|
|
1898
|
-
return this.#match(packageJSON, dependency);
|
|
1899
|
-
}
|
|
1900
|
-
async isValid(dependency, version) {
|
|
1901
|
-
const packageVersion = await this.getVersion(dependency);
|
|
1902
|
-
if (!packageVersion) return false;
|
|
1903
|
-
if (packageVersion === version) return true;
|
|
1904
|
-
const semVer = coerce(packageVersion);
|
|
1905
|
-
if (!semVer) return false;
|
|
1906
|
-
return satisfies(semVer, version);
|
|
1907
|
-
}
|
|
1908
|
-
isValidSync(dependency, version) {
|
|
1909
|
-
const packageVersion = this.getVersionSync(dependency);
|
|
1910
|
-
if (!packageVersion) return false;
|
|
1911
|
-
if (packageVersion === version) return true;
|
|
1912
|
-
const semVer = coerce(packageVersion);
|
|
1913
|
-
if (!semVer) return false;
|
|
1914
|
-
return satisfies(semVer, version);
|
|
1915
|
-
}
|
|
1916
|
-
};
|
|
1917
|
-
//#endregion
|
|
1918
1379
|
//#region src/storages/memoryStorage.ts
|
|
1919
1380
|
/**
|
|
1920
1381
|
* In-memory storage driver. Useful for testing and dry-run scenarios where
|
|
@@ -1933,7 +1394,7 @@ var PackageManager = class PackageManager {
|
|
|
1933
1394
|
* })
|
|
1934
1395
|
* ```
|
|
1935
1396
|
*/
|
|
1936
|
-
const memoryStorage =
|
|
1397
|
+
const memoryStorage = createStorage(() => {
|
|
1937
1398
|
const store = /* @__PURE__ */ new Map();
|
|
1938
1399
|
return {
|
|
1939
1400
|
name: "memory",
|
|
@@ -1963,430 +1424,6 @@ const memoryStorage = defineStorage(() => {
|
|
|
1963
1424
|
};
|
|
1964
1425
|
});
|
|
1965
1426
|
//#endregion
|
|
1966
|
-
|
|
1967
|
-
/**
|
|
1968
|
-
* @deprecated
|
|
1969
|
-
*/
|
|
1970
|
-
var FunctionParams = class FunctionParams {
|
|
1971
|
-
#items = [];
|
|
1972
|
-
get items() {
|
|
1973
|
-
return this.#items.flat();
|
|
1974
|
-
}
|
|
1975
|
-
add(item) {
|
|
1976
|
-
if (!item) return this;
|
|
1977
|
-
if (Array.isArray(item)) {
|
|
1978
|
-
item.filter((x) => x !== void 0).forEach((it) => {
|
|
1979
|
-
this.#items.push(it);
|
|
1980
|
-
});
|
|
1981
|
-
return this;
|
|
1982
|
-
}
|
|
1983
|
-
this.#items.push(item);
|
|
1984
|
-
return this;
|
|
1985
|
-
}
|
|
1986
|
-
static #orderItems(items) {
|
|
1987
|
-
return sortBy(items.filter(Boolean), [(item) => Array.isArray(item), "desc"], [(item) => !Array.isArray(item) && item.default !== void 0, "asc"], [(item) => Array.isArray(item) || (item.required ?? true), "desc"]);
|
|
1988
|
-
}
|
|
1989
|
-
static #addParams(acc, item) {
|
|
1990
|
-
const { enabled = true, name, type, required = true, ...rest } = item;
|
|
1991
|
-
if (!enabled) return acc;
|
|
1992
|
-
if (!name) {
|
|
1993
|
-
acc.push(`${type}${rest.default ? ` = ${rest.default}` : ""}`);
|
|
1994
|
-
return acc;
|
|
1995
|
-
}
|
|
1996
|
-
const parameterName = name.startsWith("{") ? name : camelCase(name);
|
|
1997
|
-
if (type) if (required) acc.push(`${parameterName}: ${type}${rest.default ? ` = ${rest.default}` : ""}`);
|
|
1998
|
-
else acc.push(`${parameterName}?: ${type}`);
|
|
1999
|
-
else acc.push(`${parameterName}`);
|
|
2000
|
-
return acc;
|
|
2001
|
-
}
|
|
2002
|
-
static toObject(items) {
|
|
2003
|
-
let type = [];
|
|
2004
|
-
let name = [];
|
|
2005
|
-
const enabled = items.every((item) => item.enabled) ? items.at(0)?.enabled : true;
|
|
2006
|
-
const required = items.every((item) => item.required) ?? true;
|
|
2007
|
-
items.forEach((item) => {
|
|
2008
|
-
name = FunctionParams.#addParams(name, {
|
|
2009
|
-
...item,
|
|
2010
|
-
type: void 0
|
|
2011
|
-
});
|
|
2012
|
-
if (items.some((item) => item.type)) type = FunctionParams.#addParams(type, item);
|
|
2013
|
-
});
|
|
2014
|
-
return {
|
|
2015
|
-
name: `{ ${name.join(", ")} }`,
|
|
2016
|
-
type: type.length ? `{ ${type.join("; ")} }` : void 0,
|
|
2017
|
-
enabled,
|
|
2018
|
-
required
|
|
2019
|
-
};
|
|
2020
|
-
}
|
|
2021
|
-
toObject() {
|
|
2022
|
-
const items = FunctionParams.#orderItems(this.#items).flat();
|
|
2023
|
-
return FunctionParams.toObject(items);
|
|
2024
|
-
}
|
|
2025
|
-
static toString(items) {
|
|
2026
|
-
return FunctionParams.#orderItems(items).reduce((acc, item) => {
|
|
2027
|
-
if (Array.isArray(item)) {
|
|
2028
|
-
if (item.length <= 0) return acc;
|
|
2029
|
-
const subItems = FunctionParams.#orderItems(item);
|
|
2030
|
-
const objectItem = FunctionParams.toObject(subItems);
|
|
2031
|
-
return FunctionParams.#addParams(acc, objectItem);
|
|
2032
|
-
}
|
|
2033
|
-
return FunctionParams.#addParams(acc, item);
|
|
2034
|
-
}, []).join(", ");
|
|
2035
|
-
}
|
|
2036
|
-
toString() {
|
|
2037
|
-
const items = FunctionParams.#orderItems(this.#items);
|
|
2038
|
-
return FunctionParams.toString(items);
|
|
2039
|
-
}
|
|
2040
|
-
};
|
|
2041
|
-
//#endregion
|
|
2042
|
-
//#region src/utils/formatters.ts
|
|
2043
|
-
/**
|
|
2044
|
-
* Check if a formatter command is available in the system.
|
|
2045
|
-
*
|
|
2046
|
-
* @param formatter - The formatter to check ('biome', 'prettier', or 'oxfmt')
|
|
2047
|
-
* @returns Promise that resolves to true if the formatter is available, false otherwise
|
|
2048
|
-
*
|
|
2049
|
-
* @remarks
|
|
2050
|
-
* This function checks availability by running `<formatter> --version` command.
|
|
2051
|
-
* All supported formatters (biome, prettier, oxfmt) implement the --version flag.
|
|
2052
|
-
*/
|
|
2053
|
-
async function isFormatterAvailable(formatter) {
|
|
2054
|
-
try {
|
|
2055
|
-
await x(formatter, ["--version"], { nodeOptions: { stdio: "ignore" } });
|
|
2056
|
-
return true;
|
|
2057
|
-
} catch {
|
|
2058
|
-
return false;
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
|
-
/**
|
|
2062
|
-
* Detect which formatter is available in the system.
|
|
2063
|
-
*
|
|
2064
|
-
* @returns Promise that resolves to the first available formatter or undefined if none are found
|
|
2065
|
-
*
|
|
2066
|
-
* @remarks
|
|
2067
|
-
* Checks in order of preference: biome, oxfmt, prettier.
|
|
2068
|
-
* Uses the `--version` flag to detect if each formatter command is available.
|
|
2069
|
-
* This is a reliable method as all supported formatters implement this flag.
|
|
2070
|
-
*
|
|
2071
|
-
* @example
|
|
2072
|
-
* ```typescript
|
|
2073
|
-
* const formatter = await detectFormatter()
|
|
2074
|
-
* if (formatter) {
|
|
2075
|
-
* console.log(`Using ${formatter} for formatting`)
|
|
2076
|
-
* } else {
|
|
2077
|
-
* console.log('No formatter found')
|
|
2078
|
-
* }
|
|
2079
|
-
* ```
|
|
2080
|
-
*/
|
|
2081
|
-
async function detectFormatter() {
|
|
2082
|
-
for (const formatter of [
|
|
2083
|
-
"biome",
|
|
2084
|
-
"oxfmt",
|
|
2085
|
-
"prettier"
|
|
2086
|
-
]) if (await isFormatterAvailable(formatter)) return formatter;
|
|
2087
|
-
}
|
|
2088
|
-
//#endregion
|
|
2089
|
-
//#region src/utils/TreeNode.ts
|
|
2090
|
-
var TreeNode = class TreeNode {
|
|
2091
|
-
data;
|
|
2092
|
-
parent;
|
|
2093
|
-
children = [];
|
|
2094
|
-
#cachedLeaves = void 0;
|
|
2095
|
-
constructor(data, parent) {
|
|
2096
|
-
this.data = data;
|
|
2097
|
-
this.parent = parent;
|
|
2098
|
-
}
|
|
2099
|
-
addChild(data) {
|
|
2100
|
-
const child = new TreeNode(data, this);
|
|
2101
|
-
if (!this.children) this.children = [];
|
|
2102
|
-
this.children.push(child);
|
|
2103
|
-
return child;
|
|
2104
|
-
}
|
|
2105
|
-
get root() {
|
|
2106
|
-
if (!this.parent) return this;
|
|
2107
|
-
return this.parent.root;
|
|
2108
|
-
}
|
|
2109
|
-
get leaves() {
|
|
2110
|
-
if (!this.children || this.children.length === 0) return [this];
|
|
2111
|
-
if (this.#cachedLeaves) return this.#cachedLeaves;
|
|
2112
|
-
const leaves = [];
|
|
2113
|
-
for (const child of this.children) leaves.push(...child.leaves);
|
|
2114
|
-
this.#cachedLeaves = leaves;
|
|
2115
|
-
return leaves;
|
|
2116
|
-
}
|
|
2117
|
-
forEach(callback) {
|
|
2118
|
-
if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
|
|
2119
|
-
callback(this);
|
|
2120
|
-
for (const child of this.children) child.forEach(callback);
|
|
2121
|
-
return this;
|
|
2122
|
-
}
|
|
2123
|
-
findDeep(predicate) {
|
|
2124
|
-
if (typeof predicate !== "function") throw new TypeError("find() predicate must be a function");
|
|
2125
|
-
return this.leaves.find(predicate);
|
|
2126
|
-
}
|
|
2127
|
-
forEachDeep(callback) {
|
|
2128
|
-
if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
|
|
2129
|
-
this.leaves.forEach(callback);
|
|
2130
|
-
}
|
|
2131
|
-
filterDeep(callback) {
|
|
2132
|
-
if (typeof callback !== "function") throw new TypeError("filter() callback must be a function");
|
|
2133
|
-
return this.leaves.filter(callback);
|
|
2134
|
-
}
|
|
2135
|
-
mapDeep(callback) {
|
|
2136
|
-
if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
|
|
2137
|
-
return this.leaves.map(callback);
|
|
2138
|
-
}
|
|
2139
|
-
static build(files, root) {
|
|
2140
|
-
try {
|
|
2141
|
-
const filteredTree = buildDirectoryTree(files, root);
|
|
2142
|
-
if (!filteredTree) return null;
|
|
2143
|
-
const treeNode = new TreeNode({
|
|
2144
|
-
name: filteredTree.name,
|
|
2145
|
-
path: filteredTree.path,
|
|
2146
|
-
file: filteredTree.file,
|
|
2147
|
-
type: getMode(filteredTree.path)
|
|
2148
|
-
});
|
|
2149
|
-
const recurse = (node, item) => {
|
|
2150
|
-
const subNode = node.addChild({
|
|
2151
|
-
name: item.name,
|
|
2152
|
-
path: item.path,
|
|
2153
|
-
file: item.file,
|
|
2154
|
-
type: getMode(item.path)
|
|
2155
|
-
});
|
|
2156
|
-
if (item.children?.length) item.children?.forEach((child) => {
|
|
2157
|
-
recurse(subNode, child);
|
|
2158
|
-
});
|
|
2159
|
-
};
|
|
2160
|
-
filteredTree.children?.forEach((child) => {
|
|
2161
|
-
recurse(treeNode, child);
|
|
2162
|
-
});
|
|
2163
|
-
return treeNode;
|
|
2164
|
-
} catch (error) {
|
|
2165
|
-
throw new Error("Something went wrong with creating barrel files with the TreeNode class", { cause: error });
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
};
|
|
2169
|
-
const normalizePath = (p) => p.replaceAll("\\", "/");
|
|
2170
|
-
function buildDirectoryTree(files, rootFolder = "") {
|
|
2171
|
-
const normalizedRootFolder = normalizePath(rootFolder);
|
|
2172
|
-
const rootPrefix = normalizedRootFolder.endsWith("/") ? normalizedRootFolder : `${normalizedRootFolder}/`;
|
|
2173
|
-
const filteredFiles = files.filter((file) => {
|
|
2174
|
-
const normalizedFilePath = normalizePath(file.path);
|
|
2175
|
-
return rootFolder ? normalizedFilePath.startsWith(rootPrefix) && !normalizedFilePath.endsWith(".json") : !normalizedFilePath.endsWith(".json");
|
|
2176
|
-
});
|
|
2177
|
-
if (filteredFiles.length === 0) return null;
|
|
2178
|
-
const root = {
|
|
2179
|
-
name: rootFolder || "",
|
|
2180
|
-
path: rootFolder || "",
|
|
2181
|
-
children: []
|
|
2182
|
-
};
|
|
2183
|
-
filteredFiles.forEach((file) => {
|
|
2184
|
-
const parts = file.path.slice(rootFolder.length).split("/").filter(Boolean);
|
|
2185
|
-
let currentLevel = root.children;
|
|
2186
|
-
let currentPath = normalizePath(rootFolder);
|
|
2187
|
-
parts.forEach((part, index) => {
|
|
2188
|
-
currentPath = path.posix.join(currentPath, part);
|
|
2189
|
-
let existingNode = currentLevel.find((node) => node.name === part);
|
|
2190
|
-
if (!existingNode) {
|
|
2191
|
-
if (index === parts.length - 1) existingNode = {
|
|
2192
|
-
name: part,
|
|
2193
|
-
file,
|
|
2194
|
-
path: currentPath
|
|
2195
|
-
};
|
|
2196
|
-
else existingNode = {
|
|
2197
|
-
name: part,
|
|
2198
|
-
path: currentPath,
|
|
2199
|
-
children: []
|
|
2200
|
-
};
|
|
2201
|
-
currentLevel.push(existingNode);
|
|
2202
|
-
}
|
|
2203
|
-
if (!existingNode.file) currentLevel = existingNode.children;
|
|
2204
|
-
});
|
|
2205
|
-
});
|
|
2206
|
-
return root;
|
|
2207
|
-
}
|
|
2208
|
-
//#endregion
|
|
2209
|
-
//#region src/BarrelManager.ts
|
|
2210
|
-
/** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */
|
|
2211
|
-
var BarrelManager = class {
|
|
2212
|
-
getFiles({ files: generatedFiles, root }) {
|
|
2213
|
-
const cachedFiles = /* @__PURE__ */ new Map();
|
|
2214
|
-
TreeNode.build(generatedFiles, root)?.forEach((treeNode) => {
|
|
2215
|
-
if (!treeNode || !treeNode.children || !treeNode.parent?.data.path) return;
|
|
2216
|
-
const barrelFile = {
|
|
2217
|
-
path: join(treeNode.parent?.data.path, "index.ts"),
|
|
2218
|
-
baseName: "index.ts",
|
|
2219
|
-
exports: [],
|
|
2220
|
-
imports: [],
|
|
2221
|
-
sources: []
|
|
2222
|
-
};
|
|
2223
|
-
const previousBarrelFile = cachedFiles.get(barrelFile.path);
|
|
2224
|
-
treeNode.leaves.forEach((item) => {
|
|
2225
|
-
if (!item.data.name) return;
|
|
2226
|
-
(item.data.file?.sources || []).forEach((source) => {
|
|
2227
|
-
if (!item.data.file?.path || !source.isIndexable || !source.name) return;
|
|
2228
|
-
if (previousBarrelFile?.sources.some((item) => item.name === source.name && item.isTypeOnly === source.isTypeOnly)) return;
|
|
2229
|
-
barrelFile.exports.push({
|
|
2230
|
-
name: [source.name],
|
|
2231
|
-
path: getRelativePath(treeNode.parent?.data.path, item.data.path),
|
|
2232
|
-
isTypeOnly: source.isTypeOnly
|
|
2233
|
-
});
|
|
2234
|
-
barrelFile.sources.push({
|
|
2235
|
-
name: source.name,
|
|
2236
|
-
isTypeOnly: source.isTypeOnly,
|
|
2237
|
-
value: "",
|
|
2238
|
-
isExportable: false,
|
|
2239
|
-
isIndexable: false
|
|
2240
|
-
});
|
|
2241
|
-
});
|
|
2242
|
-
});
|
|
2243
|
-
if (previousBarrelFile) {
|
|
2244
|
-
previousBarrelFile.sources.push(...barrelFile.sources);
|
|
2245
|
-
previousBarrelFile.exports?.push(...barrelFile.exports || []);
|
|
2246
|
-
} else cachedFiles.set(barrelFile.path, barrelFile);
|
|
2247
|
-
});
|
|
2248
|
-
return [...cachedFiles.values()];
|
|
2249
|
-
}
|
|
2250
|
-
};
|
|
2251
|
-
//#endregion
|
|
2252
|
-
//#region src/utils/getBarrelFiles.ts
|
|
2253
|
-
function trimExtName(text) {
|
|
2254
|
-
const dotIndex = text.lastIndexOf(".");
|
|
2255
|
-
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
2256
|
-
return text;
|
|
2257
|
-
}
|
|
2258
|
-
async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
2259
|
-
if (!type || type === "propagate") return [];
|
|
2260
|
-
const barrelManager = new BarrelManager();
|
|
2261
|
-
const pathToBuildFrom = join(root, output.path);
|
|
2262
|
-
if (trimExtName(pathToBuildFrom).endsWith("index")) return [];
|
|
2263
|
-
const barrelFiles = barrelManager.getFiles({
|
|
2264
|
-
files,
|
|
2265
|
-
root: pathToBuildFrom,
|
|
2266
|
-
meta
|
|
2267
|
-
});
|
|
2268
|
-
if (type === "all") return barrelFiles.map((file) => {
|
|
2269
|
-
return {
|
|
2270
|
-
...file,
|
|
2271
|
-
exports: file.exports?.map((exportItem) => {
|
|
2272
|
-
return {
|
|
2273
|
-
...exportItem,
|
|
2274
|
-
name: void 0
|
|
2275
|
-
};
|
|
2276
|
-
})
|
|
2277
|
-
};
|
|
2278
|
-
});
|
|
2279
|
-
return barrelFiles.map((indexFile) => {
|
|
2280
|
-
return {
|
|
2281
|
-
...indexFile,
|
|
2282
|
-
meta
|
|
2283
|
-
};
|
|
2284
|
-
});
|
|
2285
|
-
}
|
|
2286
|
-
//#endregion
|
|
2287
|
-
//#region src/utils/getPlugins.ts
|
|
2288
|
-
function isJSONPlugins(plugins) {
|
|
2289
|
-
return Array.isArray(plugins) && plugins.some((plugin) => Array.isArray(plugin) && typeof plugin[0] === "string");
|
|
2290
|
-
}
|
|
2291
|
-
function isObjectPlugins(plugins) {
|
|
2292
|
-
return plugins instanceof Object && !Array.isArray(plugins);
|
|
2293
|
-
}
|
|
2294
|
-
function getPlugins(plugins) {
|
|
2295
|
-
if (isObjectPlugins(plugins)) throw new Error("Object plugins are not supported anymore, best to use http://kubb.dev/getting-started/configure#json");
|
|
2296
|
-
if (isJSONPlugins(plugins)) throw new Error("JSON plugins are not supported anymore, best to use http://kubb.dev/getting-started/configure#json");
|
|
2297
|
-
return Promise.resolve(plugins);
|
|
2298
|
-
}
|
|
2299
|
-
//#endregion
|
|
2300
|
-
//#region src/utils/getConfigs.ts
|
|
2301
|
-
/**
|
|
2302
|
-
* Converting UserConfig to Config Array without a change in the object beside the JSON convert.
|
|
2303
|
-
*/
|
|
2304
|
-
async function getConfigs(config, args) {
|
|
2305
|
-
let userConfigs = await (typeof config === "function" ? Promise.resolve(config(args)) : Promise.resolve(config));
|
|
2306
|
-
if (!Array.isArray(userConfigs)) userConfigs = [userConfigs];
|
|
2307
|
-
const results = [];
|
|
2308
|
-
for (const item of userConfigs) {
|
|
2309
|
-
const plugins = item.plugins ? await getPlugins(item.plugins) : void 0;
|
|
2310
|
-
results.push({
|
|
2311
|
-
...item,
|
|
2312
|
-
plugins
|
|
2313
|
-
});
|
|
2314
|
-
}
|
|
2315
|
-
return results;
|
|
2316
|
-
}
|
|
2317
|
-
//#endregion
|
|
2318
|
-
//#region src/utils/linters.ts
|
|
2319
|
-
async function isLinterAvailable(linter) {
|
|
2320
|
-
try {
|
|
2321
|
-
await x(linter, ["--version"], { nodeOptions: { stdio: "ignore" } });
|
|
2322
|
-
return true;
|
|
2323
|
-
} catch {
|
|
2324
|
-
return false;
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
async function detectLinter() {
|
|
2328
|
-
for (const linter of [
|
|
2329
|
-
"biome",
|
|
2330
|
-
"oxlint",
|
|
2331
|
-
"eslint"
|
|
2332
|
-
]) if (await isLinterAvailable(linter)) return linter;
|
|
2333
|
-
}
|
|
2334
|
-
//#endregion
|
|
2335
|
-
//#region src/utils/resolveOptions.ts
|
|
2336
|
-
function matchesOperationPattern(node, type, pattern) {
|
|
2337
|
-
switch (type) {
|
|
2338
|
-
case "tag": return node.tags.some((tag) => !!tag.match(pattern));
|
|
2339
|
-
case "operationId": return !!node.operationId.match(pattern);
|
|
2340
|
-
case "path": return !!node.path.match(pattern);
|
|
2341
|
-
case "method": return !!node.method.toLowerCase().match(pattern);
|
|
2342
|
-
default: return false;
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
function matchesSchemaPattern(node, type, pattern) {
|
|
2346
|
-
switch (type) {
|
|
2347
|
-
case "schemaName": return node.name ? !!node.name.match(pattern) : false;
|
|
2348
|
-
default: return null;
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
/**
|
|
2352
|
-
* Resolves the effective plugin options for a given AST node by applying
|
|
2353
|
-
* `exclude`, `include`, and `override` rules from the plugin configuration.
|
|
2354
|
-
*
|
|
2355
|
-
* Returns `null` when the node is excluded or not matched by `include`.
|
|
2356
|
-
* Returns the merged options (base options merged with any matching `override`) otherwise.
|
|
2357
|
-
*
|
|
2358
|
-
* Supported filter types for `OperationNode`: `tag`, `operationId`, `path`, `method`.
|
|
2359
|
-
* Supported filter types for `SchemaNode`: `schemaName`.
|
|
2360
|
-
*
|
|
2361
|
-
* @example
|
|
2362
|
-
* const resolved = resolveOptions(operationNode, { options, exclude, include, override })
|
|
2363
|
-
* if (!resolved) return // excluded
|
|
2364
|
-
*/
|
|
2365
|
-
function resolveOptions(node, { options, exclude = [], include, override = [] }) {
|
|
2366
|
-
if (isOperationNode(node)) {
|
|
2367
|
-
if (exclude.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
2368
|
-
if (include && !include.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
2369
|
-
const overrideOptions = override.find(({ type, pattern }) => matchesOperationPattern(node, type, pattern))?.options;
|
|
2370
|
-
return {
|
|
2371
|
-
...options,
|
|
2372
|
-
...overrideOptions
|
|
2373
|
-
};
|
|
2374
|
-
}
|
|
2375
|
-
if (isSchemaNode(node)) {
|
|
2376
|
-
if (exclude.some(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)) return null;
|
|
2377
|
-
if (include) {
|
|
2378
|
-
const applicable = include.map(({ type, pattern }) => matchesSchemaPattern(node, type, pattern)).filter((r) => r !== null);
|
|
2379
|
-
if (applicable.length > 0 && !applicable.includes(true)) return null;
|
|
2380
|
-
}
|
|
2381
|
-
const overrideOptions = override.find(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)?.options;
|
|
2382
|
-
return {
|
|
2383
|
-
...options,
|
|
2384
|
-
...overrideOptions
|
|
2385
|
-
};
|
|
2386
|
-
}
|
|
2387
|
-
return options;
|
|
2388
|
-
}
|
|
2389
|
-
//#endregion
|
|
2390
|
-
export { AsyncEventEmitter, FunctionParams, PackageManager, PluginManager, PromiseManager, URLPath, build, build as default, defineAdapter, defineConfig, defineGenerator, defineLogger, definePlugin, definePrinter, defineStorage, detectFormatter, detectLinter, formatters, fsStorage, getBarrelFiles, getConfigs, getMode, isInputPath, linters, logLevel, memoryStorage, resolveOptions, safeBuild, setup };
|
|
1427
|
+
export { AsyncEventEmitter, FileManager, FileProcessor, PluginDriver, URLPath, ast, createAdapter, createKubb, createRenderer, createStorage, defineGenerator, defineLogger, defineParser, definePlugin, defineResolver, fsStorage, isInputPath, logLevel, memoryStorage };
|
|
2391
1428
|
|
|
2392
1429
|
//# sourceMappingURL=index.js.map
|