@kubb/middleware-barrel 5.0.0-beta.12 → 5.0.0-beta.13

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.cjs CHANGED
@@ -69,7 +69,7 @@ function buildTree(rootPath, filePaths) {
69
69
  children: [],
70
70
  isFile: false
71
71
  };
72
- const childIndex = /* @__PURE__ */ new Map();
72
+ const childIndex = /* @__PURE__ */ new WeakMap();
73
73
  childIndex.set(root, /* @__PURE__ */ new Map());
74
74
  const rootPrefix = `${normalizedRoot}/`;
75
75
  for (const filePath of filePaths) {
@@ -175,16 +175,17 @@ const namedStrategy = ({ dirPath, leafPath, sourceFile }) => {
175
175
  };
176
176
  const LEAF_STRATEGIES = new Map([["all", allStrategy], ["named", namedStrategy]]);
177
177
  /**
178
- * Post-order walk that emits a barrel per visited directory.
178
+ * Post-order walk that yields a barrel per visited directory.
179
+ * Returns the list of leaf file paths collected in this subtree (used by the parent call).
179
180
  */
180
- function walkAllOrNamed(node, params, isRoot, out) {
181
+ function* walkAllOrNamed(node, params, isRoot) {
181
182
  const subtreeLeaves = [];
182
183
  for (const child of node.children) {
183
184
  if (child.isFile) {
184
185
  if (!isBarrelPath(child.path)) subtreeLeaves.push(child.path);
185
186
  continue;
186
187
  }
187
- const childLeaves = walkAllOrNamed(child, params, false, out);
188
+ const childLeaves = yield* walkAllOrNamed(child, params, false);
188
189
  for (const leaf of childLeaves) subtreeLeaves.push(leaf);
189
190
  }
190
191
  if (!isRoot && !params.recursive) return subtreeLeaves;
@@ -193,14 +194,14 @@ function walkAllOrNamed(node, params, isRoot, out) {
193
194
  leafPath,
194
195
  sourceFile: params.sourceFiles.get(leafPath)
195
196
  }));
196
- if (exports.length > 0) out.push(makeBarrel(node.path, exports));
197
+ if (exports.length > 0) yield makeBarrel(node.path, exports);
197
198
  return subtreeLeaves;
198
199
  }
199
200
  /**
200
- * Recursive walk that emits one barrel per directory, re-exporting files and sub-barrels.
201
+ * Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.
201
202
  * Used when nested: true.
202
203
  */
203
- function walkNested(node, out) {
204
+ function* walkNested(node) {
204
205
  const exports = [];
205
206
  for (const child of node.children) {
206
207
  if (child.isFile) {
@@ -208,10 +209,10 @@ function walkNested(node, out) {
208
209
  exports.push((0, _kubb_ast.createExport)({ path: toRelativeModulePath(node.path, child.path) }));
209
210
  continue;
210
211
  }
211
- walkNested(child, out);
212
+ yield* walkNested(child);
212
213
  exports.push((0, _kubb_ast.createExport)({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }));
213
214
  }
214
- if (exports.length > 0) out.push(makeBarrel(node.path, exports));
215
+ if (exports.length > 0) yield makeBarrel(node.path, exports);
215
216
  }
216
217
  function indexRelevantFiles(files, outputPath) {
217
218
  const outputPrefix = `${toPosixPath(outputPath)}/`;
@@ -231,25 +232,32 @@ function indexRelevantFiles(files, outputPath) {
231
232
  };
232
233
  }
233
234
  /**
234
- * Generates barrel `FileNode`s for the directory rooted at `outputPath`.
235
+ * Yields barrel `FileNode`s for the directory rooted at `outputPath`.
236
+ *
237
+ * @example
238
+ * ```ts
239
+ * for (const file of getBarrelFiles({ outputPath, files, barrelType })) {
240
+ * upsertFile(file)
241
+ * }
242
+ * // or collect into an array
243
+ * const barrels = [...getBarrelFiles({ outputPath, files, barrelType })]
244
+ * ```
235
245
  */
236
- function getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }) {
246
+ function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }) {
237
247
  const { sourceFiles, paths } = indexRelevantFiles(files, outputPath);
238
- if (paths.length === 0) return [];
248
+ if (paths.length === 0) return;
239
249
  const tree = buildTree(outputPath, paths);
240
- const result = [];
241
250
  if (nested) {
242
- walkNested(tree, result);
243
- return result;
251
+ yield* walkNested(tree);
252
+ return;
244
253
  }
245
254
  const strategy = LEAF_STRATEGIES.get(barrelType);
246
- if (!strategy) return result;
247
- walkAllOrNamed(tree, {
255
+ if (!strategy) return;
256
+ yield* walkAllOrNamed(tree, {
248
257
  sourceFiles,
249
258
  strategy,
250
259
  recursive
251
- }, true, result);
252
- return result;
260
+ }, true);
253
261
  }
