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