@kubb/plugin-barrel 5.6.2 → 5.7.0

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
@@ -385,6 +385,15 @@ function withBarrelBannerFooter({ file, plugin, config }) {
385
385
  * and to guard the `kubb:plugin:end` handler against reacting to its own lifecycle hook.
386
386
  */
387
387
  const pluginBarrelName = "plugin-barrel";
388
+ const states = /* @__PURE__ */ new WeakMap();
389
+ function getState(config) {
390
+ const state = states.get(config) ?? {
391
+ excludedTargets: [],
392
+ pendingBarrels: []
393
+ };
394
+ states.set(config, state);
395
+ return state;
396
+ }
388
397
  /**
389
398
  * Generates an `index.ts` for every plugin output directory and one root
390
399
  * barrel at `config.output.path/index.ts` after the build completes. Ships
@@ -418,66 +427,64 @@ const pluginBarrelName = "plugin-barrel";
418
427
  * })
419
428
  * ```
420
429
  */
421
- const pluginBarrel = (0, _kubb_kit.definePlugin)(() => {
422
- const excludedTargets = [];
423
- const pendingBarrels = [];
424
- return {
425
- name: pluginBarrelName,
426
- enforce: "post",
427
- hooks: {
428
- "kubb:plugin:end"({ plugin, config }) {
429
- if (plugin.name === "plugin-barrel") return;
430
- const barrelConfig = plugin.options.output?.barrel ?? config.output.barrel ?? false;
431
- const base = node_path.default.resolve(config.root, config.output.path);
432
- const target = node_path.default.resolve(base, plugin.options.output.path);
433
- if (barrelConfig === false) {
434
- excludedTargets.push(target);
435
- return;
436
- }
437
- if (plugin.options.output.mode === "file") return;
438
- if (!isPathInside(target, base)) throw new _kubb_kit.Diagnostics.Error({
439
- code: _kubb_kit.Diagnostics.code.pathTraversal,
440
- severity: "error",
441
- message: `Output path "${plugin.options.output.path}" of plugin "${plugin.name}" resolves outside the output directory "${base}".`,
442
- help: "Keep the plugin `output.path` within `config.output.path`.",
443
- location: { kind: "config" }
444
- });
445
- pendingBarrels.push({
446
- plugin,
447
- target,
448
- barrelType: barrelConfig.type,
449
- nested: barrelConfig.nested ?? false
450
- });
451
- },
452
- "kubb:plugins:end"({ files, config, upsertFile }) {
453
- const rootBarrelConfig = config.output.barrel ?? false;
454
- const outputPath = node_path.default.resolve(config.root, config.output.path);
455
- const relevantFiles = files.filter((file) => !excludedTargets.some((target) => isPathInside(file.path, target)));
456
- excludedTargets.length = 0;
457
- const index = buildBarrelIndex(outputPath, relevantFiles);
458
- const reportedCollisions = /* @__PURE__ */ new Set();
459
- for (const { plugin, target, barrelType, nested } of pendingBarrels) for (const file of getBarrelFiles({
460
- index,
461
- targetPath: target,
462
- barrelType,
463
- nested,
464
- recursive: true,
465
- reportedCollisions
466
- })) upsertFile(withBarrelBannerFooter({
467
- file,
468
- plugin,
469
- config
470
- }));
471
- pendingBarrels.length = 0;
472
- if (rootBarrelConfig === false) return;
473
- for (const file of getBarrelFiles({
474
- index,
475
- barrelType: rootBarrelConfig.type,
476
- reportedCollisions
477
- })) upsertFile(file);
430
+ const pluginBarrel = (0, _kubb_kit.definePlugin)({
431
+ meta: { name: pluginBarrelName },
432
+ enforce: "post",
433
+ hooks: {
434
+ "kubb:plugin:end"({ plugin, config }) {
435
+ if (plugin.name === "plugin-barrel") return;
436
+ const { excludedTargets, pendingBarrels } = getState(config);
437
+ const barrelConfig = plugin.options.output?.barrel ?? config.output.barrel ?? false;
438
+ const base = node_path.default.resolve(config.root, config.output.path);
439
+ const target = node_path.default.resolve(base, plugin.options.output.path);
440
+ if (barrelConfig === false) {
441
+ excludedTargets.push(target);
442
+ return;
478
443
  }
444
+ if (plugin.options.output.mode === "file") return;
445
+ if (!isPathInside(target, base)) throw new _kubb_kit.Diagnostics.Error({
446
+ code: _kubb_kit.Diagnostics.code.pathTraversal,
447
+ severity: "error",
448
+ message: `Output path "${plugin.options.output.path}" of plugin "${plugin.name}" resolves outside the output directory "${base}".`,
449
+ help: "Keep the plugin `output.path` within `config.output.path`.",
450
+ location: { kind: "config" }
451
+ });
452
+ pendingBarrels.push({
453
+ plugin,
454
+ target,
455
+ barrelType: barrelConfig.type,
456
+ nested: barrelConfig.nested ?? false
457
+ });
458
+ },
459
+ "kubb:plugins:end"({ files, config, upsertFile }) {
460
+ const { excludedTargets, pendingBarrels } = getState(config);
461
+ const rootBarrelConfig = config.output.barrel ?? false;
462
+ const outputPath = node_path.default.resolve(config.root, config.output.path);
463
+ const relevantFiles = files.filter((file) => !excludedTargets.some((target) => isPathInside(file.path, target)));
464
+ excludedTargets.length = 0;
465
+ const index = buildBarrelIndex(outputPath, relevantFiles);
466
+ const reportedCollisions = /* @__PURE__ */ new Set();
467
+ for (const { plugin, target, barrelType, nested } of pendingBarrels) for (const file of getBarrelFiles({
468
+ index,
469
+ targetPath: target,
470
+ barrelType,
471
+ nested,
472
+ recursive: true,
473
+ reportedCollisions
474
+ })) upsertFile(withBarrelBannerFooter({
475
+ file,
476
+ plugin,
477
+ config
478
+ }));
479
+ pendingBarrels.length = 0;
480
+ if (rootBarrelConfig === false) return;
481
+ for (const file of getBarrelFiles({
482
+ index,
483
+ barrelType: rootBarrelConfig.type,
484
+ reportedCollisions
485
+ })) upsertFile(file);
479
486
  }
480
- };
487
+ }
481
488
  });
482
489
  //#endregion
483
490
  exports.pluginBarrel = pluginBarrel;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["resolve","relative","isAbsolute","posix","Diagnostics","ast","extname","definePlugin","path","Diagnostics"],"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 { 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 * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\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 let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\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 * 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 * 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 a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\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) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { extname, posix } from 'node:path'\nimport { ast, Diagnostics } from '@kubb/kit'\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 */\nfunction 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 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, filePath }: { fromDir: string; filePath: string }): string {\n return `./${posix.relative(fromDir, filePath)}`\n}\n\nfunction isBarrelPath(path: string): boolean {\n return path.endsWith(BARREL_SUFFIX)\n}\n\ntype MakeBarrelParams = {\n dirPath: string\n exports: Array<ast.ExportNode>\n sourceFiles: ReadonlyMap<string, ast.FileNode>\n reportedCollisions: Set<string>\n}\n\nfunction makeBarrel({ dirPath, exports, sourceFiles, reportedCollisions }: MakeBarrelParams): ast.FileNode {\n const names = new Map<string, string>()\n const uniqueExports: Array<ast.ExportNode> = []\n\n for (const item of exports) {\n const itemPath = posix.join(dirPath, item.path)\n const itemNames = Array.isArray(item.name)\n ? item.name\n : item.name\n ? [item.name]\n : sourceFiles.get(itemPath)?.sources.flatMap((source) => (source.name ? [source.name] : []))\n const uniqueNames: Array<string> = []\n\n for (const name of itemNames ?? []) {\n const first = names.get(name)\n if (!first) {\n names.set(name, item.path)\n uniqueNames.push(name)\n continue\n }\n\n const key = [name, posix.join(dirPath, first), itemPath].join('\\0')\n if (reportedCollisions.has(key)) continue\n reportedCollisions.add(key)\n Diagnostics.report({\n code: Diagnostics.code.barrelDuplicateExport,\n severity: 'error',\n message: `\"${name}\" is exported by both \"${first}\" and \"${item.path}\", so \"${dirPath}${BARREL_SUFFIX}\" cannot re-export both.`,\n help: 'Rename one of the colliding declarations, or configure the plugin resolver to produce distinct names.',\n })\n }\n\n if (!itemNames || item.name == null || uniqueNames.length > 0) {\n uniqueExports.push(item.name == null ? item : ast.factory.createExport({ ...item, name: Array.isArray(item.name) ? uniqueNames : uniqueNames[0] }))\n }\n }\n\n return ast.factory.createFile({ baseName: 'index.ts', path: `${dirPath}${BARREL_SUFFIX}`, exports: uniqueExports })\n}\n\ntype LeafStrategy = (params: { dirPath: string; leafPath: string; sourceFile: ast.FileNode | undefined }) => Array<ast.ExportNode>\n\nfunction indexableNames({ sources, isTypeOnly }: { sources: ReadonlyArray<ast.SourceNode>; isTypeOnly: boolean }): Array<string> {\n const names = sources.flatMap((source) => (source.isIndexable && source.name && Boolean(source.isTypeOnly) === isTypeOnly ? [source.name] : []))\n return [...new Set(names)].sort()\n}\n\nconst allStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const sources = sourceFile?.sources ?? []\n if (sources.length > 0 && !sources.some((source) => source.isIndexable)) return []\n return [ast.factory.createExport({ path: toRelativeModulePath({ fromDir: dirPath, filePath: leafPath }) })]\n}\n\nconst namedStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const modulePath = toRelativeModulePath({ fromDir: dirPath, filePath: leafPath })\n\n if (!sourceFile) return [ast.factory.createExport({ path: modulePath })]\n\n const valueNames = indexableNames({ sources: sourceFile.sources, isTypeOnly: false })\n const typeNames = indexableNames({ sources: sourceFile.sources, isTypeOnly: true })\n\n if (valueNames.length === 0 && typeNames.length === 0) {\n if (sourceFile.sources.length > 0) return []\n return [ast.factory.createExport({ path: modulePath })]\n }\n\n const exports: Array<ast.ExportNode> = []\n if (valueNames.length > 0) {\n exports.push(ast.factory.createExport({ name: valueNames, path: modulePath }))\n }\n if (typeNames.length > 0) {\n exports.push(ast.factory.createExport({ name: typeNames, path: modulePath, isTypeOnly: true }))\n }\n return exports\n}\n\ntype WalkParams = {\n sourceFiles: ReadonlyMap<string, ast.FileNode>\n strategy: LeafStrategy\n reportedCollisions: Set<string>\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(\n node: BuildTree,\n params: WalkParams,\n { isRoot, recursive }: { isRoot: boolean; recursive: boolean },\n): Generator<ast.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, { isRoot: false, recursive })\n for (const leaf of childLeaves) subtreeLeaves.push(leaf)\n }\n\n if (!isRoot && !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({ dirPath: node.path, exports, sourceFiles: params.sourceFiles, reportedCollisions: params.reportedCollisions })\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. 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: WalkParams): Generator<ast.FileNode, boolean> {\n const exports: Array<ast.ExportNode> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (isBarrelPath(child.path)) continue\n exports.push(...params.strategy({ dirPath: node.path, leafPath: child.path, sourceFile: params.sourceFiles.get(child.path) }))\n continue\n }\n\n const childYieldedBarrel = yield* walkNested(child, params)\n if (childYieldedBarrel) {\n exports.push(ast.factory.createExport({ path: toRelativeModulePath({ fromDir: node.path, filePath: `${child.path}${BARREL_SUFFIX}` }) }))\n }\n }\n\n if (exports.length > 0) {\n yield makeBarrel({ dirPath: node.path, exports, sourceFiles: params.sourceFiles, reportedCollisions: params.reportedCollisions })\n return true\n }\n\n return false\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 */\ntype BarrelIndex = {\n tree: BuildTree\n sourceFiles: ReadonlyMap<string, ast.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<ast.FileNode>): BarrelIndex {\n const outputPrefix = `${toPosixPath(outputPath)}/`\n const sourceFiles = new Map<string, ast.FileNode>()\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 }\n\n return { tree: buildTree(outputPath, [...sourceFiles.keys()]), 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 * Collision identities reported by related barrel generations in the same build.\n */\n reportedCollisions?: Set<string>\n}\n\n/**\n * Yields barrel `ast.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({\n index,\n targetPath,\n barrelType,\n nested = false,\n recursive = false,\n reportedCollisions = new Set(),\n}: GetBarrelFilesParams): Generator<ast.FileNode> {\n const node = targetPath ? findNode(index.tree, toPosixPath(targetPath)) : index.tree\n if (!node) return\n\n const strategy = barrelType === 'named' ? namedStrategy : allStrategy\n\n if (nested) {\n yield* walkNested(node, { sourceFiles: index.sourceFiles, strategy, reportedCollisions })\n return\n }\n\n yield* walkAllOrNamed(node, { sourceFiles: index.sourceFiles, strategy, reportedCollisions }, { isRoot: true, recursive })\n}\n","import path from 'node:path'\nimport { isPathInside } from '@internals/utils'\nimport { definePlugin, Diagnostics } from '@kubb/kit'\nimport type { ast, Config, NormalizedPlugin, Plugin } from '@kubb/kit'\nimport type { BarrelType, PluginBarrelConfig } from './types.ts'\nimport { buildBarrelIndex, getBarrelFiles } 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: ast.FileNode; plugin: NormalizedPlugin; config: Config }): ast.FileNode {\n const output = plugin.options?.output\n const resolver = plugin.resolver\n const hasBanner = output?.banner !== undefined\n const hasFooter = output?.footer !== undefined\n if (!resolver || (!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\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 excludedTargets: Array<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 // A plugin-level `false` wins over an enabled root barrel.\n const barrelConfig: PluginBarrelConfig | false = plugin.options.output?.barrel ?? config.output.barrel ?? false\n const base = path.resolve(config.root, config.output.path)\n const target = path.resolve(base, plugin.options.output.path)\n\n if (barrelConfig === false) {\n excludedTargets.push(target)\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 if (!isPathInside(target, base)) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.pathTraversal,\n severity: 'error',\n message: `Output path \"${plugin.options.output.path}\" of plugin \"${plugin.name}\" resolves outside the output directory \"${base}\".`,\n help: 'Keep the plugin `output.path` within `config.output.path`.',\n location: { kind: 'config' },\n })\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 = files.filter((file) => !excludedTargets.some((target) => isPathInside(file.path, target)))\n excludedTargets.length = 0\n\n const index = buildBarrelIndex(outputPath, relevantFiles)\n const reportedCollisions = new Set<string>()\n\n for (const { plugin, target, barrelType, nested } of pendingBarrels) {\n for (const file of getBarrelFiles({ index, targetPath: target, barrelType, nested, recursive: true, reportedCollisions })) {\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, reportedCollisions })) {\n upsertFile(file)\n }\n },\n },\n }\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["resolve","relative","isAbsolute","posix","Diagnostics","ast","extname","definePlugin","path","Diagnostics"],"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 { 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 * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\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 let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\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 * 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 * 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 a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\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) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { extname, posix } from 'node:path'\nimport { ast, Diagnostics } from '@kubb/kit'\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 */\nfunction 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 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, filePath }: { fromDir: string; filePath: string }): string {\n return `./${posix.relative(fromDir, filePath)}`\n}\n\nfunction isBarrelPath(path: string): boolean {\n return path.endsWith(BARREL_SUFFIX)\n}\n\ntype MakeBarrelParams = {\n dirPath: string\n exports: Array<ast.ExportNode>\n sourceFiles: ReadonlyMap<string, ast.FileNode>\n reportedCollisions: Set<string>\n}\n\nfunction makeBarrel({ dirPath, exports, sourceFiles, reportedCollisions }: MakeBarrelParams): ast.FileNode {\n const names = new Map<string, string>()\n const uniqueExports: Array<ast.ExportNode> = []\n\n for (const item of exports) {\n const itemPath = posix.join(dirPath, item.path)\n const itemNames = Array.isArray(item.name)\n ? item.name\n : item.name\n ? [item.name]\n : sourceFiles.get(itemPath)?.sources.flatMap((source) => (source.name ? [source.name] : []))\n const uniqueNames: Array<string> = []\n\n for (const name of itemNames ?? []) {\n const first = names.get(name)\n if (!first) {\n names.set(name, item.path)\n uniqueNames.push(name)\n continue\n }\n\n const key = [name, posix.join(dirPath, first), itemPath].join('\\0')\n if (reportedCollisions.has(key)) continue\n reportedCollisions.add(key)\n Diagnostics.report({\n code: Diagnostics.code.barrelDuplicateExport,\n severity: 'error',\n message: `\"${name}\" is exported by both \"${first}\" and \"${item.path}\", so \"${dirPath}${BARREL_SUFFIX}\" cannot re-export both.`,\n help: 'Rename one of the colliding declarations, or configure the plugin resolver to produce distinct names.',\n })\n }\n\n if (!itemNames || item.name == null || uniqueNames.length > 0) {\n uniqueExports.push(item.name == null ? item : ast.factory.createExport({ ...item, name: Array.isArray(item.name) ? uniqueNames : uniqueNames[0] }))\n }\n }\n\n return ast.factory.createFile({ baseName: 'index.ts', path: `${dirPath}${BARREL_SUFFIX}`, exports: uniqueExports })\n}\n\ntype LeafStrategy = (params: { dirPath: string; leafPath: string; sourceFile: ast.FileNode | undefined }) => Array<ast.ExportNode>\n\nfunction indexableNames({ sources, isTypeOnly }: { sources: ReadonlyArray<ast.SourceNode>; isTypeOnly: boolean }): Array<string> {\n const names = sources.flatMap((source) => (source.isIndexable && source.name && Boolean(source.isTypeOnly) === isTypeOnly ? [source.name] : []))\n return [...new Set(names)].sort()\n}\n\nconst allStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const sources = sourceFile?.sources ?? []\n if (sources.length > 0 && !sources.some((source) => source.isIndexable)) return []\n return [ast.factory.createExport({ path: toRelativeModulePath({ fromDir: dirPath, filePath: leafPath }) })]\n}\n\nconst namedStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const modulePath = toRelativeModulePath({ fromDir: dirPath, filePath: leafPath })\n\n if (!sourceFile) return [ast.factory.createExport({ path: modulePath })]\n\n const valueNames = indexableNames({ sources: sourceFile.sources, isTypeOnly: false })\n const typeNames = indexableNames({ sources: sourceFile.sources, isTypeOnly: true })\n\n if (valueNames.length === 0 && typeNames.length === 0) {\n if (sourceFile.sources.length > 0) return []\n return [ast.factory.createExport({ path: modulePath })]\n }\n\n const exports: Array<ast.ExportNode> = []\n if (valueNames.length > 0) {\n exports.push(ast.factory.createExport({ name: valueNames, path: modulePath }))\n }\n if (typeNames.length > 0) {\n exports.push(ast.factory.createExport({ name: typeNames, path: modulePath, isTypeOnly: true }))\n }\n return exports\n}\n\ntype WalkParams = {\n sourceFiles: ReadonlyMap<string, ast.FileNode>\n strategy: LeafStrategy\n reportedCollisions: Set<string>\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(\n node: BuildTree,\n params: WalkParams,\n { isRoot, recursive }: { isRoot: boolean; recursive: boolean },\n): Generator<ast.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, { isRoot: false, recursive })\n for (const leaf of childLeaves) subtreeLeaves.push(leaf)\n }\n\n if (!isRoot && !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({ dirPath: node.path, exports, sourceFiles: params.sourceFiles, reportedCollisions: params.reportedCollisions })\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. 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: WalkParams): Generator<ast.FileNode, boolean> {\n const exports: Array<ast.ExportNode> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (isBarrelPath(child.path)) continue\n exports.push(...params.strategy({ dirPath: node.path, leafPath: child.path, sourceFile: params.sourceFiles.get(child.path) }))\n continue\n }\n\n const childYieldedBarrel = yield* walkNested(child, params)\n if (childYieldedBarrel) {\n exports.push(ast.factory.createExport({ path: toRelativeModulePath({ fromDir: node.path, filePath: `${child.path}${BARREL_SUFFIX}` }) }))\n }\n }\n\n if (exports.length > 0) {\n yield makeBarrel({ dirPath: node.path, exports, sourceFiles: params.sourceFiles, reportedCollisions: params.reportedCollisions })\n return true\n }\n\n return false\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 */\ntype BarrelIndex = {\n tree: BuildTree\n sourceFiles: ReadonlyMap<string, ast.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<ast.FileNode>): BarrelIndex {\n const outputPrefix = `${toPosixPath(outputPath)}/`\n const sourceFiles = new Map<string, ast.FileNode>()\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 }\n\n return { tree: buildTree(outputPath, [...sourceFiles.keys()]), 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 * Collision identities reported by related barrel generations in the same build.\n */\n reportedCollisions?: Set<string>\n}\n\n/**\n * Yields barrel `ast.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({\n index,\n targetPath,\n barrelType,\n nested = false,\n recursive = false,\n reportedCollisions = new Set(),\n}: GetBarrelFilesParams): Generator<ast.FileNode> {\n const node = targetPath ? findNode(index.tree, toPosixPath(targetPath)) : index.tree\n if (!node) return\n\n const strategy = barrelType === 'named' ? namedStrategy : allStrategy\n\n if (nested) {\n yield* walkNested(node, { sourceFiles: index.sourceFiles, strategy, reportedCollisions })\n return\n }\n\n yield* walkAllOrNamed(node, { sourceFiles: index.sourceFiles, strategy, reportedCollisions }, { isRoot: true, recursive })\n}\n","import path from 'node:path'\nimport { isPathInside } from '@internals/utils'\nimport { definePlugin, Diagnostics } from '@kubb/kit'\nimport type { ast, Config, NormalizedPlugin, Plugin } from '@kubb/kit'\nimport type { BarrelType, PluginBarrelConfig } from './types.ts'\nimport { buildBarrelIndex, getBarrelFiles } 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: ast.FileNode; plugin: NormalizedPlugin; config: Config }): ast.FileNode {\n const output = plugin.options?.output\n const resolver = plugin.resolver\n const hasBanner = output?.banner !== undefined\n const hasFooter = output?.footer !== undefined\n if (!resolver || (!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\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\ntype BarrelState = {\n excludedTargets: Array<string>\n pendingBarrels: Array<PendingBarrel>\n}\n\n// One state per build, keyed by the build's `config`. The object form has no factory closure to hold it.\nconst states = new WeakMap<Config, BarrelState>()\n\nfunction getState(config: Config): BarrelState {\n const state = states.get(config) ?? { excludedTargets: [], pendingBarrels: [] }\n states.set(config, state)\n return state\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 meta: { name: pluginBarrelName },\n enforce: 'post',\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 { excludedTargets, pendingBarrels } = getState(config)\n\n // A plugin-level `false` wins over an enabled root barrel.\n const barrelConfig: PluginBarrelConfig | false = plugin.options.output?.barrel ?? config.output.barrel ?? false\n const base = path.resolve(config.root, config.output.path)\n const target = path.resolve(base, plugin.options.output.path)\n\n if (barrelConfig === false) {\n excludedTargets.push(target)\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 if (!isPathInside(target, base)) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.pathTraversal,\n severity: 'error',\n message: `Output path \"${plugin.options.output.path}\" of plugin \"${plugin.name}\" resolves outside the output directory \"${base}\".`,\n help: 'Keep the plugin `output.path` within `config.output.path`.',\n location: { kind: 'config' },\n })\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 { excludedTargets, pendingBarrels } = getState(config)\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 = files.filter((file) => !excludedTargets.some((target) => isPathInside(file.path, target)))\n excludedTargets.length = 0\n\n const index = buildBarrelIndex(outputPath, relevantFiles)\n const reportedCollisions = new Set<string>()\n\n for (const { plugin, target, barrelType, nested } of pendingBarrels) {\n for (const file of getBarrelFiles({ index, targetPath: target, barrelType, nested, recursive: true, reportedCollisions })) {\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, reportedCollisions })) {\n upsertFile(file)\n }\n },\n },\n})\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
- import { Plugin } from "@kubb/kit";
3
2
  //#region src/plugin.d.ts
4
3
  /**
5
4
  * Canonical plugin name for `@kubb/plugin-barrel`. Used for driver lookups
@@ -39,7 +38,7 @@ export declare const pluginBarrelName = "plugin-barrel";
39
38
  * })
40
39
  * ```
41
40
  */
