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