254
262
  /**
255
263
  * Builds a POSIX-normalized prefix for a plugin's output directory, with a trailing `/`.
@@ -321,14 +329,13 @@ const middlewareBarrel = (0, _kubb_core.defineMiddleware)(() => {
321
329
  const target = (0, node_path.resolve)(base, plugin.options.output.path);
322
330
  const relative = node_path.default.relative(base, target);
323
331
  if (relative.startsWith("..") || node_path.default.isAbsolute(relative)) throw new Error("Invalid output path");
324
- const barrelFiles = getBarrelFiles({
332
+ for (const file of getBarrelFiles({
325
333
  outputPath: target,
326
334
  files,
327
335
  barrelType,
328
336
  nested,
329
337
  recursive: true
330
- });
331
- if (barrelFiles.length > 0) upsertFile(...barrelFiles);
338
+ })) upsertFile(file);
332
339
  },
333
340
  "kubb:plugins:end"({ files, config, upsertFile }) {
334
341
  const barrelConfig = config.output.barrel ?? { type: "named" };
@@ -336,12 +343,11 @@ const middlewareBarrel = (0, _kubb_core.defineMiddleware)(() => {
336
343
  excludedPrefixes.clear();
337
344
  if (barrelConfig === false) return;
338
345
  const barrelType = barrelConfig.type;
339
- const rootBarrelFiles = getBarrelFiles({
346
+ for (const file of getBarrelFiles({
340
347
  outputPath: (0, node_path.resolve)(config.root, config.output.path),
341
348
  files: filteredFiles,
342
349
  barrelType
343
- });
344
- if (rootBarrelFiles.length > 0) upsertFile(...rootBarrelFiles);
350
+ })) upsertFile(file);
345
351
  }
346
352
  }
347
353
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["path"],"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 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 { 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 // 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\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 { defineMiddleware } from '@kubb/core'\nimport type { Middleware } from '@kubb/core'\nimport type { BarrelConfig, PluginBarrelConfig } from './types.ts'\nimport { getBarrelFiles, getPluginOutputPrefix, isExcludedPath } from './utils.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.cjs","names":["path"],"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 // 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 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, 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) }))\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 { defineMiddleware } from '@kubb/core'\nimport type { Middleware } from '@kubb/core'\nimport type { BarrelConfig, PluginBarrelConfig } from './types.ts'\nimport { getBarrelFiles, getPluginOutputPrefix, isExcludedPath } from './utils.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 for (const file of getBarrelFiles({ outputPath: target, files, barrelType, nested, recursive: true })) {\n upsertFile(file)\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/dist/index.js CHANGED
@@ -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) {
@@ -152,16 +152,17 @@ const namedStrategy = ({ dirPath, leafPath, sourceFile }) => {
152
152
  };
153
153
  const LEAF_STRATEGIES = new Map([["all", allStrategy], ["named", namedStrategy]]);
154
154
  /**
155
- * 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).
156
157
  */
