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