@kubb/core 5.0.0-alpha.54 → 5.0.0-alpha.56

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/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { t as __name } from "./chunk--u3MIqq1.js";
2
- import { c as DEFAULT_EXTENSION, d as camelCase, i as defineResolver, l as DEFAULT_STUDIO_URL, n as applyHookResult, o as BARREL_FILENAME, r as FileManager, s as DEFAULT_BANNER, t as PluginDriver, u as logLevel } from "./PluginDriver-BU7faPiI.js";
2
+ import { a as DEFAULT_BANNER, c as logLevel, i as defineResolver, l as camelCase, n as applyHookResult, o as DEFAULT_EXTENSION, r as FileManager, s as DEFAULT_STUDIO_URL, t as PluginDriver } from "./PluginDriver-Cn9cRX4m.js";
3
3
  import { EventEmitter } from "node:events";
4
4
  import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
5
- import path, { dirname, join, posix, resolve } from "node:path";
5
+ import { dirname, join, resolve } from "node:path";
6
6
  import * as ast from "@kubb/ast";
7
- import { createExport, createFile, createSource, extractStringsFromNodes, transform, walk } from "@kubb/ast";
7
+ import { extractStringsFromNodes, transform, walk } from "@kubb/ast";
8
8
  import { version } from "node:process";
9
9
  //#region ../../internals/utils/src/errors.ts