157
- function walkAllOrNamed(node, params, isRoot, out) {
158
+ function* walkAllOrNamed(node, params, isRoot) {
158
159
  const subtreeLeaves = [];
159
160
  for (const child of node.children) {
160
161
  if (child.isFile) {
161
162
  if (!isBarrelPath(child.path)) subtreeLeaves.push(child.path);
162
163
  continue;
163
164
  }
164
- const childLeaves = walkAllOrNamed(child, params, false, out);
165
+ const childLeaves = yield* walkAllOrNamed(child, params, false);
165
166
  for (const leaf of childLeaves) subtreeLeaves.push(leaf);
166
167
  }
167
168
  if (!isRoot && !params.recursive) return subtreeLeaves;
@@ -170,14 +171,14 @@ function walkAllOrNamed(node, params, isRoot, out) {
170
171
  leafPath,
171
172
  sourceFile: params.sourceFiles.get(leafPath)
172
173
  }));
173
- if (exports.length > 0) out.push(makeBarrel(node.path, exports));
174
+ if (exports.length > 0) yield makeBarrel(node.path, exports);
174
175
  return subtreeLeaves;
175
176
  }
176
177
  /**
177
- * 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.
178
179
  * Used when nested: true.
179
180
  */
180
- function walkNested(node, out) {
181
+ function* walkNested(node) {
181
182
  const exports = [];
182
183
  for (const child of node.children) {
183
184
  if (child.isFile) {
@@ -185,10 +186,10 @@ function walkNested(node, out) {
185
186
  exports.push(createExport({ path: toRelativeModulePath(node.path, child.path) }));
186
187
  continue;
187
188
  }
188
- walkNested(child, out);
189
+ yield* walkNested(child);
189
190
  exports.push(createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }));
190
191
  }
191
- if (exports.length > 0) out.push(makeBarrel(node.path, exports));
192
+ if (exports.length > 0) yield makeBarrel(node.path, exports);
192
193
  }
193
194
  function indexRelevantFiles(files, outputPath) {
194
195
  const outputPrefix = `${toPosixPath(outputPath)}/`;
@@ -208,25 +209,32 @@ function indexRelevantFiles(files, outputPath) {
208
209
  };
209
210
  }
210
211
  /**
211
- * 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
+ * ```
212
222
  */
213
- function getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }) {
223
+ function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }) {
214
224
  const { sourceFiles, paths } = indexRelevantFiles(files, outputPath);
215
- if (paths.length === 0) return [];
225
+ if (paths.length === 0) return;
216
226
  const tree = buildTree(outputPath, paths);
217
- const result = [];
218
227
  if (nested) {
219
- walkNested(tree, result);
220
- return result;
228
+ yield* walkNested(tree);
229
+ return;
221
230
  }
222
231
  const strategy = LEAF_STRATEGIES.get(barrelType);
223
- if (!strategy) return result;
224
- walkAllOrNamed(tree, {
232
+ if (!strategy) return;
233
+ yield* walkAllOrNamed(tree, {
225
234
  sourceFiles,
226
235
  strategy,
227
236
  recursive
228
- }, true, result);
229
- return result;
237
+ }, true);
230
238
  }