42
- export declare const pluginBarrel: (options?: object | undefined) => Plugin<import("@kubb/core").PluginFactoryOptions<string, object, object, import("@kubb/core").Resolver>>;
41
+ export declare const pluginBarrel: import("@kubb/core").PluginFactory<import("@kubb/core").PluginFactoryOptions<string, object, object, import("@kubb/core").Resolver>>;
43
42
  //#endregion
44
43
  //#region src/types.d.ts
45
44
  /**
package/dist/index.js CHANGED
@@ -362,6 +362,15 @@ function withBarrelBannerFooter({ file, plugin, config }) {
362
362
  * and to guard the `kubb:plugin:end` handler against reacting to its own lifecycle hook.
363
363
  */
364
364
  const pluginBarrelName = "plugin-barrel";
365
+ const states = /* @__PURE__ */ new WeakMap();
366
+ function getState(config) {
367
+ const state = states.get(config) ?? {
368
+ excludedTargets: [],
369
+ pendingBarrels: []
370
+ };
371
+ states.set(config, state);
372
+ return state;
373
+ }
365
374
  /**
366
375
  * Generates an `index.ts` for every plugin output directory and one root
367
376
  * barrel at `config.output.path/index.ts` after the build completes. Ships
@@ -395,66 +404,64 @@ const pluginBarrelName = "plugin-barrel";
395
404
  * })
396
405
  * ```
397
406
  */
