{"version":3,"file":"index.cjs","names":["ast","extname","resolve","definePlugin","path"],"sources":["../../../internals/utils/src/fs.ts","../src/utils.ts","../src/plugin.ts"],"sourcesContent":["import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { camelCase } from './casing.ts'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n *   const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n  if (runtime.isBun) {\n    return Bun.file(path).exists()\n  }\n  return access(path).then(\n    () => true,\n    () => false,\n  )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n  if (runtime.isBun) {\n    return Bun.file(path).text()\n  }\n  return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n  /**\n   * When `true`, re-reads the file immediately after writing and throws if the\n   * content does not match — useful for catching write failures on unreliable file systems.\n   */\n  sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n  return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source)         // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source)         // null — file unchanged\n * await write('./src/Pet.ts', '  ')           // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n  const trimmed = data.trim()\n  if (trimmed === '') return null\n\n  const content = `${trimmed}\\n`\n  const resolved = resolve(path)\n\n  if (runtime.isBun) {\n    const file = Bun.file(resolved)\n    const oldContent = (await file.exists()) ? await file.text() : ''\n    if (matchesStored({ stored: oldContent, source: trimmed })) return null\n    await Bun.write(resolved, content)\n    return content\n  }\n\n  try {\n    const oldContent = await readFile(resolved, { encoding: 'utf-8' })\n    if (matchesStored({ stored: oldContent, source: trimmed })) return null\n  } catch {\n    /* file doesn't exist yet */\n  }\n\n  // Creating the directory up front costs a syscall per file, and every file after the first in a\n  // directory pays it for nothing. Write first and only fall back when the directory is missing,\n  // which also stays correct when something removed it mid-run.\n  try {\n    await writeFile(resolved, content, { encoding: 'utf-8' })\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n    await mkdir(dirname(resolved), { recursive: true })\n    await writeFile(resolved, content, { encoding: 'utf-8' })\n  }\n\n  if (options.sanity) {\n    const savedData = await readFile(resolved, { encoding: 'utf-8' })\n    if (savedData !== content) {\n      throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n    }\n    return savedData\n  }\n\n  return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n  return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards destructive operations: before wiping an output directory, check that it does not contain\n * the project root, otherwise a `clean` would delete `kubb.config` and every source file.\n *\n * @example\n * isPathInside('./src/gen', '.')   // true  — nested inside the root\n * isPathInside('.', '.')           // true  — the same directory counts as inside\n * isPathInside('.', './src/gen')   // false — the root is not inside its own output\n * isPathInside('../other', '.')    // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n  const resolvedPath = resolve(path)\n  const resolvedParent = resolve(parent)\n  if (resolvedPath === resolvedParent) return true\n\n  const rel = relative(resolvedParent, resolvedPath)\n  return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n\n/**\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\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts')             // 'petStore'\n * trimExtName('/src/models/pet.ts')      // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts')  // '/project.v2/gen/pet'\n * trimExtName('noExtension')             // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n  const dotIndex = text.lastIndexOf('.')\n  if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n    return text.slice(0, dotIndex)\n  }\n  return text\n}\n\n/**\n * Builds a nested file path from a dotted name. Splits on dots that precede a letter\n * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases\n * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.\n *\n * Empty segments are dropped before joining. They arise when the name starts with a dot\n * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to\n * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an\n * absolute path, letting generated files escape the configured output directory.\n *\n * @example Nested path from a dotted name\n * `toFilePath('pet.petId') // 'pet/petId'`\n *\n * @example PascalCase the final segment\n * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`\n *\n * @example Suffix applied to the final segment only\n * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`\n */\nexport function toFilePath(name: string, caseLast: (part: string) => string = camelCase): string {\n  const parts = name.split(/\\.(?=[a-zA-Z])/)\n  return parts\n    .map((part, i) => (i === parts.length - 1 ? caseLast(part) : camelCase(part)))\n    .filter(Boolean)\n    .join('/')\n}\n","import { extname, resolve } from 'node:path'\nimport { ast, type ExportNode, type FileNode, type SourceNode } from '@kubb/ast'\nimport type { Config, NormalizedPlugin } from '@kubb/core'\nimport { 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\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 */\ntype 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\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\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 ast.factory.createFile({\n    baseName: 'index.ts',\n    path: `${dirPath}${BARREL_SUFFIX}`,\n    exports,\n    sources: [],\n    imports: [],\n    // Default to no banner/footer. The barrel plugin 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 [ast.factory.createExport({ path: toRelativeModulePath(dirPath, leafPath) })]\n}\n\nconst namedStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n  const modulePath = toRelativeModulePath(dirPath, leafPath)\n\n  if (!sourceFile) return [ast.factory.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 [ast.factory.createExport({ path: modulePath })]\n  }\n\n  const exports: Array<ExportNode> = []\n  if (valueNames.size > 0) {\n    exports.push(ast.factory.createExport({ name: [...valueNames].sort(), path: modulePath }))\n  }\n  if (typeNames.size > 0) {\n    exports.push(ast.factory.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\ntype NestedWalkParams = {\n  sourceFiles: ReadonlyMap<string, FileNode>\n  strategy: LeafStrategy\n}\n\n/**\n * Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.\n * Used when nested: true. Leaf files honor the barrel `strategy`, so `named` emits explicit\n * named exports instead of wildcards. Sub-directory barrels are chained with a wildcard\n * re-export, which forwards the names the child barrel already curated. Returns whether this\n * node yielded a barrel, so a parent never re-exports a sub-directory that produced nothing.\n */\nfunction* walkNested(node: BuildTree, params: NestedWalkParams): Generator<FileNode, boolean> {\n  const exports: Array<ExportNode> = []\n\n  for (const child of node.children) {\n    if (child.isFile) {\n      if (isBarrelPath(child.path)) continue\n      const sourceFile = params.sourceFiles.get(child.path) ?? null\n      exports.push(...params.strategy({ dirPath: node.path, leafPath: child.path, sourceFile }))\n      continue\n    }\n\n    const childYieldedBarrel = yield* walkNested(child, params)\n    if (childYieldedBarrel) {\n      exports.push(ast.factory.createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }))\n    }\n  }\n\n  if (exports.length > 0) {\n    yield makeBarrel(node.path, exports)\n    return true\n  }\n\n  return false\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\n/**\n * A directory tree plus the source-file lookup it was built from, scoped to a single output root.\n * Build it once with {@link buildBarrelIndex} and derive every barrel (per-plugin and root) from\n * it via {@link getBarrelFiles}, instead of re-scanning the full file set once per barrel.\n */\nexport type BarrelIndex = {\n  tree: BuildTree\n  sourceFiles: ReadonlyMap<string, FileNode>\n}\n\n/**\n * Indexes `files` once for the directory rooted at `outputPath`: filters to indexable source\n * files under that path and builds their directory tree. Reuse the result across every barrel\n * derived from the same root rather than re-filtering and re-building per barrel.\n */\nexport function buildBarrelIndex(outputPath: string, files: ReadonlyArray<FileNode>): BarrelIndex {\n  const { sourceFiles, paths } = indexRelevantFiles(files, outputPath)\n  return { tree: buildTree(outputPath, paths), sourceFiles }\n}\n\n/**\n * Locates the node for `targetPath` within an index tree, walking down through directory nodes\n * only. Returns `undefined` when no file exists at or under `targetPath` (nothing to barrel).\n */\nfunction findNode(node: BuildTree, targetPath: string): BuildTree | undefined {\n  if (node.path === targetPath) return node\n  if (node.isFile || !targetPath.startsWith(`${node.path}/`)) return undefined\n\n  for (const child of node.children) {\n    if (!child.isFile && (child.path === targetPath || targetPath.startsWith(`${child.path}/`))) {\n      return findNode(child, targetPath)\n    }\n  }\n\n  return undefined\n}\n\ntype GetBarrelFilesParams = {\n  /**\n   * Index built once via {@link buildBarrelIndex} for the shared output root.\n   */\n  index: BarrelIndex\n  /**\n   * Absolute directory the barrel(s) should be rooted at, a subtree of the index root.\n   * Defaults to the index root.\n   */\n  targetPath?: string\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  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  recursive?: boolean\n}\n\n/**\n * Yields barrel `FileNode`s for `targetPath` (or the index root), derived from a shared index.\n * Locating the subtree is a bounded walk down from the root, so deriving many barrels (one per\n * plugin, plus the root) from one index avoids re-scanning the full file set for each.\n *\n * @example\n * ```ts\n * const index = buildBarrelIndex(outputPath, files)\n * for (const file of getBarrelFiles({ index, targetPath, barrelType })) {\n *   upsertFile(file)\n * }\n * ```\n */\nexport function* getBarrelFiles({ index, targetPath, barrelType, nested = false, recursive = false }: GetBarrelFilesParams): Generator<FileNode> {\n  const node = targetPath ? findNode(index.tree, toPosixPath(targetPath)) : index.tree\n  if (!node) return\n\n  const strategy = LEAF_STRATEGIES.get(barrelType)\n  if (!strategy) return\n\n  if (nested) {\n    yield* walkNested(node, { sourceFiles: index.sourceFiles, strategy })\n    return\n  }\n\n  yield* walkAllOrNamed(node, { sourceFiles: index.sourceFiles, strategy, recursive }, true)\n}\n\n/**\n * Builds a POSIX-normalized prefix for a plugin's output. A directory output gets a trailing `/`,\n * while a `mode: 'file'` output (the path is the file itself) gets the exact path with no 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  const resolved = toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path))\n  return plugin.options.output.mode === 'file' ? resolved : `${resolved}/`\n}\n\n/**\n * Returns `true` when `filePath` lives under any of the given excluded prefixes. A prefix with a\n * trailing `/` matches a directory subtree, and a prefix without one matches that exact file\n * (used for `mode: 'file'` outputs).\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  // Plain `for...of` over the Set rather than `.values().some()`: the iterator-helper `some`\n  // allocates an iterator object per call, and this runs once per file during barrel generation.\n  for (const prefix of prefixes) {\n    const matched = prefix.endsWith('/') ? normalized.startsWith(prefix) : normalized === prefix\n    if (matched) return true\n  }\n  return false\n}\n","import path from 'node:path'\nimport type { FileNode } from '@kubb/ast'\nimport { definePlugin } from '@kubb/core'\nimport type { Config, NormalizedPlugin, Plugin } from '@kubb/core'\nimport type { BarrelConfig, BarrelType, PluginBarrelConfig } from './types.ts'\nimport { buildBarrelIndex, 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.default.banner(undefined, context) : file.banner,\n    footer: hasFooter ? resolver.default.footer(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 `{ type: 'named' | 'all' }` to opt this plugin into a barrel. Set to `false`\n         * (the default) to disable barrel generation for this plugin entirely, which also\n         * excludes the plugin's files from the root barrel.\n         *\n         * Falls back to `config.output.barrel` when omitted.\n         *\n         * @default false\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 `{ type: 'named' | 'all' }` to opt into a root barrel. Individual plugins can\n         * override this via their own `output.barrel`.\n         *\n         * @default false\n         */\n        barrel?: BarrelConfig | false\n      }\n    }\n  }\n}\n\n/**\n * Canonical plugin name for `@kubb/plugin-barrel`. Used for driver lookups\n * and to guard the `kubb:plugin:end` handler against reacting to its own lifecycle hook.\n */\nexport const pluginBarrelName = 'plugin-barrel' satisfies Plugin['name']\n\ntype PendingBarrel = {\n  plugin: NormalizedPlugin\n  target: string\n  barrelType: BarrelType\n  nested: boolean\n}\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`, but generates\n * nothing until a barrel is configured.\n *\n * Each plugin inherits `output.barrel` from `config.output.barrel` (which\n * defaults to `false`, no barrel). Set `barrel: { type: 'named' | 'all' }` on\n * the root config, a plugin, or both to opt in; a plugin-level `false`\n * overrides an enabled root barrel and also excludes that plugin's files\n * from the root barrel.\n *\n * A plugin with `output.mode: 'file'` gets no per-plugin barrel, since its output\n * is a single file. The root barrel re-exports that file directly.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@kubb/core'\n * import { pluginBarrel } from '@kubb/plugin-barrel'\n * import { pluginTs } from '@kubb/plugin-ts'\n * import { pluginZod } from '@kubb/plugin-zod'\n *\n * export default defineConfig({\n *   input: './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 *     pluginBarrel(),\n *   ],\n * })\n * ```\n */\nexport const pluginBarrel = definePlugin(() => {\n  const excludedPrefixes = new Set<string>()\n  const pendingBarrels: Array<PendingBarrel> = []\n\n  return {\n    name: pluginBarrelName,\n    enforce: 'post' as const,\n    hooks: {\n      'kubb:plugin:end'({ plugin, config }) {\n        // Skip reactions to the barrel plugin's own lifecycle hook\n        if (plugin.name === pluginBarrelName) return\n\n        const pluginBarrelOpt = plugin.options.output?.barrel\n        const configBarrel = config.output.barrel\n\n        // Root config barrel doesn't have nested, so we add it\n        const barrelConfig: PluginBarrelConfig | false = (() => {\n          if (pluginBarrelOpt !== undefined) return pluginBarrelOpt\n          if (configBarrel !== undefined) return configBarrel === false ? false : { ...configBarrel, nested: false }\n          return false\n        })()\n\n        if (barrelConfig === false) {\n          excludedPrefixes.add(getPluginOutputPrefix(plugin, config))\n          return\n        }\n\n        // `mode: 'file'` writes a single file, so there is no directory to barrel. The root barrel\n        // re-exports that file as a direct leaf of `config.output.path`.\n        if (plugin.options.output.mode === 'file') {\n          return\n        }\n\n        const base = path.resolve(config.root, config.output.path)\n        const target = path.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\n        // Only the target directory and barrel strategy are recorded here. The actual file-set\n        // scan and directory-tree build happen once, in `kubb:plugins:end`, and are shared by\n        // every plugin barrel plus the root barrel instead of repeating per plugin.\n        pendingBarrels.push({ plugin, target, barrelType: barrelConfig.type, nested: barrelConfig.nested ?? false })\n      },\n      'kubb:plugins:end'({ files, config, upsertFile }) {\n        const rootBarrelConfig = config.output.barrel ?? false\n        const outputPath = path.resolve(config.root, config.output.path)\n\n        // A `barrel: false` plugin gets no barrel and stays out of the root, so drop its files\n        // once here. Every barrel below then derives from a single index of what remains.\n        const relevantFiles = excludedPrefixes.size === 0 ? files : files.filter((f) => !isExcludedPath(f.path, excludedPrefixes))\n        excludedPrefixes.clear()\n\n        const index = buildBarrelIndex(outputPath, relevantFiles)\n\n        for (const { plugin, target, barrelType, nested } of pendingBarrels) {\n          for (const file of getBarrelFiles({ index, targetPath: target, barrelType, nested, recursive: true })) {\n            upsertFile(withBarrelBannerFooter({ file, plugin, config }))\n          }\n        }\n        pendingBarrels.length = 0\n\n        if (rootBarrelConfig === false) return\n\n        for (const file of getBarrelFiles({ index, barrelType: rootBarrelConfig.type })) {\n          upsertFile(file)\n        }\n      },\n    },\n  }\n})\n"],"mappings":"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"}