231
239
  /**
232
240
  * Builds a POSIX-normalized prefix for a plugin's output directory, with a trailing `/`.
@@ -298,14 +306,13 @@ const middlewareBarrel = defineMiddleware(() => {
298
306
  const target = resolve(base, plugin.options.output.path);
299
307
  const relative = path.relative(base, target);
300
308
  if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error("Invalid output path");
301
- const barrelFiles = getBarrelFiles({
309
+ for (const file of getBarrelFiles({
302
310
  outputPath: target,
303
311
  files,
304
312
  barrelType,
305
313
  nested,
306
314
  recursive: true
307
- });
308
- if (barrelFiles.length > 0) upsertFile(...barrelFiles);
315
+ })) upsertFile(file);
309
316
  },
310
317
  "kubb:plugins:end"({ files, config, upsertFile }) {
311
318
  const barrelConfig = config.output.barrel ?? { type: "named" };
@@ -313,12 +320,11 @@ const middlewareBarrel = defineMiddleware(() => {
313
320
  excludedPrefixes.clear();
314
321
  if (barrelConfig === false) return;
315
322
  const barrelType = barrelConfig.type;
316
- const rootBarrelFiles = getBarrelFiles({
323
+ for (const file of getBarrelFiles({
317
324
  outputPath: resolve(config.root, config.output.path),
318
325
  files: filteredFiles,
319
326
  barrelType
320
- });
321
- if (rootBarrelFiles.length > 0) upsertFile(...rootBarrelFiles);
327
+ })) upsertFile(file);
322
328
  }
323
329
  }
324
330
  };
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.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 { 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 // 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\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 { defineMiddleware } from '@kubb/core'\nimport type { Middleware } from '@kubb/core'\nimport type { BarrelConfig, PluginBarrelConfig } from './types.ts'\nimport { getBarrelFiles, getPluginOutputPrefix, isExcludedPath } from './utils.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 // 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 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, 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) }))\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 { defineMiddleware } from '@kubb/core'\nimport type { Middleware } from '@kubb/core'\nimport type { BarrelConfig, PluginBarrelConfig } from './types.ts'\nimport { getBarrelFiles, getPluginOutputPrefix, isExcludedPath } from './utils.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 for (const file of getBarrelFiles({ outputPath: target, files, barrelType, nested, recursive: true })) {\n upsertFile(file)\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/middleware-barrel",
3
- "version": "5.0.0-beta.12",
3
+ "version": "5.0.0-beta.13",
4
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",
@@ -43,13 +43,13 @@
43
43
  "registry": "https://registry.npmjs.org/"
44
44
  },
45
45
  "dependencies": {
46
- "@kubb/ast": "5.0.0-beta.12"
46
+ "@kubb/ast": "5.0.0-beta.13"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@internals/utils": "0.0.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@kubb/core": "5.0.0-beta.12"
52
+ "@kubb/core": "5.0.0-beta.13"
53
53
  },
54
54
  "engines": {
55
55
  "node": ">=22"
package/src/middleware.ts CHANGED
@@ -98,16 +98,8 @@ export const middlewareBarrel = defineMiddleware(() => {
98
98
  if (relative.startsWith('..') || path.isAbsolute(relative)) {
99
99
  throw new Error('Invalid output path')
100
100
  }
101
- const barrelFiles = getBarrelFiles({
102
- outputPath: target,
103
- files,
104
- barrelType,
105
- nested,
106
- recursive: true,
107
- })
108
-
109
- if (barrelFiles.length > 0) {
110
- upsertFile(...barrelFiles)
101
+ for (const file of getBarrelFiles({ outputPath: target, files, barrelType, nested, recursive: true })) {
102
+ upsertFile(file)
111
103
  }
112
104
  },
113
105
  'kubb:plugins:end'({ files, config, upsertFile }) {
@@ -120,14 +112,8 @@ export const middlewareBarrel = defineMiddleware(() => {
120
112
 
121
113
  const barrelType = barrelConfig.type
122
114
 
123
- const rootBarrelFiles = getBarrelFiles({
124
- outputPath: resolve(config.root, config.output.path),
125
- files: filteredFiles,
126
- barrelType,
127
- })
128
-
129
- if (rootBarrelFiles.length > 0) {
130
- upsertFile(...rootBarrelFiles)
115
+ for (const file of getBarrelFiles({ outputPath: resolve(config.root, config.output.path), files: filteredFiles, barrelType })) {
116
+ upsertFile(file)
131
117
  }
132
118
  },
133
119
  },
package/src/utils.ts CHANGED
@@ -99,9 +99,10 @@ type LeafWalkParams = {
99
99
  }
100
100
 
101
101
  /**
102
- * Post-order walk that emits a barrel per visited directory.
102
+ * Post-order walk that yields a barrel per visited directory.
103
+ * Returns the list of leaf file paths collected in this subtree (used by the parent call).
103
104
  */
