@kubb/core 5.0.0-alpha.33 → 5.0.0-alpha.35
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/dist/{PluginDriver-BBi_41VF.d.ts → PluginDriver-D8lWvtUg.d.ts} +743 -375
- package/dist/hooks.d.ts +1 -1
- package/dist/index.cjs +1203 -932
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +22 -141
- package/dist/index.js +1220 -942
- package/dist/index.js.map +1 -1
- package/package.json +4 -5
- package/src/FileManager.ts +1 -1
- package/src/FileProcessor.ts +1 -1
- package/src/Kubb.ts +145 -38
- package/src/PluginDriver.ts +318 -40
- package/src/constants.ts +1 -1
- package/src/{build.ts → createKubb.ts} +180 -122
- package/src/createPlugin.ts +1 -0
- package/src/createRenderer.ts +57 -0
- package/src/defineGenerator.ts +57 -84
- package/src/defineLogger.ts +2 -2
- package/src/defineParser.ts +3 -2
- package/src/definePlugin.ts +95 -0
- package/src/defineResolver.ts +1 -1
- package/src/devtools.ts +1 -1
- package/src/index.ts +5 -6
- package/src/renderNode.ts +35 -0
- package/src/types.ts +275 -209
- package/src/utils/TreeNode.ts +1 -1
- package/src/utils/getBarrelFiles.ts +3 -3
- package/src/utils/getFunctionParams.ts +14 -7
- package/src/utils/isInputPath.ts +2 -2
- package/src/utils/packageJSON.ts +2 -3
- package/src/defineConfig.ts +0 -51
- package/src/definePresets.ts +0 -16
- package/src/renderNode.tsx +0 -28
- package/src/utils/getConfigs.ts +0 -16
- package/src/utils/getPreset.ts +0 -78
package/dist/index.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { t as __name } from "./chunk--u3MIqq1.js";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
5
5
|
import path, { basename, dirname, extname, join, posix, resolve } from "node:path";
|
|
6
|
-
import
|
|
6
|
+
import * as ast from "@kubb/ast";
|
|
7
|
+
import { composeTransformers, createExport, createFile, createSource, definePrinter, extractStringsFromNodes, isOperationNode, isSchemaNode, transform, walk } from "@kubb/ast";
|
|
7
8
|
import { performance } from "node:perf_hooks";
|
|
8
9
|
import { deflateSync } from "fflate";
|
|
9
10
|
import { x } from "tinyexec";
|
|
10
|
-
import { createRenderer } from "@kubb/renderer-jsx";
|
|
11
|
-
import { Fragment, jsx } from "@kubb/renderer-jsx/jsx-runtime";
|
|
12
11
|
import { version } from "node:process";
|
|
13
12
|
import { sortBy } from "remeda";
|
|
14
|
-
import * as pkg from "empathic/package";
|
|
15
13
|
import { coerce, satisfies } from "semver";
|
|
16
14
|
//#region ../../internals/utils/src/errors.ts
|
|
17
15
|
/**
|
|
@@ -129,6 +127,18 @@ var AsyncEventEmitter = class {
|
|
|
129
127
|
this.#emitter.off(eventName, handler);
|
|
130
128
|
}
|
|
131
129
|
/**
|
|
130
|
+
* Returns the number of listeners registered for `eventName`.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* emitter.on('build', handler)
|
|
135
|
+
* emitter.listenerCount('build') // 1
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
listenerCount(eventName) {
|
|
139
|
+
return this.#emitter.listenerCount(eventName);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
132
142
|
* Removes all listeners from every event channel.
|
|
133
143
|
*
|
|
134
144
|
* @example
|
|
@@ -234,6 +244,26 @@ function formatMs(ms) {
|
|
|
234
244
|
//#endregion
|
|
235
245
|
//#region ../../internals/utils/src/fs.ts
|
|
236
246
|
/**
|
|
247
|
+
* Walks up the directory tree from `cwd` (defaults to `process.cwd()`) and
|
|
248
|
+
* returns the absolute path of the nearest `package.json`, or `null` when none
|
|
249
|
+
* is found before reaching the filesystem root.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* const pkgPath = findPackageJSON('/home/user/project/src') // '/home/user/project/package.json'
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
function findPackageJSON(cwd) {
|
|
257
|
+
let dir = cwd ? resolve(cwd) : process.cwd();
|
|
258
|
+
while (true) {
|
|
259
|
+
const pkgPath = join(dir, "package.json");
|
|
260
|
+
if (existsSync(pkgPath)) return pkgPath;
|
|
261
|
+
const parent = dirname(dir);
|
|
262
|
+
if (parent === dir) return null;
|
|
263
|
+
dir = parent;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
237
267
|
* Converts all backslashes to forward slashes.
|
|
238
268
|
* Extended-length Windows paths (`\\?\...`) are left unchanged.
|
|
239
269
|
*/
|
|
@@ -722,8 +752,31 @@ const formatters = {
|
|
|
722
752
|
}
|
|
723
753
|
};
|
|
724
754
|
//#endregion
|
|
755
|
+
//#region src/createAdapter.ts
|
|
756
|
+
/**
|
|
757
|
+
* Creates an adapter factory. Call the returned function with optional options to get the adapter instance.
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* export const myAdapter = createAdapter<MyAdapter>((options) => {
|
|
761
|
+
* return {
|
|
762
|
+
* name: 'my-adapter',
|
|
763
|
+
* options,
|
|
764
|
+
* async parse(source) { ... },
|
|
765
|
+
* }
|
|
766
|
+
* })
|
|
767
|
+
*
|
|
768
|
+
* // instantiate
|
|
769
|
+
* const adapter = myAdapter({ validate: true })
|
|
770
|
+
*/
|
|
771
|
+
function createAdapter(build) {
|
|
772
|
+
return (options) => build(options ?? {});
|
|
773
|
+
}
|
|
774
|
+
//#endregion
|
|
725
775
|
//#region ../../node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
|
|
726
|
-
var Node = class {
|
|
776
|
+
var Node$1 = class {
|
|
777
|
+
static {
|
|
778
|
+
__name(this, "Node");
|
|
779
|
+
}
|
|
727
780
|
value;
|
|
728
781
|
next;
|
|
729
782
|
constructor(value) {
|
|
@@ -738,7 +791,7 @@ var Queue = class {
|
|
|
738
791
|
this.clear();
|
|
739
792
|
}
|
|
740
793
|
enqueue(value) {
|
|
741
|
-
const node = new Node(value);
|
|
794
|
+
const node = new Node$1(value);
|
|
742
795
|
if (this.#head) {
|
|
743
796
|
this.#tail.next = node;
|
|
744
797
|
this.#tail = node;
|
|
@@ -892,249 +945,881 @@ var FileProcessor = class {
|
|
|
892
945
|
}
|
|
893
946
|
};
|
|
894
947
|
//#endregion
|
|
895
|
-
//#region src/
|
|
948
|
+
//#region src/definePlugin.ts
|
|
896
949
|
/**
|
|
897
|
-
*
|
|
898
|
-
*
|
|
899
|
-
* The JSON representation is deflate-compressed with {@link deflateSync} before
|
|
900
|
-
* base64url encoding, which typically reduces payload size by 70–80 % and
|
|
901
|
-
* keeps URLs well within browser and server path-length limits.
|
|
950
|
+
* Returns `true` when `plugin` is a hook-style plugin created with `definePlugin`.
|
|
902
951
|
*
|
|
903
|
-
*
|
|
904
|
-
|
|
905
|
-
function encodeAst(input) {
|
|
906
|
-
const compressed = deflateSync(new TextEncoder().encode(JSON.stringify(input)));
|
|
907
|
-
return Buffer.from(compressed).toString("base64url");
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Constructs the Kubb Studio URL for the given `InputNode`.
|
|
911
|
-
* When `options.ast` is `true`, navigates to the AST inspector (`/ast`).
|
|
912
|
-
* The `input` is encoded and attached as the `?root=` query parameter so Studio
|
|
913
|
-
* can decode and render it without a round-trip to any server.
|
|
952
|
+
* Used by `PluginDriver` to distinguish hook-style plugins from legacy `createPlugin` plugins
|
|
953
|
+
* so it can normalize them and register their handlers on the `AsyncEventEmitter`.
|
|
914
954
|
*/
|
|
915
|
-
function
|
|
916
|
-
return
|
|
955
|
+
function isHookStylePlugin(plugin) {
|
|
956
|
+
return typeof plugin === "object" && plugin !== null && "hooks" in plugin;
|
|
917
957
|
}
|
|
918
958
|
/**
|
|
919
|
-
*
|
|
959
|
+
* Creates a plugin factory using the new hook-style (`hooks:`) API.
|
|
920
960
|
*
|
|
921
|
-
*
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
const url = getStudioUrl(input, studioUrl, options);
|
|
925
|
-
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
926
|
-
const args = process.platform === "win32" ? [
|
|
927
|
-
"/c",
|
|
928
|
-
"start",
|
|
929
|
-
"",
|
|
930
|
-
url
|
|
931
|
-
] : [url];
|
|
932
|
-
try {
|
|
933
|
-
await x(cmd, args);
|
|
934
|
-
} catch {
|
|
935
|
-
console.log(`\n ${url}\n`);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
//#endregion
|
|
939
|
-
//#region src/FileManager.ts
|
|
940
|
-
function mergeFile(a, b) {
|
|
941
|
-
return {
|
|
942
|
-
...a,
|
|
943
|
-
sources: [...a.sources || [], ...b.sources || []],
|
|
944
|
-
imports: [...a.imports || [], ...b.imports || []],
|
|
945
|
-
exports: [...a.exports || [], ...b.exports || []]
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
/**
|
|
949
|
-
* In-memory file store for generated files.
|
|
961
|
+
* The returned factory is called with optional options and produces a `HookStylePlugin`
|
|
962
|
+
* that coexists with plugins created via the legacy `createPlugin` API in the same
|
|
963
|
+
* `kubb.config.ts`.
|
|
950
964
|
*
|
|
951
|
-
*
|
|
952
|
-
*
|
|
965
|
+
* Lifecycle handlers are registered on the `PluginDriver`'s `AsyncEventEmitter`, enabling
|
|
966
|
+
* both the plugin's own handlers and external tooling (CLI, devtools) to observe every event.
|
|
953
967
|
*
|
|
954
968
|
* @example
|
|
955
969
|
* ```ts
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
*
|
|
959
|
-
*
|
|
960
|
-
*
|
|
970
|
+
* // With PluginFactoryOptions (recommended for real plugins)
|
|
971
|
+
* export const pluginTs = definePlugin<PluginTs>((options) => ({
|
|
972
|
+
* name: 'plugin-ts',
|
|
973
|
+
* hooks: {
|
|
974
|
+
* 'kubb:plugin:setup'(ctx) {
|
|
975
|
+
* ctx.setResolver(resolverTs) // typed as Partial<ResolverTs>
|
|
976
|
+
* },
|
|
977
|
+
* },
|
|
978
|
+
* }))
|
|
961
979
|
* ```
|
|
962
980
|
*/
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
/**
|
|
967
|
-
* Adds one or more files. Files with the same path are merged — sources, imports,
|
|
968
|
-
* and exports from all calls with the same path are concatenated together.
|
|
969
|
-
*/
|
|
970
|
-
add(...files) {
|
|
971
|
-
const resolvedFiles = [];
|
|
972
|
-
const mergedFiles = /* @__PURE__ */ new Map();
|
|
973
|
-
files.forEach((file) => {
|
|
974
|
-
const existing = mergedFiles.get(file.path);
|
|
975
|
-
if (existing) mergedFiles.set(file.path, mergeFile(existing, file));
|
|
976
|
-
else mergedFiles.set(file.path, file);
|
|
977
|
-
});
|
|
978
|
-
for (const file of mergedFiles.values()) {
|
|
979
|
-
const resolvedFile = createFile(file);
|
|
980
|
-
this.#cache.set(resolvedFile.path, resolvedFile);
|
|
981
|
-
this.#filesCache = null;
|
|
982
|
-
resolvedFiles.push(resolvedFile);
|
|
983
|
-
}
|
|
984
|
-
return resolvedFiles;
|
|
985
|
-
}
|
|
986
|
-
/**
|
|
987
|
-
* Adds or merges one or more files.
|
|
988
|
-
* If a file with the same path already exists, its sources/imports/exports are merged together.
|
|
989
|
-
*/
|
|
990
|
-
upsert(...files) {
|
|
991
|
-
const resolvedFiles = [];
|
|
992
|
-
const mergedFiles = /* @__PURE__ */ new Map();
|
|
993
|
-
files.forEach((file) => {
|
|
994
|
-
const existing = mergedFiles.get(file.path);
|
|
995
|
-
if (existing) mergedFiles.set(file.path, mergeFile(existing, file));
|
|
996
|
-
else mergedFiles.set(file.path, file);
|
|
997
|
-
});
|
|
998
|
-
for (const file of mergedFiles.values()) {
|
|
999
|
-
const existing = this.#cache.get(file.path);
|
|
1000
|
-
const resolvedFile = createFile(existing ? mergeFile(existing, file) : file);
|
|
1001
|
-
this.#cache.set(resolvedFile.path, resolvedFile);
|
|
1002
|
-
this.#filesCache = null;
|
|
1003
|
-
resolvedFiles.push(resolvedFile);
|
|
1004
|
-
}
|
|
1005
|
-
return resolvedFiles;
|
|
1006
|
-
}
|
|
1007
|
-
getByPath(path) {
|
|
1008
|
-
return this.#cache.get(path) ?? null;
|
|
1009
|
-
}
|
|
1010
|
-
deleteByPath(path) {
|
|
1011
|
-
this.#cache.delete(path);
|
|
1012
|
-
this.#filesCache = null;
|
|
1013
|
-
}
|
|
1014
|
-
clear() {
|
|
1015
|
-
this.#cache.clear();
|
|
1016
|
-
this.#filesCache = null;
|
|
1017
|
-
}
|
|
1018
|
-
/**
|
|
1019
|
-
* All stored files, sorted by path length (shorter paths first).
|
|
1020
|
-
* Barrel/index files (e.g. index.ts) are sorted last within each length bucket.
|
|
1021
|
-
*/
|
|
1022
|
-
get files() {
|
|
1023
|
-
if (this.#filesCache) return this.#filesCache;
|
|
1024
|
-
const keys = [...this.#cache.keys()].sort((a, b) => {
|
|
1025
|
-
if (a.length !== b.length) return a.length - b.length;
|
|
1026
|
-
const aIsIndex = trimExtName$1(a).endsWith("index");
|
|
1027
|
-
if (aIsIndex !== trimExtName$1(b).endsWith("index")) return aIsIndex ? 1 : -1;
|
|
1028
|
-
return 0;
|
|
1029
|
-
});
|
|
1030
|
-
const files = [];
|
|
1031
|
-
for (const key of keys) {
|
|
1032
|
-
const file = this.#cache.get(key);
|
|
1033
|
-
if (file) files.push(file);
|
|
1034
|
-
}
|
|
1035
|
-
this.#filesCache = files;
|
|
1036
|
-
return files;
|
|
1037
|
-
}
|
|
1038
|
-
};
|
|
981
|
+
function definePlugin(factory) {
|
|
982
|
+
return (options) => factory(options ?? {});
|
|
983
|
+
}
|
|
1039
984
|
//#endregion
|
|
1040
|
-
//#region src/
|
|
985
|
+
//#region src/defineResolver.ts
|
|
1041
986
|
/**
|
|
1042
|
-
*
|
|
1043
|
-
*
|
|
1044
|
-
* - Each function receives the accumulated state from the previous call.
|
|
1045
|
-
* - Skips functions that return a falsy value (acts as a no-op for that step).
|
|
1046
|
-
* - Returns an array of all individual results.
|
|
1047
|
-
* @deprecated
|
|
987
|
+
* Checks if an operation matches a pattern for a given filter type (`tag`, `operationId`, `path`, `method`).
|
|
1048
988
|
*/
|
|
1049
|
-
function
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
return
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
}
|
|
989
|
+
function matchesOperationPattern(node, type, pattern) {
|
|
990
|
+
switch (type) {
|
|
991
|
+
case "tag": return node.tags.some((tag) => !!tag.match(pattern));
|
|
992
|
+
case "operationId": return !!node.operationId.match(pattern);
|
|
993
|
+
case "path": return !!node.path.match(pattern);
|
|
994
|
+
case "method": return !!node.method.toLowerCase().match(pattern);
|
|
995
|
+
case "contentType": return !!node.requestBody?.contentType?.match(pattern);
|
|
996
|
+
default: return false;
|
|
997
|
+
}
|
|
1058
998
|
}
|
|
1059
999
|
/**
|
|
1060
|
-
*
|
|
1000
|
+
* Checks if a schema matches a pattern for a given filter type (`schemaName`).
|
|
1061
1001
|
*
|
|
1062
|
-
*
|
|
1063
|
-
* - Subsequent functions are skipped once a match is found.
|
|
1064
|
-
* @deprecated
|
|
1002
|
+
* Returns `null` when the filter type doesn't apply to schemas.
|
|
1065
1003
|
*/
|
|
1066
|
-
function
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
});
|
|
1072
|
-
return promise;
|
|
1004
|
+
function matchesSchemaPattern(node, type, pattern) {
|
|
1005
|
+
switch (type) {
|
|
1006
|
+
case "schemaName": return node.name ? !!node.name.match(pattern) : false;
|
|
1007
|
+
default: return null;
|
|
1008
|
+
}
|
|
1073
1009
|
}
|
|
1074
1010
|
/**
|
|
1075
|
-
*
|
|
1011
|
+
* Default name resolver used by `defineResolver`.
|
|
1076
1012
|
*
|
|
1077
|
-
* -
|
|
1078
|
-
* -
|
|
1079
|
-
*
|
|
1013
|
+
* - `camelCase` for `function` and `file` types.
|
|
1014
|
+
* - `PascalCase` for `type`.
|
|
1015
|
+
* - `camelCase` for everything else.
|
|
1080
1016
|
*/
|
|
1081
|
-
function
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1017
|
+
function defaultResolver(name, type) {
|
|
1018
|
+
let resolvedName = camelCase(name);
|
|
1019
|
+
if (type === "file" || type === "function") resolvedName = camelCase(name, { isFile: type === "file" });
|
|
1020
|
+
if (type === "type") resolvedName = pascalCase(name);
|
|
1021
|
+
return resolvedName;
|
|
1085
1022
|
}
|
|
1086
|
-
//#endregion
|
|
1087
|
-
//#region src/PluginDriver.ts
|
|
1088
1023
|
/**
|
|
1089
|
-
*
|
|
1024
|
+
* Default option resolver — applies include/exclude filters and merges matching override options.
|
|
1090
1025
|
*
|
|
1091
|
-
*
|
|
1026
|
+
* Returns `null` when the node is filtered out by an `exclude` rule or not matched by any `include` rule.
|
|
1027
|
+
*
|
|
1028
|
+
* @example Include/exclude filtering
|
|
1092
1029
|
* ```ts
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1030
|
+
* const options = defaultResolveOptions(operationNode, {
|
|
1031
|
+
* options: { output: 'types' },
|
|
1032
|
+
* exclude: [{ type: 'tag', pattern: 'internal' }],
|
|
1033
|
+
* })
|
|
1034
|
+
* // → null when node has tag 'internal'
|
|
1095
1035
|
* ```
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1036
|
+
*
|
|
1037
|
+
* @example Override merging
|
|
1038
|
+
* ```ts
|
|
1039
|
+
* const options = defaultResolveOptions(operationNode, {
|
|
1040
|
+
* options: { enumType: 'asConst' },
|
|
1041
|
+
* override: [{ type: 'operationId', pattern: 'listPets', options: { enumType: 'enum' } }],
|
|
1042
|
+
* })
|
|
1043
|
+
* // → { enumType: 'enum' } when operationId matches
|
|
1044
|
+
* ```
|
|
1045
|
+
*/
|
|
1046
|
+
function defaultResolveOptions(node, { options, exclude = [], include, override = [] }) {
|
|
1047
|
+
if (isOperationNode(node)) {
|
|
1048
|
+
if (exclude.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
1049
|
+
if (include && !include.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
1050
|
+
const overrideOptions = override.find(({ type, pattern }) => matchesOperationPattern(node, type, pattern))?.options;
|
|
1051
|
+
return {
|
|
1052
|
+
...options,
|
|
1053
|
+
...overrideOptions
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
if (isSchemaNode(node)) {
|
|
1057
|
+
if (exclude.some(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)) return null;
|
|
1058
|
+
if (include) {
|
|
1059
|
+
const applicable = include.map(({ type, pattern }) => matchesSchemaPattern(node, type, pattern)).filter((r) => r !== null);
|
|
1060
|
+
if (applicable.length > 0 && !applicable.includes(true)) return null;
|
|
1061
|
+
}
|
|
1062
|
+
const overrideOptions = override.find(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)?.options;
|
|
1063
|
+
return {
|
|
1064
|
+
...options,
|
|
1065
|
+
...overrideOptions
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
return options;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Default path resolver used by `defineResolver`.
|
|
1072
|
+
*
|
|
1073
|
+
* - Returns the output directory in `single` mode.
|
|
1074
|
+
* - Resolves into a tag- or path-based subdirectory when `group` and a `tag`/`path` value are provided.
|
|
1075
|
+
* - Falls back to a flat `output/baseName` path otherwise.
|
|
1076
|
+
*
|
|
1077
|
+
* A custom `group.name` function overrides the default subdirectory naming.
|
|
1078
|
+
* For `tag` groups the default is `${camelCase(tag)}Controller`.
|
|
1079
|
+
* For `path` groups the default is the first path segment after `/`.
|
|
1080
|
+
*
|
|
1081
|
+
* @example Flat output
|
|
1082
|
+
* ```ts
|
|
1083
|
+
* defaultResolvePath({ baseName: 'petTypes.ts' }, { root: '/src', output: { path: 'types' } })
|
|
1084
|
+
* // → '/src/types/petTypes.ts'
|
|
1085
|
+
* ```
|
|
1086
|
+
*
|
|
1087
|
+
* @example Tag-based grouping
|
|
1088
|
+
* ```ts
|
|
1089
|
+
* defaultResolvePath(
|
|
1090
|
+
* { baseName: 'petTypes.ts', tag: 'pets' },
|
|
1091
|
+
* { root: '/src', output: { path: 'types' }, group: { type: 'tag' } },
|
|
1092
|
+
* )
|
|
1093
|
+
* // → '/src/types/petsController/petTypes.ts'
|
|
1094
|
+
* ```
|
|
1095
|
+
*
|
|
1096
|
+
* @example Path-based grouping
|
|
1097
|
+
* ```ts
|
|
1098
|
+
* defaultResolvePath(
|
|
1099
|
+
* { baseName: 'petTypes.ts', path: '/pets/list' },
|
|
1100
|
+
* { root: '/src', output: { path: 'types' }, group: { type: 'path' } },
|
|
1101
|
+
* )
|
|
1102
|
+
* // → '/src/types/pets/petTypes.ts'
|
|
1103
|
+
* ```
|
|
1104
|
+
*
|
|
1105
|
+
* @example Single-file mode
|
|
1106
|
+
* ```ts
|
|
1107
|
+
* defaultResolvePath(
|
|
1108
|
+
* { baseName: 'petTypes.ts', pathMode: 'single' },
|
|
1109
|
+
* { root: '/src', output: { path: 'types' } },
|
|
1110
|
+
* )
|
|
1111
|
+
* // → '/src/types'
|
|
1112
|
+
* ```
|
|
1113
|
+
*/
|
|
1114
|
+
function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }, { root, output, group }) {
|
|
1115
|
+
if ((pathMode ?? getMode(path.resolve(root, output.path))) === "single") return path.resolve(root, output.path);
|
|
1116
|
+
if (group && (groupPath || tag)) return path.resolve(root, output.path, group.name({ group: group.type === "path" ? groupPath : tag }), baseName);
|
|
1117
|
+
return path.resolve(root, output.path, baseName);
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Default file resolver used by `defineResolver`.
|
|
1121
|
+
*
|
|
1122
|
+
* Resolves a `FileNode` by combining name resolution (`resolver.default`) with
|
|
1123
|
+
* path resolution (`resolver.resolvePath`). The resolved file always has empty
|
|
1124
|
+
* `sources`, `imports`, and `exports` arrays — consumers populate those separately.
|
|
1125
|
+
*
|
|
1126
|
+
* In `single` mode the name is omitted and the file sits directly in the output directory.
|
|
1127
|
+
*
|
|
1128
|
+
* @example Resolve a schema file
|
|
1129
|
+
* ```ts
|
|
1130
|
+
* const file = defaultResolveFile.call(resolver,
|
|
1131
|
+
* { name: 'pet', extname: '.ts' },
|
|
1132
|
+
* { root: '/src', output: { path: 'types' } },
|
|
1133
|
+
* )
|
|
1134
|
+
* // → { baseName: 'pet.ts', path: '/src/types/pet.ts', sources: [], ... }
|
|
1135
|
+
* ```
|
|
1136
|
+
*
|
|
1137
|
+
* @example Resolve an operation file with tag grouping
|
|
1138
|
+
* ```ts
|
|
1139
|
+
* const file = defaultResolveFile.call(resolver,
|
|
1140
|
+
* { name: 'listPets', extname: '.ts', tag: 'pets' },
|
|
1141
|
+
* { root: '/src', output: { path: 'types' }, group: { type: 'tag' } },
|
|
1142
|
+
* )
|
|
1143
|
+
* // → { baseName: 'listPets.ts', path: '/src/types/petsController/listPets.ts', ... }
|
|
1144
|
+
* ```
|
|
1145
|
+
*/
|
|
1146
|
+
function defaultResolveFile({ name, extname, tag, path: groupPath }, context) {
|
|
1147
|
+
const pathMode = getMode(path.resolve(context.root, context.output.path));
|
|
1148
|
+
const baseName = `${pathMode === "single" ? "" : this.default(name, "file")}${extname}`;
|
|
1149
|
+
const filePath = this.resolvePath({
|
|
1150
|
+
baseName,
|
|
1151
|
+
pathMode,
|
|
1152
|
+
tag,
|
|
1153
|
+
path: groupPath
|
|
1154
|
+
}, context);
|
|
1155
|
+
return createFile({
|
|
1156
|
+
path: filePath,
|
|
1157
|
+
baseName: path.basename(filePath),
|
|
1158
|
+
meta: { pluginName: this.pluginName },
|
|
1159
|
+
sources: [],
|
|
1160
|
+
imports: [],
|
|
1161
|
+
exports: []
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Generates the default "Generated by Kubb" banner from config and optional node metadata.
|
|
1166
|
+
*/
|
|
1167
|
+
function buildDefaultBanner({ title, description, version, config }) {
|
|
1168
|
+
try {
|
|
1169
|
+
let source = "";
|
|
1170
|
+
if (Array.isArray(config.input)) {
|
|
1171
|
+
const first = config.input[0];
|
|
1172
|
+
if (first && "path" in first) source = path.basename(first.path);
|
|
1173
|
+
} else if ("path" in config.input) source = path.basename(config.input.path);
|
|
1174
|
+
else if ("data" in config.input) source = "text content";
|
|
1175
|
+
let banner = "/**\n* Generated by Kubb (https://kubb.dev/).\n* Do not edit manually.\n";
|
|
1176
|
+
if (config.output.defaultBanner === "simple") {
|
|
1177
|
+
banner += "*/\n";
|
|
1178
|
+
return banner;
|
|
1179
|
+
}
|
|
1180
|
+
if (source) banner += `* Source: ${source}\n`;
|
|
1181
|
+
if (title) banner += `* Title: ${title}\n`;
|
|
1182
|
+
if (description) {
|
|
1183
|
+
const formattedDescription = description.replace(/\n/gm, "\n* ");
|
|
1184
|
+
banner += `* Description: ${formattedDescription}\n`;
|
|
1185
|
+
}
|
|
1186
|
+
if (version) banner += `* OpenAPI spec version: ${version}\n`;
|
|
1187
|
+
banner += "*/\n";
|
|
1188
|
+
return banner;
|
|
1189
|
+
} catch (_error) {
|
|
1190
|
+
return "/**\n* Generated by Kubb (https://kubb.dev/).\n* Do not edit manually.\n*/";
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Default banner resolver — returns the banner string for a generated file.
|
|
1195
|
+
*
|
|
1196
|
+
* A user-supplied `output.banner` overrides the default Kubb "Generated by Kubb" notice.
|
|
1197
|
+
* When no `output.banner` is set, the Kubb notice is used (including `title` and `version`
|
|
1198
|
+
* from the OAS spec when a `node` is provided).
|
|
1199
|
+
*
|
|
1200
|
+
* - When `output.banner` is a function and `node` is provided, returns `output.banner(node)`.
|
|
1201
|
+
* - When `output.banner` is a function and `node` is absent, falls back to the Kubb notice.
|
|
1202
|
+
* - When `output.banner` is a string, returns it directly.
|
|
1203
|
+
* - When `config.output.defaultBanner` is `false`, returns `undefined`.
|
|
1204
|
+
* - Otherwise returns the Kubb "Generated by Kubb" notice.
|
|
1205
|
+
*
|
|
1206
|
+
* @example String banner overrides default
|
|
1207
|
+
* ```ts
|
|
1208
|
+
* defaultResolveBanner(undefined, { output: { banner: '// my banner' }, config })
|
|
1209
|
+
* // → '// my banner'
|
|
1210
|
+
* ```
|
|
1211
|
+
*
|
|
1212
|
+
* @example Function banner with node
|
|
1213
|
+
* ```ts
|
|
1214
|
+
* defaultResolveBanner(inputNode, { output: { banner: (node) => `// v${node.version}` }, config })
|
|
1215
|
+
* // → '// v3.0.0'
|
|
1216
|
+
* ```
|
|
1217
|
+
*
|
|
1218
|
+
* @example No user banner — Kubb notice with OAS metadata
|
|
1219
|
+
* ```ts
|
|
1220
|
+
* defaultResolveBanner(inputNode, { config })
|
|
1221
|
+
* // → '/** Generated by Kubb ... Title: Pet Store ... *\/'
|
|
1222
|
+
* ```
|
|
1223
|
+
*
|
|
1224
|
+
* @example Disabled default banner
|
|
1225
|
+
* ```ts
|
|
1226
|
+
* defaultResolveBanner(undefined, { config: { output: { defaultBanner: false }, ...config } })
|
|
1227
|
+
* // → undefined
|
|
1228
|
+
* ```
|
|
1229
|
+
*/
|
|
1230
|
+
function defaultResolveBanner(node, { output, config }) {
|
|
1231
|
+
if (typeof output?.banner === "function") return output.banner(node);
|
|
1232
|
+
if (typeof output?.banner === "string") return output.banner;
|
|
1233
|
+
if (config.output.defaultBanner === false) return;
|
|
1234
|
+
return buildDefaultBanner({
|
|
1235
|
+
title: node?.meta?.title,
|
|
1236
|
+
version: node?.meta?.version,
|
|
1237
|
+
config
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Default footer resolver — returns the footer string for a generated file.
|
|
1242
|
+
*
|
|
1243
|
+
* - When `output.footer` is a function and `node` is provided, calls it with the node.
|
|
1244
|
+
* - When `output.footer` is a function and `node` is absent, returns `undefined`.
|
|
1245
|
+
* - When `output.footer` is a string, returns it directly.
|
|
1246
|
+
* - Otherwise returns `undefined`.
|
|
1247
|
+
*
|
|
1248
|
+
* @example String footer
|
|
1249
|
+
* ```ts
|
|
1250
|
+
* defaultResolveFooter(undefined, { output: { footer: '// end of file' }, config })
|
|
1251
|
+
* // → '// end of file'
|
|
1252
|
+
* ```
|
|
1253
|
+
*
|
|
1254
|
+
* @example Function footer with node
|
|
1255
|
+
* ```ts
|
|
1256
|
+
* defaultResolveFooter(inputNode, { output: { footer: (node) => `// ${node.title}` }, config })
|
|
1257
|
+
* // → '// Pet Store'
|
|
1258
|
+
* ```
|
|
1259
|
+
*/
|
|
1260
|
+
function defaultResolveFooter(node, { output }) {
|
|
1261
|
+
if (typeof output?.footer === "function") return node ? output.footer(node) : void 0;
|
|
1262
|
+
if (typeof output?.footer === "string") return output.footer;
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Defines a resolver for a plugin, injecting built-in defaults for name casing,
|
|
1266
|
+
* include/exclude/override filtering, path resolution, and file construction.
|
|
1267
|
+
*
|
|
1268
|
+
* All four defaults can be overridden by providing them in the builder function:
|
|
1269
|
+
* - `default` — name casing strategy (camelCase / PascalCase)
|
|
1270
|
+
* - `resolveOptions` — include/exclude/override filtering
|
|
1271
|
+
* - `resolvePath` — output path computation
|
|
1272
|
+
* - `resolveFile` — full `FileNode` construction
|
|
1273
|
+
*
|
|
1274
|
+
* Methods in the builder have access to `this` (the full resolver object), so they
|
|
1275
|
+
* can call other resolver methods without circular imports.
|
|
1276
|
+
*
|
|
1277
|
+
* @example Basic resolver with naming helpers
|
|
1278
|
+
* ```ts
|
|
1279
|
+
* export const resolver = defineResolver<PluginTs>(() => ({
|
|
1280
|
+
* name: 'default',
|
|
1281
|
+
* resolveName(node) {
|
|
1282
|
+
* return this.default(node.name, 'function')
|
|
1283
|
+
* },
|
|
1284
|
+
* resolveTypedName(node) {
|
|
1285
|
+
* return this.default(node.name, 'type')
|
|
1286
|
+
* },
|
|
1287
|
+
* }))
|
|
1288
|
+
* ```
|
|
1289
|
+
*
|
|
1290
|
+
* @example Override resolvePath for a custom output structure
|
|
1291
|
+
* ```ts
|
|
1292
|
+
* export const resolver = defineResolver<PluginTs>(() => ({
|
|
1293
|
+
* name: 'custom',
|
|
1294
|
+
* resolvePath({ baseName }, { root, output }) {
|
|
1295
|
+
* return path.resolve(root, output.path, 'generated', baseName)
|
|
1296
|
+
* },
|
|
1297
|
+
* }))
|
|
1298
|
+
* ```
|
|
1299
|
+
*
|
|
1300
|
+
* @example Use this.default inside a helper
|
|
1301
|
+
* ```ts
|
|
1302
|
+
* export const resolver = defineResolver<PluginTs>(() => ({
|
|
1303
|
+
* name: 'default',
|
|
1304
|
+
* resolveParamName(node, param) {
|
|
1305
|
+
* return this.default(`${node.operationId} ${param.in} ${param.name}`, 'type')
|
|
1306
|
+
* },
|
|
1307
|
+
* }))
|
|
1308
|
+
* ```
|
|
1309
|
+
*/
|
|
1310
|
+
function defineResolver(build) {
|
|
1311
|
+
return {
|
|
1312
|
+
default: defaultResolver,
|
|
1313
|
+
resolveOptions: defaultResolveOptions,
|
|
1314
|
+
resolvePath: defaultResolvePath,
|
|
1315
|
+
resolveFile: defaultResolveFile,
|
|
1316
|
+
resolveBanner: defaultResolveBanner,
|
|
1317
|
+
resolveFooter: defaultResolveFooter,
|
|
1318
|
+
...build()
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
//#endregion
|
|
1322
|
+
//#region src/devtools.ts
|
|
1323
|
+
/**
|
|
1324
|
+
* Encodes an `InputNode` as a compressed, URL-safe string.
|
|
1325
|
+
*
|
|
1326
|
+
* The JSON representation is deflate-compressed with {@link deflateSync} before
|
|
1327
|
+
* base64url encoding, which typically reduces payload size by 70–80 % and
|
|
1328
|
+
* keeps URLs well within browser and server path-length limits.
|
|
1329
|
+
*
|
|
1330
|
+
* Use {@link decodeAst} to reverse.
|
|
1331
|
+
*/
|
|
1332
|
+
function encodeAst(input) {
|
|
1333
|
+
const compressed = deflateSync(new TextEncoder().encode(JSON.stringify(input)));
|
|
1334
|
+
return Buffer.from(compressed).toString("base64url");
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Constructs the Kubb Studio URL for the given `InputNode`.
|
|
1338
|
+
* When `options.ast` is `true`, navigates to the AST inspector (`/ast`).
|
|
1339
|
+
* The `input` is encoded and attached as the `?root=` query parameter so Studio
|
|
1340
|
+
* can decode and render it without a round-trip to any server.
|
|
1341
|
+
*/
|
|
1342
|
+
function getStudioUrl(input, studioUrl, options = {}) {
|
|
1343
|
+
return `${studioUrl.replace(/\/$/, "")}${options.ast ? "/ast" : ""}?root=${encodeAst(input)}`;
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Opens the Kubb Studio URL for the given `InputNode` in the default browser —
|
|
1347
|
+
*
|
|
1348
|
+
* Falls back to printing the URL if the browser cannot be launched.
|
|
1349
|
+
*/
|
|
1350
|
+
async function openInStudio(input, studioUrl, options = {}) {
|
|
1351
|
+
const url = getStudioUrl(input, studioUrl, options);
|
|
1352
|
+
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
1353
|
+
const args = process.platform === "win32" ? [
|
|
1354
|
+
"/c",
|
|
1355
|
+
"start",
|
|
1356
|
+
"",
|
|
1357
|
+
url
|
|
1358
|
+
] : [url];
|
|
1359
|
+
try {
|
|
1360
|
+
await x(cmd, args);
|
|
1361
|
+
} catch {
|
|
1362
|
+
console.log(`\n ${url}\n`);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
//#endregion
|
|
1366
|
+
//#region src/FileManager.ts
|
|
1367
|
+
function mergeFile(a, b) {
|
|
1368
|
+
return {
|
|
1369
|
+
...a,
|
|
1370
|
+
sources: [...a.sources || [], ...b.sources || []],
|
|
1371
|
+
imports: [...a.imports || [], ...b.imports || []],
|
|
1372
|
+
exports: [...a.exports || [], ...b.exports || []]
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* In-memory file store for generated files.
|
|
1377
|
+
*
|
|
1378
|
+
* Files with the same `path` are merged — sources, imports, and exports are concatenated.
|
|
1379
|
+
* The `files` getter returns all stored files sorted by path length (shortest first).
|
|
1380
|
+
*
|
|
1381
|
+
* @example
|
|
1382
|
+
* ```ts
|
|
1383
|
+
* import { FileManager } from '@kubb/core'
|
|
1384
|
+
*
|
|
1385
|
+
* const manager = new FileManager()
|
|
1386
|
+
* manager.upsert(myFile)
|
|
1387
|
+
* console.log(manager.files) // all stored files
|
|
1388
|
+
* ```
|
|
1389
|
+
*/
|
|
1390
|
+
var FileManager = class {
|
|
1391
|
+
#cache = /* @__PURE__ */ new Map();
|
|
1392
|
+
#filesCache = null;
|
|
1393
|
+
/**
|
|
1394
|
+
* Adds one or more files. Files with the same path are merged — sources, imports,
|
|
1395
|
+
* and exports from all calls with the same path are concatenated together.
|
|
1396
|
+
*/
|
|
1397
|
+
add(...files) {
|
|
1398
|
+
const resolvedFiles = [];
|
|
1399
|
+
const mergedFiles = /* @__PURE__ */ new Map();
|
|
1400
|
+
files.forEach((file) => {
|
|
1401
|
+
const existing = mergedFiles.get(file.path);
|
|
1402
|
+
if (existing) mergedFiles.set(file.path, mergeFile(existing, file));
|
|
1403
|
+
else mergedFiles.set(file.path, file);
|
|
1404
|
+
});
|
|
1405
|
+
for (const file of mergedFiles.values()) {
|
|
1406
|
+
const resolvedFile = createFile(file);
|
|
1407
|
+
this.#cache.set(resolvedFile.path, resolvedFile);
|
|
1408
|
+
this.#filesCache = null;
|
|
1409
|
+
resolvedFiles.push(resolvedFile);
|
|
1410
|
+
}
|
|
1411
|
+
return resolvedFiles;
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Adds or merges one or more files.
|
|
1415
|
+
* If a file with the same path already exists, its sources/imports/exports are merged together.
|
|
1416
|
+
*/
|
|
1417
|
+
upsert(...files) {
|
|
1418
|
+
const resolvedFiles = [];
|
|
1419
|
+
const mergedFiles = /* @__PURE__ */ new Map();
|
|
1420
|
+
files.forEach((file) => {
|
|
1421
|
+
const existing = mergedFiles.get(file.path);
|
|
1422
|
+
if (existing) mergedFiles.set(file.path, mergeFile(existing, file));
|
|
1423
|
+
else mergedFiles.set(file.path, file);
|
|
1424
|
+
});
|
|
1425
|
+
for (const file of mergedFiles.values()) {
|
|
1426
|
+
const existing = this.#cache.get(file.path);
|
|
1427
|
+
const resolvedFile = createFile(existing ? mergeFile(existing, file) : file);
|
|
1428
|
+
this.#cache.set(resolvedFile.path, resolvedFile);
|
|
1429
|
+
this.#filesCache = null;
|
|
1430
|
+
resolvedFiles.push(resolvedFile);
|
|
1431
|
+
}
|
|
1432
|
+
return resolvedFiles;
|
|
1433
|
+
}
|
|
1434
|
+
getByPath(path) {
|
|
1435
|
+
return this.#cache.get(path) ?? null;
|
|
1436
|
+
}
|
|
1437
|
+
deleteByPath(path) {
|
|
1438
|
+
this.#cache.delete(path);
|
|
1439
|
+
this.#filesCache = null;
|
|
1440
|
+
}
|
|
1441
|
+
clear() {
|
|
1442
|
+
this.#cache.clear();
|
|
1443
|
+
this.#filesCache = null;
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* All stored files, sorted by path length (shorter paths first).
|
|
1447
|
+
* Barrel/index files (e.g. index.ts) are sorted last within each length bucket.
|
|
1448
|
+
*/
|
|
1449
|
+
get files() {
|
|
1450
|
+
if (this.#filesCache) return this.#filesCache;
|
|
1451
|
+
const keys = [...this.#cache.keys()].sort((a, b) => {
|
|
1452
|
+
if (a.length !== b.length) return a.length - b.length;
|
|
1453
|
+
const aIsIndex = trimExtName$1(a).endsWith("index");
|
|
1454
|
+
if (aIsIndex !== trimExtName$1(b).endsWith("index")) return aIsIndex ? 1 : -1;
|
|
1455
|
+
return 0;
|
|
1456
|
+
});
|
|
1457
|
+
const files = [];
|
|
1458
|
+
for (const key of keys) {
|
|
1459
|
+
const file = this.#cache.get(key);
|
|
1460
|
+
if (file) files.push(file);
|
|
1461
|
+
}
|
|
1462
|
+
this.#filesCache = files;
|
|
1463
|
+
return files;
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
1466
|
+
//#endregion
|
|
1467
|
+
//#region src/renderNode.ts
|
|
1468
|
+
/**
|
|
1469
|
+
* Handles the return value of a plugin AST hook or generator method.
|
|
1470
|
+
*
|
|
1471
|
+
* - Renderer output → rendered via the provided `rendererFactory` (e.g. JSX), files stored in `driver.fileManager`
|
|
1472
|
+
* - `Array<FileNode>` → added directly into `driver.fileManager`
|
|
1473
|
+
* - `void` / `null` / `undefined` → no-op (plugin handled it via `this.upsertFile`)
|
|
1474
|
+
*
|
|
1475
|
+
* Pass a `rendererFactory` (e.g. `jsxRenderer` from `@kubb/renderer-jsx`) when the result
|
|
1476
|
+
* may be a renderer element. Generators that only return `Array<FileNode>` do not need one.
|
|
1477
|
+
*/
|
|
1478
|
+
async function applyHookResult(result, driver, rendererFactory) {
|
|
1479
|
+
if (!result) return;
|
|
1480
|
+
if (Array.isArray(result)) {
|
|
1481
|
+
driver.fileManager.upsert(...result);
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
if (!rendererFactory) return;
|
|
1485
|
+
const renderer = rendererFactory();
|
|
1486
|
+
await renderer.render(result);
|
|
1487
|
+
driver.fileManager.upsert(...renderer.files);
|
|
1488
|
+
renderer.unmount();
|
|
1489
|
+
}
|
|
1490
|
+
//#endregion
|
|
1491
|
+
//#region src/utils/executeStrategies.ts
|
|
1492
|
+
/**
|
|
1493
|
+
* Runs promise functions in sequence, threading each result into the next call.
|
|
1494
|
+
*
|
|
1495
|
+
* - Each function receives the accumulated state from the previous call.
|
|
1496
|
+
* - Skips functions that return a falsy value (acts as a no-op for that step).
|
|
1497
|
+
* - Returns an array of all individual results.
|
|
1498
|
+
* @deprecated
|
|
1499
|
+
*/
|
|
1500
|
+
function hookSeq(promises) {
|
|
1501
|
+
return promises.filter(Boolean).reduce((promise, func) => {
|
|
1502
|
+
if (typeof func !== "function") throw new Error("HookSeq needs a function that returns a promise `() => Promise<unknown>`");
|
|
1503
|
+
return promise.then((state) => {
|
|
1504
|
+
const calledFunc = func(state);
|
|
1505
|
+
if (calledFunc) return calledFunc.then(Array.prototype.concat.bind(state));
|
|
1506
|
+
return state;
|
|
1507
|
+
});
|
|
1508
|
+
}, Promise.resolve([]));
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Runs promise functions in sequence and returns the first non-null result.
|
|
1512
|
+
*
|
|
1513
|
+
* - Stops as soon as `nullCheck` passes for a result (default: `!== null`).
|
|
1514
|
+
* - Subsequent functions are skipped once a match is found.
|
|
1515
|
+
* @deprecated
|
|
1516
|
+
*/
|
|
1517
|
+
function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
1518
|
+
let promise = Promise.resolve(null);
|
|
1519
|
+
for (const func of promises.filter(Boolean)) promise = promise.then((state) => {
|
|
1520
|
+
if (nullCheck(state)) return state;
|
|
1521
|
+
return func(state);
|
|
1522
|
+
});
|
|
1523
|
+
return promise;
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Runs promise functions concurrently and returns all settled results.
|
|
1527
|
+
*
|
|
1528
|
+
* - Limits simultaneous executions to `concurrency` (default: unlimited).
|
|
1529
|
+
* - Uses `Promise.allSettled` so individual failures do not cancel other tasks.
|
|
1530
|
+
* @deprecated
|
|
1531
|
+
*/
|
|
1532
|
+
function hookParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
1533
|
+
const limit = pLimit(concurrency);
|
|
1534
|
+
const tasks = promises.filter(Boolean).map((promise) => limit(() => promise()));
|
|
1535
|
+
return Promise.allSettled(tasks);
|
|
1536
|
+
}
|
|
1537
|
+
//#endregion
|
|
1538
|
+
//#region src/PluginDriver.ts
|
|
1539
|
+
/**
|
|
1540
|
+
* Returns `'single'` when `fileOrFolder` has a file extension, `'split'` otherwise.
|
|
1541
|
+
*
|
|
1542
|
+
* @example
|
|
1543
|
+
* ```ts
|
|
1544
|
+
* getMode('src/gen/types.ts') // 'single'
|
|
1545
|
+
* getMode('src/gen/types') // 'split'
|
|
1546
|
+
* ```
|
|
1547
|
+
*/
|
|
1548
|
+
function getMode(fileOrFolder) {
|
|
1549
|
+
if (!fileOrFolder) return "split";
|
|
1550
|
+
return extname(fileOrFolder) ? "single" : "split";
|
|
1551
|
+
}
|
|
1552
|
+
const hookFirstNullCheck = (state) => !!state?.result;
|
|
1553
|
+
var PluginDriver = class {
|
|
1103
1554
|
config;
|
|
1104
1555
|
options;
|
|
1105
1556
|
/**
|
|
1106
1557
|
* The universal `@kubb/ast` `InputNode` produced by the adapter, set by
|
|
1107
1558
|
* the build pipeline after the adapter's `parse()` resolves.
|
|
1108
1559
|
*/
|
|
1109
|
-
inputNode = void 0;
|
|
1110
|
-
adapter = void 0;
|
|
1111
|
-
#studioIsOpen = false;
|
|
1560
|
+
inputNode = void 0;
|
|
1561
|
+
adapter = void 0;
|
|
1562
|
+
#studioIsOpen = false;
|
|
1563
|
+
/**
|
|
1564
|
+
* Central file store for all generated files.
|
|
1565
|
+
* Plugins should use `this.addFile()` / `this.upsertFile()` (via their context) to
|
|
1566
|
+
* add files; this property gives direct read/write access when needed.
|
|
1567
|
+
*/
|
|
1568
|
+
fileManager = new FileManager();
|
|
1569
|
+
plugins = /* @__PURE__ */ new Map();
|
|
1570
|
+
/**
|
|
1571
|
+
* Tracks which plugins have generators registered via `addGenerator()` (event-based path).
|
|
1572
|
+
* Used by the build loop to decide whether to emit generator events for a given plugin.
|
|
1573
|
+
*/
|
|
1574
|
+
#pluginsWithEventGenerators = /* @__PURE__ */ new Set();
|
|
1575
|
+
#resolvers = /* @__PURE__ */ new Map();
|
|
1576
|
+
#defaultResolvers = /* @__PURE__ */ new Map();
|
|
1577
|
+
#hookListeners = /* @__PURE__ */ new Map();
|
|
1578
|
+
constructor(config, options) {
|
|
1579
|
+
this.config = config;
|
|
1580
|
+
this.options = {
|
|
1581
|
+
...options,
|
|
1582
|
+
hooks: options.hooks
|
|
1583
|
+
};
|
|
1584
|
+
config.plugins.map((rawPlugin) => {
|
|
1585
|
+
if (isHookStylePlugin(rawPlugin)) return this.#normalizeHookStylePlugin(rawPlugin);
|
|
1586
|
+
return {
|
|
1587
|
+
...rawPlugin,
|
|
1588
|
+
buildStart: rawPlugin.buildStart ?? (() => {}),
|
|
1589
|
+
buildEnd: rawPlugin.buildEnd ?? (() => {})
|
|
1590
|
+
};
|
|
1591
|
+
}).filter((plugin) => {
|
|
1592
|
+
if (typeof plugin.apply === "function") return plugin.apply(config);
|
|
1593
|
+
return true;
|
|
1594
|
+
}).sort((a, b) => {
|
|
1595
|
+
if (b.dependencies?.includes(a.name)) return -1;
|
|
1596
|
+
if (a.dependencies?.includes(b.name)) return 1;
|
|
1597
|
+
return 0;
|
|
1598
|
+
}).forEach((plugin) => {
|
|
1599
|
+
this.plugins.set(plugin.name, plugin);
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
get hooks() {
|
|
1603
|
+
if (!this.options.hooks) throw new Error("hooks are not defined");
|
|
1604
|
+
return this.options.hooks;
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Creates a `Plugin`-compatible object from a hook-style plugin and registers
|
|
1608
|
+
* its lifecycle handlers on the `AsyncEventEmitter`.
|
|
1609
|
+
*
|
|
1610
|
+
* The normalized plugin has an empty `buildStart` — generators registered via
|
|
1611
|
+
* `addGenerator()` in `kubb:plugin:setup` are stored on `normalizedPlugin.generators`
|
|
1612
|
+
* and used by `runPluginAstHooks` during the build.
|
|
1613
|
+
*/
|
|
1614
|
+
#normalizeHookStylePlugin(hookPlugin) {
|
|
1615
|
+
const generators = [];
|
|
1616
|
+
const driver = this;
|
|
1617
|
+
const normalizedPlugin = {
|
|
1618
|
+
name: hookPlugin.name,
|
|
1619
|
+
dependencies: hookPlugin.dependencies,
|
|
1620
|
+
options: {
|
|
1621
|
+
output: { path: "." },
|
|
1622
|
+
exclude: [],
|
|
1623
|
+
override: []
|
|
1624
|
+
},
|
|
1625
|
+
generators,
|
|
1626
|
+
inject: () => void 0,
|
|
1627
|
+
resolveName(name, type) {
|
|
1628
|
+
return driver.getResolver(hookPlugin.name).default(name, type);
|
|
1629
|
+
},
|
|
1630
|
+
resolvePath(baseName, pathMode, resolveOptions) {
|
|
1631
|
+
const resolver = driver.getResolver(hookPlugin.name);
|
|
1632
|
+
const opts = normalizedPlugin.options;
|
|
1633
|
+
const group = resolveOptions?.group;
|
|
1634
|
+
return resolver.resolvePath({
|
|
1635
|
+
baseName,
|
|
1636
|
+
pathMode,
|
|
1637
|
+
tag: group?.tag,
|
|
1638
|
+
path: group?.path
|
|
1639
|
+
}, {
|
|
1640
|
+
root: resolve(driver.config.root, driver.config.output.path),
|
|
1641
|
+
output: opts.output,
|
|
1642
|
+
group: opts.group
|
|
1643
|
+
});
|
|
1644
|
+
},
|
|
1645
|
+
buildStart() {},
|
|
1646
|
+
buildEnd() {}
|
|
1647
|
+
};
|
|
1648
|
+
this.registerPluginHooks(hookPlugin, normalizedPlugin);
|
|
1649
|
+
return normalizedPlugin;
|
|
1650
|
+
}
|
|
1651
|
+
/**
|
|
1652
|
+
* Registers a hook-style plugin's lifecycle handlers on the shared `AsyncEventEmitter`.
|
|
1653
|
+
*
|
|
1654
|
+
* For `kubb:plugin:setup`, the registered listener wraps the globally emitted context with a
|
|
1655
|
+
* plugin-specific one so that `addGenerator`, `setResolver`, `setTransformer`, and
|
|
1656
|
+
* `setRenderer` all target the correct `normalizedPlugin` entry in the plugins map.
|
|
1657
|
+
*
|
|
1658
|
+
* All other hooks are iterated and registered directly as pass-through listeners.
|
|
1659
|
+
* Any event key present in the global `KubbHooks` interface can be subscribed to.
|
|
1660
|
+
*
|
|
1661
|
+
* External tooling can subscribe to any of these events via `hooks.on(...)` to observe
|
|
1662
|
+
* the plugin lifecycle without modifying plugin behavior.
|
|
1663
|
+
*/
|
|
1664
|
+
registerPluginHooks(hookPlugin, normalizedPlugin) {
|
|
1665
|
+
const { hooks } = hookPlugin;
|
|
1666
|
+
if (hooks["kubb:plugin:setup"]) {
|
|
1667
|
+
const setupHandler = (globalCtx) => {
|
|
1668
|
+
const pluginCtx = {
|
|
1669
|
+
...globalCtx,
|
|
1670
|
+
options: hookPlugin.options ?? {},
|
|
1671
|
+
addGenerator: (gen) => {
|
|
1672
|
+
this.registerGenerator(normalizedPlugin.name, gen);
|
|
1673
|
+
},
|
|
1674
|
+
setResolver: (resolver) => {
|
|
1675
|
+
this.setPluginResolver(normalizedPlugin.name, resolver);
|
|
1676
|
+
},
|
|
1677
|
+
setTransformer: (visitor) => {
|
|
1678
|
+
normalizedPlugin.transformer = visitor;
|
|
1679
|
+
},
|
|
1680
|
+
setRenderer: (renderer) => {
|
|
1681
|
+
normalizedPlugin.renderer = renderer;
|
|
1682
|
+
},
|
|
1683
|
+
setOptions: (opts) => {
|
|
1684
|
+
normalizedPlugin.options = {
|
|
1685
|
+
...normalizedPlugin.options,
|
|
1686
|
+
...opts
|
|
1687
|
+
};
|
|
1688
|
+
},
|
|
1689
|
+
injectFile: (file) => {
|
|
1690
|
+
const fileNode = createFile({
|
|
1691
|
+
baseName: file.baseName,
|
|
1692
|
+
path: file.path,
|
|
1693
|
+
sources: file.sources ?? [],
|
|
1694
|
+
imports: [],
|
|
1695
|
+
exports: []
|
|
1696
|
+
});
|
|
1697
|
+
this.fileManager.add(fileNode);
|
|
1698
|
+
}
|
|
1699
|
+
};
|
|
1700
|
+
return hooks["kubb:plugin:setup"](pluginCtx);
|
|
1701
|
+
};
|
|
1702
|
+
this.hooks.on("kubb:plugin:setup", setupHandler);
|
|
1703
|
+
this.#trackHookListener("kubb:plugin:setup", setupHandler);
|
|
1704
|
+
}
|
|
1705
|
+
for (const [event, handler] of Object.entries(hooks)) {
|
|
1706
|
+
if (event === "kubb:plugin:setup" || !handler) continue;
|
|
1707
|
+
this.hooks.on(event, handler);
|
|
1708
|
+
this.#trackHookListener(event, handler);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1112
1711
|
/**
|
|
1113
|
-
*
|
|
1114
|
-
*
|
|
1115
|
-
*
|
|
1712
|
+
* Emits the `kubb:plugin:setup` event so that all registered hook-style plugin listeners
|
|
1713
|
+
* can configure generators, resolvers, transformers and renderers before `buildStart` runs.
|
|
1714
|
+
*
|
|
1715
|
+
* Call this once from `safeBuild` before the plugin execution loop begins.
|
|
1116
1716
|
*/
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
}).sort((a, b) => {
|
|
1129
|
-
if (b.pre?.includes(a.name)) return 1;
|
|
1130
|
-
if (b.post?.includes(a.name)) return -1;
|
|
1131
|
-
return 0;
|
|
1132
|
-
}).forEach((plugin) => {
|
|
1133
|
-
this.plugins.set(plugin.name, plugin);
|
|
1717
|
+
async emitSetupHooks() {
|
|
1718
|
+
await this.hooks.emit("kubb:plugin:setup", {
|
|
1719
|
+
config: this.config,
|
|
1720
|
+
addGenerator: () => {},
|
|
1721
|
+
setResolver: () => {},
|
|
1722
|
+
setTransformer: () => {},
|
|
1723
|
+
setRenderer: () => {},
|
|
1724
|
+
setOptions: () => {},
|
|
1725
|
+
injectFile: () => {},
|
|
1726
|
+
updateConfig: () => {},
|
|
1727
|
+
options: {}
|
|
1134
1728
|
});
|
|
1135
1729
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1730
|
+
/**
|
|
1731
|
+
* Registers a generator for the given plugin on the shared event emitter.
|
|
1732
|
+
*
|
|
1733
|
+
* The generator's `schema`, `operation`, and `operations` methods are registered as
|
|
1734
|
+
* listeners on `kubb:generate:schema`, `kubb:generate:operation`, and `kubb:generate:operations`
|
|
1735
|
+
* respectively. Each listener is scoped to the owning plugin via a `ctx.plugin.name` check
|
|
1736
|
+
* so that generators from different plugins do not cross-fire.
|
|
1737
|
+
*
|
|
1738
|
+
* The renderer resolution chain is: `generator.renderer → plugin.renderer → config.renderer`.
|
|
1739
|
+
* Set `generator.renderer = null` to explicitly opt out of rendering even when the plugin
|
|
1740
|
+
* declares a renderer.
|
|
1741
|
+
*
|
|
1742
|
+
* Call this method inside `addGenerator()` (in `kubb:plugin:setup`) to wire up a generator.
|
|
1743
|
+
*/
|
|
1744
|
+
registerGenerator(pluginName, gen) {
|
|
1745
|
+
const resolveRenderer = () => {
|
|
1746
|
+
const plugin = this.plugins.get(pluginName);
|
|
1747
|
+
return gen.renderer === null ? void 0 : gen.renderer ?? plugin?.renderer ?? this.config.renderer;
|
|
1748
|
+
};
|
|
1749
|
+
if (gen.schema) {
|
|
1750
|
+
const schemaHandler = async (node, ctx) => {
|
|
1751
|
+
if (ctx.plugin.name !== pluginName) return;
|
|
1752
|
+
await applyHookResult(await gen.schema(node, ctx), this, resolveRenderer());
|
|
1753
|
+
};
|
|
1754
|
+
this.hooks.on("kubb:generate:schema", schemaHandler);
|
|
1755
|
+
this.#trackHookListener("kubb:generate:schema", schemaHandler);
|
|
1756
|
+
}
|
|
1757
|
+
if (gen.operation) {
|
|
1758
|
+
const operationHandler = async (node, ctx) => {
|
|
1759
|
+
if (ctx.plugin.name !== pluginName) return;
|
|
1760
|
+
await applyHookResult(await gen.operation(node, ctx), this, resolveRenderer());
|
|
1761
|
+
};
|
|
1762
|
+
this.hooks.on("kubb:generate:operation", operationHandler);
|
|
1763
|
+
this.#trackHookListener("kubb:generate:operation", operationHandler);
|
|
1764
|
+
}
|
|
1765
|
+
if (gen.operations) {
|
|
1766
|
+
const operationsHandler = async (nodes, ctx) => {
|
|
1767
|
+
if (ctx.plugin.name !== pluginName) return;
|
|
1768
|
+
await applyHookResult(await gen.operations(nodes, ctx), this, resolveRenderer());
|
|
1769
|
+
};
|
|
1770
|
+
this.hooks.on("kubb:generate:operations", operationsHandler);
|
|
1771
|
+
this.#trackHookListener("kubb:generate:operations", operationsHandler);
|
|
1772
|
+
}
|
|
1773
|
+
this.#pluginsWithEventGenerators.add(pluginName);
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Returns `true` when at least one generator was registered for the given plugin
|
|
1777
|
+
* via `addGenerator()` in `kubb:plugin:setup` (event-based path).
|
|
1778
|
+
*
|
|
1779
|
+
* Used by the build loop to decide whether to walk the AST and emit generator events
|
|
1780
|
+
* for a plugin that has no static `plugin.generators`.
|
|
1781
|
+
*/
|
|
1782
|
+
hasRegisteredGenerators(pluginName) {
|
|
1783
|
+
return this.#pluginsWithEventGenerators.has(pluginName);
|
|
1784
|
+
}
|
|
1785
|
+
dispose() {
|
|
1786
|
+
for (const [event, handlers] of this.#hookListeners) for (const handler of handlers) this.hooks.off(event, handler);
|
|
1787
|
+
this.#hookListeners.clear();
|
|
1788
|
+
this.#pluginsWithEventGenerators.clear();
|
|
1789
|
+
}
|
|
1790
|
+
#trackHookListener(event, handler) {
|
|
1791
|
+
let handlers = this.#hookListeners.get(event);
|
|
1792
|
+
if (!handlers) {
|
|
1793
|
+
handlers = /* @__PURE__ */ new Set();
|
|
1794
|
+
this.#hookListeners.set(event, handlers);
|
|
1795
|
+
}
|
|
1796
|
+
handlers.add(handler);
|
|
1797
|
+
}
|
|
1798
|
+
#createDefaultResolver(pluginName) {
|
|
1799
|
+
const existingResolver = this.#defaultResolvers.get(pluginName);
|
|
1800
|
+
if (existingResolver) return existingResolver;
|
|
1801
|
+
const resolver = defineResolver(() => ({
|
|
1802
|
+
name: "default",
|
|
1803
|
+
pluginName
|
|
1804
|
+
}));
|
|
1805
|
+
this.#defaultResolvers.set(pluginName, resolver);
|
|
1806
|
+
return resolver;
|
|
1807
|
+
}
|
|
1808
|
+
setPluginResolver(pluginName, partial) {
|
|
1809
|
+
const merged = {
|
|
1810
|
+
...this.#createDefaultResolver(pluginName),
|
|
1811
|
+
...partial
|
|
1812
|
+
};
|
|
1813
|
+
this.#resolvers.set(pluginName, merged);
|
|
1814
|
+
const plugin = this.plugins.get(pluginName);
|
|
1815
|
+
if (plugin) plugin.resolver = merged;
|
|
1816
|
+
}
|
|
1817
|
+
getResolver(pluginName) {
|
|
1818
|
+
const dynamicResolver = this.#resolvers.get(pluginName);
|
|
1819
|
+
if (dynamicResolver) return dynamicResolver;
|
|
1820
|
+
const pluginResolver = this.plugins.get(pluginName)?.resolver;
|
|
1821
|
+
if (pluginResolver) return pluginResolver;
|
|
1822
|
+
return this.#createDefaultResolver(pluginName);
|
|
1138
1823
|
}
|
|
1139
1824
|
getContext(plugin) {
|
|
1140
1825
|
const driver = this;
|
|
@@ -1146,7 +1831,7 @@ var PluginDriver = class {
|
|
|
1146
1831
|
getMode(output) {
|
|
1147
1832
|
return getMode(resolve(driver.config.root, driver.config.output.path, output.path));
|
|
1148
1833
|
},
|
|
1149
|
-
|
|
1834
|
+
hooks: driver.hooks,
|
|
1150
1835
|
plugin,
|
|
1151
1836
|
getPlugin: driver.getPlugin.bind(driver),
|
|
1152
1837
|
requirePlugin: driver.requirePlugin.bind(driver),
|
|
@@ -1164,19 +1849,19 @@ var PluginDriver = class {
|
|
|
1164
1849
|
return driver.adapter;
|
|
1165
1850
|
},
|
|
1166
1851
|
get resolver() {
|
|
1167
|
-
return plugin.
|
|
1852
|
+
return driver.getResolver(plugin.name);
|
|
1168
1853
|
},
|
|
1169
1854
|
get transformer() {
|
|
1170
1855
|
return plugin.transformer;
|
|
1171
1856
|
},
|
|
1172
1857
|
warn(message) {
|
|
1173
|
-
driver.
|
|
1858
|
+
driver.hooks.emit("kubb:warn", message);
|
|
1174
1859
|
},
|
|
1175
1860
|
error(error) {
|
|
1176
|
-
driver.
|
|
1861
|
+
driver.hooks.emit("kubb:error", typeof error === "string" ? new Error(error) : error);
|
|
1177
1862
|
},
|
|
1178
1863
|
info(message) {
|
|
1179
|
-
driver.
|
|
1864
|
+
driver.hooks.emit("kubb:info", message);
|
|
1180
1865
|
},
|
|
1181
1866
|
openInStudio(options) {
|
|
1182
1867
|
if (!driver.config.devtools || driver.#studioIsOpen) return;
|
|
@@ -1187,10 +1872,13 @@ var PluginDriver = class {
|
|
|
1187
1872
|
return openInStudio(driver.inputNode, studioUrl, options);
|
|
1188
1873
|
}
|
|
1189
1874
|
};
|
|
1190
|
-
|
|
1875
|
+
let mergedExtras = {};
|
|
1191
1876
|
for (const p of this.plugins.values()) if (typeof p.inject === "function") {
|
|
1192
1877
|
const result = p.inject.call(baseContext);
|
|
1193
|
-
if (result !== null && typeof result === "object")
|
|
1878
|
+
if (result !== null && typeof result === "object") mergedExtras = {
|
|
1879
|
+
...mergedExtras,
|
|
1880
|
+
...result
|
|
1881
|
+
};
|
|
1194
1882
|
}
|
|
1195
1883
|
return {
|
|
1196
1884
|
...baseContext,
|
|
@@ -1266,7 +1954,7 @@ var PluginDriver = class {
|
|
|
1266
1954
|
async hookForPlugin({ pluginName, hookName, parameters }) {
|
|
1267
1955
|
const plugin = this.plugins.get(pluginName);
|
|
1268
1956
|
if (!plugin) return [null];
|
|
1269
|
-
this.
|
|
1957
|
+
this.hooks.emit("kubb:plugins:hook:progress:start", {
|
|
1270
1958
|
hookName,
|
|
1271
1959
|
plugins: [plugin]
|
|
1272
1960
|
});
|
|
@@ -1276,7 +1964,7 @@ var PluginDriver = class {
|
|
|
1276
1964
|
parameters,
|
|
1277
1965
|
plugin
|
|
1278
1966
|
});
|
|
1279
|
-
this.
|
|
1967
|
+
this.hooks.emit("kubb:plugins:hook:progress:end", { hookName });
|
|
1280
1968
|
return [result];
|
|
1281
1969
|
}
|
|
1282
1970
|
/**
|
|
@@ -1299,7 +1987,7 @@ var PluginDriver = class {
|
|
|
1299
1987
|
async hookFirst({ hookName, parameters, skipped }) {
|
|
1300
1988
|
const plugins = [];
|
|
1301
1989
|
for (const plugin of this.plugins.values()) if (hookName in plugin && (skipped ? !skipped.has(plugin) : true)) plugins.push(plugin);
|
|
1302
|
-
this.
|
|
1990
|
+
this.hooks.emit("kubb:plugins:hook:progress:start", {
|
|
1303
1991
|
hookName,
|
|
1304
1992
|
plugins
|
|
1305
1993
|
});
|
|
@@ -1317,7 +2005,7 @@ var PluginDriver = class {
|
|
|
1317
2005
|
});
|
|
1318
2006
|
};
|
|
1319
2007
|
}), hookFirstNullCheck);
|
|
1320
|
-
this.
|
|
2008
|
+
this.hooks.emit("kubb:plugins:hook:progress:end", { hookName });
|
|
1321
2009
|
return result;
|
|
1322
2010
|
}
|
|
1323
2011
|
/**
|
|
@@ -1342,12 +2030,12 @@ var PluginDriver = class {
|
|
|
1342
2030
|
return parseResult;
|
|
1343
2031
|
}
|
|
1344
2032
|
/**
|
|
1345
|
-
* Runs all plugins in parallel based on `this.plugin` order and `
|
|
2033
|
+
* Runs all plugins in parallel based on `this.plugin` order and `dependencies` settings.
|
|
1346
2034
|
*/
|
|
1347
2035
|
async hookParallel({ hookName, parameters }) {
|
|
1348
2036
|
const plugins = [];
|
|
1349
2037
|
for (const plugin of this.plugins.values()) if (hookName in plugin) plugins.push(plugin);
|
|
1350
|
-
this.
|
|
2038
|
+
this.hooks.emit("kubb:plugins:hook:progress:start", {
|
|
1351
2039
|
hookName,
|
|
1352
2040
|
plugins
|
|
1353
2041
|
});
|
|
@@ -1368,7 +2056,7 @@ var PluginDriver = class {
|
|
|
1368
2056
|
const plugin = plugins[index];
|
|
1369
2057
|
if (plugin) {
|
|
1370
2058
|
const startTime = pluginStartTimes.get(plugin) ?? performance.now();
|
|
1371
|
-
this.
|
|
2059
|
+
this.hooks.emit("kubb:error", result.reason, {
|
|
1372
2060
|
plugin,
|
|
1373
2061
|
hookName,
|
|
1374
2062
|
strategy: "hookParallel",
|
|
@@ -1378,19 +2066,19 @@ var PluginDriver = class {
|
|
|
1378
2066
|
}
|
|
1379
2067
|
}
|
|
1380
2068
|
});
|
|
1381
|
-
this.
|
|
2069
|
+
this.hooks.emit("kubb:plugins:hook:progress:end", { hookName });
|
|
1382
2070
|
return results.reduce((acc, result) => {
|
|
1383
2071
|
if (result.status === "fulfilled") acc.push(result.value);
|
|
1384
2072
|
return acc;
|
|
1385
2073
|
}, []);
|
|
1386
2074
|
}
|
|
1387
2075
|
/**
|
|
1388
|
-
*
|
|
2076
|
+
* Execute a lifecycle hook sequentially for all plugins that implement it.
|
|
1389
2077
|
*/
|
|
1390
2078
|
async hookSeq({ hookName, parameters }) {
|
|
1391
2079
|
const plugins = [];
|
|
1392
2080
|
for (const plugin of this.plugins.values()) if (hookName in plugin) plugins.push(plugin);
|
|
1393
|
-
this.
|
|
2081
|
+
this.hooks.emit("kubb:plugins:hook:progress:start", {
|
|
1394
2082
|
hookName,
|
|
1395
2083
|
plugins
|
|
1396
2084
|
});
|
|
@@ -1402,7 +2090,7 @@ var PluginDriver = class {
|
|
|
1402
2090
|
plugin
|
|
1403
2091
|
});
|
|
1404
2092
|
}));
|
|
1405
|
-
this.
|
|
2093
|
+
this.hooks.emit("kubb:plugins:hook:progress:end", { hookName });
|
|
1406
2094
|
}
|
|
1407
2095
|
getPlugin(pluginName) {
|
|
1408
2096
|
return this.plugins.get(pluginName);
|
|
@@ -1413,13 +2101,10 @@ var PluginDriver = class {
|
|
|
1413
2101
|
return plugin;
|
|
1414
2102
|
}
|
|
1415
2103
|
/**
|
|
1416
|
-
*
|
|
1417
|
-
* @param hookName Name of the plugin hook. Must be either in `PluginHooks` or `OutputPluginValueHooks`.
|
|
1418
|
-
* @param args Arguments passed to the plugin hook.
|
|
1419
|
-
* @param plugin The actual pluginObject to run.
|
|
2104
|
+
* Emit hook-processing completion metadata after a plugin hook resolves.
|
|
1420
2105
|
*/
|
|
1421
2106
|
#emitProcessingEnd({ startTime, output, strategy, hookName, plugin, parameters }) {
|
|
1422
|
-
this.
|
|
2107
|
+
this.hooks.emit("kubb:plugins:hook:processing:end", {
|
|
1423
2108
|
duration: Math.round(performance.now() - startTime),
|
|
1424
2109
|
parameters,
|
|
1425
2110
|
output,
|
|
@@ -1431,7 +2116,7 @@ var PluginDriver = class {
|
|
|
1431
2116
|
#execute({ strategy, hookName, parameters, plugin }) {
|
|
1432
2117
|
const hook = plugin[hookName];
|
|
1433
2118
|
if (!hook) return null;
|
|
1434
|
-
this.
|
|
2119
|
+
this.hooks.emit("kubb:plugins:hook:processing:start", {
|
|
1435
2120
|
strategy,
|
|
1436
2121
|
hookName,
|
|
1437
2122
|
parameters,
|
|
@@ -1451,7 +2136,7 @@ var PluginDriver = class {
|
|
|
1451
2136
|
});
|
|
1452
2137
|
return output;
|
|
1453
2138
|
} catch (error) {
|
|
1454
|
-
this.
|
|
2139
|
+
this.hooks.emit("kubb:error", error, {
|
|
1455
2140
|
plugin,
|
|
1456
2141
|
hookName,
|
|
1457
2142
|
strategy,
|
|
@@ -1462,15 +2147,12 @@ var PluginDriver = class {
|
|
|
1462
2147
|
})();
|
|
1463
2148
|
}
|
|
1464
2149
|
/**
|
|
1465
|
-
*
|
|
1466
|
-
* @param hookName Name of the plugin hook. Must be in `PluginHooks`.
|
|
1467
|
-
* @param args Arguments passed to the plugin hook.
|
|
1468
|
-
* @param plugin The actual plugin
|
|
2150
|
+
* Execute a plugin lifecycle hook synchronously and return its output.
|
|
1469
2151
|
*/
|
|
1470
2152
|
#executeSync({ strategy, hookName, parameters, plugin }) {
|
|
1471
2153
|
const hook = plugin[hookName];
|
|
1472
2154
|
if (!hook) return null;
|
|
1473
|
-
this.
|
|
2155
|
+
this.hooks.emit("kubb:plugins:hook:processing:start", {
|
|
1474
2156
|
strategy,
|
|
1475
2157
|
hookName,
|
|
1476
2158
|
parameters,
|
|
@@ -1489,7 +2171,7 @@ var PluginDriver = class {
|
|
|
1489
2171
|
});
|
|
1490
2172
|
return output;
|
|
1491
2173
|
} catch (error) {
|
|
1492
|
-
this.
|
|
2174
|
+
this.hooks.emit("kubb:error", error, {
|
|
1493
2175
|
plugin,
|
|
1494
2176
|
hookName,
|
|
1495
2177
|
strategy,
|
|
@@ -1500,26 +2182,6 @@ var PluginDriver = class {
|
|
|
1500
2182
|
}
|
|
1501
2183
|
};
|
|
1502
2184
|
//#endregion
|
|
1503
|
-
//#region src/renderNode.tsx
|
|
1504
|
-
/**
|
|
1505
|
-
* Handles the return value of a plugin AST hook or generator method.
|
|
1506
|
-
*
|
|
1507
|
-
* - React element → rendered via renderer-jsx, files stored in `driver.fileManager`
|
|
1508
|
-
* - `Array<FileNode>` → upserted directly into `driver.fileManager`
|
|
1509
|
-
* - `void` / `null` / `undefined` → no-op (plugin handled it via `this.upsertFile`)
|
|
1510
|
-
*/
|
|
1511
|
-
async function applyHookResult(result, driver) {
|
|
1512
|
-
if (!result) return;
|
|
1513
|
-
if (Array.isArray(result)) {
|
|
1514
|
-
driver.fileManager.upsert(...result);
|
|
1515
|
-
return;
|
|
1516
|
-
}
|
|
1517
|
-
const renderer = createRenderer();
|
|
1518
|
-
await renderer.render(/* @__PURE__ */ jsx(Fragment, { children: result }));
|
|
1519
|
-
driver.fileManager.upsert(...renderer.files);
|
|
1520
|
-
renderer.unmount();
|
|
1521
|
-
}
|
|
1522
|
-
//#endregion
|
|
1523
2185
|
//#region src/createStorage.ts
|
|
1524
2186
|
/**
|
|
1525
2187
|
* Creates a storage factory. Call the returned function with optional options to get the storage instance.
|
|
@@ -1617,7 +2279,7 @@ const fsStorage = createStorage(() => ({
|
|
|
1617
2279
|
}));
|
|
1618
2280
|
//#endregion
|
|
1619
2281
|
//#region package.json
|
|
1620
|
-
var version$1 = "5.0.0-alpha.
|
|
2282
|
+
var version$1 = "5.0.0-alpha.35";
|
|
1621
2283
|
//#endregion
|
|
1622
2284
|
//#region src/utils/diagnostics.ts
|
|
1623
2285
|
/**
|
|
@@ -1878,24 +2540,14 @@ function isInputPath(config) {
|
|
|
1878
2540
|
return typeof config?.input === "object" && config.input !== null && "path" in config.input;
|
|
1879
2541
|
}
|
|
1880
2542
|
//#endregion
|
|
1881
|
-
//#region src/
|
|
1882
|
-
/**
|
|
1883
|
-
* Initializes all Kubb infrastructure for a build without executing any plugins.
|
|
1884
|
-
*
|
|
1885
|
-
* - Validates the input path (when applicable).
|
|
1886
|
-
* - Applies config defaults (`root`, `output.*`, `devtools`).
|
|
1887
|
-
* - Runs the adapter (if configured) to produce the universal `InputNode`.
|
|
1888
|
-
* When no adapter is supplied and `@kubb/adapter-oas` is installed as an
|
|
1889
|
-
*
|
|
1890
|
-
* Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
|
|
1891
|
-
* via the `overrides` argument to reuse the same infrastructure across multiple runs.
|
|
1892
|
-
*/
|
|
2543
|
+
//#region src/createKubb.ts
|
|
1893
2544
|
async function setup(options) {
|
|
1894
|
-
const { config: userConfig
|
|
2545
|
+
const { config: userConfig } = options;
|
|
2546
|
+
const hooks = options.hooks ?? new AsyncEventEmitter();
|
|
1895
2547
|
const sources = /* @__PURE__ */ new Map();
|
|
1896
2548
|
const diagnosticInfo = getDiagnosticInfo();
|
|
1897
|
-
if (Array.isArray(userConfig.input)) await
|
|
1898
|
-
await
|
|
2549
|
+
if (Array.isArray(userConfig.input)) await hooks.emit("kubb:warn", "This feature is still under development — use with caution");
|
|
2550
|
+
await hooks.emit("kubb:debug", {
|
|
1899
2551
|
date: /* @__PURE__ */ new Date(),
|
|
1900
2552
|
logs: [
|
|
1901
2553
|
"Configuration:",
|
|
@@ -1914,7 +2566,7 @@ async function setup(options) {
|
|
|
1914
2566
|
try {
|
|
1915
2567
|
if (isInputPath(userConfig) && !new URLPath(userConfig.input.path).isURL) {
|
|
1916
2568
|
await exists(userConfig.input.path);
|
|
1917
|
-
await
|
|
2569
|
+
await hooks.emit("kubb:debug", {
|
|
1918
2570
|
date: /* @__PURE__ */ new Date(),
|
|
1919
2571
|
logs: [`✓ Input file validated: ${userConfig.input.path}`]
|
|
1920
2572
|
});
|
|
@@ -1927,8 +2579,8 @@ async function setup(options) {
|
|
|
1927
2579
|
}
|
|
1928
2580
|
if (!userConfig.adapter) throw new Error("Adapter should be defined");
|
|
1929
2581
|
const config = {
|
|
1930
|
-
root: userConfig.root || process.cwd(),
|
|
1931
2582
|
...userConfig,
|
|
2583
|
+
root: userConfig.root || process.cwd(),
|
|
1932
2584
|
parsers: userConfig.parsers ?? [],
|
|
1933
2585
|
adapter: userConfig.adapter,
|
|
1934
2586
|
output: {
|
|
@@ -1946,26 +2598,26 @@ async function setup(options) {
|
|
|
1946
2598
|
};
|
|
1947
2599
|
const storage = config.output.write === false ? null : config.output.storage ?? fsStorage();
|
|
1948
2600
|
if (config.output.clean) {
|
|
1949
|
-
await
|
|
2601
|
+
await hooks.emit("kubb:debug", {
|
|
1950
2602
|
date: /* @__PURE__ */ new Date(),
|
|
1951
2603
|
logs: ["Cleaning output directories", ` • Output: ${config.output.path}`]
|
|
1952
2604
|
});
|
|
1953
2605
|
await storage?.clear(resolve(config.root, config.output.path));
|
|
1954
2606
|
}
|
|
1955
2607
|
const driver = new PluginDriver(config, {
|
|
1956
|
-
|
|
2608
|
+
hooks,
|
|
1957
2609
|
concurrency: 15
|
|
1958
2610
|
});
|
|
1959
2611
|
const adapter = config.adapter;
|
|
1960
2612
|
if (!adapter) throw new Error("No adapter configured. Please provide an adapter in your kubb.config.ts.");
|
|
1961
2613
|
const source = inputToAdapterSource(config);
|
|
1962
|
-
await
|
|
2614
|
+
await hooks.emit("kubb:debug", {
|
|
1963
2615
|
date: /* @__PURE__ */ new Date(),
|
|
1964
2616
|
logs: [`Running adapter: ${adapter.name}`]
|
|
1965
2617
|
});
|
|
1966
2618
|
driver.adapter = adapter;
|
|
1967
2619
|
driver.inputNode = await adapter.parse(source);
|
|
1968
|
-
await
|
|
2620
|
+
await hooks.emit("kubb:debug", {
|
|
1969
2621
|
date: /* @__PURE__ */ new Date(),
|
|
1970
2622
|
logs: [
|
|
1971
2623
|
`✓ Adapter '${adapter.name}' resolved InputNode`,
|
|
@@ -1975,53 +2627,32 @@ async function setup(options) {
|
|
|
1975
2627
|
});
|
|
1976
2628
|
return {
|
|
1977
2629
|
config,
|
|
1978
|
-
|
|
2630
|
+
hooks,
|
|
1979
2631
|
driver,
|
|
1980
2632
|
sources,
|
|
1981
2633
|
storage
|
|
1982
2634
|
};
|
|
1983
2635
|
}
|
|
1984
2636
|
/**
|
|
1985
|
-
* Runs a full Kubb build and throws on any error or plugin failure.
|
|
1986
|
-
*
|
|
1987
|
-
* Internally delegates to {@link safeBuild} and rethrows collected errors.
|
|
1988
|
-
* Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
|
|
1989
|
-
*/
|
|
1990
|
-
async function build(options, overrides) {
|
|
1991
|
-
const { files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides);
|
|
1992
|
-
if (error) throw error;
|
|
1993
|
-
if (failedPlugins.size > 0) {
|
|
1994
|
-
const errors = [...failedPlugins].map(({ error }) => error);
|
|
1995
|
-
throw new BuildError(`Build Error with ${failedPlugins.size} failed plugins`, { errors });
|
|
1996
|
-
}
|
|
1997
|
-
return {
|
|
1998
|
-
failedPlugins,
|
|
1999
|
-
files,
|
|
2000
|
-
driver,
|
|
2001
|
-
pluginTimings,
|
|
2002
|
-
error: void 0,
|
|
2003
|
-
sources
|
|
2004
|
-
};
|
|
2005
|
-
}
|
|
2006
|
-
/**
|
|
2007
2637
|
* Walks the AST and dispatches nodes to a plugin's direct AST hooks
|
|
2008
2638
|
* (`schema`, `operation`, `operations`).
|
|
2009
|
-
*
|
|
2010
|
-
* - Each hook accepts a single handler **or an array** — all entries are called in sequence.
|
|
2011
|
-
* - Nodes that are excluded by `exclude`/`include` plugin options are skipped automatically.
|
|
2012
|
-
* - Return values are handled via `applyHookResult`: React elements are rendered,
|
|
2013
|
-
* `FileNode[]` are written via upsert, and `void` is a no-op (manual handling).
|
|
2014
|
-
* - Barrel files are generated automatically when `output.barrelType` is set.
|
|
2015
2639
|
*/
|
|
2016
2640
|
async function runPluginAstHooks(plugin, context) {
|
|
2017
2641
|
const { adapter, inputNode, resolver, driver } = context;
|
|
2018
2642
|
const { exclude, include, override } = plugin.options;
|
|
2019
2643
|
if (!adapter || !inputNode) throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. pluginOas()) before this plugin in your Kubb config.`);
|
|
2644
|
+
function resolveRenderer(gen) {
|
|
2645
|
+
return gen.renderer === null ? void 0 : gen.renderer ?? plugin.renderer ?? context.config.renderer;
|
|
2646
|
+
}
|
|
2647
|
+
const generators = plugin.generators ?? [];
|
|
2020
2648
|
const collectedOperations = [];
|
|
2649
|
+
const generatorContext = {
|
|
2650
|
+
...context,
|
|
2651
|
+
resolver: driver.getResolver(plugin.name)
|
|
2652
|
+
};
|
|
2021
2653
|
await walk(inputNode, {
|
|
2022
2654
|
depth: "shallow",
|
|
2023
2655
|
async schema(node) {
|
|
2024
|
-
if (!plugin.schema) return;
|
|
2025
2656
|
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node;
|
|
2026
2657
|
const options = resolver.resolveOptions(transformedNode, {
|
|
2027
2658
|
options: plugin.options,
|
|
@@ -2030,7 +2661,15 @@ async function runPluginAstHooks(plugin, context) {
|
|
|
2030
2661
|
override
|
|
2031
2662
|
});
|
|
2032
2663
|
if (options === null) return;
|
|
2033
|
-
|
|
2664
|
+
const ctx = {
|
|
2665
|
+
...generatorContext,
|
|
2666
|
+
options
|
|
2667
|
+
};
|
|
2668
|
+
for (const gen of generators) {
|
|
2669
|
+
if (!gen.schema) continue;
|
|
2670
|
+
await applyHookResult(await gen.schema(transformedNode, ctx), driver, resolveRenderer(gen));
|
|
2671
|
+
}
|
|
2672
|
+
await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
|
|
2034
2673
|
},
|
|
2035
2674
|
async operation(node) {
|
|
2036
2675
|
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node;
|
|
@@ -2042,28 +2681,43 @@ async function runPluginAstHooks(plugin, context) {
|
|
|
2042
2681
|
});
|
|
2043
2682
|
if (options !== null) {
|
|
2044
2683
|
collectedOperations.push(transformedNode);
|
|
2045
|
-
|
|
2684
|
+
const ctx = {
|
|
2685
|
+
...generatorContext,
|
|
2686
|
+
options
|
|
2687
|
+
};
|
|
2688
|
+
for (const gen of generators) {
|
|
2689
|
+
if (!gen.operation) continue;
|
|
2690
|
+
await applyHookResult(await gen.operation(transformedNode, ctx), driver, resolveRenderer(gen));
|
|
2691
|
+
}
|
|
2692
|
+
await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
|
|
2046
2693
|
}
|
|
2047
2694
|
}
|
|
2048
2695
|
});
|
|
2049
|
-
if (
|
|
2696
|
+
if (collectedOperations.length > 0) {
|
|
2697
|
+
const ctx = {
|
|
2698
|
+
...generatorContext,
|
|
2699
|
+
options: plugin.options
|
|
2700
|
+
};
|
|
2701
|
+
for (const gen of generators) {
|
|
2702
|
+
if (!gen.operations) continue;
|
|
2703
|
+
await applyHookResult(await gen.operations(collectedOperations, ctx), driver, resolveRenderer(gen));
|
|
2704
|
+
}
|
|
2705
|
+
await driver.hooks.emit("kubb:generate:operations", collectedOperations, ctx);
|
|
2706
|
+
}
|
|
2050
2707
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
*
|
|
2054
|
-
* - Installs each plugin in order, recording failures in `failedPlugins`.
|
|
2055
|
-
* - Generates the root barrel file when `output.barrelType` is set.
|
|
2056
|
-
* - Writes all files through the driver's FileManager and FileProcessor.
|
|
2057
|
-
*
|
|
2058
|
-
* Returns a {@link BuildOutput} even on failure — inspect `error` and
|
|
2059
|
-
* `failedPlugins` to determine whether the build succeeded.
|
|
2060
|
-
*/
|
|
2061
|
-
async function safeBuild(options, overrides) {
|
|
2062
|
-
const { driver, events, sources, storage } = overrides ? overrides : await setup(options);
|
|
2708
|
+
async function safeBuild(setupResult) {
|
|
2709
|
+
const { driver, hooks, sources, storage } = setupResult;
|
|
2063
2710
|
const failedPlugins = /* @__PURE__ */ new Set();
|
|
2064
2711
|
const pluginTimings = /* @__PURE__ */ new Map();
|
|
2065
2712
|
const config = driver.config;
|
|
2066
2713
|
try {
|
|
2714
|
+
await driver.emitSetupHooks();
|
|
2715
|
+
if (driver.adapter && driver.inputNode) await hooks.emit("kubb:build:start", {
|
|
2716
|
+
config,
|
|
2717
|
+
adapter: driver.adapter,
|
|
2718
|
+
inputNode: driver.inputNode,
|
|
2719
|
+
getPlugin: (name) => driver.getPlugin(name)
|
|
2720
|
+
});
|
|
2067
2721
|
for (const plugin of driver.plugins.values()) {
|
|
2068
2722
|
const context = driver.getContext(plugin);
|
|
2069
2723
|
const hrStart = process.hrtime();
|
|
@@ -2071,13 +2725,13 @@ async function safeBuild(options, overrides) {
|
|
|
2071
2725
|
const root = resolve(config.root, config.output.path);
|
|
2072
2726
|
try {
|
|
2073
2727
|
const timestamp = /* @__PURE__ */ new Date();
|
|
2074
|
-
await
|
|
2075
|
-
await
|
|
2728
|
+
await hooks.emit("kubb:plugin:start", plugin);
|
|
2729
|
+
await hooks.emit("kubb:debug", {
|
|
2076
2730
|
date: timestamp,
|
|
2077
2731
|
logs: ["Starting plugin...", ` • Plugin Name: ${plugin.name}`]
|
|
2078
2732
|
});
|
|
2079
2733
|
await plugin.buildStart.call(context);
|
|
2080
|
-
if (plugin.
|
|
2734
|
+
if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) await runPluginAstHooks(plugin, context);
|
|
2081
2735
|
if (output) {
|
|
2082
2736
|
const barrelFiles = await getBarrelFiles(driver.fileManager.files, {
|
|
2083
2737
|
type: output.barrelType ?? "named",
|
|
@@ -2089,11 +2743,11 @@ async function safeBuild(options, overrides) {
|
|
|
2089
2743
|
}
|
|
2090
2744
|
const duration = getElapsedMs(hrStart);
|
|
2091
2745
|
pluginTimings.set(plugin.name, duration);
|
|
2092
|
-
await
|
|
2746
|
+
await hooks.emit("kubb:plugin:end", plugin, {
|
|
2093
2747
|
duration,
|
|
2094
2748
|
success: true
|
|
2095
2749
|
});
|
|
2096
|
-
await
|
|
2750
|
+
await hooks.emit("kubb:debug", {
|
|
2097
2751
|
date: /* @__PURE__ */ new Date(),
|
|
2098
2752
|
logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
|
|
2099
2753
|
});
|
|
@@ -2101,12 +2755,12 @@ async function safeBuild(options, overrides) {
|
|
|
2101
2755
|
const error = caughtError;
|
|
2102
2756
|
const errorTimestamp = /* @__PURE__ */ new Date();
|
|
2103
2757
|
const duration = getElapsedMs(hrStart);
|
|
2104
|
-
await
|
|
2758
|
+
await hooks.emit("kubb:plugin:end", plugin, {
|
|
2105
2759
|
duration,
|
|
2106
2760
|
success: false,
|
|
2107
2761
|
error
|
|
2108
2762
|
});
|
|
2109
|
-
await
|
|
2763
|
+
await hooks.emit("kubb:debug", {
|
|
2110
2764
|
date: errorTimestamp,
|
|
2111
2765
|
logs: [
|
|
2112
2766
|
"✗ Plugin start failed",
|
|
@@ -2125,7 +2779,7 @@ async function safeBuild(options, overrides) {
|
|
|
2125
2779
|
if (config.output.barrelType) {
|
|
2126
2780
|
const rootPath = resolve(resolve(config.root), config.output.path, BARREL_FILENAME);
|
|
2127
2781
|
const rootDir = dirname(rootPath);
|
|
2128
|
-
await
|
|
2782
|
+
await hooks.emit("kubb:debug", {
|
|
2129
2783
|
date: /* @__PURE__ */ new Date(),
|
|
2130
2784
|
logs: [
|
|
2131
2785
|
"Generating barrel file",
|
|
@@ -2136,7 +2790,7 @@ async function safeBuild(options, overrides) {
|
|
|
2136
2790
|
const barrelFiles = driver.fileManager.files.filter((file) => {
|
|
2137
2791
|
return file.sources.some((source) => source.isIndexable);
|
|
2138
2792
|
});
|
|
2139
|
-
await
|
|
2793
|
+
await hooks.emit("kubb:debug", {
|
|
2140
2794
|
date: /* @__PURE__ */ new Date(),
|
|
2141
2795
|
logs: [`Found ${barrelFiles.length} indexable files for barrel export`]
|
|
2142
2796
|
});
|
|
@@ -2156,7 +2810,7 @@ async function safeBuild(options, overrides) {
|
|
|
2156
2810
|
meta: {}
|
|
2157
2811
|
});
|
|
2158
2812
|
driver.fileManager.upsert(rootFile);
|
|
2159
|
-
await
|
|
2813
|
+
await hooks.emit("kubb:debug", {
|
|
2160
2814
|
date: /* @__PURE__ */ new Date(),
|
|
2161
2815
|
logs: [`✓ Generated barrel file (${rootFile.exports?.length || 0} exports)`]
|
|
2162
2816
|
});
|
|
@@ -2165,7 +2819,7 @@ async function safeBuild(options, overrides) {
|
|
|
2165
2819
|
const parsersMap = /* @__PURE__ */ new Map();
|
|
2166
2820
|
for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
|
|
2167
2821
|
const fileProcessor = new FileProcessor();
|
|
2168
|
-
await
|
|
2822
|
+
await hooks.emit("kubb:debug", {
|
|
2169
2823
|
date: /* @__PURE__ */ new Date(),
|
|
2170
2824
|
logs: [`Writing ${files.length} files...`]
|
|
2171
2825
|
});
|
|
@@ -2173,591 +2827,272 @@ async function safeBuild(options, overrides) {
|
|
|
2173
2827
|
parsers: parsersMap,
|
|
2174
2828
|
extension: config.output.extension,
|
|
2175
2829
|
onStart: async (processingFiles) => {
|
|
2176
|
-
await
|
|
2177
|
-
},
|
|
2178
|
-
onUpdate: async ({ file, source, processed, total, percentage }) => {
|
|
2179
|
-
await events.emit("file:processing:update", {
|
|
2180
|
-
file,
|
|
2181
|
-
source,
|
|
2182
|
-
processed,
|
|
2183
|
-
total,
|
|
2184
|
-
percentage,
|
|
2185
|
-
config
|
|
2186
|
-
});
|
|
2187
|
-
if (source) {
|
|
2188
|
-
await storage?.setItem(file.path, source);
|
|
2189
|
-
sources.set(file.path, source);
|
|
2190
|
-
}
|
|
2830
|
+
await hooks.emit("kubb:files:processing:start", processingFiles);
|
|
2191
2831
|
},
|
|
2192
|
-
|
|
2193
|
-
await
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
return {
|
|
2213
|
-
failedPlugins,
|
|
2214
|
-
files: [],
|
|
2215
|
-
driver,
|
|
2216
|
-
pluginTimings,
|
|
2217
|
-
error,
|
|
2218
|
-
sources
|
|
2219
|
-
};
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }) {
|
|
2223
|
-
const pluginNameMap = /* @__PURE__ */ new Map();
|
|
2224
|
-
for (const plugin of driver.plugins.values()) pluginNameMap.set(plugin.name, plugin);
|
|
2225
|
-
return barrelFiles.flatMap((file) => {
|
|
2226
|
-
const containsOnlyTypes = file.sources?.every((source) => source.isTypeOnly);
|
|
2227
|
-
return (file.sources ?? []).flatMap((source) => {
|
|
2228
|
-
if (!file.path || !source.isIndexable) return [];
|
|
2229
|
-
const meta = file.meta;
|
|
2230
|
-
const pluginOptions = (meta?.pluginName ? pluginNameMap.get(meta.pluginName) : void 0)?.options;
|
|
2231
|
-
if (!pluginOptions || pluginOptions.output?.barrelType === false) return [];
|
|
2232
|
-
const exportName = config.output.barrelType === "all" ? void 0 : source.name ? [source.name] : void 0;
|
|
2233
|
-
if (exportName?.some((n) => existingExports.has(n))) return [];
|
|
2234
|
-
return [createExport({
|
|
2235
|
-
name: exportName,
|
|
2236
|
-
path: getRelativePath(rootDir, file.path),
|
|
2237
|
-
isTypeOnly: config.output.barrelType === "all" ? containsOnlyTypes : source.isTypeOnly
|
|
2238
|
-
})];
|
|
2239
|
-
});
|
|
2240
|
-
});
|
|
2241
|
-
}
|
|
2242
|
-
/**
|
|
2243
|
-
* Maps the resolved `Config['input']` shape into an `AdapterSource` that
|
|
2244
|
-
* the adapter's `parse()` can consume.
|
|
2245
|
-
*/
|
|
2246
|
-
function inputToAdapterSource(config) {
|
|
2247
|
-
if (Array.isArray(config.input)) return {
|
|
2248
|
-
type: "paths",
|
|
2249
|
-
paths: config.input.map((i) => new URLPath(i.path).isURL ? i.path : resolve(config.root, i.path))
|
|
2250
|
-
};
|
|
2251
|
-
if ("data" in config.input) return {
|
|
2252
|
-
type: "data",
|
|
2253
|
-
data: config.input.data
|
|
2254
|
-
};
|
|
2255
|
-
if (new URLPath(config.input.path).isURL) return {
|
|
2256
|
-
type: "path",
|
|
2257
|
-
path: config.input.path
|
|
2258
|
-
};
|
|
2259
|
-
return {
|
|
2260
|
-
type: "path",
|
|
2261
|
-
path: resolve(config.root, config.input.path)
|
|
2262
|
-
};
|
|
2263
|
-
}
|
|
2264
|
-
//#endregion
|
|
2265
|
-
//#region src/createAdapter.ts
|
|
2266
|
-
/**
|
|
2267
|
-
* Creates an adapter factory. Call the returned function with optional options to get the adapter instance.
|
|
2268
|
-
*
|
|
2269
|
-
* @example
|
|
2270
|
-
* export const myAdapter = createAdapter<MyAdapter>((options) => {
|
|
2271
|
-
* return {
|
|
2272
|
-
* name: 'my-adapter',
|
|
2273
|
-
* options,
|
|
2274
|
-
* async parse(source) { ... },
|
|
2275
|
-
* }
|
|
2276
|
-
* })
|
|
2277
|
-
*
|
|
2278
|
-
* // instantiate
|
|
2279
|
-
* const adapter = myAdapter({ validate: true })
|
|
2280
|
-
*/
|
|
2281
|
-
function createAdapter(build) {
|
|
2282
|
-
return (options) => build(options ?? {});
|
|
2283
|
-
}
|
|
2284
|
-
//#endregion
|
|
2285
|
-
//#region src/createPlugin.ts
|
|
2286
|
-
/**
|
|
2287
|
-
* Creates a plugin factory. Call the returned function with optional options to get the plugin instance.
|
|
2288
|
-
*
|
|
2289
|
-
* @example
|
|
2290
|
-
* ```ts
|
|
2291
|
-
* export const myPlugin = createPlugin<MyPlugin>((options) => {
|
|
2292
|
-
* return {
|
|
2293
|
-
* name: 'my-plugin',
|
|
2294
|
-
* get options() { return options },
|
|
2295
|
-
* resolvePath(baseName) { ... },
|
|
2296
|
-
* resolveName(name, type) { ... },
|
|
2297
|
-
* }
|
|
2298
|
-
* })
|
|
2299
|
-
*
|
|
2300
|
-
* // instantiate
|
|
2301
|
-
* const plugin = myPlugin({ output: { path: 'src/gen' } })
|
|
2302
|
-
* ```
|
|
2303
|
-
*/
|
|
2304
|
-
function createPlugin(build) {
|
|
2305
|
-
return (options) => build(options ?? {});
|
|
2306
|
-
}
|
|
2307
|
-
//#endregion
|
|
2308
|
-
//#region src/defineConfig.ts
|
|
2309
|
-
function defineConfig(config) {
|
|
2310
|
-
return config;
|
|
2311
|
-
}
|
|
2312
|
-
//#endregion
|
|
2313
|
-
//#region src/defineGenerator.ts
|
|
2314
|
-
/**
|
|
2315
|
-
* Defines a generator. Returns the object as-is with correct `this` typings.
|
|
2316
|
-
* No type discrimination (`type: 'react' | 'core'`) needed — `applyHookResult`
|
|
2317
|
-
* handles React elements and `File[]` uniformly.
|
|
2318
|
-
*/
|
|
2319
|
-
function defineGenerator(generator) {
|
|
2320
|
-
return generator;
|
|
2321
|
-
}
|
|
2322
|
-
/**
|
|
2323
|
-
* Merges an array of generators into a single generator.
|
|
2324
|
-
*
|
|
2325
|
-
* The merged generator's `schema`, `operation`, and `operations` methods run
|
|
2326
|
-
* the corresponding method from each input generator in sequence, applying each
|
|
2327
|
-
* result via `applyHookResult`. This eliminates the need to write the loop
|
|
2328
|
-
* manually in each plugin.
|
|
2329
|
-
*
|
|
2330
|
-
* @param generators - Array of generators to merge into a single generator.
|
|
2331
|
-
*
|
|
2332
|
-
* @example
|
|
2333
|
-
* ```ts
|
|
2334
|
-
* const merged = mergeGenerators(generators)
|
|
2335
|
-
*
|
|
2336
|
-
* return {
|
|
2337
|
-
* name: pluginName,
|
|
2338
|
-
* schema: merged.schema,
|
|
2339
|
-
* operation: merged.operation,
|
|
2340
|
-
* operations: merged.operations,
|
|
2341
|
-
* }
|
|
2342
|
-
* ```
|
|
2343
|
-
*/
|
|
2344
|
-
function mergeGenerators(generators) {
|
|
2345
|
-
return {
|
|
2346
|
-
name: generators.length > 0 ? generators.map((g) => g.name).join("+") : "merged",
|
|
2347
|
-
async schema(node, options) {
|
|
2348
|
-
for (const gen of generators) {
|
|
2349
|
-
if (!gen.schema) continue;
|
|
2350
|
-
await applyHookResult(await gen.schema.call(this, node, options), this.driver);
|
|
2351
|
-
}
|
|
2352
|
-
},
|
|
2353
|
-
async operation(node, options) {
|
|
2354
|
-
for (const gen of generators) {
|
|
2355
|
-
if (!gen.operation) continue;
|
|
2356
|
-
await applyHookResult(await gen.operation.call(this, node, options), this.driver);
|
|
2357
|
-
}
|
|
2358
|
-
},
|
|
2359
|
-
async operations(nodes, options) {
|
|
2360
|
-
for (const gen of generators) {
|
|
2361
|
-
if (!gen.operations) continue;
|
|
2362
|
-
await applyHookResult(await gen.operations.call(this, nodes, options), this.driver);
|
|
2832
|
+
onUpdate: async ({ file, source, processed, total, percentage }) => {
|
|
2833
|
+
await hooks.emit("kubb:file:processing:update", {
|
|
2834
|
+
file,
|
|
2835
|
+
source,
|
|
2836
|
+
processed,
|
|
2837
|
+
total,
|
|
2838
|
+
percentage,
|
|
2839
|
+
config
|
|
2840
|
+
});
|
|
2841
|
+
if (source) {
|
|
2842
|
+
await storage?.setItem(file.path, source);
|
|
2843
|
+
sources.set(file.path, source);
|
|
2844
|
+
}
|
|
2845
|
+
},
|
|
2846
|
+
onEnd: async (processedFiles) => {
|
|
2847
|
+
await hooks.emit("kubb:files:processing:end", processedFiles);
|
|
2848
|
+
await hooks.emit("kubb:debug", {
|
|
2849
|
+
date: /* @__PURE__ */ new Date(),
|
|
2850
|
+
logs: [`✓ File write process completed for ${processedFiles.length} files`]
|
|
2851
|
+
});
|
|
2363
2852
|
}
|
|
2853
|
+
});
|
|
2854
|
+
for (const plugin of driver.plugins.values()) if (plugin.buildEnd) {
|
|
2855
|
+
const context = driver.getContext(plugin);
|
|
2856
|
+
await plugin.buildEnd.call(context);
|
|
2364
2857
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
* Wraps a logger definition into a typed {@link Logger}.
|
|
2371
|
-
*
|
|
2372
|
-
* @example
|
|
2373
|
-
* export const myLogger = defineLogger({
|
|
2374
|
-
* name: 'my-logger',
|
|
2375
|
-
* install(context, options) {
|
|
2376
|
-
* context.on('info', (message) => console.log('ℹ', message))
|
|
2377
|
-
* context.on('error', (error) => console.error('✗', error.message))
|
|
2378
|
-
* },
|
|
2379
|
-
* })
|
|
2380
|
-
*/
|
|
2381
|
-
function defineLogger(logger) {
|
|
2382
|
-
return logger;
|
|
2383
|
-
}
|
|
2384
|
-
//#endregion
|
|
2385
|
-
//#region src/defineParser.ts
|
|
2386
|
-
/**
|
|
2387
|
-
* Defines a parser with type safety.
|
|
2388
|
-
*
|
|
2389
|
-
* Use this function to create parsers that transform generated files to strings
|
|
2390
|
-
* based on their extension.
|
|
2391
|
-
*
|
|
2392
|
-
* @example
|
|
2393
|
-
* ```ts
|
|
2394
|
-
* import { defineParser } from '@kubb/core'
|
|
2395
|
-
*
|
|
2396
|
-
* export const jsonParser = defineParser({
|
|
2397
|
-
* name: 'json',
|
|
2398
|
-
* extNames: ['.json'],
|
|
2399
|
-
* parse(file) {
|
|
2400
|
-
* const { extractStringsFromNodes } = await import('@kubb/ast')
|
|
2401
|
-
* return file.sources.map((s) => extractStringsFromNodes(s.nodes ?? [])).join('\n')
|
|
2402
|
-
* },
|
|
2403
|
-
* })
|
|
2404
|
-
* ```
|
|
2405
|
-
*/
|
|
2406
|
-
function defineParser(parser) {
|
|
2407
|
-
return parser;
|
|
2408
|
-
}
|
|
2409
|
-
//#endregion
|
|
2410
|
-
//#region src/definePresets.ts
|
|
2411
|
-
/**
|
|
2412
|
-
* Creates a typed presets registry object — a named collection of {@link Preset} entries.
|
|
2413
|
-
*
|
|
2414
|
-
* @example
|
|
2415
|
-
* import { definePreset, definePresets } from '@kubb/core'
|
|
2416
|
-
* import { resolverTsLegacy } from '@kubb/plugin-ts'
|
|
2417
|
-
*
|
|
2418
|
-
* export const myPresets = definePresets({
|
|
2419
|
-
* kubbV4: definePreset('kubbV4', { resolvers: [resolverTsLegacy] }),
|
|
2420
|
-
* })
|
|
2421
|
-
*/
|
|
2422
|
-
function definePresets(presets) {
|
|
2423
|
-
return presets;
|
|
2424
|
-
}
|
|
2425
|
-
//#endregion
|
|
2426
|
-
//#region src/defineResolver.ts
|
|
2427
|
-
/**
|
|
2428
|
-
* Checks if an operation matches a pattern for a given filter type (`tag`, `operationId`, `path`, `method`).
|
|
2429
|
-
*/
|
|
2430
|
-
function matchesOperationPattern(node, type, pattern) {
|
|
2431
|
-
switch (type) {
|
|
2432
|
-
case "tag": return node.tags.some((tag) => !!tag.match(pattern));
|
|
2433
|
-
case "operationId": return !!node.operationId.match(pattern);
|
|
2434
|
-
case "path": return !!node.path.match(pattern);
|
|
2435
|
-
case "method": return !!node.method.toLowerCase().match(pattern);
|
|
2436
|
-
case "contentType": return !!node.requestBody?.contentType?.match(pattern);
|
|
2437
|
-
default: return false;
|
|
2438
|
-
}
|
|
2439
|
-
}
|
|
2440
|
-
/**
|
|
2441
|
-
* Checks if a schema matches a pattern for a given filter type (`schemaName`).
|
|
2442
|
-
*
|
|
2443
|
-
* Returns `null` when the filter type doesn't apply to schemas.
|
|
2444
|
-
*/
|
|
2445
|
-
function matchesSchemaPattern(node, type, pattern) {
|
|
2446
|
-
switch (type) {
|
|
2447
|
-
case "schemaName": return node.name ? !!node.name.match(pattern) : false;
|
|
2448
|
-
default: return null;
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
/**
|
|
2452
|
-
* Default name resolver used by `defineResolver`.
|
|
2453
|
-
*
|
|
2454
|
-
* - `camelCase` for `function` and `file` types.
|
|
2455
|
-
* - `PascalCase` for `type`.
|
|
2456
|
-
* - `camelCase` for everything else.
|
|
2457
|
-
*/
|
|
2458
|
-
function defaultResolver(name, type) {
|
|
2459
|
-
let resolvedName = camelCase(name);
|
|
2460
|
-
if (type === "file" || type === "function") resolvedName = camelCase(name, { isFile: type === "file" });
|
|
2461
|
-
if (type === "type") resolvedName = pascalCase(name);
|
|
2462
|
-
return resolvedName;
|
|
2463
|
-
}
|
|
2464
|
-
/**
|
|
2465
|
-
* Default option resolver — applies include/exclude filters and merges matching override options.
|
|
2466
|
-
*
|
|
2467
|
-
* Returns `null` when the node is filtered out by an `exclude` rule or not matched by any `include` rule.
|
|
2468
|
-
*
|
|
2469
|
-
* @example Include/exclude filtering
|
|
2470
|
-
* ```ts
|
|
2471
|
-
* const options = defaultResolveOptions(operationNode, {
|
|
2472
|
-
* options: { output: 'types' },
|
|
2473
|
-
* exclude: [{ type: 'tag', pattern: 'internal' }],
|
|
2474
|
-
* })
|
|
2475
|
-
* // → null when node has tag 'internal'
|
|
2476
|
-
* ```
|
|
2477
|
-
*
|
|
2478
|
-
* @example Override merging
|
|
2479
|
-
* ```ts
|
|
2480
|
-
* const options = defaultResolveOptions(operationNode, {
|
|
2481
|
-
* options: { enumType: 'asConst' },
|
|
2482
|
-
* override: [{ type: 'operationId', pattern: 'listPets', options: { enumType: 'enum' } }],
|
|
2483
|
-
* })
|
|
2484
|
-
* // → { enumType: 'enum' } when operationId matches
|
|
2485
|
-
* ```
|
|
2486
|
-
*/
|
|
2487
|
-
function defaultResolveOptions(node, { options, exclude = [], include, override = [] }) {
|
|
2488
|
-
if (isOperationNode(node)) {
|
|
2489
|
-
if (exclude.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
2490
|
-
if (include && !include.some(({ type, pattern }) => matchesOperationPattern(node, type, pattern))) return null;
|
|
2491
|
-
const overrideOptions = override.find(({ type, pattern }) => matchesOperationPattern(node, type, pattern))?.options;
|
|
2858
|
+
await hooks.emit("kubb:build:end", {
|
|
2859
|
+
files,
|
|
2860
|
+
config,
|
|
2861
|
+
outputDir: resolve(config.root, config.output.path)
|
|
2862
|
+
});
|
|
2492
2863
|
return {
|
|
2493
|
-
|
|
2494
|
-
|
|
2864
|
+
failedPlugins,
|
|
2865
|
+
files,
|
|
2866
|
+
driver,
|
|
2867
|
+
pluginTimings,
|
|
2868
|
+
sources
|
|
2495
2869
|
};
|
|
2496
|
-
}
|
|
2497
|
-
if (isSchemaNode(node)) {
|
|
2498
|
-
if (exclude.some(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)) return null;
|
|
2499
|
-
if (include) {
|
|
2500
|
-
const applicable = include.map(({ type, pattern }) => matchesSchemaPattern(node, type, pattern)).filter((r) => r !== null);
|
|
2501
|
-
if (applicable.length > 0 && !applicable.includes(true)) return null;
|
|
2502
|
-
}
|
|
2503
|
-
const overrideOptions = override.find(({ type, pattern }) => matchesSchemaPattern(node, type, pattern) === true)?.options;
|
|
2870
|
+
} catch (error) {
|
|
2504
2871
|
return {
|
|
2505
|
-
|
|
2506
|
-
|
|
2872
|
+
failedPlugins,
|
|
2873
|
+
files: [],
|
|
2874
|
+
driver,
|
|
2875
|
+
pluginTimings,
|
|
2876
|
+
error,
|
|
2877
|
+
sources
|
|
2507
2878
|
};
|
|
2879
|
+
} finally {
|
|
2880
|
+
driver.dispose();
|
|
2508
2881
|
}
|
|
2509
|
-
return options;
|
|
2510
2882
|
}
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
if (
|
|
2557
|
-
|
|
2558
|
-
|
|
2883
|
+
async function build(setupResult) {
|
|
2884
|
+
const { files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(setupResult);
|
|
2885
|
+
if (error) throw error;
|
|
2886
|
+
if (failedPlugins.size > 0) {
|
|
2887
|
+
const errors = [...failedPlugins].map(({ error }) => error);
|
|
2888
|
+
throw new BuildError(`Build Error with ${failedPlugins.size} failed plugins`, { errors });
|
|
2889
|
+
}
|
|
2890
|
+
return {
|
|
2891
|
+
failedPlugins,
|
|
2892
|
+
files,
|
|
2893
|
+
driver,
|
|
2894
|
+
pluginTimings,
|
|
2895
|
+
error: void 0,
|
|
2896
|
+
sources
|
|
2897
|
+
};
|
|
2898
|
+
}
|
|
2899
|
+
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }) {
|
|
2900
|
+
const pluginNameMap = /* @__PURE__ */ new Map();
|
|
2901
|
+
for (const plugin of driver.plugins.values()) pluginNameMap.set(plugin.name, plugin);
|
|
2902
|
+
return barrelFiles.flatMap((file) => {
|
|
2903
|
+
const containsOnlyTypes = file.sources?.every((source) => source.isTypeOnly);
|
|
2904
|
+
return (file.sources ?? []).flatMap((source) => {
|
|
2905
|
+
if (!file.path || !source.isIndexable) return [];
|
|
2906
|
+
const meta = file.meta;
|
|
2907
|
+
const pluginOptions = (meta?.pluginName ? pluginNameMap.get(meta.pluginName) : void 0)?.options;
|
|
2908
|
+
if (!pluginOptions || pluginOptions.output?.barrelType === false) return [];
|
|
2909
|
+
const exportName = config.output.barrelType === "all" ? void 0 : source.name ? [source.name] : void 0;
|
|
2910
|
+
if (exportName?.some((n) => existingExports.has(n))) return [];
|
|
2911
|
+
return [createExport({
|
|
2912
|
+
name: exportName,
|
|
2913
|
+
path: getRelativePath(rootDir, file.path),
|
|
2914
|
+
isTypeOnly: config.output.barrelType === "all" ? containsOnlyTypes : source.isTypeOnly
|
|
2915
|
+
})];
|
|
2916
|
+
});
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
function inputToAdapterSource(config) {
|
|
2920
|
+
if (Array.isArray(config.input)) return {
|
|
2921
|
+
type: "paths",
|
|
2922
|
+
paths: config.input.map((i) => new URLPath(i.path).isURL ? i.path : resolve(config.root, i.path))
|
|
2923
|
+
};
|
|
2924
|
+
if ("data" in config.input) return {
|
|
2925
|
+
type: "data",
|
|
2926
|
+
data: config.input.data
|
|
2927
|
+
};
|
|
2928
|
+
if (new URLPath(config.input.path).isURL) return {
|
|
2929
|
+
type: "path",
|
|
2930
|
+
path: config.input.path
|
|
2931
|
+
};
|
|
2932
|
+
return {
|
|
2933
|
+
type: "path",
|
|
2934
|
+
path: resolve(config.root, config.input.path)
|
|
2935
|
+
};
|
|
2559
2936
|
}
|
|
2560
2937
|
/**
|
|
2561
|
-
*
|
|
2562
|
-
*
|
|
2563
|
-
* Resolves a `FileNode` by combining name resolution (`resolver.default`) with
|
|
2564
|
-
* path resolution (`resolver.resolvePath`). The resolved file always has empty
|
|
2565
|
-
* `sources`, `imports`, and `exports` arrays — consumers populate those separately.
|
|
2938
|
+
* Creates a Kubb instance bound to a single config entry.
|
|
2566
2939
|
*
|
|
2567
|
-
*
|
|
2940
|
+
* The instance holds shared state (`hooks`, `sources`, `driver`, `config`) across the
|
|
2941
|
+
* `setup → build` lifecycle. Attach event listeners to `kubb.hooks` before
|
|
2942
|
+
* calling `setup()` or `build()`.
|
|
2568
2943
|
*
|
|
2569
|
-
* @example
|
|
2944
|
+
* @example
|
|
2570
2945
|
* ```ts
|
|
2571
|
-
* const
|
|
2572
|
-
* { name: 'pet', extname: '.ts' },
|
|
2573
|
-
* { root: '/src', output: { path: 'types' } },
|
|
2574
|
-
* )
|
|
2575
|
-
* // → { baseName: 'pet.ts', path: '/src/types/pet.ts', sources: [], ... }
|
|
2576
|
-
* ```
|
|
2946
|
+
* const kubb = createKubb({ config })
|
|
2577
2947
|
*
|
|
2578
|
-
*
|
|
2579
|
-
*
|
|
2580
|
-
*
|
|
2581
|
-
*
|
|
2582
|
-
*
|
|
2583
|
-
* )
|
|
2584
|
-
* // → { baseName: 'listPets.ts', path: '/src/types/petsController/listPets.ts', ... }
|
|
2948
|
+
* kubb.hooks.on('kubb:plugin:end', (plugin, { duration }) => {
|
|
2949
|
+
* console.log(`${plugin.name} completed in ${duration}ms`)
|
|
2950
|
+
* })
|
|
2951
|
+
*
|
|
2952
|
+
* const { files, failedPlugins } = await kubb.safeBuild()
|
|
2585
2953
|
* ```
|
|
2586
2954
|
*/
|
|
2587
|
-
function
|
|
2588
|
-
const
|
|
2589
|
-
|
|
2590
|
-
const
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
let banner = "/**\n* Generated by Kubb (https://kubb.dev/).\n* Do not edit manually.\n";
|
|
2617
|
-
if (config.output.defaultBanner === "simple") {
|
|
2618
|
-
banner += "*/\n";
|
|
2619
|
-
return banner;
|
|
2620
|
-
}
|
|
2621
|
-
if (source) banner += `* Source: ${source}\n`;
|
|
2622
|
-
if (title) banner += `* Title: ${title}\n`;
|
|
2623
|
-
if (description) {
|
|
2624
|
-
const formattedDescription = description.replace(/\n/gm, "\n* ");
|
|
2625
|
-
banner += `* Description: ${formattedDescription}\n`;
|
|
2955
|
+
function createKubb(options) {
|
|
2956
|
+
const hooks = options.hooks ?? new AsyncEventEmitter();
|
|
2957
|
+
let setupResult;
|
|
2958
|
+
const instance = {
|
|
2959
|
+
get hooks() {
|
|
2960
|
+
return hooks;
|
|
2961
|
+
},
|
|
2962
|
+
get sources() {
|
|
2963
|
+
return setupResult?.sources ?? /* @__PURE__ */ new Map();
|
|
2964
|
+
},
|
|
2965
|
+
get driver() {
|
|
2966
|
+
return setupResult?.driver;
|
|
2967
|
+
},
|
|
2968
|
+
get config() {
|
|
2969
|
+
return setupResult?.config;
|
|
2970
|
+
},
|
|
2971
|
+
async setup() {
|
|
2972
|
+
setupResult = await setup({
|
|
2973
|
+
config: options.config,
|
|
2974
|
+
hooks
|
|
2975
|
+
});
|
|
2976
|
+
},
|
|
2977
|
+
async build() {
|
|
2978
|
+
if (!setupResult) await instance.setup();
|
|
2979
|
+
return build(setupResult);
|
|
2980
|
+
},
|
|
2981
|
+
async safeBuild() {
|
|
2982
|
+
if (!setupResult) await instance.setup();
|
|
2983
|
+
return safeBuild(setupResult);
|
|
2626
2984
|
}
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
return banner;
|
|
2630
|
-
} catch (_error) {
|
|
2631
|
-
return "/**\n* Generated by Kubb (https://kubb.dev/).\n* Do not edit manually.\n*/";
|
|
2632
|
-
}
|
|
2985
|
+
};
|
|
2986
|
+
return instance;
|
|
2633
2987
|
}
|
|
2988
|
+
//#endregion
|
|
2989
|
+
//#region src/createPlugin.ts
|
|
2634
2990
|
/**
|
|
2635
|
-
*
|
|
2636
|
-
*
|
|
2637
|
-
* A user-supplied `output.banner` overrides the default Kubb "Generated by Kubb" notice.
|
|
2638
|
-
* When no `output.banner` is set, the Kubb notice is used (including `title` and `version`
|
|
2639
|
-
* from the OAS spec when a `node` is provided).
|
|
2640
|
-
*
|
|
2641
|
-
* - When `output.banner` is a function and `node` is provided, returns `output.banner(node)`.
|
|
2642
|
-
* - When `output.banner` is a function and `node` is absent, falls back to the Kubb notice.
|
|
2643
|
-
* - When `output.banner` is a string, returns it directly.
|
|
2644
|
-
* - When `config.output.defaultBanner` is `false`, returns `undefined`.
|
|
2645
|
-
* - Otherwise returns the Kubb "Generated by Kubb" notice.
|
|
2646
|
-
*
|
|
2647
|
-
* @example String banner overrides default
|
|
2648
|
-
* ```ts
|
|
2649
|
-
* defaultResolveBanner(undefined, { output: { banner: '// my banner' }, config })
|
|
2650
|
-
* // → '// my banner'
|
|
2651
|
-
* ```
|
|
2652
|
-
*
|
|
2653
|
-
* @example Function banner with node
|
|
2654
|
-
* ```ts
|
|
2655
|
-
* defaultResolveBanner(inputNode, { output: { banner: (node) => `// v${node.version}` }, config })
|
|
2656
|
-
* // → '// v3.0.0'
|
|
2657
|
-
* ```
|
|
2991
|
+
* Creates a plugin factory. Call the returned function with optional options to get the plugin instance.
|
|
2658
2992
|
*
|
|
2659
|
-
* @example
|
|
2993
|
+
* @example
|
|
2660
2994
|
* ```ts
|
|
2661
|
-
*
|
|
2662
|
-
*
|
|
2663
|
-
*
|
|
2995
|
+
* export const myPlugin = createPlugin<MyPlugin>((options) => {
|
|
2996
|
+
* return {
|
|
2997
|
+
* name: 'my-plugin',
|
|
2998
|
+
* get options() { return options },
|
|
2999
|
+
* resolvePath(baseName) { ... },
|
|
3000
|
+
* resolveName(name, type) { ... },
|
|
3001
|
+
* }
|
|
3002
|
+
* })
|
|
2664
3003
|
*
|
|
2665
|
-
*
|
|
2666
|
-
*
|
|
2667
|
-
* defaultResolveBanner(undefined, { config: { output: { defaultBanner: false }, ...config } })
|
|
2668
|
-
* // → undefined
|
|
3004
|
+
* // instantiate
|
|
3005
|
+
* const plugin = myPlugin({ output: { path: 'src/gen' } })
|
|
2669
3006
|
* ```
|
|
3007
|
+
* @deprecated use definePlugin instead
|
|
2670
3008
|
*/
|
|
2671
|
-
function
|
|
2672
|
-
|
|
2673
|
-
if (typeof output?.banner === "string") return output.banner;
|
|
2674
|
-
if (config.output.defaultBanner === false) return;
|
|
2675
|
-
return buildDefaultBanner({
|
|
2676
|
-
title: node?.meta?.title,
|
|
2677
|
-
version: node?.meta?.version,
|
|
2678
|
-
config
|
|
2679
|
-
});
|
|
3009
|
+
function createPlugin(build) {
|
|
3010
|
+
return (options) => build(options ?? {});
|
|
2680
3011
|
}
|
|
3012
|
+
//#endregion
|
|
3013
|
+
//#region src/createRenderer.ts
|
|
2681
3014
|
/**
|
|
2682
|
-
*
|
|
3015
|
+
* Creates a renderer factory for use in generator definitions.
|
|
2683
3016
|
*
|
|
2684
|
-
*
|
|
2685
|
-
*
|
|
2686
|
-
*
|
|
2687
|
-
* - Otherwise returns `undefined`.
|
|
3017
|
+
* Wrap your renderer factory function with this helper to register it as the
|
|
3018
|
+
* renderer for a generator. Core will call this factory once per render cycle
|
|
3019
|
+
* to obtain a fresh renderer instance.
|
|
2688
3020
|
*
|
|
2689
|
-
* @example
|
|
3021
|
+
* @example
|
|
2690
3022
|
* ```ts
|
|
2691
|
-
*
|
|
2692
|
-
*
|
|
2693
|
-
*
|
|
3023
|
+
* // packages/renderer-jsx/src/index.ts
|
|
3024
|
+
* export const jsxRenderer = createRenderer(() => {
|
|
3025
|
+
* const runtime = new Runtime()
|
|
3026
|
+
* return {
|
|
3027
|
+
* async render(element) { await runtime.render(element) },
|
|
3028
|
+
* get files() { return runtime.nodes },
|
|
3029
|
+
* unmount(error) { runtime.unmount(error) },
|
|
3030
|
+
* }
|
|
3031
|
+
* })
|
|
2694
3032
|
*
|
|
2695
|
-
*
|
|
2696
|
-
*
|
|
2697
|
-
*
|
|
2698
|
-
*
|
|
3033
|
+
* // packages/plugin-zod/src/generators/zodGenerator.tsx
|
|
3034
|
+
* import { jsxRenderer } from '@kubb/renderer-jsx'
|
|
3035
|
+
* export const zodGenerator = defineGenerator<PluginZod>({
|
|
3036
|
+
* name: 'zod',
|
|
3037
|
+
* renderer: jsxRenderer,
|
|
3038
|
+
* schema(node, options) { return <File ...>...</File> },
|
|
3039
|
+
* })
|
|
2699
3040
|
* ```
|
|
2700
3041
|
*/
|
|
2701
|
-
function
|
|
2702
|
-
|
|
2703
|
-
if (typeof output?.footer === "string") return output.footer;
|
|
3042
|
+
function createRenderer(factory) {
|
|
3043
|
+
return factory;
|
|
2704
3044
|
}
|
|
3045
|
+
//#endregion
|
|
3046
|
+
//#region src/defineGenerator.ts
|
|
2705
3047
|
/**
|
|
2706
|
-
* Defines a
|
|
2707
|
-
*
|
|
2708
|
-
*
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
*
|
|
3048
|
+
* Defines a generator. Returns the object as-is with correct `this` typings.
|
|
3049
|
+
* `applyHookResult` handles renderer elements and `File[]` uniformly using
|
|
3050
|
+
* the generator's declared `renderer` factory.
|
|
3051
|
+
*/
|
|
3052
|
+
function defineGenerator(generator) {
|
|
3053
|
+
return generator;
|
|
3054
|
+
}
|
|
3055
|
+
//#endregion
|
|
3056
|
+
//#region src/defineLogger.ts
|
|
3057
|
+
/**
|
|
3058
|
+
* Wraps a logger definition into a typed {@link Logger}.
|
|
2717
3059
|
*
|
|
2718
|
-
* @example
|
|
2719
|
-
*
|
|
2720
|
-
*
|
|
2721
|
-
*
|
|
2722
|
-
*
|
|
2723
|
-
*
|
|
2724
|
-
* },
|
|
2725
|
-
* resolveTypedName(node) {
|
|
2726
|
-
* return this.default(node.name, 'type')
|
|
3060
|
+
* @example
|
|
3061
|
+
* export const myLogger = defineLogger({
|
|
3062
|
+
* name: 'my-logger',
|
|
3063
|
+
* install(context, options) {
|
|
3064
|
+
* context.on('kubb:info', (message) => console.log('ℹ', message))
|
|
3065
|
+
* context.on('kubb:error', (error) => console.error('✗', error.message))
|
|
2727
3066
|
* },
|
|
2728
|
-
* })
|
|
2729
|
-
|
|
3067
|
+
* })
|
|
3068
|
+
*/
|
|
3069
|
+
function defineLogger(logger) {
|
|
3070
|
+
return logger;
|
|
3071
|
+
}
|
|
3072
|
+
//#endregion
|
|
3073
|
+
//#region src/defineParser.ts
|
|
3074
|
+
/**
|
|
3075
|
+
* Defines a parser with type safety.
|
|
2730
3076
|
*
|
|
2731
|
-
*
|
|
2732
|
-
*
|
|
2733
|
-
* export const resolver = defineResolver<PluginTs>(() => ({
|
|
2734
|
-
* name: 'custom',
|
|
2735
|
-
* resolvePath({ baseName }, { root, output }) {
|
|
2736
|
-
* return path.resolve(root, output.path, 'generated', baseName)
|
|
2737
|
-
* },
|
|
2738
|
-
* }))
|
|
2739
|
-
* ```
|
|
3077
|
+
* Use this function to create parsers that transform generated files to strings
|
|
3078
|
+
* based on their extension.
|
|
2740
3079
|
*
|
|
2741
|
-
* @example
|
|
3080
|
+
* @example
|
|
2742
3081
|
* ```ts
|
|
2743
|
-
*
|
|
2744
|
-
*
|
|
2745
|
-
*
|
|
2746
|
-
*
|
|
3082
|
+
* import { defineParser } from '@kubb/core'
|
|
3083
|
+
*
|
|
3084
|
+
* export const jsonParser = defineParser({
|
|
3085
|
+
* name: 'json',
|
|
3086
|
+
* extNames: ['.json'],
|
|
3087
|
+
* parse(file) {
|
|
3088
|
+
* const { extractStringsFromNodes } = await import('@kubb/ast')
|
|
3089
|
+
* return file.sources.map((s) => extractStringsFromNodes(s.nodes ?? [])).join('\n')
|
|
2747
3090
|
* },
|
|
2748
|
-
* })
|
|
3091
|
+
* })
|
|
2749
3092
|
* ```
|
|
2750
3093
|
*/
|
|
2751
|
-
function
|
|
2752
|
-
return
|
|
2753
|
-
default: defaultResolver,
|
|
2754
|
-
resolveOptions: defaultResolveOptions,
|
|
2755
|
-
resolvePath: defaultResolvePath,
|
|
2756
|
-
resolveFile: defaultResolveFile,
|
|
2757
|
-
resolveBanner: defaultResolveBanner,
|
|
2758
|
-
resolveFooter: defaultResolveFooter,
|
|
2759
|
-
...build()
|
|
2760
|
-
};
|
|
3094
|
+
function defineParser(parser) {
|
|
3095
|
+
return parser;
|
|
2761
3096
|
}
|
|
2762
3097
|
//#endregion
|
|
2763
3098
|
//#region src/storages/memoryStorage.ts
|
|
@@ -2847,22 +3182,6 @@ async function detectFormatter() {
|
|
|
2847
3182
|
return null;
|
|
2848
3183
|
}
|
|
2849
3184
|
//#endregion
|
|
2850
|
-
//#region src/utils/getConfigs.ts
|
|
2851
|
-
/**
|
|
2852
|
-
* Resolves a {@link ConfigInput} into a normalized array of {@link Config} objects.
|
|
2853
|
-
*
|
|
2854
|
-
* - Awaits the config when it is a `Promise`.
|
|
2855
|
-
* - Calls the factory function with `args` when the config is a function.
|
|
2856
|
-
* - Wraps a single config object in an array for uniform downstream handling.
|
|
2857
|
-
*/
|
|
2858
|
-
async function getConfigs(config, args) {
|
|
2859
|
-
const resolved = await (typeof config === "function" ? config(args) : config);
|
|
2860
|
-
return (Array.isArray(resolved) ? resolved : [resolved]).map((item) => ({
|
|
2861
|
-
plugins: [],
|
|
2862
|
-
...item
|
|
2863
|
-
}));
|
|
2864
|
-
}
|
|
2865
|
-
//#endregion
|
|
2866
3185
|
//#region src/utils/getFunctionParams.ts
|
|
2867
3186
|
function order(items) {
|
|
2868
3187
|
return sortBy(items.filter(Boolean), ([_key, item]) => {
|
|
@@ -2981,47 +3300,6 @@ var FunctionParams = class FunctionParams {
|
|
|
2981
3300
|
}
|
|
2982
3301
|
};
|
|
2983
3302
|
//#endregion
|
|
2984
|
-
//#region src/utils/getPreset.ts
|
|
2985
|
-
/**
|
|
2986
|
-
* Returns a copy of `defaults` where each function in `userOverrides` is wrapped
|
|
2987
|
-
* so a `null`/`undefined` return falls back to the original. Non-function values
|
|
2988
|
-
* are assigned directly. All calls use the merged object as `this`.
|
|
2989
|
-
*/
|
|
2990
|
-
function withFallback(defaults, userOverrides) {
|
|
2991
|
-
const merged = { ...defaults };
|
|
2992
|
-
for (const key of Object.keys(userOverrides)) {
|
|
2993
|
-
const userVal = userOverrides[key];
|
|
2994
|
-
const defaultVal = defaults[key];
|
|
2995
|
-
if (typeof userVal === "function" && typeof defaultVal === "function") merged[key] = (...args) => userVal.apply(merged, args) ?? defaultVal.apply(merged, args);
|
|
2996
|
-
else if (userVal !== void 0) merged[key] = userVal;
|
|
2997
|
-
}
|
|
2998
|
-
return merged;
|
|
2999
|
-
}
|
|
3000
|
-
/**
|
|
3001
|
-
* Resolves a named preset into a resolver, transformer, and generators.
|
|
3002
|
-
*
|
|
3003
|
-
* - Selects the preset resolver; wraps it with user overrides using null/undefined fallback.
|
|
3004
|
-
* - Composes the preset's transformers into a single visitor; wraps it with the user transformer using null/undefined fallback.
|
|
3005
|
-
* - Combines preset generators with user-supplied generators; falls back to the `default` preset's generators when neither provides any.
|
|
3006
|
-
*/
|
|
3007
|
-
function getPreset(params) {
|
|
3008
|
-
const { preset: presetName, presets, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = params;
|
|
3009
|
-
const preset = presets[presetName];
|
|
3010
|
-
const presetResolver = preset?.resolver ?? presets["default"].resolver;
|
|
3011
|
-
const resolver = userResolver ? withFallback(presetResolver, userResolver) : presetResolver;
|
|
3012
|
-
const presetTransformers = preset?.transformers ?? [];
|
|
3013
|
-
const presetTransformer = presetTransformers.length > 0 ? composeTransformers$1(...presetTransformers) : void 0;
|
|
3014
|
-
const transformer = presetTransformer && userTransformer ? withFallback(presetTransformer, userTransformer) : userTransformer ?? presetTransformer;
|
|
3015
|
-
const presetGenerators = preset?.generators ?? [];
|
|
3016
|
-
const defaultGenerators = presets["default"]?.generators ?? [];
|
|
3017
|
-
return {
|
|
3018
|
-
resolver,
|
|
3019
|
-
transformer,
|
|
3020
|
-
generators: presetGenerators.length > 0 || userGenerators.length > 0 ? [...presetGenerators, ...userGenerators] : defaultGenerators,
|
|
3021
|
-
preset
|
|
3022
|
-
};
|
|
3023
|
-
}
|
|
3024
|
-
//#endregion
|
|
3025
3303
|
//#region src/utils/linters.ts
|
|
3026
3304
|
/**
|
|
3027
3305
|
* Returns `true` when the given linter is installed and callable.
|
|
@@ -3063,7 +3341,7 @@ async function detectLinter() {
|
|
|
3063
3341
|
//#endregion
|
|
3064
3342
|
//#region src/utils/packageJSON.ts
|
|
3065
3343
|
function getPackageJSONSync(cwd) {
|
|
3066
|
-
const pkgPath =
|
|
3344
|
+
const pkgPath = findPackageJSON(cwd);
|
|
3067
3345
|
if (!pkgPath) return null;
|
|
3068
3346
|
return JSON.parse(readSync(pkgPath));
|
|
3069
3347
|
}
|
|
@@ -3104,6 +3382,6 @@ function satisfiesDependency(dependency, version, cwd) {
|
|
|
3104
3382
|
return satisfies(semVer, version);
|
|
3105
3383
|
}
|
|
3106
3384
|
//#endregion
|
|
3107
|
-
export { AsyncEventEmitter, FunctionParams, PluginDriver, URLPath,
|
|
3385
|
+
export { AsyncEventEmitter, FunctionParams, PluginDriver, URLPath, ast, buildDefaultBanner, composeTransformers, createAdapter, createFunctionParams, createKubb, createPlugin, createRenderer, createStorage, defaultResolveBanner, defaultResolveFile, defaultResolveFooter, defaultResolveOptions, defaultResolvePath, defineGenerator, defineLogger, defineParser, definePlugin, definePrinter, defineResolver, detectFormatter, detectLinter, formatters, fsStorage, getBarrelFiles, getFunctionParams, getMode, isInputPath, linters, logLevel, memoryStorage, satisfiesDependency };
|
|
3108
3386
|
|
|
3109
3387
|
//# sourceMappingURL=index.js.map
|