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