104
- function walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean, out: Array<FileNode>): Array<string> {
105
+ function* walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean): Generator<FileNode, string[]> {
105
106
  const subtreeLeaves: Array<string> = []
106
107
 
107
108
  for (const child of node.children) {
@@ -110,7 +111,7 @@ function walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean
110
111
  continue
111
112
  }
112
113
 
113
- const childLeaves = walkAllOrNamed(child, params, false, out)
114
+ const childLeaves = yield* walkAllOrNamed(child, params, false)
114
115
  for (const leaf of childLeaves) subtreeLeaves.push(leaf)
115
116
  }
116
117
 
@@ -119,17 +120,17 @@ function walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean
119
120
  const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({ dirPath: node.path, leafPath, sourceFile: params.sourceFiles.get(leafPath) }))
120
121
 
121
122
  if (exports.length > 0) {
122
- out.push(makeBarrel(node.path, exports))
123
+ yield makeBarrel(node.path, exports)
123
124
  }
124
125
 
125
126
  return subtreeLeaves
126
127
  }
127
128
 
128
129
  /**
129
- * Recursive walk that emits one barrel per directory, re-exporting files and sub-barrels.
130
+ * Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.
130
131
  * Used when nested: true.
131
132
  */
132
- function walkNested(node: BuildTree, out: Array<FileNode>): void {
133
+ function* walkNested(node: BuildTree): Generator<FileNode> {
133
134
  const exports: Array<ExportNode> = []
134
135
 
135
136
  for (const child of node.children) {
@@ -139,12 +140,12 @@ function walkNested(node: BuildTree, out: Array<FileNode>): void {
139
140
  continue
140
141
  }
141
142
 
142
- walkNested(child, out)
143
+ yield* walkNested(child)
143
144
  exports.push(createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }))
144
145
  }
145
146
 
146
147
  if (exports.length > 0) {
147
- out.push(makeBarrel(node.path, exports))
148
+ yield makeBarrel(node.path, exports)
148
149
  }
149
150
  }
150
151
 
@@ -204,26 +205,32 @@ type GetBarrelFilesParams = {
204
205
  }
205
206
 
206
207
  /**
207
- * Generates barrel `FileNode`s for the directory rooted at `outputPath`.
208
+ * Yields barrel `FileNode`s for the directory rooted at `outputPath`.
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * for (const file of getBarrelFiles({ outputPath, files, barrelType })) {
213
+ * upsertFile(file)
214
+ * }
215
+ * // or collect into an array
216
+ * const barrels = [...getBarrelFiles({ outputPath, files, barrelType })]
217
+ * ```
208
218
  */
209
- export function getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }: GetBarrelFilesParams): Array<FileNode> {
219
+ export function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }: GetBarrelFilesParams): Generator<FileNode> {
210
220
  const { sourceFiles, paths } = indexRelevantFiles(files, outputPath)
211
- if (paths.length === 0) return []
221
+ if (paths.length === 0) return
212
222
 
213
223
  const tree = buildTree(outputPath, paths)
214
- const result: Array<FileNode> = []
215
224
 
216
- // Use nested walk for hierarchical barrel structure
217
225
  if (nested) {
218
- walkNested(tree, result)
219
- return result
226
+ yield* walkNested(tree)
227
+ return
220
228
  }
221
229
 
222
230
  const strategy = LEAF_STRATEGIES.get(barrelType)
223
- if (!strategy) return result
231
+ if (!strategy) return
224
232
 
225
- walkAllOrNamed(tree, { sourceFiles, strategy, recursive }, true, result)
226
- return result
233
+ yield* walkAllOrNamed(tree, { sourceFiles, strategy, recursive }, true)
227
234
  }
228
235
 
229
236
  /**