10
10
  /**
@@ -181,29 +181,6 @@ function formatMs(ms) {
181
181
  //#endregion
182
182
  //#region ../../internals/utils/src/fs.ts
183
183
  /**
184
- * Converts all backslashes to forward slashes.
185
- * Extended-length Windows paths (`\\?\...`) are left unchanged.
186
- */
187
- function toSlash(p) {
188
- if (p.startsWith("\\\\?\\")) return p;
189
- return p.replaceAll("\\", "/");
190
- }
191
- /**
192
- * Returns the relative path from `rootDir` to `filePath`, always using forward slashes
193
- * and prefixed with `./` when not already traversing upward.
194
- *
195
- * @example
196
- * ```ts
197
- * getRelativePath('/src/components', '/src/components/Button.tsx') // './Button.tsx'
198
- * getRelativePath('/src/components', '/src/utils/helpers.ts') // '../utils/helpers.ts'
199
- * ```
200
- */
201
- function getRelativePath(rootDir, filePath) {
202
- if (!rootDir || !filePath) throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ""} ${filePath || ""}`);
203
- const relativePath = posix.relative(toSlash(rootDir), toSlash(filePath));
204
- return relativePath.startsWith("../") ? relativePath : `./${relativePath}`;
205
- }
206
- /**
207
184
  * Resolves to `true` when the file or directory at `path` exists.
208
185
  * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
209
186
  *
@@ -737,7 +714,7 @@ const fsStorage = createStorage(() => ({
737
714
  }));
738
715
  //#endregion
739
716
  //#region package.json
740
- var version$1 = "5.0.0-alpha.54";
717
+ var version$1 = "5.0.0-alpha.56";
741
718
  //#endregion
742
719
  //#region src/utils/diagnostics.ts
743
720
  /**
@@ -756,239 +733,6 @@ function getDiagnosticInfo() {
756
733
  };
757
734
  }
758
735
  //#endregion
759
- //#region src/utils/TreeNode.ts
760
- /**
761
- * Tree structure used to build per-directory barrel (`index.ts`) files from a
762
- * flat list of generated {@link FileNode} entries.
763
- *
764
- * Each node represents either a directory or a file within the output tree.
765
- * Use {@link TreeNode.build} to construct a root node from a file list, then
766
- * traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
767
- * `*Deep` helpers.
768
- */
769
- var TreeNode = class TreeNode {
770
- data;
771
- parent;
772
- children = [];
773
- #cachedLeaves = void 0;
774
- constructor(data, parent) {
775
- this.data = data;
776
- this.parent = parent;
777
- }
778
- addChild(data) {
779
- const child = new TreeNode(data, this);
780
- if (!this.children) this.children = [];
781
- this.children.push(child);
782
- return child;
783
- }
784
- /**
785
- * Returns the root ancestor of this node, walking up via `parent` links.
786
- */
787
- get root() {
788
- if (!this.parent) return this;
789
- return this.parent.root;
790
- }
791
- /**
792
- * Returns all leaf descendants (nodes with no children) of this node.
793
- *
794
- * Results are cached after the first traversal.
795
- */
796
- get leaves() {
797
- if (!this.children || this.children.length === 0) return [this];
798
- if (this.#cachedLeaves) return this.#cachedLeaves;
799
- const leaves = [];
800
- for (const child of this.children) leaves.push(...child.leaves);
801
- this.#cachedLeaves = leaves;
802
- return leaves;
803
- }
804
- /**
805
- * Visits this node and every descendant in depth-first order.
806
- */
807
- forEach(callback) {
808
- if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
809
- callback(this);
810
- for (const child of this.children) child.forEach(callback);
811
- return this;
812
- }
813
- /**
814
- * Finds the first leaf that satisfies `predicate`, or `undefined` when none match.
815
- */
816
- findDeep(predicate) {
817
- if (typeof predicate !== "function") throw new TypeError("find() predicate must be a function");
818
- return this.leaves.find(predicate);
819
- }
820
- /**
821
- * Calls `callback` for every leaf of this node.
822
- */
823
- forEachDeep(callback) {
824
- if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
825
- this.leaves.forEach(callback);
826
- }
827
- /**
828
- * Returns all leaves that satisfy `callback`.
829
- */
830
- filterDeep(callback) {
831
- if (typeof callback !== "function") throw new TypeError("filter() callback must be a function");
832
- return this.leaves.filter(callback);
833
- }
834
- /**
835
- * Maps every leaf through `callback` and returns the resulting array.
836
- */
837
- mapDeep(callback) {
838
- if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
839
- return this.leaves.map(callback);
840
- }
841
- /**
842
- * Builds a {@link TreeNode} tree from a flat list of files.
843
- *
844
- * - Filters to files under `root` (when provided) and skips `.json` files.
845
- * - Returns `null` when no files match.
846
- */
847
- static build(files, root) {
848
- try {
849
- const filteredTree = buildDirectoryTree(files, root);
850
- if (!filteredTree) return null;
851
- const treeNode = new TreeNode({
852
- name: filteredTree.name,
853
- path: filteredTree.path,
854
- file: filteredTree.file,
855
- type: PluginDriver.getMode(filteredTree.path)
856
- });
857
- const recurse = (node, item) => {
858
- const subNode = node.addChild({
859
- name: item.name,
860
- path: item.path,
861
- file: item.file,
862
- type: PluginDriver.getMode(item.path)
863
- });
864
- if (item.children?.length) item.children?.forEach((child) => {
865
- recurse(subNode, child);
866
- });
867
- };
868
- filteredTree.children?.forEach((child) => {
869
- recurse(treeNode, child);
870
- });
871
- return treeNode;
872
- } catch (error) {
873
- throw new Error("Something went wrong with creating barrel files with the TreeNode class", { cause: error });
874
- }
875
- }
876
- };
877
- const normalizePath = (p) => p.replaceAll("\\", "/");
878
- function buildDirectoryTree(files, rootFolder = "") {
879
- const normalizedRootFolder = normalizePath(rootFolder);
880
- const rootPrefix = normalizedRootFolder.endsWith("/") ? normalizedRootFolder : `${normalizedRootFolder}/`;
881
- const filteredFiles = files.filter((file) => {
882
- const normalizedFilePath = normalizePath(file.path);
883
- return rootFolder ? normalizedFilePath.startsWith(rootPrefix) && !normalizedFilePath.endsWith(".json") : !normalizedFilePath.endsWith(".json");
884
- });
885
- if (filteredFiles.length === 0) return null;
886
- const root = {
887
- name: rootFolder || "",
888
- path: rootFolder || "",
889
- children: []
890
- };
891
- filteredFiles.forEach((file) => {
892
- const parts = file.path.slice(rootFolder.length).split("/").filter(Boolean);
893
- let currentLevel = root.children;
894
- let currentPath = normalizePath(rootFolder);
895
- parts.forEach((part, index) => {
896
- currentPath = path.posix.join(currentPath, part);
897
- let existingNode = currentLevel.find((node) => node.name === part);
898
- if (!existingNode) {
899
- if (index === parts.length - 1) existingNode = {
900
- name: part,
901
- file,
902
- path: currentPath
903
- };
904
- else existingNode = {
905
- name: part,
906
- path: currentPath,
907
- children: []
908
- };
909
- currentLevel.push(existingNode);
910
- }
911
- if (!existingNode.file) currentLevel = existingNode.children;
912
- });
913
- });
914
- return root;
915
- }
916
- //#endregion
917
- //#region src/utils/getBarrelFiles.ts
918
- function getBarrelFilesByRoot(root, files) {
919
- const cachedFiles = /* @__PURE__ */ new Map();
920
- TreeNode.build(files, root)?.forEach((treeNode) => {
921
- if (!treeNode?.children || !treeNode.parent?.data.path) return;
922
- const barrelFile = createFile({
923
- path: join(treeNode.parent?.data.path, BARREL_FILENAME),
924
- baseName: BARREL_FILENAME,
925
- exports: [],
926
- imports: [],
927
- sources: []
928
- });
929
- const previousBarrelFile = cachedFiles.get(barrelFile.path);
930
- treeNode.leaves.forEach((item) => {
931
- if (!item.data.name) return;
932
- (item.data.file?.sources || []).forEach((source) => {
933
- if (!item.data.file?.path || !source.isIndexable || !source.name) return;
934
- if (previousBarrelFile?.sources.some((item) => item.name === source.name && item.isTypeOnly === source.isTypeOnly)) return;
935
- barrelFile.exports.push(createExport({
936
- name: [source.name],
937
- path: getRelativePath(treeNode.parent?.data.path, item.data.path),
938
- isTypeOnly: source.isTypeOnly
939
- }));
940
- barrelFile.sources.push(createSource({
941
- name: source.name,
942
- isTypeOnly: source.isTypeOnly,
943
- isExportable: false,
944
- isIndexable: false
945
- }));
946
- });
947
- });
948
- if (previousBarrelFile) {
949
- previousBarrelFile.sources.push(...barrelFile.sources);
950
- previousBarrelFile.exports.push(...barrelFile.exports);
951
- } else cachedFiles.set(barrelFile.path, barrelFile);
952
- });
953
- return [...cachedFiles.values()];
954
- }
955
- function trimExtName(text) {
956
- const dotIndex = text.lastIndexOf(".");
957
- if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
958
- return text;
959
- }
960
- /**
961
- * Generates `index.ts` barrel files for all directories under `root/output.path`.
962
- *
963
- * - Returns an empty array when `type` is falsy or `'propagate'`.
964
- * - Skips generation when the output path itself ends with `index` (already a barrel).
965
- * - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
966
- * - Attaches `meta` to each barrel file for downstream plugin identification.
967
- */
968
- async function getBarrelFiles(files, { type, meta = {}, root, output }) {
969
- if (!type || type === "propagate") return [];
970
- const pathToBuildFrom = join(root, output.path);
971
- if (trimExtName(pathToBuildFrom).endsWith("index")) return [];
972
- const barrelFiles = getBarrelFilesByRoot(pathToBuildFrom, files);
973
- if (type === "all") return barrelFiles.map((file) => {
974
- return {
975
- ...file,
976
- exports: file.exports.map((exportItem) => {
977
- return {
978
- ...exportItem,
979
- name: void 0
980
- };
981
- })
982
- };
983
- });
984
- return barrelFiles.map((indexFile) => {
985
- return {
986
- ...indexFile,
987
- meta
988
- };
989
- });
990
- }
991
- //#endregion
992
736
  //#region src/utils/isInputPath.ts
993
737
  function isInputPath(config) {
994
738
  return typeof config?.input === "object" && config.input !== null && "path" in config.input;
@@ -1038,7 +782,6 @@ async function setup(userConfig, options = {}) {
1038
782
  adapter: userConfig.adapter,
1039
783
  output: {
1040
784
  write: true,
1041
- barrelType: "named",
1042
785
  extension: DEFAULT_EXTENSION,
1043
786
  defaultBanner: DEFAULT_BANNER,
1044
787
  ...userConfig.output
@@ -1058,6 +801,7 @@ async function setup(userConfig, options = {}) {
1058
801
  await storage?.clear(resolve(config.root, config.output.path));
1059
802
  }
1060
803
  const driver = new PluginDriver(config, { hooks });
804
+ for (const middleware of config.middleware ?? []) middleware.install(hooks);
1061
805
  const adapter = config.adapter;
1062
806
  if (!adapter) throw new Error("No adapter configured. Please provide an adapter in your kubb.config.ts.");
1063
807
  const source = inputToAdapterSource(config);
@@ -1166,13 +910,15 @@ async function safeBuild(setupResult) {
1166
910
  config,
1167
911
  adapter: driver.adapter,
1168
912
  inputNode: driver.inputNode,
1169
- getPlugin: driver.getPlugin.bind(driver)
913
+ getPlugin: driver.getPlugin.bind(driver),
914
+ get files() {
915
+ return driver.fileManager.files;
916
+ },
917
+ upsertFile: (...files) => driver.fileManager.upsert(...files)
1170
918
  });
1171
919
  for (const plugin of driver.plugins.values()) {
1172
920
  const context = driver.getContext(plugin);
1173
921
  const hrStart = process.hrtime();
1174
- const { output } = plugin.options ?? {};
1175
- const root = resolve(config.root, config.output.path);
1176
922
  try {
1177
923
  const timestamp = /* @__PURE__ */ new Date();
1178
924
  await hooks.emit("kubb:plugin:start", { plugin });
@@ -1181,15 +927,6 @@ async function safeBuild(setupResult) {
1181
927
  logs: ["Starting plugin...", ` • Plugin Name: ${plugin.name}`]
1182
928
  });
1183
929
  if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) await runPluginAstHooks(plugin, context);
1184
- if (output) {
1185
- const barrelFiles = await getBarrelFiles(driver.fileManager.files, {
1186
- type: output.barrelType ?? "named",
1187
- root,
1188
- output,
1189
- meta: { pluginName: plugin.name }
1190
- });
1191
- await context.upsertFile(...barrelFiles);
1192
- }
1193
930
  const duration = getElapsedMs(hrStart);
1194
931
  pluginTimings.set(plugin.name, duration);
1195
932
  await hooks.emit("kubb:plugin:end", {
@@ -1227,45 +964,13 @@ async function safeBuild(setupResult) {
1227
964
  });
1228
965
  }
1229
966
  }
1230
- if (config.output.barrelType) {
1231
- const rootPath = resolve(resolve(config.root), config.output.path, BARREL_FILENAME);
1232
- const rootDir = dirname(rootPath);
1233
- await hooks.emit("kubb:debug", {
1234
- date: /* @__PURE__ */ new Date(),
1235
- logs: [
1236
- "Generating barrel file",
1237
- ` • Type: ${config.output.barrelType}`,
1238
- ` • Path: ${rootPath}`
1239
- ]
1240
- });
1241
- const barrelFiles = driver.fileManager.files.filter((file) => {
1242
- return file.sources.some((source) => source.isIndexable);
1243
- });
1244
- await hooks.emit("kubb:debug", {
1245
- date: /* @__PURE__ */ new Date(),
1246
- logs: [`Found ${barrelFiles.length} indexable files for barrel export`]
1247
- });
1248
- const existingBarrel = driver.fileManager.files.find((f) => f.path === rootPath);
1249
- const rootFile = createFile({
1250
- path: rootPath,
1251
- baseName: BARREL_FILENAME,
1252
- exports: buildBarrelExports({
1253
- barrelFiles,
1254
- rootDir,
1255
- existingExports: new Set(existingBarrel?.exports?.flatMap((e) => Array.isArray(e.name) ? e.name : [e.name]).filter((n) => Boolean(n)) ?? []),
1256
- config,
1257
- driver
1258
- }).map((e) => createExport(e)),
1259
- sources: [],
1260
- imports: [],
1261
- meta: {}
1262
- });
1263
- driver.fileManager.upsert(rootFile);
1264
- await hooks.emit("kubb:debug", {
1265
- date: /* @__PURE__ */ new Date(),
1266
- logs: [`✓ Generated barrel file (${rootFile.exports?.length || 0} exports)`]
1267
- });
1268
- }
967
+ await hooks.emit("kubb:plugins:end", {
968
+ config,
969
+ get files() {
970
+ return driver.fileManager.files;
971
+ },
972
+ upsertFile: (...files) => driver.fileManager.upsert(...files)
973
+ });
1269
974
  const files = driver.fileManager.files;
1270
975
  const parsersMap = /* @__PURE__ */ new Map();
1271
976
  for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
@@ -1343,26 +1048,6 @@ async function build(setupResult) {
1343
1048
  sources
1344
1049
  };
1345
1050
  }
1346
- function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }) {
1347
- const pluginNameMap = /* @__PURE__ */ new Map();
1348
- for (const plugin of driver.plugins.values()) pluginNameMap.set(plugin.name, plugin);
1349
- return barrelFiles.flatMap((file) => {
1350
- const containsOnlyTypes = file.sources?.every((source) => source.isTypeOnly);
1351
- return (file.sources ?? []).flatMap((source) => {
1352
- if (!file.path || !source.isIndexable) return [];
1353
- const meta = file.meta;
1354
- const pluginOptions = (meta?.pluginName ? pluginNameMap.get(meta.pluginName) : void 0)?.options;
1355
- if (!pluginOptions || pluginOptions.output?.barrelType === false) return [];
1356
- const exportName = config.output.barrelType === "all" ? void 0 : source.name ? [source.name] : void 0;
1357
- if (exportName?.some((n) => existingExports.has(n))) return [];
1358
- return [createExport({
1359
- name: exportName,
1360
- path: getRelativePath(rootDir, file.path),
1361
- isTypeOnly: config.output.barrelType === "all" ? containsOnlyTypes : source.isTypeOnly
1362
- })];
1363
- });
1364
- });
1365
- }
1366
1051
  function inputToAdapterSource(config) {
1367
1052
  if (Array.isArray(config.input)) return {
1368
1053
  type: "paths",
@@ -1491,6 +1176,28 @@ function defineLogger(logger) {
1491
1176
  return logger;
1492
1177
  }
1493
1178
  //#endregion
1179
+ //#region src/defineMiddleware.ts
1180
+ /**
1181
+ * Identity factory for middleware.
1182
+ * Returns the middleware object unchanged but provides a typed entry-point
1183
+ * to define middleware with proper inference.
1184
+ *
1185
+ * @example
1186
+ * ```ts
1187
+ * export const myMiddleware = defineMiddleware({
1188
+ * name: 'my-middleware',
1189
+ * install(hooks) {
1190
+ * hooks.on('kubb:build:end', ({ files }) => {
1191
+ * console.log(`Build complete with ${files.length} files`)
1192
+ * })
1193
+ * },
1194
+ * })
1195
+ * ```
1196
+ */
1197
+ function defineMiddleware(middleware) {
1198
+ return middleware;
1199
+ }
1200
+ //#endregion
1494
1201
  //#region src/defineParser.ts
1495
1202
  /**
1496
1203
  * Defines a parser with type safety.
@@ -1593,6 +1300,6 @@ const memoryStorage = createStorage(() => {
1593
1300
  };
1594
1301
  });
1595
1302
  //#endregion
1596
- export { AsyncEventEmitter, FileManager, FileProcessor, PluginDriver, URLPath, ast, createAdapter, createKubb, createRenderer, createStorage, defineGenerator, defineLogger, defineParser, definePlugin, defineResolver, fsStorage, isInputPath, logLevel, memoryStorage };
1303
+ export { AsyncEventEmitter, FileManager, FileProcessor, PluginDriver, URLPath, ast, createAdapter, createKubb, createRenderer, createStorage, defineGenerator, defineLogger, defineMiddleware, defineParser, definePlugin, defineResolver, fsStorage, isInputPath, logLevel, memoryStorage };
1597
1304
 
1598
1305
  //# sourceMappingURL=index.js.map