@kubb/middleware-barrel 5.0.0-beta.4 → 5.0.0-beta.41

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,4 +1,4 @@
1
- import "./chunk--u3MIqq1.js";
1
+ import "./chunk-C0LytTxp.js";
2
2
  import path, { extname, resolve } from "node:path";
3
3
  import { defineMiddleware } from "@kubb/core";
4
4
  import { createExport, createFile } from "@kubb/ast";
@@ -46,7 +46,7 @@ function buildTree(rootPath, filePaths) {
46
46
  children: [],
47
47
  isFile: false
48
48
  };
49
- const childIndex = /* @__PURE__ */ new Map();
49
+ const childIndex = /* @__PURE__ */ new WeakMap();
50
50
  childIndex.set(root, /* @__PURE__ */ new Map());
51
51
  const rootPrefix = `${normalizedRoot}/`;
52
52
  for (const filePath of filePaths) {
@@ -86,39 +86,14 @@ function compareByPath(a, b) {
86
86
  return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
87
87
  }
88
88
  //#endregion
89
- //#region src/utils/excludedPaths.ts
90
- /**
91
- * Builds a POSIX-normalized prefix for a plugin's output directory, with a trailing `/`.
92
- *
93
- * Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.
94
- */
95
- function getPluginOutputPrefix(plugin, config) {
96
- return `${toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path))}/`;
97
- }
98
- /**
99
- * Returns `true` when `filePath` lives under any of the given excluded directory prefixes.
100
- *
101
- * Both sides are POSIX-normalized so Windows backslash paths match correctly.
102
- */
103
- function isExcludedPath(filePath, prefixes) {
104
- const normalized = toPosixPath(filePath);
105
- return prefixes.values().some((prefix) => normalized.startsWith(prefix));
106
- }
107
- //#endregion
108
- //#region src/constants.ts
109
- /**
110
- * Full file name for barrel files (with extension).
111
- */
112
- const BARREL_FILENAME = "index.ts";
113
- //#endregion
114
- //#region src/utils/getBarrelFiles.ts
89
+ //#region src/utils.ts
115
90
  const SOURCE_EXTENSIONS = new Set([
116
91
  ".ts",
117
92
  ".tsx",
118
93
  ".js",
119
94
  ".jsx"
120
95
  ]);
121
- const BARREL_SUFFIX = `/${BARREL_FILENAME}`;
96
+ const BARREL_SUFFIX = `/index.ts`;
122
97
  function toRelativeModulePath(fromDir, filePath) {
123
98
  return `./${filePath.slice(fromDir.length + 1)}`;
124
99
  }
@@ -127,7 +102,7 @@ function isBarrelPath(path) {
127
102
  }
128
103
  function makeBarrel(dirPath, exports) {
129
104
  return createFile({
130
- baseName: BARREL_FILENAME,
105
+ baseName: "index.ts",
131
106
  path: `${dirPath}${BARREL_SUFFIX}`,
132
107
  exports,
133
108
  sources: [],
@@ -177,32 +152,33 @@ const namedStrategy = ({ dirPath, leafPath, sourceFile }) => {
177
152
  };
178
153
  const LEAF_STRATEGIES = new Map([["all", allStrategy], ["named", namedStrategy]]);
179
154
  /**
180
- * Post-order walk that emits a barrel per visited directory.
155
+ * Post-order walk that yields a barrel per visited directory.
156
+ * Returns the list of leaf file paths collected in this subtree (used by the parent call).
181
157
  */
182
- function walkAllOrNamed(node, params, isRoot, out) {
158
+ function* walkAllOrNamed(node, params, isRoot) {
183
159
  const subtreeLeaves = [];
184
160
  for (const child of node.children) {
185
161
  if (child.isFile) {
186
162
  if (!isBarrelPath(child.path)) subtreeLeaves.push(child.path);
187
163
  continue;
188
164
  }
189
- const childLeaves = walkAllOrNamed(child, params, false, out);
165
+ const childLeaves = yield* walkAllOrNamed(child, params, false);
190
166
  for (const leaf of childLeaves) subtreeLeaves.push(leaf);
191
167
  }
192
168
  if (!isRoot && !params.recursive) return subtreeLeaves;
193
169
  const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({
194
170
  dirPath: node.path,
195
171
  leafPath,
196
- sourceFile: params.sourceFiles.get(leafPath)
172
+ sourceFile: params.sourceFiles.get(leafPath) ?? null
197
173
  }));
198
- if (exports.length > 0) out.push(makeBarrel(node.path, exports));
174
+ if (exports.length > 0) yield makeBarrel(node.path, exports);
199
175
  return subtreeLeaves;
200
176
  }
201
177
  /**
202
- * Recursive walk that emits one barrel per directory, re-exporting files and sub-barrels.
178
+ * Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.
203
179
  * Used when nested: true.
204
180
  */
205
- function walkNested(node, out) {
181
+ function* walkNested(node) {
206
182
  const exports = [];
207
183
  for (const child of node.children) {
208
184
  if (child.isFile) {
@@ -210,10 +186,10 @@ function walkNested(node, out) {
210
186
  exports.push(createExport({ path: toRelativeModulePath(node.path, child.path) }));
211
187
  continue;
212
188
  }
213
- walkNested(child, out);
189
+ yield* walkNested(child);
214
190
  exports.push(createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }));
215
191
  }
216
- if (exports.length > 0) out.push(makeBarrel(node.path, exports));
192
+ if (exports.length > 0) yield makeBarrel(node.path, exports);
217
193
  }
218
194
  function indexRelevantFiles(files, outputPath) {
219
195
  const outputPrefix = `${toPosixPath(outputPath)}/`;
@@ -233,40 +209,104 @@ function indexRelevantFiles(files, outputPath) {
233
209
  };
234
210
  }
235
211
  /**
236
- * Generates barrel `FileNode`s for the directory rooted at `outputPath`.
212
+ * Yields barrel `FileNode`s for the directory rooted at `outputPath`.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * for (const file of getBarrelFiles({ outputPath, files, barrelType })) {
217
+ * upsertFile(file)
218
+ * }
219
+ * // or collect into an array
220
+ * const barrels = [...getBarrelFiles({ outputPath, files, barrelType })]
221
+ * ```
237
222
  */
238
- function getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }) {
223
+ function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }) {
239
224
  const { sourceFiles, paths } = indexRelevantFiles(files, outputPath);
240
- if (paths.length === 0) return [];
225
+ if (paths.length === 0) return;
241
226
  const tree = buildTree(outputPath, paths);
242
- const result = [];
243
227
  if (nested) {
244
- walkNested(tree, result);
245
- return result;
228
+ yield* walkNested(tree);
229
+ return;
246
230
  }
247
231
  const strategy = LEAF_STRATEGIES.get(barrelType);
248
- if (!strategy) return result;
249
- walkAllOrNamed(tree, {
232
+ if (!strategy) return;
233
+ yield* walkAllOrNamed(tree, {
250
234
  sourceFiles,
251
235
  strategy,
252
236
  recursive
253
- }, true, result);
254
- return result;
237
+ }, true);
238
+ }
239
+ /**
240
+ * Builds a POSIX-normalized prefix for a plugin's output directory, with a trailing `/`.
241
+ *
242
+ * Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.
243
+ */
244
+ function getPluginOutputPrefix(plugin, config) {
245
+ return `${toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path))}/`;
246
+ }
247
+ /**
248
+ * Returns `true` when `filePath` lives under any of the given excluded directory prefixes.
249
+ *
250
+ * Both sides are POSIX-normalized so Windows backslash paths match correctly.
251
+ */
252
+ function isExcludedPath(filePath, prefixes) {
253
+ const normalized = toPosixPath(filePath);
254
+ return prefixes.values().some((prefix) => normalized.startsWith(prefix));
255
255
  }
256
256
  //#endregion
257
257
  //#region src/middleware.ts
258
258
  /**
259
- * Generates `index.ts` barrel files for each plugin and a root barrel at `config.output.path/index.ts`.
259
+ * Applies a plugin's configured `output.banner`/`footer` to a barrel file, flagged as `isBarrel`.
260
+ *
261
+ * Resolves through the plugin's own resolver, and only when the plugin explicitly sets a
262
+ * banner/footer, so barrels stay banner-free by default and never inherit the implicit
263
+ * "Generated by Kubb" notice.
264
+ */
265
+ function withBarrelBannerFooter({ file, plugin, config }) {
266
+ const output = plugin.options?.output;
267
+ const resolver = plugin.resolver;
268
+ if (!resolver) return file;
269
+ const hasBanner = output?.banner !== void 0;
270
+ const hasFooter = output?.footer !== void 0;
271
+ if (!hasBanner && !hasFooter) return file;
272
+ const context = {
273
+ output,
274
+ config,
275
+ file: {
276
+ path: file.path,
277
+ baseName: file.baseName,
278
+ isBarrel: true
279
+ }
280
+ };
281
+ return {
282
+ ...file,
283
+ banner: hasBanner ? resolver.resolveBanner(void 0, context) : file.banner,
284
+ footer: hasFooter ? resolver.resolveFooter(void 0, context) : file.footer
285
+ };
286
+ }
287
+ /**
288
+ * Canonical middleware name for `@kubb/middleware-barrel`. Used for driver
289
+ * lookups.
290
+ */
291
+ const middlewareBarrelName = "middleware-barrel";
292
+ /**
293
+ * Generates an `index.ts` for every plugin output directory and one root
294
+ * barrel at `config.output.path/index.ts` after the build completes. Ships
295
+ * with Kubb and is registered by default in `defineConfig`.
260
296
  *
261
- * Each plugin inherits `output.barrel` from `config.output.barrel` (defaults to `{ type: 'named' }`).
262
- * Set `barrel: false` on a plugin to disable its barrel and exclude it from the root barrel.
297
+ * Each plugin inherits `output.barrel` from `config.output.barrel` (which
298
+ * defaults to `{ type: 'named' }`). Set `barrel: false` on a plugin to skip
299
+ * its barrel and also exclude its files from the root barrel.
263
300
  *
264
301
  * @example
265
302
  * ```ts
266
303
  * import { defineConfig } from '@kubb/core'
267
304
  * import { middlewareBarrel } from '@kubb/middleware-barrel'
305
+ * import { pluginTs } from '@kubb/plugin-ts'
306
+ * import { pluginZod } from '@kubb/plugin-zod'
268
307
  *
269
308
  * export default defineConfig({
309
+ * input: { path: './petStore.yaml' },
270
310
  * output: { path: 'src/gen', barrel: { type: 'named' } },
271
311
  * plugins: [
272
312
  * pluginTs({ output: { path: 'types', barrel: { type: 'all' } } }),
@@ -276,10 +316,6 @@ function getBarrelFiles({ outputPath, files, barrelType, nested = false, recursi
276
316
  * })
277
317
  * ```
278
318
  */
279
- /**
280
- * Stable string identifier for the barrel middleware.
281
- */
282
- const middlewareBarrelName = "middleware-barrel";
283
319
  const middlewareBarrel = defineMiddleware(() => {
284
320
  const excludedPrefixes = /* @__PURE__ */ new Set();
285
321
  return {
@@ -289,13 +325,14 @@ const middlewareBarrel = defineMiddleware(() => {
289
325
  const pluginBarrel = plugin.options.output?.barrel;
290
326
  const configBarrel = config.output.barrel;
291
327
  const defaultBarrel = { type: "named" };
292
- let barrelConfig;
293
- if (pluginBarrel !== void 0) barrelConfig = pluginBarrel;
294
- else if (configBarrel !== void 0) barrelConfig = configBarrel === false ? false : {
295
- ...configBarrel,
296
- nested: false
297
- };
298
- else barrelConfig = defaultBarrel;
328
+ const barrelConfig = (() => {
329
+ if (pluginBarrel !== void 0) return pluginBarrel;
330
+ if (configBarrel !== void 0) return configBarrel === false ? false : {
331
+ ...configBarrel,
332
+ nested: false
333
+ };
334
+ return defaultBarrel;
335
+ })();
299
336
  if (barrelConfig === false) {
300
337
  excludedPrefixes.add(getPluginOutputPrefix(plugin, config));
301
338
  return;
@@ -306,14 +343,17 @@ const middlewareBarrel = defineMiddleware(() => {
306
343
  const target = resolve(base, plugin.options.output.path);
307
344
  const relative = path.relative(base, target);
308
345
  if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error("Invalid output path");
309
- const barrelFiles = getBarrelFiles({
346
+ for (const file of getBarrelFiles({
310
347
  outputPath: target,
311
348
  files,
312
349
  barrelType,
313
350
  nested,
314
351
  recursive: true
315
- });
316
- if (barrelFiles.length > 0) upsertFile(...barrelFiles);
352
+ })) upsertFile(withBarrelBannerFooter({
353
+ file,
354
+ plugin,
355
+ config
356
+ }));
317
357
  },
318
358
  "kubb:plugins:end"({ files, config, upsertFile }) {
319
359
  const barrelConfig = config.output.barrel ?? { type: "named" };
@@ -321,12 +361,11 @@ const middlewareBarrel = defineMiddleware(() => {
321
361
  excludedPrefixes.clear();
322
362
  if (barrelConfig === false) return;
323
363
  const barrelType = barrelConfig.type;
324
- const rootBarrelFiles = getBarrelFiles({
364
+ for (const file of getBarrelFiles({
325
365
  outputPath: resolve(config.root, config.output.path),
326
366
  files: filteredFiles,
327
367
  barrelType
328
- });
329
- if (rootBarrelFiles.length > 0) upsertFile(...rootBarrelFiles);
368
+ })) upsertFile(file);
330
369
  }
331
370
  }
332
371
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../internals/utils/src/path.ts","../../../internals/utils/src/buildTree.ts","../src/utils/excludedPaths.ts","../src/constants.ts","../src/utils/getBarrelFiles.ts","../src/middleware.ts"],"sourcesContent":["/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n","import { toPosixPath } from './path.ts'\n\n/**\n * A node in the directory tree used to compute barrel file exports.\n * Either represents a directory (with `children`) or a file (`isFile: true`, empty `children`).\n */\nexport type BuildTree = {\n /**\n * Absolute filesystem path of this directory or file. Always normalized to POSIX (`/`) separators.\n */\n path: string\n /**\n * Sub-directories and files contained within this directory.\n * Always empty for file nodes.\n */\n children: Array<BuildTree>\n /**\n * `true` when this node represents a file (leaf), `false` for directory nodes.\n */\n isFile: boolean\n}\n\n/**\n * Builds a directory tree rooted at `rootPath` from a list of absolute file paths.\n * Paths outside `rootPath` are silently ignored. Children are sorted alphabetically\n * by path so consumers (barrel exports, propagated indexes) emit a deterministic order.\n *\n * Both POSIX (`/`) and Windows (`\\`) separators are accepted in input paths; emitted node\n * paths are always POSIX-normalized so downstream prefix/lookup operations behave the same\n * across platforms.\n *\n * @example\n * ```ts\n * buildTree('/src/gen/types', [\n * '/src/gen/types/pet.ts',\n * '/src/gen/types/pets/listPets.ts',\n * ])\n * ```\n */\nexport function buildTree(rootPath: string, filePaths: ReadonlyArray<string>): BuildTree {\n const normalizedRoot = toPosixPath(rootPath)\n const root: BuildTree = { path: normalizedRoot, children: [], isFile: false }\n // Per-directory child lookup avoids the O(N) `Array.find` scan during insertion.\n const childIndex = new Map<BuildTree, Map<string, BuildTree>>()\n childIndex.set(root, new Map())\n\n const rootPrefix = `${normalizedRoot}/`\n\n for (const filePath of filePaths) {\n const normalized = toPosixPath(filePath)\n if (!normalized.startsWith(rootPrefix)) continue\n\n const parts = normalized.slice(rootPrefix.length).split('/')\n if (parts.length === 0) continue\n\n let current = root\n const lastIndex = parts.length - 1\n for (const [i, part] of parts.entries()) {\n if (!part) continue\n\n const isLast = i === lastIndex\n const siblings = childIndex.get(current)!\n let child = siblings.get(part)\n if (!child) {\n child = { path: `${current.path}/${part}`, children: [], isFile: isLast }\n current.children.push(child)\n siblings.set(part, child)\n if (!isLast) childIndex.set(child, new Map())\n }\n current = child\n }\n }\n\n sortTree(root)\n\n return root\n}\n\nfunction sortTree(node: BuildTree): void {\n if (node.children.length === 0) return\n node.children.sort(compareByPath)\n for (const child of node.children) {\n if (!child.isFile) sortTree(child)\n }\n}\n\nfunction compareByPath(a: BuildTree, b: BuildTree): number {\n return a.path < b.path ? -1 : a.path > b.path ? 1 : 0\n}\n","import { resolve } from 'node:path'\nimport type { Config, NormalizedPlugin } from '@kubb/core'\nimport { toPosixPath } from '@internals/utils'\n\n/**\n * Builds a POSIX-normalized prefix for a plugin's output directory, with a trailing `/`.\n *\n * Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.\n */\nexport function getPluginOutputPrefix(plugin: NormalizedPlugin, config: Config): string {\n return `${toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path))}/`\n}\n\n/**\n * Returns `true` when `filePath` lives under any of the given excluded directory prefixes.\n *\n * Both sides are POSIX-normalized so Windows backslash paths match correctly.\n */\nexport function isExcludedPath(filePath: string, prefixes: ReadonlySet<string>): boolean {\n const normalized = toPosixPath(filePath)\n return prefixes.values().some((prefix) => normalized.startsWith(prefix))\n}\n","/**\n * Full file name for barrel files (with extension).\n */\nexport const BARREL_FILENAME = 'index.ts' as const\n","import { extname } from 'node:path'\nimport { createExport, createFile } from '@kubb/ast'\nimport type { ExportNode, FileNode, SourceNode } from '@kubb/ast'\nimport { BARREL_FILENAME } from '../constants.ts'\nimport type { BarrelType } from '../types.ts'\nimport { type BuildTree, buildTree, toPosixPath } from '@internals/utils'\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\nconst BARREL_SUFFIX = `/${BARREL_FILENAME}`\n\nfunction toRelativeModulePath(fromDir: string, filePath: string): string {\n return `./${filePath.slice(fromDir.length + 1)}`\n}\n\nfunction isBarrelPath(path: string): boolean {\n return path.endsWith(BARREL_SUFFIX)\n}\n\nfunction makeBarrel(dirPath: string, exports: Array<ExportNode>): FileNode {\n return createFile({\n baseName: BARREL_FILENAME,\n path: `${dirPath}${BARREL_SUFFIX}`,\n exports,\n sources: [],\n imports: [],\n // Barrel files must never carry a banner or footer: they only re-export\n // symbols and adding a directive like \"use server\" would break consumers.\n banner: undefined,\n footer: undefined,\n })\n}\n\ntype LeafContext = {\n dirPath: string\n leafPath: string\n sourceFile: FileNode | undefined\n}\n\ntype LeafStrategy = (ctx: LeafContext) => Array<ExportNode>\n\nfunction hasOnlyNonIndexableSources(sources: ReadonlyArray<SourceNode>): boolean {\n if (sources.length === 0) return false\n for (const source of sources) {\n if (source.isIndexable) return false\n }\n return true\n}\n\nfunction partitionIndexableNames(sources: ReadonlyArray<SourceNode>): Map<boolean, Set<string>> {\n const byTypeOnly = new Map<boolean, Set<string>>([\n [false, new Set()],\n [true, new Set()],\n ])\n for (const source of sources) {\n if (!source.isIndexable || !source.name) continue\n byTypeOnly.get(Boolean(source.isTypeOnly))!.add(source.name)\n }\n return byTypeOnly\n}\n\nconst allStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n if (sourceFile && hasOnlyNonIndexableSources(sourceFile.sources)) return []\n return [createExport({ path: toRelativeModulePath(dirPath, leafPath) })]\n}\n\nconst namedStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const modulePath = toRelativeModulePath(dirPath, leafPath)\n\n if (!sourceFile) return [createExport({ path: modulePath })]\n\n const namesByTypeOnly = partitionIndexableNames(sourceFile.sources)\n const valueNames = namesByTypeOnly.get(false)!\n const typeNames = namesByTypeOnly.get(true)!\n\n if (valueNames.size === 0 && typeNames.size === 0) {\n if (sourceFile.sources.length > 0) return []\n return [createExport({ path: modulePath })]\n }\n\n const exports: Array<ExportNode> = []\n if (valueNames.size > 0) {\n exports.push(createExport({ name: [...valueNames].sort(), path: modulePath }))\n }\n if (typeNames.size > 0) {\n exports.push(createExport({ name: [...typeNames].sort(), path: modulePath, isTypeOnly: true }))\n }\n return exports\n}\n\nconst LEAF_STRATEGIES: ReadonlyMap<BarrelType, LeafStrategy> = new Map([\n ['all', allStrategy],\n ['named', namedStrategy],\n])\n\ntype LeafWalkParams = {\n sourceFiles: ReadonlyMap<string, FileNode>\n strategy: LeafStrategy\n recursive: boolean\n}\n\n/**\n * Post-order walk that emits a barrel per visited directory.\n */\nfunction walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean, out: Array<FileNode>): Array<string> {\n const subtreeLeaves: Array<string> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (!isBarrelPath(child.path)) subtreeLeaves.push(child.path)\n continue\n }\n\n const childLeaves = walkAllOrNamed(child, params, false, out)\n for (const leaf of childLeaves) subtreeLeaves.push(leaf)\n }\n\n if (!isRoot && !params.recursive) return subtreeLeaves\n\n const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({ dirPath: node.path, leafPath, sourceFile: params.sourceFiles.get(leafPath) }))\n\n if (exports.length > 0) {\n out.push(makeBarrel(node.path, exports))\n }\n\n return subtreeLeaves\n}\n\n/**\n * Recursive walk that emits one barrel per directory, re-exporting files and sub-barrels.\n * Used when nested: true.\n */\nfunction walkNested(node: BuildTree, out: Array<FileNode>): void {\n const exports: Array<ExportNode> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (isBarrelPath(child.path)) continue\n exports.push(createExport({ path: toRelativeModulePath(node.path, child.path) }))\n continue\n }\n\n walkNested(child, out)\n exports.push(createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }))\n }\n\n if (exports.length > 0) {\n out.push(makeBarrel(node.path, exports))\n }\n}\n\ntype IndexedFiles = {\n sourceFiles: ReadonlyMap<string, FileNode>\n paths: ReadonlyArray<string>\n}\n\nfunction indexRelevantFiles(files: ReadonlyArray<FileNode>, outputPath: string): IndexedFiles {\n const outputPrefix = `${toPosixPath(outputPath)}/`\n const sourceFiles = new Map<string, FileNode>()\n const paths: Array<string> = []\n\n for (const file of files) {\n const normalized = toPosixPath(file.path)\n if (!normalized.startsWith(outputPrefix)) continue\n if (isBarrelPath(normalized)) continue\n if (!SOURCE_EXTENSIONS.has(extname(normalized))) continue\n\n sourceFiles.set(normalized, file)\n paths.push(normalized)\n }\n\n return { sourceFiles, paths }\n}\n\ntype GetBarrelFilesParams = {\n /**\n * Absolute directory the barrel(s) should be rooted at.\n * Only files living under this path are considered.\n */\n outputPath: string\n /**\n * Pool of generated files to scan for indexable sources.\n */\n files: ReadonlyArray<FileNode>\n /**\n * Export strategy used when emitting each barrel.\n * - `'all'` re-exports the whole module (`export * from './x'`)\n * - `'named'` re-exports only the indexable named symbols\n */\n barrelType: BarrelType\n /**\n * Generate an `index.ts` in every sub-directory, each re-exporting only what's directly inside it (hierarchical).\n * When false, uses flat generation strategy with optional recursive subdirectory barrels.\n *\n * @default false\n */\n nested?: boolean\n /**\n * Also generate a barrel for each sub-directory when nested is false.\n * No effect when nested is true (always generates hierarchical structure).\n *\n * @default false\n */\n recursive?: boolean\n}\n\n/**\n * Generates barrel `FileNode`s for the directory rooted at `outputPath`.\n */\nexport function getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }: GetBarrelFilesParams): Array<FileNode> {\n const { sourceFiles, paths } = indexRelevantFiles(files, outputPath)\n if (paths.length === 0) return []\n\n const tree = buildTree(outputPath, paths)\n const result: Array<FileNode> = []\n\n // Use nested walk for hierarchical barrel structure\n if (nested) {\n walkNested(tree, result)\n return result\n }\n\n const strategy = LEAF_STRATEGIES.get(barrelType)\n if (!strategy) return result\n\n walkAllOrNamed(tree, { sourceFiles, strategy, recursive }, true, result)\n return result\n}\n","import path from 'node:path'\nimport { resolve } from 'node:path'\nimport { defineMiddleware } from '@kubb/core'\nimport type { Middleware } from '@kubb/core'\nimport type { BarrelConfig, PluginBarrelConfig } from './types.ts'\nimport { getPluginOutputPrefix, isExcludedPath } from './utils/excludedPaths.ts'\nimport { getBarrelFiles } from './utils/getBarrelFiles.ts'\n\ndeclare global {\n namespace Kubb {\n interface PluginOptionsRegistry {\n output: {\n /**\n * Barrel configuration for this plugin's output.\n * Set to `false` to disable barrel generation for this plugin entirely; doing so also\n * excludes the plugin's files from the root barrel.\n *\n * Falls back to `config.output.barrel` when omitted.\n *\n * @default { type: 'named' }\n */\n barrel?: PluginBarrelConfig | false\n }\n }\n interface ConfigOptionsRegistry {\n output: {\n /**\n * Barrel configuration for the root barrel file at `config.output.path/index.ts`.\n * Set to `false` to disable root barrel generation. Individual plugins can override\n * this via their own `output.barrel`.\n *\n * @default { type: 'named' }\n */\n barrel?: BarrelConfig | false\n }\n }\n }\n}\n\n/**\n * Generates `index.ts` barrel files for each plugin and a root barrel at `config.output.path/index.ts`.\n *\n * Each plugin inherits `output.barrel` from `config.output.barrel` (defaults to `{ type: 'named' }`).\n * Set `barrel: false` on a plugin to disable its barrel and exclude it from the root barrel.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@kubb/core'\n * import { middlewareBarrel } from '@kubb/middleware-barrel'\n *\n * export default defineConfig({\n * output: { path: 'src/gen', barrel: { type: 'named' } },\n * plugins: [\n * pluginTs({ output: { path: 'types', barrel: { type: 'all' } } }),\n * pluginZod({ output: { path: 'schemas' } }),\n * ],\n * middleware: [middlewareBarrel()],\n * })\n * ```\n */\n\n/**\n * Stable string identifier for the barrel middleware.\n */\nexport const middlewareBarrelName = 'middleware-barrel' satisfies Middleware['name']\n\nexport const middlewareBarrel = defineMiddleware(() => {\n const excludedPrefixes = new Set<string>()\n\n return {\n name: middlewareBarrelName,\n hooks: {\n 'kubb:plugin:end'({ plugin, config, files, upsertFile }) {\n const pluginBarrel = plugin.options.output?.barrel\n const configBarrel = config.output.barrel\n const defaultBarrel = { type: 'named' } as const\n\n let barrelConfig: PluginBarrelConfig | false\n if (pluginBarrel !== undefined) {\n barrelConfig = pluginBarrel\n } else if (configBarrel !== undefined) {\n // Root config barrel doesn't have nested, so we add it\n barrelConfig = configBarrel === false ? false : { ...configBarrel, nested: false }\n } else {\n barrelConfig = defaultBarrel\n }\n\n if (barrelConfig === false) {\n excludedPrefixes.add(getPluginOutputPrefix(plugin, config))\n return\n }\n\n const barrelType = barrelConfig.type\n const nested = barrelConfig.nested ?? false\n\n const base = resolve(config.root, config.output.path)\n const target = resolve(base, plugin.options.output.path)\n const relative = path.relative(base, target)\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error('Invalid output path')\n }\n const barrelFiles = getBarrelFiles({\n outputPath: target,\n files,\n barrelType,\n nested,\n recursive: true,\n })\n\n if (barrelFiles.length > 0) {\n upsertFile(...barrelFiles)\n }\n },\n 'kubb:plugins:end'({ files, config, upsertFile }) {\n const barrelConfig = config.output.barrel ?? { type: 'named' }\n\n const filteredFiles = excludedPrefixes.size === 0 ? files : files.filter((f) => !isExcludedPath(f.path, excludedPrefixes))\n excludedPrefixes.clear()\n\n if (barrelConfig === false) return\n\n const barrelType = barrelConfig.type\n\n const rootBarrelFiles = getBarrelFiles({\n outputPath: resolve(config.root, config.output.path),\n files: filteredFiles,\n barrelType,\n })\n\n if (rootBarrelFiles.length > 0) {\n upsertFile(...rootBarrelFiles)\n }\n },\n },\n }\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../internals/utils/src/path.ts","../../../internals/utils/src/buildTree.ts","../src/utils.ts","../src/middleware.ts"],"sourcesContent":["/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n","import { toPosixPath } from './path.ts'\n\n/**\n * A node in the directory tree used to compute barrel file exports.\n * Either represents a directory (with `children`) or a file (`isFile: true`, empty `children`).\n */\nexport type BuildTree = {\n /**\n * Absolute filesystem path of this directory or file. Always normalized to POSIX (`/`) separators.\n */\n path: string\n /**\n * Sub-directories and files contained within this directory.\n * Always empty for file nodes.\n */\n children: Array<BuildTree>\n /**\n * `true` when this node represents a file (leaf), `false` for directory nodes.\n */\n isFile: boolean\n}\n\n/**\n * Builds a directory tree rooted at `rootPath` from a list of absolute file paths.\n * Paths outside `rootPath` are silently ignored. Children are sorted alphabetically\n * by path so consumers (barrel exports, propagated indexes) emit a deterministic order.\n *\n * Both POSIX (`/`) and Windows (`\\`) separators are accepted in input paths; emitted node\n * paths are always POSIX-normalized so downstream prefix/lookup operations behave the same\n * across platforms.\n *\n * @example\n * ```ts\n * buildTree('/src/gen/types', [\n * '/src/gen/types/pet.ts',\n * '/src/gen/types/pets/listPets.ts',\n * ])\n * ```\n */\nexport function buildTree(rootPath: string, filePaths: ReadonlyArray<string>): BuildTree {\n const normalizedRoot = toPosixPath(rootPath)\n const root: BuildTree = { path: normalizedRoot, children: [], isFile: false }\n // Per-directory child lookup avoids the O(N) `Array.find` scan during insertion.\n // WeakMap keyed by object identity so directory nodes are GC-eligible once the tree is discarded.\n const childIndex = new WeakMap<BuildTree, Map<string, BuildTree>>()\n childIndex.set(root, new Map())\n\n const rootPrefix = `${normalizedRoot}/`\n\n for (const filePath of filePaths) {\n const normalized = toPosixPath(filePath)\n if (!normalized.startsWith(rootPrefix)) continue\n\n const parts = normalized.slice(rootPrefix.length).split('/')\n if (parts.length === 0) continue\n\n let current = root\n const lastIndex = parts.length - 1\n for (const [i, part] of parts.entries()) {\n if (!part) continue\n\n const isLast = i === lastIndex\n const siblings = childIndex.get(current)!\n let child = siblings.get(part)\n if (!child) {\n child = { path: `${current.path}/${part}`, children: [], isFile: isLast }\n current.children.push(child)\n siblings.set(part, child)\n if (!isLast) childIndex.set(child, new Map())\n }\n current = child\n }\n }\n\n sortTree(root)\n\n return root\n}\n\nfunction sortTree(node: BuildTree): void {\n if (node.children.length === 0) return\n node.children.sort(compareByPath)\n for (const child of node.children) {\n if (!child.isFile) sortTree(child)\n }\n}\n\nfunction compareByPath(a: BuildTree, b: BuildTree): number {\n return a.path < b.path ? -1 : a.path > b.path ? 1 : 0\n}\n","import { extname, resolve } from 'node:path'\nimport { createExport, createFile } from '@kubb/ast'\nimport type { ExportNode, FileNode, SourceNode } from '@kubb/ast'\nimport type { Config, NormalizedPlugin } from '@kubb/core'\nimport { type BuildTree, buildTree, toPosixPath } from '@internals/utils'\nimport type { BarrelType } from './types.ts'\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\nconst BARREL_SUFFIX = `/index.ts`\n\nfunction toRelativeModulePath(fromDir: string, filePath: string): string {\n return `./${filePath.slice(fromDir.length + 1)}`\n}\n\nfunction isBarrelPath(path: string): boolean {\n return path.endsWith(BARREL_SUFFIX)\n}\n\nfunction makeBarrel(dirPath: string, exports: Array<ExportNode>): FileNode {\n return createFile({\n baseName: 'index.ts',\n path: `${dirPath}${BARREL_SUFFIX}`,\n exports,\n sources: [],\n imports: [],\n // Default to no banner/footer. The middleware resolves a configured plugin\n // banner/footer (with isBarrel: true) afterwards, so a `banner` function can\n // decide per file whether a barrel should carry a directive like \"use server\".\n banner: undefined,\n footer: undefined,\n })\n}\n\ntype LeafContext = {\n dirPath: string\n leafPath: string\n sourceFile: FileNode | null\n}\n\ntype LeafStrategy = (ctx: LeafContext) => Array<ExportNode>\n\nfunction hasOnlyNonIndexableSources(sources: ReadonlyArray<SourceNode>): boolean {\n if (sources.length === 0) return false\n for (const source of sources) {\n if (source.isIndexable) return false\n }\n return true\n}\n\nfunction partitionIndexableNames(sources: ReadonlyArray<SourceNode>): Map<boolean, Set<string>> {\n const byTypeOnly = new Map<boolean, Set<string>>([\n [false, new Set()],\n [true, new Set()],\n ])\n for (const source of sources) {\n if (!source.isIndexable || !source.name) continue\n byTypeOnly.get(Boolean(source.isTypeOnly))!.add(source.name)\n }\n return byTypeOnly\n}\n\nconst allStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n if (sourceFile && hasOnlyNonIndexableSources(sourceFile.sources)) return []\n return [createExport({ path: toRelativeModulePath(dirPath, leafPath) })]\n}\n\nconst namedStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const modulePath = toRelativeModulePath(dirPath, leafPath)\n\n if (!sourceFile) return [createExport({ path: modulePath })]\n\n const namesByTypeOnly = partitionIndexableNames(sourceFile.sources)\n const valueNames = namesByTypeOnly.get(false)!\n const typeNames = namesByTypeOnly.get(true)!\n\n if (valueNames.size === 0 && typeNames.size === 0) {\n if (sourceFile.sources.length > 0) return []\n return [createExport({ path: modulePath })]\n }\n\n const exports: Array<ExportNode> = []\n if (valueNames.size > 0) {\n exports.push(createExport({ name: [...valueNames].sort(), path: modulePath }))\n }\n if (typeNames.size > 0) {\n exports.push(createExport({ name: [...typeNames].sort(), path: modulePath, isTypeOnly: true }))\n }\n return exports\n}\n\nconst LEAF_STRATEGIES: ReadonlyMap<BarrelType, LeafStrategy> = new Map([\n ['all', allStrategy],\n ['named', namedStrategy],\n])\n\ntype LeafWalkParams = {\n sourceFiles: ReadonlyMap<string, FileNode>\n strategy: LeafStrategy\n recursive: boolean\n}\n\n/**\n * Post-order walk that yields a barrel per visited directory.\n * Returns the list of leaf file paths collected in this subtree (used by the parent call).\n */\nfunction* walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean): Generator<FileNode, Array<string>> {\n const subtreeLeaves: Array<string> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (!isBarrelPath(child.path)) subtreeLeaves.push(child.path)\n continue\n }\n\n const childLeaves = yield* walkAllOrNamed(child, params, false)\n for (const leaf of childLeaves) subtreeLeaves.push(leaf)\n }\n\n if (!isRoot && !params.recursive) return subtreeLeaves\n\n const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({ dirPath: node.path, leafPath, sourceFile: params.sourceFiles.get(leafPath) ?? null }))\n\n if (exports.length > 0) {\n yield makeBarrel(node.path, exports)\n }\n\n return subtreeLeaves\n}\n\n/**\n * Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.\n * Used when nested: true.\n */\nfunction* walkNested(node: BuildTree): Generator<FileNode> {\n const exports: Array<ExportNode> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (isBarrelPath(child.path)) continue\n exports.push(createExport({ path: toRelativeModulePath(node.path, child.path) }))\n continue\n }\n\n yield* walkNested(child)\n exports.push(createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }))\n }\n\n if (exports.length > 0) {\n yield makeBarrel(node.path, exports)\n }\n}\n\ntype IndexedFiles = {\n sourceFiles: ReadonlyMap<string, FileNode>\n paths: ReadonlyArray<string>\n}\n\nfunction indexRelevantFiles(files: ReadonlyArray<FileNode>, outputPath: string): IndexedFiles {\n const outputPrefix = `${toPosixPath(outputPath)}/`\n const sourceFiles = new Map<string, FileNode>()\n const paths: Array<string> = []\n\n for (const file of files) {\n const normalized = toPosixPath(file.path)\n if (!normalized.startsWith(outputPrefix)) continue\n if (isBarrelPath(normalized)) continue\n if (!SOURCE_EXTENSIONS.has(extname(normalized))) continue\n\n sourceFiles.set(normalized, file)\n paths.push(normalized)\n }\n\n return { sourceFiles, paths }\n}\n\ntype GetBarrelFilesParams = {\n /**\n * Absolute directory the barrel(s) should be rooted at.\n * Only files living under this path are considered.\n */\n outputPath: string\n /**\n * Pool of generated files to scan for indexable sources.\n */\n files: ReadonlyArray<FileNode>\n /**\n * Export strategy used when emitting each barrel.\n * - `'all'` re-exports the whole module (`export * from './x'`)\n * - `'named'` re-exports only the indexable named symbols\n */\n barrelType: BarrelType\n /**\n * Generate an `index.ts` in every sub-directory, each re-exporting only what's directly inside it (hierarchical).\n * When false, uses flat generation strategy with optional recursive subdirectory barrels.\n *\n * @default false\n */\n nested?: boolean\n /**\n * Also generate a barrel for each sub-directory when nested is false.\n * No effect when nested is true (always generates hierarchical structure).\n *\n * @default false\n */\n recursive?: boolean\n}\n\n/**\n * Yields barrel `FileNode`s for the directory rooted at `outputPath`.\n *\n * @example\n * ```ts\n * for (const file of getBarrelFiles({ outputPath, files, barrelType })) {\n * upsertFile(file)\n * }\n * // or collect into an array\n * const barrels = [...getBarrelFiles({ outputPath, files, barrelType })]\n * ```\n */\nexport function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }: GetBarrelFilesParams): Generator<FileNode> {\n const { sourceFiles, paths } = indexRelevantFiles(files, outputPath)\n if (paths.length === 0) return\n\n const tree = buildTree(outputPath, paths)\n\n if (nested) {\n yield* walkNested(tree)\n return\n }\n\n const strategy = LEAF_STRATEGIES.get(barrelType)\n if (!strategy) return\n\n yield* walkAllOrNamed(tree, { sourceFiles, strategy, recursive }, true)\n}\n\n/**\n * Builds a POSIX-normalized prefix for a plugin's output directory, with a trailing `/`.\n *\n * Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.\n */\nexport function getPluginOutputPrefix(plugin: NormalizedPlugin, config: Config): string {\n return `${toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path))}/`\n}\n\n/**\n * Returns `true` when `filePath` lives under any of the given excluded directory prefixes.\n *\n * Both sides are POSIX-normalized so Windows backslash paths match correctly.\n */\nexport function isExcludedPath(filePath: string, prefixes: ReadonlySet<string>): boolean {\n const normalized = toPosixPath(filePath)\n return prefixes.values().some((prefix) => normalized.startsWith(prefix))\n}\n","import path from 'node:path'\nimport { resolve } from 'node:path'\nimport type { FileNode } from '@kubb/ast'\nimport { defineMiddleware } from '@kubb/core'\nimport type { Config, Middleware, NormalizedPlugin } from '@kubb/core'\nimport type { BarrelConfig, PluginBarrelConfig } from './types.ts'\nimport { getBarrelFiles, getPluginOutputPrefix, isExcludedPath } from './utils.ts'\n\n/**\n * Applies a plugin's configured `output.banner`/`footer` to a barrel file, flagged as `isBarrel`.\n *\n * Resolves through the plugin's own resolver, and only when the plugin explicitly sets a\n * banner/footer, so barrels stay banner-free by default and never inherit the implicit\n * \"Generated by Kubb\" notice.\n */\nfunction withBarrelBannerFooter({ file, plugin, config }: { file: FileNode; plugin: NormalizedPlugin; config: Config }): FileNode {\n const output = plugin.options?.output\n const resolver = plugin.resolver\n if (!resolver) return file\n\n const hasBanner = output?.banner !== undefined\n const hasFooter = output?.footer !== undefined\n if (!hasBanner && !hasFooter) return file\n\n const context = { output, config, file: { path: file.path, baseName: file.baseName, isBarrel: true } }\n return {\n ...file,\n banner: hasBanner ? resolver.resolveBanner(undefined, context) : file.banner,\n footer: hasFooter ? resolver.resolveFooter(undefined, context) : file.footer,\n }\n}\n\ndeclare global {\n namespace Kubb {\n interface PluginOptionsRegistry {\n output: {\n /**\n * Barrel configuration for this plugin's output.\n * Set to `false` to disable barrel generation for this plugin entirely. Doing so also\n * excludes the plugin's files from the root barrel.\n *\n * Falls back to `config.output.barrel` when omitted.\n *\n * @default { type: 'named' }\n */\n barrel?: PluginBarrelConfig | false\n }\n }\n interface ConfigOptionsRegistry {\n output: {\n /**\n * Barrel configuration for the root barrel file at `config.output.path/index.ts`.\n * Set to `false` to disable root barrel generation. Individual plugins can override\n * this via their own `output.barrel`.\n *\n * @default { type: 'named' }\n */\n barrel?: BarrelConfig | false\n }\n }\n }\n}\n\n/**\n * Canonical middleware name for `@kubb/middleware-barrel`. Used for driver\n * lookups.\n */\nexport const middlewareBarrelName = 'middleware-barrel' satisfies Middleware['name']\n\n/**\n * Generates an `index.ts` for every plugin output directory and one root\n * barrel at `config.output.path/index.ts` after the build completes. Ships\n * with Kubb and is registered by default in `defineConfig`.\n *\n * Each plugin inherits `output.barrel` from `config.output.barrel` (which\n * defaults to `{ type: 'named' }`). Set `barrel: false` on a plugin to skip\n * its barrel and also exclude its files from the root barrel.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@kubb/core'\n * import { middlewareBarrel } from '@kubb/middleware-barrel'\n * import { pluginTs } from '@kubb/plugin-ts'\n * import { pluginZod } from '@kubb/plugin-zod'\n *\n * export default defineConfig({\n * input: { path: './petStore.yaml' },\n * output: { path: 'src/gen', barrel: { type: 'named' } },\n * plugins: [\n * pluginTs({ output: { path: 'types', barrel: { type: 'all' } } }),\n * pluginZod({ output: { path: 'schemas' } }),\n * ],\n * middleware: [middlewareBarrel()],\n * })\n * ```\n */\nexport const middlewareBarrel = defineMiddleware(() => {\n const excludedPrefixes = new Set<string>()\n\n return {\n name: middlewareBarrelName,\n hooks: {\n 'kubb:plugin:end'({ plugin, config, files, upsertFile }) {\n const pluginBarrel = plugin.options.output?.barrel\n const configBarrel = config.output.barrel\n const defaultBarrel = { type: 'named' } as const\n\n // Root config barrel doesn't have nested, so we add it\n const barrelConfig: PluginBarrelConfig | false = (() => {\n if (pluginBarrel !== undefined) return pluginBarrel\n if (configBarrel !== undefined) return configBarrel === false ? false : { ...configBarrel, nested: false }\n return defaultBarrel\n })()\n\n if (barrelConfig === false) {\n excludedPrefixes.add(getPluginOutputPrefix(plugin, config))\n return\n }\n\n const barrelType = barrelConfig.type\n const nested = barrelConfig.nested ?? false\n\n const base = resolve(config.root, config.output.path)\n const target = resolve(base, plugin.options.output.path)\n const relative = path.relative(base, target)\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error('Invalid output path')\n }\n for (const file of getBarrelFiles({ outputPath: target, files, barrelType, nested, recursive: true })) {\n upsertFile(withBarrelBannerFooter({ file, plugin, config }))\n }\n },\n 'kubb:plugins:end'({ files, config, upsertFile }) {\n const barrelConfig = config.output.barrel ?? { type: 'named' }\n\n const filteredFiles = excludedPrefixes.size === 0 ? files : files.filter((f) => !isExcludedPath(f.path, excludedPrefixes))\n excludedPrefixes.clear()\n\n if (barrelConfig === false) return\n\n const barrelType = barrelConfig.type\n\n for (const file of getBarrelFiles({ outputPath: resolve(config.root, config.output.path), files: filteredFiles, barrelType })) {\n upsertFile(file)\n }\n },\n },\n }\n})\n"],"mappings":"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"}
package/extension.yaml CHANGED
@@ -2,7 +2,7 @@ $schema: https://kubb.dev/schemas/extension.json
2
2
  kind: middleware
3
3
  id: middleware-barrel
4
4
  name: Barrel
5
- description: Automatically generates barrel files (index.ts exports) for every plugin output directory. Ships with Kubb and is enabled by default.
5
+ description: Generates `index.ts` re-export files for every plugin output and one root barrel. Ships with Kubb and is enabled by default.
6
6
  category: output
7
7
  type: official
8
8
  npmPackage: '@kubb/middleware-barrel'
@@ -26,9 +26,9 @@ resources:
26
26
  changelog: https://github.com/kubb-labs/kubb/blob/main/packages/middleware-barrel/CHANGELOG.md
27
27
  featured: true
28
28
  intro: |-
29
- The barrel middleware automatically generates `index.ts` (and `index.js`) re-export files for every plugin output directory **and** a root barrel at `config.output.path/index.ts` after the build finishes.
29
+ `@kubb/middleware-barrel` generates an `index.ts` for every plugin output directory and one root barrel at `output.path/index.ts` after the build completes. The result is a single import surface for every consumer — `import { Pet, usePetByIdQuery, petMock } from './gen'`.
30
30
 
31
- It ships with Kubb and is registered as a default middleware in `defineConfig`, so you get barrel files out of the box with no extra configuration. When `middlewareBarrel` is part of `config.middleware`, `defineConfig` also applies a default `output.barrel` of `{ type: 'named' }`. Custom middleware lists that omit `middlewareBarrel` leave `barrel` untouched.
31
+ The middleware ships with Kubb and is registered by default in `defineConfig`, so barrels appear out of the box with no extra configuration. When `middlewareBarrel` is part of `config.middleware`, `defineConfig` also applies a default `output.barrel` of `{ type: 'named' }`. Custom middleware lists that omit `middlewareBarrel` leave `barrel` untouched.
32
32
 
33
33
  Plugins inherit `output.barrel` from `config.output.barrel` when their own value is omitted. Setting `barrel: false` on a plugin disables that plugin's barrel **and** excludes its files from the root barrel.
34
34
  options:
@@ -37,18 +37,20 @@ options:
37
37
  required: false
38
38
  default: "{ type: 'named' }"
39
39
  description: |-
40
- Re-export style for the generated barrel files. Configured via `output.barrel` in `defineConfig` (root level) or per plugin via the plugin's own `output.barrel`. This is not a middleware argument.
40
+ Re-export style used for every generated barrel file. Set via `output.barrel` in `defineConfig` (applies to all plugins and the root barrel) or per plugin via that plugin's own `output.barrel`. It is not an argument to the middleware itself.
41
41
 
42
42
  At the config level:
43
- - `{ type: 'all' }` — `export * from '...'` for every generated file
44
- - `{ type: 'named' }` — `export { name1, name2 } from '...'` using each file's named exports
45
- - `false` — disables barrel generation entirely
46
43
 
47
- At the plugin level, the `nested` flag is also available:
48
- - `{ type: 'named', nested: true }` — generates a barrel for every directory, re-exporting only direct children, creating a chain for imports from any depth
49
- - `false` — disables barrel generation for that plugin and excludes its files from the root barrel
44
+ - `{ type: 'all' }` `export * from '...'` for every generated file.
45
+ - `{ type: 'named' }` — `export { Foo, Bar } from '...'` using each file's named exports.
46
+ - `false` — disables barrel generation entirely.
50
47
 
51
- The `{ type: 'named' }` default is applied automatically by `defineConfig` only when `middlewareBarrel` is part of `config.middleware`.
48
+ At the plugin level you also get `nested`:
49
+
50
+ - `{ type: 'named', nested: true }` — generates a barrel for every subdirectory, re-exporting only direct children. Lets callers import from any depth.
51
+ - `false` — disables the plugin's barrel and removes its files from the root barrel.
52
+
53
+ The `{ type: 'named' }` default kicks in only when `middlewareBarrel` is part of `config.middleware`.
52
54
  codeBlock:
53
55
  lang: typescript
54
56
  title: kubb.config.ts
@@ -58,12 +60,13 @@ options:
58
60
  import { middlewareBarrel } from '@kubb/middleware-barrel'
59
61
 
60
62
  export default defineConfig({
61
- input: { path: './petstore.yaml' },
63
+ input: { path: './petStore.yaml' },
62
64
  output: { path: './src/gen', barrel: { type: 'named' } },
63
65
  middleware: [middlewareBarrel()],
66
+ plugins: [],
64
67
  })
65
68
  examples:
66
- - name: "barrel: { type: 'named' } (default)"
69
+ - name: "{ type: 'named' } (default)"
67
70
  files:
68
71
  - name: kubb.config.ts
69
72
  lang: typescript
@@ -72,10 +75,11 @@ examples:
72
75
  import { defineConfig } from 'kubb'
73
76
 
74
77
  export default defineConfig({
75
- input: { path: './petstore.yaml' },
78
+ input: { path: './petStore.yaml' },
76
79
  output: { path: './src/gen' },
80
+ plugins: [],
77
81
  })
78
- - name: 'Generated output'
82
+ - name: Generated output
79
83
  lang: typescript
80
84
  twoslash: false
81
85
  code: |-
@@ -92,7 +96,7 @@ examples:
92
96
 
93
97
  // src/gen/api/types/index.ts
94
98
  export { User } from './User'
95
- - name: "barrel: { type: 'all' }"
99
+ - name: "{ type: 'all' }"
96
100
  files:
97
101
  - name: kubb.config.ts
98
102
  lang: typescript
@@ -101,10 +105,11 @@ examples:
101
105
  import { defineConfig } from 'kubb'
102
106
 
103
107
  export default defineConfig({
104
- input: { path: './petstore.yaml' },
108
+ input: { path: './petStore.yaml' },
105
109
  output: { path: './src/gen', barrel: { type: 'all' } },
110
+ plugins: [],
106
111
  })
107
- - name: 'Generated output'
112
+ - name: Generated output
108
113
  lang: typescript
109
114
  twoslash: false
110
115
  code: |-
@@ -121,7 +126,7 @@ examples:
121
126
 
122
127
  // src/gen/api/types/index.ts
123
128
  export * from './User'
124
- - name: "barrel: { type: 'named', nested: true }"
129
+ - name: "{ type: 'named', nested: true }"
125
130
  files:
126
131
  - name: kubb.config.ts
127
132
  lang: typescript
@@ -130,10 +135,11 @@ examples:
130
135
  import { defineConfig } from 'kubb'
131
136
 
132
137
  export default defineConfig({
133
- input: { path: './petstore.yaml' },
138
+ input: { path: './petStore.yaml' },
134
139
  output: { path: './src/gen', barrel: { type: 'named', nested: true } },
140
+ plugins: [],
135
141
  })
136
- - name: 'Generated output (chained structure)'
142
+ - name: Generated output (chained structure)
137
143
  lang: typescript
138
144
  twoslash: false
139
145
  code: |-
@@ -148,7 +154,7 @@ examples:
148
154
 
149
155
  // src/gen/api/types/index.ts (exports files)
150
156
  export * from './User'
151
- - name: Disable barrel for a single plugin
157
+ - name: Disable the barrel for a single plugin
152
158
  files:
153
159
  - name: kubb.config.ts
154
160
  lang: typescript
@@ -158,17 +164,17 @@ examples:
158
164
  import { pluginTs } from '@kubb/plugin-ts'
159
165
  import { pluginZod } from '@kubb/plugin-zod'
160
166
 
161
- // pluginZod opts out: no zod/index.ts is created
167
+ // pluginZod opts out: no zod/index.ts is created,
162
168
  // and zod files are excluded from the root index.ts.
163
169
  export default defineConfig({
164
- input: { path: './petstore.yaml' },
170
+ input: { path: './petStore.yaml' },
165
171
  output: { path: './src/gen' },
166
172
  plugins: [
167
173
  pluginTs(),
168
174
  pluginZod({ output: { barrel: false } }),
169
175
  ],
170
176
  })
171
- - name: Disable the root barrel only
177
+ - name: Disable only the root barrel
172
178
  files:
173
179
  - name: kubb.config.ts
174
180
  lang: typescript
@@ -177,15 +183,16 @@ examples:
177
183
  import { defineConfig } from 'kubb'
178
184
 
179
185
  // No root index.ts is generated, but each plugin
180
- // still gets its own barrel using its inherited barrel config.
186
+ // still gets its own barrel using its inherited config.
181
187
  export default defineConfig({
182
- input: { path: './petstore.yaml' },
188
+ input: { path: './petStore.yaml' },
183
189
  output: { path: './src/gen', barrel: false },
190
+ plugins: [],
184
191
  })
185
192
  notes:
186
193
  - type: tip
187
194
  body: |-
188
- `middleware-barrel` is bundled with Kubb and enabled automatically when no `middleware` option is provided. You only need to install it explicitly when customising the barrel behaviour alongside other middlewares.
195
+ `middleware-barrel` ships with Kubb and is enabled automatically when no `middleware` option is provided. Install it explicitly only when customising barrel behaviour alongside other middlewares.
189
196
  - type: note
190
197
  body: |-
191
- `output.barrel` is only auto-defaulted to `{ type: 'named' }` when `middlewareBarrel` is part of `config.middleware`. If you provide a custom middleware list without it, set `output.barrel` yourself if you still want a barrel.
198
+ `output.barrel` is auto-defaulted to `{ type: 'named' }` only when `middlewareBarrel` is part of `config.middleware`. With a custom middleware list that omits it, set `output.barrel` yourself if you still want a barrel.
package/package.json CHANGED
@@ -1,14 +1,12 @@
1
1
  {
2
2
  "name": "@kubb/middleware-barrel",
3
- "version": "5.0.0-beta.4",
4
- "description": "Barrel-file generation middleware for Kubb. Generates index.ts barrel files for each plugin's output directory and a root index.ts after all plugins have run.",
3
+ "version": "5.0.0-beta.41",
4
+ "description": "Barrel-file middleware for Kubb. Automatically generates index.ts re-export files per plugin output directory and an optional root barrel after all plugins complete.",
5
5
  "keywords": [
6
6
  "barrel",
7
- "code-generator",
8
7
  "codegen",
9
8
  "kubb",
10
9
  "middleware",
11
- "openapi",
12
10
  "typescript"
13
11
  ],
14
12
  "license": "MIT",
@@ -44,11 +42,15 @@
44
42
  "access": "public",
45
43
  "registry": "https://registry.npmjs.org/"
46
44
  },
45
+ "dependencies": {
46
+ "@kubb/ast": "5.0.0-beta.41",
47
+ "@kubb/core": "5.0.0-beta.41"
48
+ },
47
49
  "devDependencies": {
48
50
  "@internals/utils": "0.0.0"
49
51
  },
50
52
  "peerDependencies": {
51
- "@kubb/core": "5.0.0-beta.4"
53
+ "@kubb/core": "5.0.0-beta.41"
52
54
  },
53
55
  "engines": {
54
56
  "node": ">=22"
@@ -59,6 +61,7 @@
59
61
  "lint": "oxlint .",
60
62
  "lint:fix": "oxlint --fix .",
61
63
  "release": "pnpm publish --no-git-check",
64
+ "release:stage": "pnpm stage publish --no-git-check",
62
65
  "start": "tsdown --watch",
63
66
  "test": "vitest --passWithNoTests",
64
67
  "typecheck": "tsc -p ./tsconfig.json --noEmit --emitDeclarationOnly false"