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