398
- const pluginBarrel = definePlugin(() => {
399
- const excludedTargets = [];
400
- const pendingBarrels = [];
401
- return {
402
- name: pluginBarrelName,
403
- enforce: "post",
404
- hooks: {
405
- "kubb:plugin:end"({ plugin, config }) {
406
- if (plugin.name === "plugin-barrel") return;
407
- const barrelConfig = plugin.options.output?.barrel ?? config.output.barrel ?? false;
408
- const base = path.resolve(config.root, config.output.path);
409
- const target = path.resolve(base, plugin.options.output.path);
410
- if (barrelConfig === false) {
411
- excludedTargets.push(target);
412
- return;
413
- }
414
- if (plugin.options.output.mode === "file") return;
415
- if (!isPathInside(target, base)) throw new Diagnostics.Error({
416
- code: Diagnostics.code.pathTraversal,
417
- severity: "error",
418
- message: `Output path "${plugin.options.output.path}" of plugin "${plugin.name}" resolves outside the output directory "${base}".`,
419
- help: "Keep the plugin `output.path` within `config.output.path`.",
420
- location: { kind: "config" }
421
- });
422
- pendingBarrels.push({
423
- plugin,
424
- target,
425
- barrelType: barrelConfig.type,
426
- nested: barrelConfig.nested ?? false
427
- });
428
- },
429
- "kubb:plugins:end"({ files, config, upsertFile }) {
430
- const rootBarrelConfig = config.output.barrel ?? false;
431
- const outputPath = path.resolve(config.root, config.output.path);
432
- const relevantFiles = files.filter((file) => !excludedTargets.some((target) => isPathInside(file.path, target)));
433
- excludedTargets.length = 0;
434
- const index = buildBarrelIndex(outputPath, relevantFiles);
435
- const reportedCollisions = /* @__PURE__ */ new Set();
436
- for (const { plugin, target, barrelType, nested } of pendingBarrels) for (const file of getBarrelFiles({
437
- index,
438
- targetPath: target,
439
- barrelType,
440
- nested,
441
- recursive: true,
442
- reportedCollisions
443
- })) upsertFile(withBarrelBannerFooter({
444
- file,
445
- plugin,
446
- config
447
- }));
448
- pendingBarrels.length = 0;
449
- if (rootBarrelConfig === false) return;
450
- for (const file of getBarrelFiles({
451
- index,
452
- barrelType: rootBarrelConfig.type,
453
- reportedCollisions
454
- })) upsertFile(file);
407
+ const pluginBarrel = definePlugin({
408
+ meta: { name: pluginBarrelName },
409
+ enforce: "post",
410
+ hooks: {
411
+ "kubb:plugin:end"({ plugin, config }) {
412
+ if (plugin.name === "plugin-barrel") return;
413
+ const { excludedTargets, pendingBarrels } = getState(config);
414
+ const barrelConfig = plugin.options.output?.barrel ?? config.output.barrel ?? false;
415
+ const base = path.resolve(config.root, config.output.path);
416
+ const target = path.resolve(base, plugin.options.output.path);
417
+ if (barrelConfig === false) {
418
+ excludedTargets.push(target);
419
+ return;
455
420
  }
421
+ if (plugin.options.output.mode === "file") return;
422
+ if (!isPathInside(target, base)) throw new Diagnostics.Error({
423
+ code: Diagnostics.code.pathTraversal,
424
+ severity: "error",
425
+ message: `Output path "${plugin.options.output.path}" of plugin "${plugin.name}" resolves outside the output directory "${base}".`,
426
+ help: "Keep the plugin `output.path` within `config.output.path`.",
427
+ location: { kind: "config" }
428
+ });
429
+ pendingBarrels.push({
430
+ plugin,
431
+ target,
432
+ barrelType: barrelConfig.type,
433
+ nested: barrelConfig.nested ?? false
434
+ });
435
+ },
436
+ "kubb:plugins:end"({ files, config, upsertFile }) {
437
+ const { excludedTargets, pendingBarrels } = getState(config);
438
+ const rootBarrelConfig = config.output.barrel ?? false;
439
+ const outputPath = path.resolve(config.root, config.output.path);
440
+ const relevantFiles = files.filter((file) => !excludedTargets.some((target) => isPathInside(file.path, target)));
441
+ excludedTargets.length = 0;
442
+ const index = buildBarrelIndex(outputPath, relevantFiles);
443
+ const reportedCollisions = /* @__PURE__ */ new Set();
444
+ for (const { plugin, target, barrelType, nested } of pendingBarrels) for (const file of getBarrelFiles({
445
+ index,
446
+ targetPath: target,
447
+ barrelType,
448
+ nested,
449
+ recursive: true,
450
+ reportedCollisions
451
+ })) upsertFile(withBarrelBannerFooter({
452
+ file,
453
+ plugin,
454
+ config
455
+ }));
456
+ pendingBarrels.length = 0;
457
+ if (rootBarrelConfig === false) return;
458
+ for (const file of getBarrelFiles({
459
+ index,
460
+ barrelType: rootBarrelConfig.type,
461
+ reportedCollisions
462
+ })) upsertFile(file);
456
463
  }
457
- };
464
+ }
458
465
  });
459
466
  //#endregion
460
467
  export { pluginBarrel, pluginBarrelName };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"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 { 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 * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\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 let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\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 * 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 * 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 a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\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) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { extname, posix } from 'node:path'\nimport { ast, Diagnostics } from '@kubb/kit'\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 */\nfunction 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 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, filePath }: { fromDir: string; filePath: string }): string {\n return `./${posix.relative(fromDir, filePath)}`\n}\n\nfunction isBarrelPath(path: string): boolean {\n return path.endsWith(BARREL_SUFFIX)\n}\n\ntype MakeBarrelParams = {\n dirPath: string\n exports: Array<ast.ExportNode>\n sourceFiles: ReadonlyMap<string, ast.FileNode>\n reportedCollisions: Set<string>\n}\n\nfunction makeBarrel({ dirPath, exports, sourceFiles, reportedCollisions }: MakeBarrelParams): ast.FileNode {\n const names = new Map<string, string>()\n const uniqueExports: Array<ast.ExportNode> = []\n\n for (const item of exports) {\n const itemPath = posix.join(dirPath, item.path)\n const itemNames = Array.isArray(item.name)\n ? item.name\n : item.name\n ? [item.name]\n : sourceFiles.get(itemPath)?.sources.flatMap((source) => (source.name ? [source.name] : []))\n const uniqueNames: Array<string> = []\n\n for (const name of itemNames ?? []) {\n const first = names.get(name)\n if (!first) {\n names.set(name, item.path)\n uniqueNames.push(name)\n continue\n }\n\n const key = [name, posix.join(dirPath, first), itemPath].join('\\0')\n if (reportedCollisions.has(key)) continue\n reportedCollisions.add(key)\n Diagnostics.report({\n code: Diagnostics.code.barrelDuplicateExport,\n severity: 'error',\n message: `\"${name}\" is exported by both \"${first}\" and \"${item.path}\", so \"${dirPath}${BARREL_SUFFIX}\" cannot re-export both.`,\n help: 'Rename one of the colliding declarations, or configure the plugin resolver to produce distinct names.',\n })\n }\n\n if (!itemNames || item.name == null || uniqueNames.length > 0) {\n uniqueExports.push(item.name == null ? item : ast.factory.createExport({ ...item, name: Array.isArray(item.name) ? uniqueNames : uniqueNames[0] }))\n }\n }\n\n return ast.factory.createFile({ baseName: 'index.ts', path: `${dirPath}${BARREL_SUFFIX}`, exports: uniqueExports })\n}\n\ntype LeafStrategy = (params: { dirPath: string; leafPath: string; sourceFile: ast.FileNode | undefined }) => Array<ast.ExportNode>\n\nfunction indexableNames({ sources, isTypeOnly }: { sources: ReadonlyArray<ast.SourceNode>; isTypeOnly: boolean }): Array<string> {\n const names = sources.flatMap((source) => (source.isIndexable && source.name && Boolean(source.isTypeOnly) === isTypeOnly ? [source.name] : []))\n return [...new Set(names)].sort()\n}\n\nconst allStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const sources = sourceFile?.sources ?? []\n if (sources.length > 0 && !sources.some((source) => source.isIndexable)) return []\n return [ast.factory.createExport({ path: toRelativeModulePath({ fromDir: dirPath, filePath: leafPath }) })]\n}\n\nconst namedStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const modulePath = toRelativeModulePath({ fromDir: dirPath, filePath: leafPath })\n\n if (!sourceFile) return [ast.factory.createExport({ path: modulePath })]\n\n const valueNames = indexableNames({ sources: sourceFile.sources, isTypeOnly: false })\n const typeNames = indexableNames({ sources: sourceFile.sources, isTypeOnly: true })\n\n if (valueNames.length === 0 && typeNames.length === 0) {\n if (sourceFile.sources.length > 0) return []\n return [ast.factory.createExport({ path: modulePath })]\n }\n\n const exports: Array<ast.ExportNode> = []\n if (valueNames.length > 0) {\n exports.push(ast.factory.createExport({ name: valueNames, path: modulePath }))\n }\n if (typeNames.length > 0) {\n exports.push(ast.factory.createExport({ name: typeNames, path: modulePath, isTypeOnly: true }))\n }\n return exports\n}\n\ntype WalkParams = {\n sourceFiles: ReadonlyMap<string, ast.FileNode>\n strategy: LeafStrategy\n reportedCollisions: Set<string>\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(\n node: BuildTree,\n params: WalkParams,\n { isRoot, recursive }: { isRoot: boolean; recursive: boolean },\n): Generator<ast.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, { isRoot: false, recursive })\n for (const leaf of childLeaves) subtreeLeaves.push(leaf)\n }\n\n if (!isRoot && !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({ dirPath: node.path, exports, sourceFiles: params.sourceFiles, reportedCollisions: params.reportedCollisions })\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. 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: WalkParams): Generator<ast.FileNode, boolean> {\n const exports: Array<ast.ExportNode> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (isBarrelPath(child.path)) continue\n exports.push(...params.strategy({ dirPath: node.path, leafPath: child.path, sourceFile: params.sourceFiles.get(child.path) }))\n continue\n }\n\n const childYieldedBarrel = yield* walkNested(child, params)\n if (childYieldedBarrel) {\n exports.push(ast.factory.createExport({ path: toRelativeModulePath({ fromDir: node.path, filePath: `${child.path}${BARREL_SUFFIX}` }) }))\n }\n }\n\n if (exports.length > 0) {\n yield makeBarrel({ dirPath: node.path, exports, sourceFiles: params.sourceFiles, reportedCollisions: params.reportedCollisions })\n return true\n }\n\n return false\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 */\ntype BarrelIndex = {\n tree: BuildTree\n sourceFiles: ReadonlyMap<string, ast.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<ast.FileNode>): BarrelIndex {\n const outputPrefix = `${toPosixPath(outputPath)}/`\n const sourceFiles = new Map<string, ast.FileNode>()\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 }\n\n return { tree: buildTree(outputPath, [...sourceFiles.keys()]), 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 * Collision identities reported by related barrel generations in the same build.\n */\n reportedCollisions?: Set<string>\n}\n\n/**\n * Yields barrel `ast.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({\n index,\n targetPath,\n barrelType,\n nested = false,\n recursive = false,\n reportedCollisions = new Set(),\n}: GetBarrelFilesParams): Generator<ast.FileNode> {\n const node = targetPath ? findNode(index.tree, toPosixPath(targetPath)) : index.tree\n if (!node) return\n\n const strategy = barrelType === 'named' ? namedStrategy : allStrategy\n\n if (nested) {\n yield* walkNested(node, { sourceFiles: index.sourceFiles, strategy, reportedCollisions })\n return\n }\n\n yield* walkAllOrNamed(node, { sourceFiles: index.sourceFiles, strategy, reportedCollisions }, { isRoot: true, recursive })\n}\n","import path from 'node:path'\nimport { isPathInside } from '@internals/utils'\nimport { definePlugin, Diagnostics } from '@kubb/kit'\nimport type { ast, Config, NormalizedPlugin, Plugin } from '@kubb/kit'\nimport type { BarrelType, PluginBarrelConfig } from './types.ts'\nimport { buildBarrelIndex, getBarrelFiles } 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: ast.FileNode; plugin: NormalizedPlugin; config: Config }): ast.FileNode {\n const output = plugin.options?.output\n const resolver = plugin.resolver\n const hasBanner = output?.banner !== undefined\n const hasFooter = output?.footer !== undefined\n if (!resolver || (!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\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 excludedTargets: Array<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 // A plugin-level `false` wins over an enabled root barrel.\n const barrelConfig: PluginBarrelConfig | false = plugin.options.output?.barrel ?? config.output.barrel ?? false\n const base = path.resolve(config.root, config.output.path)\n const target = path.resolve(base, plugin.options.output.path)\n\n if (barrelConfig === false) {\n excludedTargets.push(target)\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 if (!isPathInside(target, base)) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.pathTraversal,\n severity: 'error',\n message: `Output path \"${plugin.options.output.path}\" of plugin \"${plugin.name}\" resolves outside the output directory \"${base}\".`,\n help: 'Keep the plugin `output.path` within `config.output.path`.',\n location: { kind: 'config' },\n })\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 = files.filter((file) => !excludedTargets.some((target) => isPathInside(file.path, target)))\n excludedTargets.length = 0\n\n const index = buildBarrelIndex(outputPath, relevantFiles)\n const reportedCollisions = new Set<string>()\n\n for (const { plugin, target, barrelType, nested } of pendingBarrels) {\n for (const file of getBarrelFiles({ index, targetPath: target, barrelType, nested, recursive: true, reportedCollisions })) {\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, reportedCollisions })) {\n upsertFile(file)\n }\n },\n },\n }\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"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 { 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 * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\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 let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\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 * 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 * 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 a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\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) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { extname, posix } from 'node:path'\nimport { ast, Diagnostics } from '@kubb/kit'\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 */\nfunction 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 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, filePath }: { fromDir: string; filePath: string }): string {\n return `./${posix.relative(fromDir, filePath)}`\n}\n\nfunction isBarrelPath(path: string): boolean {\n return path.endsWith(BARREL_SUFFIX)\n}\n\ntype MakeBarrelParams = {\n dirPath: string\n exports: Array<ast.ExportNode>\n sourceFiles: ReadonlyMap<string, ast.FileNode>\n reportedCollisions: Set<string>\n}\n\nfunction makeBarrel({ dirPath, exports, sourceFiles, reportedCollisions }: MakeBarrelParams): ast.FileNode {\n const names = new Map<string, string>()\n const uniqueExports: Array<ast.ExportNode> = []\n\n for (const item of exports) {\n const itemPath = posix.join(dirPath, item.path)\n const itemNames = Array.isArray(item.name)\n ? item.name\n : item.name\n ? [item.name]\n : sourceFiles.get(itemPath)?.sources.flatMap((source) => (source.name ? [source.name] : []))\n const uniqueNames: Array<string> = []\n\n for (const name of itemNames ?? []) {\n const first = names.get(name)\n if (!first) {\n names.set(name, item.path)\n uniqueNames.push(name)\n continue\n }\n\n const key = [name, posix.join(dirPath, first), itemPath].join('\\0')\n if (reportedCollisions.has(key)) continue\n reportedCollisions.add(key)\n Diagnostics.report({\n code: Diagnostics.code.barrelDuplicateExport,\n severity: 'error',\n message: `\"${name}\" is exported by both \"${first}\" and \"${item.path}\", so \"${dirPath}${BARREL_SUFFIX}\" cannot re-export both.`,\n help: 'Rename one of the colliding declarations, or configure the plugin resolver to produce distinct names.',\n })\n }\n\n if (!itemNames || item.name == null || uniqueNames.length > 0) {\n uniqueExports.push(item.name == null ? item : ast.factory.createExport({ ...item, name: Array.isArray(item.name) ? uniqueNames : uniqueNames[0] }))\n }\n }\n\n return ast.factory.createFile({ baseName: 'index.ts', path: `${dirPath}${BARREL_SUFFIX}`, exports: uniqueExports })\n}\n\ntype LeafStrategy = (params: { dirPath: string; leafPath: string; sourceFile: ast.FileNode | undefined }) => Array<ast.ExportNode>\n\nfunction indexableNames({ sources, isTypeOnly }: { sources: ReadonlyArray<ast.SourceNode>; isTypeOnly: boolean }): Array<string> {\n const names = sources.flatMap((source) => (source.isIndexable && source.name && Boolean(source.isTypeOnly) === isTypeOnly ? [source.name] : []))\n return [...new Set(names)].sort()\n}\n\nconst allStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const sources = sourceFile?.sources ?? []\n if (sources.length > 0 && !sources.some((source) => source.isIndexable)) return []\n return [ast.factory.createExport({ path: toRelativeModulePath({ fromDir: dirPath, filePath: leafPath }) })]\n}\n\nconst namedStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const modulePath = toRelativeModulePath({ fromDir: dirPath, filePath: leafPath })\n\n if (!sourceFile) return [ast.factory.createExport({ path: modulePath })]\n\n const valueNames = indexableNames({ sources: sourceFile.sources, isTypeOnly: false })\n const typeNames = indexableNames({ sources: sourceFile.sources, isTypeOnly: true })\n\n if (valueNames.length === 0 && typeNames.length === 0) {\n if (sourceFile.sources.length > 0) return []\n return [ast.factory.createExport({ path: modulePath })]\n }\n\n const exports: Array<ast.ExportNode> = []\n if (valueNames.length > 0) {\n exports.push(ast.factory.createExport({ name: valueNames, path: modulePath }))\n }\n if (typeNames.length > 0) {\n exports.push(ast.factory.createExport({ name: typeNames, path: modulePath, isTypeOnly: true }))\n }\n return exports\n}\n\ntype WalkParams = {\n sourceFiles: ReadonlyMap<string, ast.FileNode>\n strategy: LeafStrategy\n reportedCollisions: Set<string>\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(\n node: BuildTree,\n params: WalkParams,\n { isRoot, recursive }: { isRoot: boolean; recursive: boolean },\n): Generator<ast.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, { isRoot: false, recursive })\n for (const leaf of childLeaves) subtreeLeaves.push(leaf)\n }\n\n if (!isRoot && !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({ dirPath: node.path, exports, sourceFiles: params.sourceFiles, reportedCollisions: params.reportedCollisions })\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. 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: WalkParams): Generator<ast.FileNode, boolean> {\n const exports: Array<ast.ExportNode> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (isBarrelPath(child.path)) continue\n exports.push(...params.strategy({ dirPath: node.path, leafPath: child.path, sourceFile: params.sourceFiles.get(child.path) }))\n continue\n }\n\n const childYieldedBarrel = yield* walkNested(child, params)\n if (childYieldedBarrel) {\n exports.push(ast.factory.createExport({ path: toRelativeModulePath({ fromDir: node.path, filePath: `${child.path}${BARREL_SUFFIX}` }) }))\n }\n }\n\n if (exports.length > 0) {\n yield makeBarrel({ dirPath: node.path, exports, sourceFiles: params.sourceFiles, reportedCollisions: params.reportedCollisions })\n return true\n }\n\n return false\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 */\ntype BarrelIndex = {\n tree: BuildTree\n sourceFiles: ReadonlyMap<string, ast.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<ast.FileNode>): BarrelIndex {\n const outputPrefix = `${toPosixPath(outputPath)}/`\n const sourceFiles = new Map<string, ast.FileNode>()\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 }\n\n return { tree: buildTree(outputPath, [...sourceFiles.keys()]), 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 * Collision identities reported by related barrel generations in the same build.\n */\n reportedCollisions?: Set<string>\n}\n\n/**\n * Yields barrel `ast.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({\n index,\n targetPath,\n barrelType,\n nested = false,\n recursive = false,\n reportedCollisions = new Set(),\n}: GetBarrelFilesParams): Generator<ast.FileNode> {\n const node = targetPath ? findNode(index.tree, toPosixPath(targetPath)) : index.tree\n if (!node) return\n\n const strategy = barrelType === 'named' ? namedStrategy : allStrategy\n\n if (nested) {\n yield* walkNested(node, { sourceFiles: index.sourceFiles, strategy, reportedCollisions })\n return\n }\n\n yield* walkAllOrNamed(node, { sourceFiles: index.sourceFiles, strategy, reportedCollisions }, { isRoot: true, recursive })\n}\n","import path from 'node:path'\nimport { isPathInside } from '@internals/utils'\nimport { definePlugin, Diagnostics } from '@kubb/kit'\nimport type { ast, Config, NormalizedPlugin, Plugin } from '@kubb/kit'\nimport type { BarrelType, PluginBarrelConfig } from './types.ts'\nimport { buildBarrelIndex, getBarrelFiles } 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: ast.FileNode; plugin: NormalizedPlugin; config: Config }): ast.FileNode {\n const output = plugin.options?.output\n const resolver = plugin.resolver\n const hasBanner = output?.banner !== undefined\n const hasFooter = output?.footer !== undefined\n if (!resolver || (!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\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\ntype BarrelState = {\n excludedTargets: Array<string>\n pendingBarrels: Array<PendingBarrel>\n}\n\n// One state per build, keyed by the build's `config`. The object form has no factory closure to hold it.\nconst states = new WeakMap<Config, BarrelState>()\n\nfunction getState(config: Config): BarrelState {\n const state = states.get(config) ?? { excludedTargets: [], pendingBarrels: [] }\n states.set(config, state)\n return state\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 meta: { name: pluginBarrelName },\n enforce: 'post',\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 { excludedTargets, pendingBarrels } = getState(config)\n\n // A plugin-level `false` wins over an enabled root barrel.\n const barrelConfig: PluginBarrelConfig | false = plugin.options.output?.barrel ?? config.output.barrel ?? false\n const base = path.resolve(config.root, config.output.path)\n const target = path.resolve(base, plugin.options.output.path)\n\n if (barrelConfig === false) {\n excludedTargets.push(target)\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 if (!isPathInside(target, base)) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.pathTraversal,\n severity: 'error',\n message: `Output path \"${plugin.options.output.path}\" of plugin \"${plugin.name}\" resolves outside the output directory \"${base}\".`,\n help: 'Keep the plugin `output.path` within `config.output.path`.',\n location: { kind: 'config' },\n })\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 { excludedTargets, pendingBarrels } = getState(config)\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 = files.filter((file) => !excludedTargets.some((target) => isPathInside(file.path, target)))\n excludedTargets.length = 0\n\n const index = buildBarrelIndex(outputPath, relevantFiles)\n const reportedCollisions = new Set<string>()\n\n for (const { plugin, target, barrelType, nested } of pendingBarrels) {\n for (const file of getBarrelFiles({ index, targetPath: target, barrelType, nested, recursive: true, reportedCollisions })) {\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, reportedCollisions })) {\n upsertFile(file)\n }\n },\n },\n})\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-barrel",
3
- "version": "5.6.2",
3
+ "version": "5.7.0",
4
4
  "description": "Barrel-file plugin for Kubb. Automatically generates index.ts re-export files per plugin output directory.",
5
5
  "keywords": [
6
6
  "barrel",
@@ -58,11 +58,11 @@
58
58
  "registry": "https://registry.npmjs.org/"
59
59
  },
60
60
  "dependencies": {
61
- "@kubb/kit": "5.6.2"
61
+ "@kubb/kit": "5.7.0"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@internals/utils": "0.0.1",
65
- "@kubb/core": "5.6.2"
65
+ "@kubb/core": "5.7.0"
66
66
  },
67
67
  "engines": {
68
68
  "node": ">=22"