@kubb/middleware-barrel 5.0.0-alpha.55

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/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright (c) 2026 Stijn Van Hulle
2
+
3
+ This repository contains software under two licenses:
4
+
5
+ 1. Most of the code in this repository is licensed under the
6
+ MIT License — see licenses/LICENSE-MIT for the full license text.
7
+
8
+ 2. The following components are licensed under the
9
+ GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later)
10
+ — see licenses/LICENSE-AGPL-3.0 for the full license text:
11
+
12
+ - packages/agent (published as @kubb/agent)
13
+
14
+ Each package's own LICENSE file or package.json specifies its applicable license.
@@ -0,0 +1,8 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __name = (target, value) => __defProp(target, "name", {
4
+ value,
5
+ configurable: true
6
+ });
7
+ //#endregion
8
+ export { __name as t };
package/dist/index.cjs ADDED
@@ -0,0 +1,279 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#endregion
3
+ let _kubb_core = require("@kubb/core");
4
+ let node_path = require("node:path");
5
+ let _kubb_ast = require("@kubb/ast");
6
+ //#region src/constants.ts
7
+ /**
8
+ * Base file name for barrel files (without extension).
9
+ */
10
+ const BARREL_BASENAME = "index";
11
+ /**
12
+ * Full file name for barrel files (with extension).
13
+ */
14
+ const BARREL_FILENAME = "index.ts";
15
+ //#endregion
16
+ //#region src/utils/TreeNode.ts
17
+ /**
18
+ * Builds a `TreeNode` directory tree from a list of absolute file paths.
19
+ *
20
+ * All `filePaths` must be inside `rootPath`. Paths that are outside
21
+ * the root or that equal the root are silently ignored.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const tree = buildTree('/src/gen/types', [
26
+ * '/src/gen/types/pet.ts',
27
+ * '/src/gen/types/user.ts',
28
+ * '/src/gen/types/pets/listPets.ts',
29
+ * ])
30
+ * ```
31
+ */
32
+ function buildTree(rootPath, filePaths) {
33
+ const root = {
34
+ path: rootPath,
35
+ children: [],
36
+ isFile: false
37
+ };
38
+ for (const filePath of filePaths) {
39
+ if (!filePath.startsWith(rootPath + node_path.posix.sep) && !filePath.startsWith(rootPath + "/")) continue;
40
+ const parts = filePath.slice(rootPath.length).replace(/^\//g, "").replace(/^\\/g, "").split(/[/\\]/).filter(Boolean);
41
+ let current = root;
42
+ for (let i = 0; i < parts.length; i++) {
43
+ const isLast = i === parts.length - 1;
44
+ const part = parts[i];
45
+ const childPath = `${current.path}/${part}`;
46
+ let child = current.children.find((c) => c.path === childPath);
47
+ if (!child) {
48
+ child = {
49
+ path: childPath,
50
+ children: [],
51
+ isFile: isLast
52
+ };
53
+ current.children.push(child);
54
+ }
55
+ current = child;
56
+ }
57
+ }
58
+ return root;
59
+ }
60
+ //#endregion
61
+ //#region src/utils/getBarrelFiles.ts
62
+ /**
63
+ * Derives a relative module specifier (no extension) from an absolute `filePath`
64
+ * relative to an absolute `fromDir`.
65
+ *
66
+ * @example
67
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet'
68
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag'
69
+ */
70
+ function toRelativeModulePath(fromDir, filePath) {
71
+ return `./${filePath.slice(fromDir.length).replace(/^[/\\]/g, "").replace(/\.[^/.]+$/, "")}`;
72
+ }
73
+ /**
74
+ * Generates barrel `FileNode[]` for a given directory tree node using the `'all'` strategy:
75
+ * each leaf file gets a `export * from './relPath'` in the barrel of its nearest ancestor directory.
76
+ *
77
+ * Only a single barrel file (at `treeNode.path`) is generated — sub-directory files are referenced
78
+ * with their full relative path from `treeNode.path`.
79
+ */
80
+ function getBarrelFilesAll(treeNode, _sourceFiles) {
81
+ const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`));
82
+ if (leafPaths.length === 0) return [];
83
+ return [(0, _kubb_ast.createFile)({
84
+ baseName: BARREL_FILENAME,
85
+ path: `${treeNode.path}/${BARREL_FILENAME}`,
86
+ exports: leafPaths.map((filePath) => (0, _kubb_ast.createExport)({ path: toRelativeModulePath(treeNode.path, filePath) })),
87
+ sources: [],
88
+ imports: []
89
+ })];
90
+ }
91
+ /**
92
+ * Generates barrel `FileNode[]` for a given directory tree node using the `'named'` strategy:
93
+ * each indexable source in each leaf file gets an individual named `export { name } from '...'`.
94
+ */
95
+ function getBarrelFilesNamed(treeNode, sourceFiles) {
96
+ const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`));
97
+ if (leafPaths.length === 0) return [];
98
+ const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`;
99
+ const exports = [];
100
+ for (const filePath of leafPaths) {
101
+ const sourceFile = sourceFiles.find((f) => f.path === filePath);
102
+ if (!sourceFile) {
103
+ exports.push((0, _kubb_ast.createExport)({ path: toRelativeModulePath(treeNode.path, filePath) }));
104
+ continue;
105
+ }
106
+ const indexableSources = sourceFile.sources.filter((s) => s.isIndexable && s.name);
107
+ if (indexableSources.length === 0) {
108
+ exports.push((0, _kubb_ast.createExport)({ path: toRelativeModulePath(treeNode.path, filePath) }));
109
+ continue;
110
+ }
111
+ const names = indexableSources.map((s) => s.name);
112
+ exports.push((0, _kubb_ast.createExport)({
113
+ name: names,
114
+ path: toRelativeModulePath(treeNode.path, filePath)
115
+ }));
116
+ }
117
+ if (exports.length === 0) return [];
118
+ return [(0, _kubb_ast.createFile)({
119
+ baseName: BARREL_FILENAME,
120
+ path: barrelPath,
121
+ exports,
122
+ sources: [],
123
+ imports: []
124
+ })];
125
+ }
126
+ /**
127
+ * Generates barrel `FileNode[]` for a given directory tree node using the `'propagate'` strategy:
128
+ * like `'all'` but also generates intermediate barrel files for every sub-directory, so that
129
+ * consumers can import from any depth.
130
+ *
131
+ * Leaf barrels export directly from their files; parent barrels export from their sub-barrel files.
132
+ */
133
+ function getBarrelFilesPropagate(treeNode) {
134
+ return collectPropagatedBarrels(treeNode);
135
+ }
136
+ function collectPropagatedBarrels(node) {
137
+ const result = [];
138
+ const barrelExports = [];
139
+ for (const child of node.children) if (child.isFile) {
140
+ if (!child.path.endsWith(`/index.ts`)) barrelExports.push((0, _kubb_ast.createExport)({ path: toRelativeModulePath(node.path, child.path) }));
141
+ } else {
142
+ const subBarrels = collectPropagatedBarrels(child);
143
+ result.push(...subBarrels);
144
+ const subBarrelPath = `${child.path}/${BARREL_BASENAME}`;
145
+ barrelExports.push((0, _kubb_ast.createExport)({ path: toRelativeModulePath(node.path, subBarrelPath) }));
146
+ }
147
+ if (barrelExports.length > 0) result.push((0, _kubb_ast.createFile)({
148
+ baseName: BARREL_FILENAME,
149
+ path: `${node.path}/${BARREL_FILENAME}`,
150
+ exports: barrelExports,
151
+ sources: [],
152
+ imports: []
153
+ }));
154
+ return result;
155
+ }
156
+ /**
157
+ * Collects all leaf (file) paths within a tree node recursively.
158
+ */
159
+ function collectLeafPaths(node) {
160
+ if (node.isFile) return [node.path];
161
+ return node.children.flatMap((c) => collectLeafPaths(c));
162
+ }
163
+ /**
164
+ * Generates barrel `FileNode[]` for a directory rooted at `outputPath`, given the full set of
165
+ * generated source `files`, using the specified `barrelType` strategy.
166
+ *
167
+ * Files not located inside `outputPath` are excluded automatically.
168
+ *
169
+ * @param outputPath Absolute path to the output directory.
170
+ * @param files All generated files (across all plugins).
171
+ * @param barrelType Barrel generation strategy.
172
+ */
173
+ function getBarrelFiles(outputPath, files, barrelType) {
174
+ const relevantFiles = files.filter((f) => {
175
+ const normalizedFilePath = f.path.replace(/\\/g, "/");
176
+ const normalizedOutputPath = outputPath.replace(/\\/g, "/");
177
+ return normalizedFilePath.startsWith(normalizedOutputPath + "/") && !normalizedFilePath.endsWith(`/index.ts`);
178
+ });
179
+ if (relevantFiles.length === 0) return [];
180
+ const tree = buildTree(outputPath, relevantFiles.map((f) => f.path));
181
+ switch (barrelType) {
182
+ case "all": return getBarrelFilesAll(tree, relevantFiles);
183
+ case "named": return getBarrelFilesNamed(tree, relevantFiles);
184
+ case "propagate": return getBarrelFilesPropagate(tree);
185
+ default: return [];
186
+ }
187
+ }
188
+ //#endregion
189
+ //#region src/utils/generatePerPluginBarrel.ts
190
+ /**
191
+ * Generates barrel files for a single plugin's output directory.
192
+ *
193
+ * The barrel file is placed at `resolve(config.root, plugin.options.output.path)/index.ts`
194
+ * and re-exports all files generated by that plugin, using the given `barrelType` strategy.
195
+ */
196
+ function generatePerPluginBarrel({ barrelType, plugin, files, config }) {
197
+ return getBarrelFiles((0, node_path.resolve)(config.root, plugin.options.output.path), files, barrelType);
198
+ }
199
+ //#endregion
200
+ //#region src/utils/generateRootBarrel.ts
201
+ /**
202
+ * Generates a root barrel file at `resolve(config.root, config.output.path)/index.ts`.
203
+ *
204
+ * The root barrel re-exports from all files across all plugins that are located
205
+ * inside the root output directory, using the given `barrelType` strategy.
206
+ *
207
+ * In practice this re-exports the per-plugin barrels when `barrelType = 'propagate'`,
208
+ * or all individual files when `barrelType = 'all'` or `'named'`.
209
+ */
210
+ function generateRootBarrel({ barrelType, files, config }) {
211
+ return getBarrelFiles((0, node_path.resolve)(config.root, config.output.path), files, barrelType);
212
+ }
213
+ //#endregion
214
+ //#region src/middleware.ts
215
+ /**
216
+ * Barrel-file generation middleware.
217
+ *
218
+ * When added to `config.middleware`, generates an `index.ts` barrel file for each
219
+ * plugin that has `output.barrelType` set (or inherits it from `config.output.barrelType`),
220
+ * as well as a root `index.ts` at `config.output.path` when `config.output.barrelType` is set.
221
+ *
222
+ * The `barrelType` option controls the re-export style:
223
+ * - `'all'` — `export * from '...'` for each generated file
224
+ * - `'named'` — `export { name1, name2 } from '...'` using each file's indexable sources
225
+ * - `'propagate'` — like `'all'` with intermediate barrel files for sub-directories
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * import { middlewareBarrel } from '@kubb/middleware-barrel'
230
+ *
231
+ * export default defineConfig({
232
+ * output: { path: 'src/gen', barrelType: 'named' },
233
+ * plugins: [
234
+ * pluginTs({ output: { path: 'types', barrelType: 'all' } }),
235
+ * pluginZod({ output: { path: 'schemas' } }),
236
+ * ],
237
+ * middleware: [middlewareBarrel],
238
+ * })
239
+ * ```
240
+ */
241
+ const middlewareBarrel = (0, _kubb_core.defineMiddleware)({
242
+ name: "middleware-barrel",
243
+ install(hooks) {
244
+ let ctx;
245
+ hooks.on("kubb:build:start", (buildCtx) => {
246
+ ctx = buildCtx;
247
+ });
248
+ hooks.on("kubb:plugin:end", ({ plugin }) => {
249
+ if (!ctx) return;
250
+ const normalizedPlugin = plugin;
251
+ const pluginOutput = normalizedPlugin.options.output;
252
+ const rootOutput = ctx.config.output;
253
+ const barrelType = pluginOutput?.barrelType !== void 0 ? pluginOutput.barrelType : rootOutput.barrelType;
254
+ if (!barrelType) return;
255
+ const barrelFiles = generatePerPluginBarrel({
256
+ barrelType,
257
+ plugin: normalizedPlugin,
258
+ files: ctx.files,
259
+ config: ctx.config
260
+ });
261
+ if (barrelFiles.length > 0) ctx.upsertFile(...barrelFiles);
262
+ });
263
+ hooks.on("kubb:build:end", () => {
264
+ if (!ctx) return;
265
+ const rootBarrelType = ctx.config.output.barrelType;
266
+ if (!rootBarrelType) return;
267
+ const rootBarrelFiles = generateRootBarrel({
268
+ barrelType: rootBarrelType,
269
+ files: ctx.files,
270
+ config: ctx.config
271
+ });
272
+ if (rootBarrelFiles.length > 0) ctx.upsertFile(...rootBarrelFiles);
273
+ });
274
+ }
275
+ });
276
+ //#endregion
277
+ exports.middlewareBarrel = middlewareBarrel;
278
+
279
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["posix"],"sources":["../src/constants.ts","../src/utils/TreeNode.ts","../src/utils/getBarrelFiles.ts","../src/utils/generatePerPluginBarrel.ts","../src/utils/generateRootBarrel.ts","../src/middleware.ts"],"sourcesContent":["/**\n * Base file name for barrel files (without extension).\n */\nexport const BARREL_BASENAME = 'index' as const\n\n/**\n * Full file name for barrel files (with extension).\n */\nexport const BARREL_FILENAME = 'index.ts' as const\n","import { posix } from 'node:path'\n\n/**\n * A node in a directory tree used to compute barrel file exports.\n *\n * Each `TreeNode` represents either a directory or a file entry.\n * Directory nodes have `children`; file nodes have an empty `children` array.\n */\nexport type TreeNode = {\n /**\n * Absolute path of the directory (root of this subtree) or file.\n */\n path: string\n /**\n * Child nodes (sub-directories and files) within this directory.\n */\n children: Array<TreeNode>\n /**\n * `true` when this node represents a file (leaf node).\n */\n isFile: boolean\n}\n\n/**\n * Builds a `TreeNode` directory tree from a list of absolute file paths.\n *\n * All `filePaths` must be inside `rootPath`. Paths that are outside\n * the root or that equal the root are silently ignored.\n *\n * @example\n * ```ts\n * const tree = buildTree('/src/gen/types', [\n * '/src/gen/types/pet.ts',\n * '/src/gen/types/user.ts',\n * '/src/gen/types/pets/listPets.ts',\n * ])\n * ```\n */\nexport function buildTree(rootPath: string, filePaths: ReadonlyArray<string>): TreeNode {\n const root: TreeNode = { path: rootPath, children: [], isFile: false }\n\n for (const filePath of filePaths) {\n // Only include files inside rootPath\n if (!filePath.startsWith(rootPath + posix.sep) && !filePath.startsWith(rootPath + '/')) {\n continue\n }\n\n const relative = filePath.slice(rootPath.length).replace(/^\\//g, '').replace(/^\\\\/g, '')\n const parts = relative.split(/[/\\\\]/).filter(Boolean)\n\n let current = root\n for (let i = 0; i < parts.length; i++) {\n const isLast = i === parts.length - 1\n const part = parts[i]!\n const childPath = `${current.path}/${part}`\n\n let child = current.children.find((c) => c.path === childPath)\n if (!child) {\n child = { path: childPath, children: [], isFile: isLast }\n current.children.push(child)\n }\n current = child\n }\n }\n\n return root\n}\n","import { createExport, createFile } from '@kubb/ast'\nimport type { FileNode } from '@kubb/ast'\nimport { BARREL_BASENAME, BARREL_FILENAME } from '../constants.ts'\nimport type { BarrelType } from '../types.ts'\nimport { buildTree, type TreeNode } from './TreeNode.ts'\n\n/**\n * Derives a relative module specifier (no extension) from an absolute `filePath`\n * relative to an absolute `fromDir`.\n *\n * @example\n * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet'\n * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag'\n */\nfunction toRelativeModulePath(fromDir: string, filePath: string): string {\n const relative = filePath.slice(fromDir.length).replace(/^[/\\\\]/g, '')\n // Strip extension\n const withoutExt = relative.replace(/\\.[^/.]+$/, '')\n return `./${withoutExt}`\n}\n\n/**\n * Generates barrel `FileNode[]` for a given directory tree node using the `'all'` strategy:\n * each leaf file gets a `export * from './relPath'` in the barrel of its nearest ancestor directory.\n *\n * Only a single barrel file (at `treeNode.path`) is generated — sub-directory files are referenced\n * with their full relative path from `treeNode.path`.\n */\nfunction getBarrelFilesAll(treeNode: TreeNode, _sourceFiles: ReadonlyArray<FileNode>): Array<FileNode> {\n // Collect all source file paths under this node (excluding barrel files themselves)\n const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`))\n\n if (leafPaths.length === 0) return []\n\n const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`\n const exports = leafPaths.map((filePath) =>\n createExport({\n path: toRelativeModulePath(treeNode.path, filePath),\n }),\n )\n\n return [\n createFile({\n baseName: BARREL_FILENAME,\n path: barrelPath,\n exports,\n sources: [],\n imports: [],\n }),\n ]\n}\n\n/**\n * Generates barrel `FileNode[]` for a given directory tree node using the `'named'` strategy:\n * each indexable source in each leaf file gets an individual named `export { name } from '...'`.\n */\nfunction getBarrelFilesNamed(treeNode: TreeNode, sourceFiles: ReadonlyArray<FileNode>): Array<FileNode> {\n const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`))\n\n if (leafPaths.length === 0) return []\n\n const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`\n const exports: ReturnType<typeof createExport>[] = []\n\n for (const filePath of leafPaths) {\n const sourceFile = sourceFiles.find((f) => f.path === filePath)\n if (!sourceFile) {\n // Fall back to wildcard if the source file is not in our set\n exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))\n continue\n }\n\n const indexableSources = sourceFile.sources.filter((s) => s.isIndexable && s.name)\n if (indexableSources.length === 0) {\n // No named exports: fall back to wildcard\n exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))\n continue\n }\n\n const names = indexableSources.map((s) => s.name as string)\n exports.push(\n createExport({\n name: names,\n path: toRelativeModulePath(treeNode.path, filePath),\n }),\n )\n }\n\n if (exports.length === 0) return []\n\n return [\n createFile({\n baseName: BARREL_FILENAME,\n path: barrelPath,\n exports,\n sources: [],\n imports: [],\n }),\n ]\n}\n\n/**\n * Generates barrel `FileNode[]` for a given directory tree node using the `'propagate'` strategy:\n * like `'all'` but also generates intermediate barrel files for every sub-directory, so that\n * consumers can import from any depth.\n *\n * Leaf barrels export directly from their files; parent barrels export from their sub-barrel files.\n */\nfunction getBarrelFilesPropagate(treeNode: TreeNode): Array<FileNode> {\n return collectPropagatedBarrels(treeNode)\n}\n\nfunction collectPropagatedBarrels(node: TreeNode): Array<FileNode> {\n const result: Array<FileNode> = []\n const barrelExports: ReturnType<typeof createExport>[] = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (!child.path.endsWith(`/${BARREL_FILENAME}`)) {\n barrelExports.push(createExport({ path: toRelativeModulePath(node.path, child.path) }))\n }\n } else {\n // Recurse into sub-directory\n const subBarrels = collectPropagatedBarrels(child)\n result.push(...subBarrels)\n\n // Export the sub-directory's barrel (not individual files)\n const subBarrelPath = `${child.path}/${BARREL_BASENAME}`\n barrelExports.push(createExport({ path: toRelativeModulePath(node.path, subBarrelPath) }))\n }\n }\n\n if (barrelExports.length > 0) {\n result.push(\n createFile({\n baseName: BARREL_FILENAME,\n path: `${node.path}/${BARREL_FILENAME}`,\n exports: barrelExports,\n sources: [],\n imports: [],\n }),\n )\n }\n\n return result\n}\n\n/**\n * Collects all leaf (file) paths within a tree node recursively.\n */\nfunction collectLeafPaths(node: TreeNode): Array<string> {\n if (node.isFile) return [node.path]\n return node.children.flatMap((c) => collectLeafPaths(c))\n}\n\n/**\n * Generates barrel `FileNode[]` for a directory rooted at `outputPath`, given the full set of\n * generated source `files`, using the specified `barrelType` strategy.\n *\n * Files not located inside `outputPath` are excluded automatically.\n *\n * @param outputPath Absolute path to the output directory.\n * @param files All generated files (across all plugins).\n * @param barrelType Barrel generation strategy.\n */\nexport function getBarrelFiles(outputPath: string, files: ReadonlyArray<FileNode>, barrelType: BarrelType): Array<FileNode> {\n // Only include files that live inside this outputPath\n const relevantFiles = files.filter((f) => {\n const normalizedFilePath = f.path.replace(/\\\\/g, '/')\n const normalizedOutputPath = outputPath.replace(/\\\\/g, '/')\n return normalizedFilePath.startsWith(normalizedOutputPath + '/') && !normalizedFilePath.endsWith(`/${BARREL_FILENAME}`)\n })\n\n if (relevantFiles.length === 0) return []\n\n const tree = buildTree(outputPath, relevantFiles.map((f) => f.path))\n\n switch (barrelType) {\n case 'all':\n return getBarrelFilesAll(tree, relevantFiles)\n case 'named':\n return getBarrelFilesNamed(tree, relevantFiles)\n case 'propagate':\n return getBarrelFilesPropagate(tree)\n default:\n return []\n }\n}\n","import { resolve } from 'node:path'\nimport type { FileNode } from '@kubb/ast'\nimport type { Config, NormalizedPlugin } from '@kubb/core'\nimport type { BarrelType } from '../types.ts'\nimport { getBarrelFiles } from './getBarrelFiles.ts'\n\nexport type GeneratePerPluginBarrelParams = {\n barrelType: BarrelType\n plugin: NormalizedPlugin\n files: ReadonlyArray<FileNode>\n config: Config\n}\n\n/**\n * Generates barrel files for a single plugin's output directory.\n *\n * The barrel file is placed at `resolve(config.root, plugin.options.output.path)/index.ts`\n * and re-exports all files generated by that plugin, using the given `barrelType` strategy.\n */\nexport function generatePerPluginBarrel({ barrelType, plugin, files, config }: GeneratePerPluginBarrelParams): Array<FileNode> {\n const outputPath = resolve(config.root, plugin.options.output.path)\n return getBarrelFiles(outputPath, files, barrelType)\n}\n","import { resolve } from 'node:path'\nimport type { FileNode } from '@kubb/ast'\nimport type { Config } from '@kubb/core'\nimport type { BarrelType } from '../types.ts'\nimport { getBarrelFiles } from './getBarrelFiles.ts'\n\nexport type GenerateRootBarrelParams = {\n barrelType: BarrelType\n files: ReadonlyArray<FileNode>\n config: Config\n}\n\n/**\n * Generates a root barrel file at `resolve(config.root, config.output.path)/index.ts`.\n *\n * The root barrel re-exports from all files across all plugins that are located\n * inside the root output directory, using the given `barrelType` strategy.\n *\n * In practice this re-exports the per-plugin barrels when `barrelType = 'propagate'`,\n * or all individual files when `barrelType = 'all'` or `'named'`.\n */\nexport function generateRootBarrel({ barrelType, files, config }: GenerateRootBarrelParams): Array<FileNode> {\n const outputPath = resolve(config.root, config.output.path)\n return getBarrelFiles(outputPath, files, barrelType)\n}\n","import { defineMiddleware } from '@kubb/core'\nimport type { KubbBuildStartContext, NormalizedPlugin } from '@kubb/core'\nimport './types.ts'\nimport type { BarrelType } from './types.ts'\nimport { generatePerPluginBarrel } from './utils/generatePerPluginBarrel.ts'\nimport { generateRootBarrel } from './utils/generateRootBarrel.ts'\n\n/**\n * Barrel-file generation middleware.\n *\n * When added to `config.middleware`, generates an `index.ts` barrel file for each\n * plugin that has `output.barrelType` set (or inherits it from `config.output.barrelType`),\n * as well as a root `index.ts` at `config.output.path` when `config.output.barrelType` is set.\n *\n * The `barrelType` option controls the re-export style:\n * - `'all'` — `export * from '...'` for each generated file\n * - `'named'` — `export { name1, name2 } from '...'` using each file's indexable sources\n * - `'propagate'` — like `'all'` with intermediate barrel files for sub-directories\n *\n * @example\n * ```ts\n * import { middlewareBarrel } from '@kubb/middleware-barrel'\n *\n * export default defineConfig({\n * output: { path: 'src/gen', barrelType: 'named' },\n * plugins: [\n * pluginTs({ output: { path: 'types', barrelType: 'all' } }),\n * pluginZod({ output: { path: 'schemas' } }),\n * ],\n * middleware: [middlewareBarrel],\n * })\n * ```\n */\nexport const middlewareBarrel = defineMiddleware({\n name: 'middleware-barrel',\n\n install(hooks) {\n let ctx: KubbBuildStartContext\n\n hooks.on('kubb:build:start', (buildCtx) => {\n ctx = buildCtx\n })\n\n hooks.on('kubb:plugin:end', ({ plugin }) => {\n if (!ctx) return\n\n // At runtime the plugin in kubb:plugin:end is always a NormalizedPlugin;\n // KubbPluginEndContext types it as Plugin for public API simplicity.\n const normalizedPlugin = plugin as NormalizedPlugin\n const pluginOutput = normalizedPlugin.options.output as { barrelType?: BarrelType | false } | undefined\n const rootOutput = ctx.config.output as { barrelType?: BarrelType | false }\n const barrelType = pluginOutput?.barrelType !== undefined ? pluginOutput.barrelType : rootOutput.barrelType\n\n if (!barrelType) return\n\n const barrelFiles = generatePerPluginBarrel({\n barrelType,\n plugin: normalizedPlugin,\n files: ctx.files,\n config: ctx.config,\n })\n\n if (barrelFiles.length > 0) {\n ctx.upsertFile(...barrelFiles)\n }\n })\n\n hooks.on('kubb:build:end', () => {\n if (!ctx) return\n\n const rootOutput = ctx.config.output as { barrelType?: BarrelType | false }\n const rootBarrelType = rootOutput.barrelType\n\n if (!rootBarrelType) return\n\n const rootBarrelFiles = generateRootBarrel({\n barrelType: rootBarrelType,\n files: ctx.files,\n config: ctx.config,\n })\n\n if (rootBarrelFiles.length > 0) {\n ctx.upsertFile(...rootBarrelFiles)\n }\n })\n },\n})\n"],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import { t as __name } from "./chunk--u3MIqq1.js";
2
+ import * as _$_kubb_core0 from "@kubb/core";
3
+
4
+ //#region src/types.d.ts
5
+ /**
6
+ * The barrel type controls the style of re-exports generated in barrel files.
7
+ *
8
+ * - `'all'` — `export * from '...'` (re-export everything)
9
+ * - `'named'` — `export { name1, name2 } from '...'` (named re-exports only)
10
+ * - `'propagate'` — like `'all'` but also generates intermediate barrel files for
11
+ * every sub-directory so that consumers can import from any depth.
12
+ */
13
+ type BarrelType = 'all' | 'named' | 'propagate';
14
+ declare global {
15
+ namespace Kubb {
16
+ interface PluginOptionsRegistry {
17
+ output: {
18
+ /**
19
+ * Controls which barrel file (index.ts) is generated for this plugin's output directory.
20
+ *
21
+ * - `'all'` — `export * from '...'` for every generated file.
22
+ * - `'named'` — `export { … } from '...'` using the file's named exports.
23
+ * - `'propagate'` — like `'all'` but also generates intermediate barrel files.
24
+ * - `false` — disable barrel generation for this plugin.
25
+ *
26
+ * When omitted, the root `config.output.barrelType` is used as the default.
27
+ */
28
+ barrelType?: BarrelType | false;
29
+ };
30
+ }
31
+ interface ConfigOptionsRegistry {
32
+ output: {
33
+ /**
34
+ * Controls the root barrel file (index.ts) generated at `config.output.path`.
35
+ *
36
+ * - `'all'` — `export * from '...'` for every plugin's barrel.
37
+ * - `'named'` — `export { … } from '...'` using the barrel's named exports.
38
+ * - `'propagate'` — like `'all'` but also generates intermediate barrel files.
39
+ * - `false` — disable root barrel generation.
40
+ *
41
+ * Individual plugins can override this via their own `output.barrelType`.
42
+ */
43
+ barrelType?: BarrelType | false;
44
+ };
45
+ }
46
+ }
47
+ }
48
+ //#endregion
49
+ //#region src/middleware.d.ts
50
+ /**
51
+ * Barrel-file generation middleware.
52
+ *
53
+ * When added to `config.middleware`, generates an `index.ts` barrel file for each
54
+ * plugin that has `output.barrelType` set (or inherits it from `config.output.barrelType`),
55
+ * as well as a root `index.ts` at `config.output.path` when `config.output.barrelType` is set.
56
+ *
57
+ * The `barrelType` option controls the re-export style:
58
+ * - `'all'` — `export * from '...'` for each generated file
59
+ * - `'named'` — `export { name1, name2 } from '...'` using each file's indexable sources
60
+ * - `'propagate'` — like `'all'` with intermediate barrel files for sub-directories
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * import { middlewareBarrel } from '@kubb/middleware-barrel'
65
+ *
66
+ * export default defineConfig({
67
+ * output: { path: 'src/gen', barrelType: 'named' },
68
+ * plugins: [
69
+ * pluginTs({ output: { path: 'types', barrelType: 'all' } }),
70
+ * pluginZod({ output: { path: 'schemas' } }),
71
+ * ],
72
+ * middleware: [middlewareBarrel],
73
+ * })
74
+ * ```
75
+ */
76
+ declare const middlewareBarrel: _$_kubb_core0.Middleware;
77
+ //#endregion
78
+ export { middlewareBarrel };
79
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,278 @@
1
+ import "./chunk--u3MIqq1.js";
2
+ import { defineMiddleware } from "@kubb/core";
3
+ import { posix, resolve } from "node:path";
4
+ import { createExport, createFile } from "@kubb/ast";
5
+ //#region src/constants.ts
6
+ /**
7
+ * Base file name for barrel files (without extension).
8
+ */
9
+ const BARREL_BASENAME = "index";
10
+ /**
11
+ * Full file name for barrel files (with extension).
12
+ */
13
+ const BARREL_FILENAME = "index.ts";
14
+ //#endregion
15
+ //#region src/utils/TreeNode.ts
16
+ /**
17
+ * Builds a `TreeNode` directory tree from a list of absolute file paths.
18
+ *
19
+ * All `filePaths` must be inside `rootPath`. Paths that are outside
20
+ * the root or that equal the root are silently ignored.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const tree = buildTree('/src/gen/types', [
25
+ * '/src/gen/types/pet.ts',
26
+ * '/src/gen/types/user.ts',
27
+ * '/src/gen/types/pets/listPets.ts',
28
+ * ])
29
+ * ```
30
+ */
31
+ function buildTree(rootPath, filePaths) {
32
+ const root = {
33
+ path: rootPath,
34
+ children: [],
35
+ isFile: false
36
+ };
37
+ for (const filePath of filePaths) {
38
+ if (!filePath.startsWith(rootPath + posix.sep) && !filePath.startsWith(rootPath + "/")) continue;
39
+ const parts = filePath.slice(rootPath.length).replace(/^\//g, "").replace(/^\\/g, "").split(/[/\\]/).filter(Boolean);
40
+ let current = root;
41
+ for (let i = 0; i < parts.length; i++) {
42
+ const isLast = i === parts.length - 1;
43
+ const part = parts[i];
44
+ const childPath = `${current.path}/${part}`;
45
+ let child = current.children.find((c) => c.path === childPath);
46
+ if (!child) {
47
+ child = {
48
+ path: childPath,
49
+ children: [],
50
+ isFile: isLast
51
+ };
52
+ current.children.push(child);
53
+ }
54
+ current = child;
55
+ }
56
+ }
57
+ return root;
58
+ }
59
+ //#endregion
60
+ //#region src/utils/getBarrelFiles.ts
61
+ /**
62
+ * Derives a relative module specifier (no extension) from an absolute `filePath`
63
+ * relative to an absolute `fromDir`.
64
+ *
65
+ * @example
66
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet'
67
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag'
68
+ */
69
+ function toRelativeModulePath(fromDir, filePath) {
70
+ return `./${filePath.slice(fromDir.length).replace(/^[/\\]/g, "").replace(/\.[^/.]+$/, "")}`;
71
+ }
72
+ /**
73
+ * Generates barrel `FileNode[]` for a given directory tree node using the `'all'` strategy:
74
+ * each leaf file gets a `export * from './relPath'` in the barrel of its nearest ancestor directory.
75
+ *
76
+ * Only a single barrel file (at `treeNode.path`) is generated — sub-directory files are referenced
77
+ * with their full relative path from `treeNode.path`.
78
+ */
79
+ function getBarrelFilesAll(treeNode, _sourceFiles) {
80
+ const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`));
81
+ if (leafPaths.length === 0) return [];
82
+ return [createFile({
83
+ baseName: BARREL_FILENAME,
84
+ path: `${treeNode.path}/${BARREL_FILENAME}`,
85
+ exports: leafPaths.map((filePath) => createExport({ path: toRelativeModulePath(treeNode.path, filePath) })),
86
+ sources: [],
87
+ imports: []
88
+ })];
89
+ }
90
+ /**
91
+ * Generates barrel `FileNode[]` for a given directory tree node using the `'named'` strategy:
92
+ * each indexable source in each leaf file gets an individual named `export { name } from '...'`.
93
+ */
94
+ function getBarrelFilesNamed(treeNode, sourceFiles) {
95
+ const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`));
96
+ if (leafPaths.length === 0) return [];
97
+ const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`;
98
+ const exports = [];
99
+ for (const filePath of leafPaths) {
100
+ const sourceFile = sourceFiles.find((f) => f.path === filePath);
101
+ if (!sourceFile) {
102
+ exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }));
103
+ continue;
104
+ }
105
+ const indexableSources = sourceFile.sources.filter((s) => s.isIndexable && s.name);
106
+ if (indexableSources.length === 0) {
107
+ exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }));
108
+ continue;
109
+ }
110
+ const names = indexableSources.map((s) => s.name);
111
+ exports.push(createExport({
112
+ name: names,
113
+ path: toRelativeModulePath(treeNode.path, filePath)
114
+ }));
115
+ }
116
+ if (exports.length === 0) return [];
117
+ return [createFile({
118
+ baseName: BARREL_FILENAME,
119
+ path: barrelPath,
120
+ exports,
121
+ sources: [],
122
+ imports: []
123
+ })];
124
+ }
125
+ /**
126
+ * Generates barrel `FileNode[]` for a given directory tree node using the `'propagate'` strategy:
127
+ * like `'all'` but also generates intermediate barrel files for every sub-directory, so that
128
+ * consumers can import from any depth.
129
+ *
130
+ * Leaf barrels export directly from their files; parent barrels export from their sub-barrel files.
131
+ */
132
+ function getBarrelFilesPropagate(treeNode) {
133
+ return collectPropagatedBarrels(treeNode);
134
+ }
135
+ function collectPropagatedBarrels(node) {
136
+ const result = [];
137
+ const barrelExports = [];
138
+ for (const child of node.children) if (child.isFile) {
139
+ if (!child.path.endsWith(`/index.ts`)) barrelExports.push(createExport({ path: toRelativeModulePath(node.path, child.path) }));
140
+ } else {
141
+ const subBarrels = collectPropagatedBarrels(child);
142
+ result.push(...subBarrels);
143
+ const subBarrelPath = `${child.path}/${BARREL_BASENAME}`;
144
+ barrelExports.push(createExport({ path: toRelativeModulePath(node.path, subBarrelPath) }));
145
+ }
146
+ if (barrelExports.length > 0) result.push(createFile({
147
+ baseName: BARREL_FILENAME,
148
+ path: `${node.path}/${BARREL_FILENAME}`,
149
+ exports: barrelExports,
150
+ sources: [],
151
+ imports: []
152
+ }));
153
+ return result;
154
+ }
155
+ /**
156
+ * Collects all leaf (file) paths within a tree node recursively.
157
+ */
158
+ function collectLeafPaths(node) {
159
+ if (node.isFile) return [node.path];
160
+ return node.children.flatMap((c) => collectLeafPaths(c));
161
+ }
162
+ /**
163
+ * Generates barrel `FileNode[]` for a directory rooted at `outputPath`, given the full set of
164
+ * generated source `files`, using the specified `barrelType` strategy.
165
+ *
166
+ * Files not located inside `outputPath` are excluded automatically.
167
+ *
168
+ * @param outputPath Absolute path to the output directory.
169
+ * @param files All generated files (across all plugins).
170
+ * @param barrelType Barrel generation strategy.
171
+ */
172
+ function getBarrelFiles(outputPath, files, barrelType) {
173
+ const relevantFiles = files.filter((f) => {
174
+ const normalizedFilePath = f.path.replace(/\\/g, "/");
175
+ const normalizedOutputPath = outputPath.replace(/\\/g, "/");
176
+ return normalizedFilePath.startsWith(normalizedOutputPath + "/") && !normalizedFilePath.endsWith(`/index.ts`);
177
+ });
178
+ if (relevantFiles.length === 0) return [];
179
+ const tree = buildTree(outputPath, relevantFiles.map((f) => f.path));
180
+ switch (barrelType) {
181
+ case "all": return getBarrelFilesAll(tree, relevantFiles);
182
+ case "named": return getBarrelFilesNamed(tree, relevantFiles);
183
+ case "propagate": return getBarrelFilesPropagate(tree);
184
+ default: return [];
185
+ }
186
+ }
187
+ //#endregion
188
+ //#region src/utils/generatePerPluginBarrel.ts
189
+ /**
190
+ * Generates barrel files for a single plugin's output directory.
191
+ *
192
+ * The barrel file is placed at `resolve(config.root, plugin.options.output.path)/index.ts`
193
+ * and re-exports all files generated by that plugin, using the given `barrelType` strategy.
194
+ */
195
+ function generatePerPluginBarrel({ barrelType, plugin, files, config }) {
196
+ return getBarrelFiles(resolve(config.root, plugin.options.output.path), files, barrelType);
197
+ }
198
+ //#endregion
199
+ //#region src/utils/generateRootBarrel.ts
200
+ /**
201
+ * Generates a root barrel file at `resolve(config.root, config.output.path)/index.ts`.
202
+ *
203
+ * The root barrel re-exports from all files across all plugins that are located
204
+ * inside the root output directory, using the given `barrelType` strategy.
205
+ *
206
+ * In practice this re-exports the per-plugin barrels when `barrelType = 'propagate'`,
207
+ * or all individual files when `barrelType = 'all'` or `'named'`.
208
+ */
209
+ function generateRootBarrel({ barrelType, files, config }) {
210
+ return getBarrelFiles(resolve(config.root, config.output.path), files, barrelType);
211
+ }
212
+ //#endregion
213
+ //#region src/middleware.ts
214
+ /**
215
+ * Barrel-file generation middleware.
216
+ *
217
+ * When added to `config.middleware`, generates an `index.ts` barrel file for each
218
+ * plugin that has `output.barrelType` set (or inherits it from `config.output.barrelType`),
219
+ * as well as a root `index.ts` at `config.output.path` when `config.output.barrelType` is set.
220
+ *
221
+ * The `barrelType` option controls the re-export style:
222
+ * - `'all'` — `export * from '...'` for each generated file
223
+ * - `'named'` — `export { name1, name2 } from '...'` using each file's indexable sources
224
+ * - `'propagate'` — like `'all'` with intermediate barrel files for sub-directories
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * import { middlewareBarrel } from '@kubb/middleware-barrel'
229
+ *
230
+ * export default defineConfig({
231
+ * output: { path: 'src/gen', barrelType: 'named' },
232
+ * plugins: [
233
+ * pluginTs({ output: { path: 'types', barrelType: 'all' } }),
234
+ * pluginZod({ output: { path: 'schemas' } }),
235
+ * ],
236
+ * middleware: [middlewareBarrel],
237
+ * })
238
+ * ```
239
+ */
240
+ const middlewareBarrel = defineMiddleware({
241
+ name: "middleware-barrel",
242
+ install(hooks) {
243
+ let ctx;
244
+ hooks.on("kubb:build:start", (buildCtx) => {
245
+ ctx = buildCtx;
246
+ });
247
+ hooks.on("kubb:plugin:end", ({ plugin }) => {
248
+ if (!ctx) return;
249
+ const normalizedPlugin = plugin;
250
+ const pluginOutput = normalizedPlugin.options.output;
251
+ const rootOutput = ctx.config.output;
252
+ const barrelType = pluginOutput?.barrelType !== void 0 ? pluginOutput.barrelType : rootOutput.barrelType;
253
+ if (!barrelType) return;
254
+ const barrelFiles = generatePerPluginBarrel({
255
+ barrelType,
256
+ plugin: normalizedPlugin,
257
+ files: ctx.files,
258
+ config: ctx.config
259
+ });
260
+ if (barrelFiles.length > 0) ctx.upsertFile(...barrelFiles);
261
+ });
262
+ hooks.on("kubb:build:end", () => {
263
+ if (!ctx) return;
264
+ const rootBarrelType = ctx.config.output.barrelType;
265
+ if (!rootBarrelType) return;
266
+ const rootBarrelFiles = generateRootBarrel({
267
+ barrelType: rootBarrelType,
268
+ files: ctx.files,
269
+ config: ctx.config
270
+ });
271
+ if (rootBarrelFiles.length > 0) ctx.upsertFile(...rootBarrelFiles);
272
+ });
273
+ }
274
+ });
275
+ //#endregion
276
+ export { middlewareBarrel };
277
+
278
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/constants.ts","../src/utils/TreeNode.ts","../src/utils/getBarrelFiles.ts","../src/utils/generatePerPluginBarrel.ts","../src/utils/generateRootBarrel.ts","../src/middleware.ts"],"sourcesContent":["/**\n * Base file name for barrel files (without extension).\n */\nexport const BARREL_BASENAME = 'index' as const\n\n/**\n * Full file name for barrel files (with extension).\n */\nexport const BARREL_FILENAME = 'index.ts' as const\n","import { posix } from 'node:path'\n\n/**\n * A node in a directory tree used to compute barrel file exports.\n *\n * Each `TreeNode` represents either a directory or a file entry.\n * Directory nodes have `children`; file nodes have an empty `children` array.\n */\nexport type TreeNode = {\n /**\n * Absolute path of the directory (root of this subtree) or file.\n */\n path: string\n /**\n * Child nodes (sub-directories and files) within this directory.\n */\n children: Array<TreeNode>\n /**\n * `true` when this node represents a file (leaf node).\n */\n isFile: boolean\n}\n\n/**\n * Builds a `TreeNode` directory tree from a list of absolute file paths.\n *\n * All `filePaths` must be inside `rootPath`. Paths that are outside\n * the root or that equal the root are silently ignored.\n *\n * @example\n * ```ts\n * const tree = buildTree('/src/gen/types', [\n * '/src/gen/types/pet.ts',\n * '/src/gen/types/user.ts',\n * '/src/gen/types/pets/listPets.ts',\n * ])\n * ```\n */\nexport function buildTree(rootPath: string, filePaths: ReadonlyArray<string>): TreeNode {\n const root: TreeNode = { path: rootPath, children: [], isFile: false }\n\n for (const filePath of filePaths) {\n // Only include files inside rootPath\n if (!filePath.startsWith(rootPath + posix.sep) && !filePath.startsWith(rootPath + '/')) {\n continue\n }\n\n const relative = filePath.slice(rootPath.length).replace(/^\\//g, '').replace(/^\\\\/g, '')\n const parts = relative.split(/[/\\\\]/).filter(Boolean)\n\n let current = root\n for (let i = 0; i < parts.length; i++) {\n const isLast = i === parts.length - 1\n const part = parts[i]!\n const childPath = `${current.path}/${part}`\n\n let child = current.children.find((c) => c.path === childPath)\n if (!child) {\n child = { path: childPath, children: [], isFile: isLast }\n current.children.push(child)\n }\n current = child\n }\n }\n\n return root\n}\n","import { createExport, createFile } from '@kubb/ast'\nimport type { FileNode } from '@kubb/ast'\nimport { BARREL_BASENAME, BARREL_FILENAME } from '../constants.ts'\nimport type { BarrelType } from '../types.ts'\nimport { buildTree, type TreeNode } from './TreeNode.ts'\n\n/**\n * Derives a relative module specifier (no extension) from an absolute `filePath`\n * relative to an absolute `fromDir`.\n *\n * @example\n * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet'\n * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag'\n */\nfunction toRelativeModulePath(fromDir: string, filePath: string): string {\n const relative = filePath.slice(fromDir.length).replace(/^[/\\\\]/g, '')\n // Strip extension\n const withoutExt = relative.replace(/\\.[^/.]+$/, '')\n return `./${withoutExt}`\n}\n\n/**\n * Generates barrel `FileNode[]` for a given directory tree node using the `'all'` strategy:\n * each leaf file gets a `export * from './relPath'` in the barrel of its nearest ancestor directory.\n *\n * Only a single barrel file (at `treeNode.path`) is generated — sub-directory files are referenced\n * with their full relative path from `treeNode.path`.\n */\nfunction getBarrelFilesAll(treeNode: TreeNode, _sourceFiles: ReadonlyArray<FileNode>): Array<FileNode> {\n // Collect all source file paths under this node (excluding barrel files themselves)\n const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`))\n\n if (leafPaths.length === 0) return []\n\n const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`\n const exports = leafPaths.map((filePath) =>\n createExport({\n path: toRelativeModulePath(treeNode.path, filePath),\n }),\n )\n\n return [\n createFile({\n baseName: BARREL_FILENAME,\n path: barrelPath,\n exports,\n sources: [],\n imports: [],\n }),\n ]\n}\n\n/**\n * Generates barrel `FileNode[]` for a given directory tree node using the `'named'` strategy:\n * each indexable source in each leaf file gets an individual named `export { name } from '...'`.\n */\nfunction getBarrelFilesNamed(treeNode: TreeNode, sourceFiles: ReadonlyArray<FileNode>): Array<FileNode> {\n const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`))\n\n if (leafPaths.length === 0) return []\n\n const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`\n const exports: ReturnType<typeof createExport>[] = []\n\n for (const filePath of leafPaths) {\n const sourceFile = sourceFiles.find((f) => f.path === filePath)\n if (!sourceFile) {\n // Fall back to wildcard if the source file is not in our set\n exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))\n continue\n }\n\n const indexableSources = sourceFile.sources.filter((s) => s.isIndexable && s.name)\n if (indexableSources.length === 0) {\n // No named exports: fall back to wildcard\n exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))\n continue\n }\n\n const names = indexableSources.map((s) => s.name as string)\n exports.push(\n createExport({\n name: names,\n path: toRelativeModulePath(treeNode.path, filePath),\n }),\n )\n }\n\n if (exports.length === 0) return []\n\n return [\n createFile({\n baseName: BARREL_FILENAME,\n path: barrelPath,\n exports,\n sources: [],\n imports: [],\n }),\n ]\n}\n\n/**\n * Generates barrel `FileNode[]` for a given directory tree node using the `'propagate'` strategy:\n * like `'all'` but also generates intermediate barrel files for every sub-directory, so that\n * consumers can import from any depth.\n *\n * Leaf barrels export directly from their files; parent barrels export from their sub-barrel files.\n */\nfunction getBarrelFilesPropagate(treeNode: TreeNode): Array<FileNode> {\n return collectPropagatedBarrels(treeNode)\n}\n\nfunction collectPropagatedBarrels(node: TreeNode): Array<FileNode> {\n const result: Array<FileNode> = []\n const barrelExports: ReturnType<typeof createExport>[] = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (!child.path.endsWith(`/${BARREL_FILENAME}`)) {\n barrelExports.push(createExport({ path: toRelativeModulePath(node.path, child.path) }))\n }\n } else {\n // Recurse into sub-directory\n const subBarrels = collectPropagatedBarrels(child)\n result.push(...subBarrels)\n\n // Export the sub-directory's barrel (not individual files)\n const subBarrelPath = `${child.path}/${BARREL_BASENAME}`\n barrelExports.push(createExport({ path: toRelativeModulePath(node.path, subBarrelPath) }))\n }\n }\n\n if (barrelExports.length > 0) {\n result.push(\n createFile({\n baseName: BARREL_FILENAME,\n path: `${node.path}/${BARREL_FILENAME}`,\n exports: barrelExports,\n sources: [],\n imports: [],\n }),\n )\n }\n\n return result\n}\n\n/**\n * Collects all leaf (file) paths within a tree node recursively.\n */\nfunction collectLeafPaths(node: TreeNode): Array<string> {\n if (node.isFile) return [node.path]\n return node.children.flatMap((c) => collectLeafPaths(c))\n}\n\n/**\n * Generates barrel `FileNode[]` for a directory rooted at `outputPath`, given the full set of\n * generated source `files`, using the specified `barrelType` strategy.\n *\n * Files not located inside `outputPath` are excluded automatically.\n *\n * @param outputPath Absolute path to the output directory.\n * @param files All generated files (across all plugins).\n * @param barrelType Barrel generation strategy.\n */\nexport function getBarrelFiles(outputPath: string, files: ReadonlyArray<FileNode>, barrelType: BarrelType): Array<FileNode> {\n // Only include files that live inside this outputPath\n const relevantFiles = files.filter((f) => {\n const normalizedFilePath = f.path.replace(/\\\\/g, '/')\n const normalizedOutputPath = outputPath.replace(/\\\\/g, '/')\n return normalizedFilePath.startsWith(normalizedOutputPath + '/') && !normalizedFilePath.endsWith(`/${BARREL_FILENAME}`)\n })\n\n if (relevantFiles.length === 0) return []\n\n const tree = buildTree(outputPath, relevantFiles.map((f) => f.path))\n\n switch (barrelType) {\n case 'all':\n return getBarrelFilesAll(tree, relevantFiles)\n case 'named':\n return getBarrelFilesNamed(tree, relevantFiles)\n case 'propagate':\n return getBarrelFilesPropagate(tree)\n default:\n return []\n }\n}\n","import { resolve } from 'node:path'\nimport type { FileNode } from '@kubb/ast'\nimport type { Config, NormalizedPlugin } from '@kubb/core'\nimport type { BarrelType } from '../types.ts'\nimport { getBarrelFiles } from './getBarrelFiles.ts'\n\nexport type GeneratePerPluginBarrelParams = {\n barrelType: BarrelType\n plugin: NormalizedPlugin\n files: ReadonlyArray<FileNode>\n config: Config\n}\n\n/**\n * Generates barrel files for a single plugin's output directory.\n *\n * The barrel file is placed at `resolve(config.root, plugin.options.output.path)/index.ts`\n * and re-exports all files generated by that plugin, using the given `barrelType` strategy.\n */\nexport function generatePerPluginBarrel({ barrelType, plugin, files, config }: GeneratePerPluginBarrelParams): Array<FileNode> {\n const outputPath = resolve(config.root, plugin.options.output.path)\n return getBarrelFiles(outputPath, files, barrelType)\n}\n","import { resolve } from 'node:path'\nimport type { FileNode } from '@kubb/ast'\nimport type { Config } from '@kubb/core'\nimport type { BarrelType } from '../types.ts'\nimport { getBarrelFiles } from './getBarrelFiles.ts'\n\nexport type GenerateRootBarrelParams = {\n barrelType: BarrelType\n files: ReadonlyArray<FileNode>\n config: Config\n}\n\n/**\n * Generates a root barrel file at `resolve(config.root, config.output.path)/index.ts`.\n *\n * The root barrel re-exports from all files across all plugins that are located\n * inside the root output directory, using the given `barrelType` strategy.\n *\n * In practice this re-exports the per-plugin barrels when `barrelType = 'propagate'`,\n * or all individual files when `barrelType = 'all'` or `'named'`.\n */\nexport function generateRootBarrel({ barrelType, files, config }: GenerateRootBarrelParams): Array<FileNode> {\n const outputPath = resolve(config.root, config.output.path)\n return getBarrelFiles(outputPath, files, barrelType)\n}\n","import { defineMiddleware } from '@kubb/core'\nimport type { KubbBuildStartContext, NormalizedPlugin } from '@kubb/core'\nimport './types.ts'\nimport type { BarrelType } from './types.ts'\nimport { generatePerPluginBarrel } from './utils/generatePerPluginBarrel.ts'\nimport { generateRootBarrel } from './utils/generateRootBarrel.ts'\n\n/**\n * Barrel-file generation middleware.\n *\n * When added to `config.middleware`, generates an `index.ts` barrel file for each\n * plugin that has `output.barrelType` set (or inherits it from `config.output.barrelType`),\n * as well as a root `index.ts` at `config.output.path` when `config.output.barrelType` is set.\n *\n * The `barrelType` option controls the re-export style:\n * - `'all'` — `export * from '...'` for each generated file\n * - `'named'` — `export { name1, name2 } from '...'` using each file's indexable sources\n * - `'propagate'` — like `'all'` with intermediate barrel files for sub-directories\n *\n * @example\n * ```ts\n * import { middlewareBarrel } from '@kubb/middleware-barrel'\n *\n * export default defineConfig({\n * output: { path: 'src/gen', barrelType: 'named' },\n * plugins: [\n * pluginTs({ output: { path: 'types', barrelType: 'all' } }),\n * pluginZod({ output: { path: 'schemas' } }),\n * ],\n * middleware: [middlewareBarrel],\n * })\n * ```\n */\nexport const middlewareBarrel = defineMiddleware({\n name: 'middleware-barrel',\n\n install(hooks) {\n let ctx: KubbBuildStartContext\n\n hooks.on('kubb:build:start', (buildCtx) => {\n ctx = buildCtx\n })\n\n hooks.on('kubb:plugin:end', ({ plugin }) => {\n if (!ctx) return\n\n // At runtime the plugin in kubb:plugin:end is always a NormalizedPlugin;\n // KubbPluginEndContext types it as Plugin for public API simplicity.\n const normalizedPlugin = plugin as NormalizedPlugin\n const pluginOutput = normalizedPlugin.options.output as { barrelType?: BarrelType | false } | undefined\n const rootOutput = ctx.config.output as { barrelType?: BarrelType | false }\n const barrelType = pluginOutput?.barrelType !== undefined ? pluginOutput.barrelType : rootOutput.barrelType\n\n if (!barrelType) return\n\n const barrelFiles = generatePerPluginBarrel({\n barrelType,\n plugin: normalizedPlugin,\n files: ctx.files,\n config: ctx.config,\n })\n\n if (barrelFiles.length > 0) {\n ctx.upsertFile(...barrelFiles)\n }\n })\n\n hooks.on('kubb:build:end', () => {\n if (!ctx) return\n\n const rootOutput = ctx.config.output as { barrelType?: BarrelType | false }\n const rootBarrelType = rootOutput.barrelType\n\n if (!rootBarrelType) return\n\n const rootBarrelFiles = generateRootBarrel({\n barrelType: rootBarrelType,\n files: ctx.files,\n config: ctx.config,\n })\n\n if (rootBarrelFiles.length > 0) {\n ctx.upsertFile(...rootBarrelFiles)\n }\n })\n },\n})\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@kubb/middleware-barrel",
3
+ "version": "5.0.0-alpha.55",
4
+ "description": "Barrel-file generation middleware for Kubb. Generates index.ts barrel files for each plugin's output directory and a root index.ts after all plugins have run.",
5
+ "keywords": [
6
+ "barrel",
7
+ "code-generator",
8
+ "codegen",
9
+ "kubb",
10
+ "middleware",
11
+ "openapi",
12
+ "typescript"
13
+ ],
14
+ "license": "MIT",
15
+ "author": "stijnvanhulle",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/kubb-labs/kubb.git",
19
+ "directory": "packages/middleware-barrel"
20
+ },
21
+ "files": [
22
+ "src",
23
+ "dist",
24
+ "*.d.ts",
25
+ "*.d.cts",
26
+ "!/**/**.test.**",
27
+ "!/**/__tests__/**",
28
+ "!/**/__snapshots__/**"
29
+ ],
30
+ "type": "module",
31
+ "sideEffects": false,
32
+ "main": "./dist/index.cjs",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "import": "./dist/index.js",
38
+ "require": "./dist/index.cjs"
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "registry": "https://registry.npmjs.org/"
45
+ },
46
+ "peerDependencies": {
47
+ "@kubb/core": "5.0.0-alpha.55"
48
+ },
49
+ "engines": {
50
+ "node": ">=22"
51
+ },
52
+ "scripts": {
53
+ "build": "tsdown",
54
+ "clean": "npx rimraf ./dist",
55
+ "lint": "oxlint .",
56
+ "lint:fix": "oxlint --fix .",
57
+ "release": "pnpm publish --no-git-check",
58
+ "start": "tsdown --watch",
59
+ "test": "vitest --passWithNoTests",
60
+ "typecheck": "tsc -p ./tsconfig.json --noEmit --emitDeclarationOnly false"
61
+ }
62
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Base file name for barrel files (without extension).
3
+ */
4
+ export const BARREL_BASENAME = 'index' as const
5
+
6
+ /**
7
+ * Full file name for barrel files (with extension).
8
+ */
9
+ export const BARREL_FILENAME = 'index.ts' as const
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { middlewareBarrel } from './middleware.ts'
@@ -0,0 +1,87 @@
1
+ import { defineMiddleware } from '@kubb/core'
2
+ import type { KubbBuildStartContext, NormalizedPlugin } from '@kubb/core'
3
+ import './types.ts'
4
+ import type { BarrelType } from './types.ts'
5
+ import { generatePerPluginBarrel } from './utils/generatePerPluginBarrel.ts'
6
+ import { generateRootBarrel } from './utils/generateRootBarrel.ts'
7
+
8
+ /**
9
+ * Barrel-file generation middleware.
10
+ *
11
+ * When added to `config.middleware`, generates an `index.ts` barrel file for each
12
+ * plugin that has `output.barrelType` set (or inherits it from `config.output.barrelType`),
13
+ * as well as a root `index.ts` at `config.output.path` when `config.output.barrelType` is set.
14
+ *
15
+ * The `barrelType` option controls the re-export style:
16
+ * - `'all'` — `export * from '...'` for each generated file
17
+ * - `'named'` — `export { name1, name2 } from '...'` using each file's indexable sources
18
+ * - `'propagate'` — like `'all'` with intermediate barrel files for sub-directories
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * import { middlewareBarrel } from '@kubb/middleware-barrel'
23
+ *
24
+ * export default defineConfig({
25
+ * output: { path: 'src/gen', barrelType: 'named' },
26
+ * plugins: [
27
+ * pluginTs({ output: { path: 'types', barrelType: 'all' } }),
28
+ * pluginZod({ output: { path: 'schemas' } }),
29
+ * ],
30
+ * middleware: [middlewareBarrel],
31
+ * })
32
+ * ```
33
+ */
34
+ export const middlewareBarrel = defineMiddleware({
35
+ name: 'middleware-barrel',
36
+
37
+ install(hooks) {
38
+ let ctx: KubbBuildStartContext
39
+
40
+ hooks.on('kubb:build:start', (buildCtx) => {
41
+ ctx = buildCtx
42
+ })
43
+
44
+ hooks.on('kubb:plugin:end', ({ plugin }) => {
45
+ if (!ctx) return
46
+
47
+ // At runtime the plugin in kubb:plugin:end is always a NormalizedPlugin;
48
+ // KubbPluginEndContext types it as Plugin for public API simplicity.
49
+ const normalizedPlugin = plugin as NormalizedPlugin
50
+ const pluginOutput = normalizedPlugin.options.output as { barrelType?: BarrelType | false } | undefined
51
+ const rootOutput = ctx.config.output as { barrelType?: BarrelType | false }
52
+ const barrelType = pluginOutput?.barrelType !== undefined ? pluginOutput.barrelType : rootOutput.barrelType
53
+
54
+ if (!barrelType) return
55
+
56
+ const barrelFiles = generatePerPluginBarrel({
57
+ barrelType,
58
+ plugin: normalizedPlugin,
59
+ files: ctx.files,
60
+ config: ctx.config,
61
+ })
62
+
63
+ if (barrelFiles.length > 0) {
64
+ ctx.upsertFile(...barrelFiles)
65
+ }
66
+ })
67
+
68
+ hooks.on('kubb:build:end', () => {
69
+ if (!ctx) return
70
+
71
+ const rootOutput = ctx.config.output as { barrelType?: BarrelType | false }
72
+ const rootBarrelType = rootOutput.barrelType
73
+
74
+ if (!rootBarrelType) return
75
+
76
+ const rootBarrelFiles = generateRootBarrel({
77
+ barrelType: rootBarrelType,
78
+ files: ctx.files,
79
+ config: ctx.config,
80
+ })
81
+
82
+ if (rootBarrelFiles.length > 0) {
83
+ ctx.upsertFile(...rootBarrelFiles)
84
+ }
85
+ })
86
+ },
87
+ })
package/src/types.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The barrel type controls the style of re-exports generated in barrel files.
3
+ *
4
+ * - `'all'` — `export * from '...'` (re-export everything)
5
+ * - `'named'` — `export { name1, name2 } from '...'` (named re-exports only)
6
+ * - `'propagate'` — like `'all'` but also generates intermediate barrel files for
7
+ * every sub-directory so that consumers can import from any depth.
8
+ */
9
+ export type BarrelType = 'all' | 'named' | 'propagate'
10
+
11
+ declare global {
12
+ namespace Kubb {
13
+ interface PluginOptionsRegistry {
14
+ output: {
15
+ /**
16
+ * Controls which barrel file (index.ts) is generated for this plugin's output directory.
17
+ *
18
+ * - `'all'` — `export * from '...'` for every generated file.
19
+ * - `'named'` — `export { … } from '...'` using the file's named exports.
20
+ * - `'propagate'` — like `'all'` but also generates intermediate barrel files.
21
+ * - `false` — disable barrel generation for this plugin.
22
+ *
23
+ * When omitted, the root `config.output.barrelType` is used as the default.
24
+ */
25
+ barrelType?: BarrelType | false
26
+ }
27
+ }
28
+ interface ConfigOptionsRegistry {
29
+ output: {
30
+ /**
31
+ * Controls the root barrel file (index.ts) generated at `config.output.path`.
32
+ *
33
+ * - `'all'` — `export * from '...'` for every plugin's barrel.
34
+ * - `'named'` — `export { … } from '...'` using the barrel's named exports.
35
+ * - `'propagate'` — like `'all'` but also generates intermediate barrel files.
36
+ * - `false` — disable root barrel generation.
37
+ *
38
+ * Individual plugins can override this via their own `output.barrelType`.
39
+ */
40
+ barrelType?: BarrelType | false
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,67 @@
1
+ import { posix } from 'node:path'
2
+
3
+ /**
4
+ * A node in a directory tree used to compute barrel file exports.
5
+ *
6
+ * Each `TreeNode` represents either a directory or a file entry.
7
+ * Directory nodes have `children`; file nodes have an empty `children` array.
8
+ */
9
+ export type TreeNode = {
10
+ /**
11
+ * Absolute path of the directory (root of this subtree) or file.
12
+ */
13
+ path: string
14
+ /**
15
+ * Child nodes (sub-directories and files) within this directory.
16
+ */
17
+ children: Array<TreeNode>
18
+ /**
19
+ * `true` when this node represents a file (leaf node).
20
+ */
21
+ isFile: boolean
22
+ }
23
+
24
+ /**
25
+ * Builds a `TreeNode` directory tree from a list of absolute file paths.
26
+ *
27
+ * All `filePaths` must be inside `rootPath`. Paths that are outside
28
+ * the root or that equal the root are silently ignored.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const tree = buildTree('/src/gen/types', [
33
+ * '/src/gen/types/pet.ts',
34
+ * '/src/gen/types/user.ts',
35
+ * '/src/gen/types/pets/listPets.ts',
36
+ * ])
37
+ * ```
38
+ */
39
+ export function buildTree(rootPath: string, filePaths: ReadonlyArray<string>): TreeNode {
40
+ const root: TreeNode = { path: rootPath, children: [], isFile: false }
41
+
42
+ for (const filePath of filePaths) {
43
+ // Only include files inside rootPath
44
+ if (!filePath.startsWith(rootPath + posix.sep) && !filePath.startsWith(rootPath + '/')) {
45
+ continue
46
+ }
47
+
48
+ const relative = filePath.slice(rootPath.length).replace(/^\//g, '').replace(/^\\/g, '')
49
+ const parts = relative.split(/[/\\]/).filter(Boolean)
50
+
51
+ let current = root
52
+ for (let i = 0; i < parts.length; i++) {
53
+ const isLast = i === parts.length - 1
54
+ const part = parts[i]!
55
+ const childPath = `${current.path}/${part}`
56
+
57
+ let child = current.children.find((c) => c.path === childPath)
58
+ if (!child) {
59
+ child = { path: childPath, children: [], isFile: isLast }
60
+ current.children.push(child)
61
+ }
62
+ current = child
63
+ }
64
+ }
65
+
66
+ return root
67
+ }
@@ -0,0 +1,23 @@
1
+ import { resolve } from 'node:path'
2
+ import type { FileNode } from '@kubb/ast'
3
+ import type { Config, NormalizedPlugin } from '@kubb/core'
4
+ import type { BarrelType } from '../types.ts'
5
+ import { getBarrelFiles } from './getBarrelFiles.ts'
6
+
7
+ export type GeneratePerPluginBarrelParams = {
8
+ barrelType: BarrelType
9
+ plugin: NormalizedPlugin
10
+ files: ReadonlyArray<FileNode>
11
+ config: Config
12
+ }
13
+
14
+ /**
15
+ * Generates barrel files for a single plugin's output directory.
16
+ *
17
+ * The barrel file is placed at `resolve(config.root, plugin.options.output.path)/index.ts`
18
+ * and re-exports all files generated by that plugin, using the given `barrelType` strategy.
19
+ */
20
+ export function generatePerPluginBarrel({ barrelType, plugin, files, config }: GeneratePerPluginBarrelParams): Array<FileNode> {
21
+ const outputPath = resolve(config.root, plugin.options.output.path)
22
+ return getBarrelFiles(outputPath, files, barrelType)
23
+ }
@@ -0,0 +1,25 @@
1
+ import { resolve } from 'node:path'
2
+ import type { FileNode } from '@kubb/ast'
3
+ import type { Config } from '@kubb/core'
4
+ import type { BarrelType } from '../types.ts'
5
+ import { getBarrelFiles } from './getBarrelFiles.ts'
6
+
7
+ export type GenerateRootBarrelParams = {
8
+ barrelType: BarrelType
9
+ files: ReadonlyArray<FileNode>
10
+ config: Config
11
+ }
12
+
13
+ /**
14
+ * Generates a root barrel file at `resolve(config.root, config.output.path)/index.ts`.
15
+ *
16
+ * The root barrel re-exports from all files across all plugins that are located
17
+ * inside the root output directory, using the given `barrelType` strategy.
18
+ *
19
+ * In practice this re-exports the per-plugin barrels when `barrelType = 'propagate'`,
20
+ * or all individual files when `barrelType = 'all'` or `'named'`.
21
+ */
22
+ export function generateRootBarrel({ barrelType, files, config }: GenerateRootBarrelParams): Array<FileNode> {
23
+ const outputPath = resolve(config.root, config.output.path)
24
+ return getBarrelFiles(outputPath, files, barrelType)
25
+ }
@@ -0,0 +1,188 @@
1
+ import { createExport, createFile } from '@kubb/ast'
2
+ import type { FileNode } from '@kubb/ast'
3
+ import { BARREL_BASENAME, BARREL_FILENAME } from '../constants.ts'
4
+ import type { BarrelType } from '../types.ts'
5
+ import { buildTree, type TreeNode } from './TreeNode.ts'
6
+
7
+ /**
8
+ * Derives a relative module specifier (no extension) from an absolute `filePath`
9
+ * relative to an absolute `fromDir`.
10
+ *
11
+ * @example
12
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet'
13
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag'
14
+ */
15
+ function toRelativeModulePath(fromDir: string, filePath: string): string {
16
+ const relative = filePath.slice(fromDir.length).replace(/^[/\\]/g, '')
17
+ // Strip extension
18
+ const withoutExt = relative.replace(/\.[^/.]+$/, '')
19
+ return `./${withoutExt}`
20
+ }
21
+
22
+ /**
23
+ * Generates barrel `FileNode[]` for a given directory tree node using the `'all'` strategy:
24
+ * each leaf file gets a `export * from './relPath'` in the barrel of its nearest ancestor directory.
25
+ *
26
+ * Only a single barrel file (at `treeNode.path`) is generated — sub-directory files are referenced
27
+ * with their full relative path from `treeNode.path`.
28
+ */
29
+ function getBarrelFilesAll(treeNode: TreeNode, _sourceFiles: ReadonlyArray<FileNode>): Array<FileNode> {
30
+ // Collect all source file paths under this node (excluding barrel files themselves)
31
+ const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`))
32
+
33
+ if (leafPaths.length === 0) return []
34
+
35
+ const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`
36
+ const exports = leafPaths.map((filePath) =>
37
+ createExport({
38
+ path: toRelativeModulePath(treeNode.path, filePath),
39
+ }),
40
+ )
41
+
42
+ return [
43
+ createFile({
44
+ baseName: BARREL_FILENAME,
45
+ path: barrelPath,
46
+ exports,
47
+ sources: [],
48
+ imports: [],
49
+ }),
50
+ ]
51
+ }
52
+
53
+ /**
54
+ * Generates barrel `FileNode[]` for a given directory tree node using the `'named'` strategy:
55
+ * each indexable source in each leaf file gets an individual named `export { name } from '...'`.
56
+ */
57
+ function getBarrelFilesNamed(treeNode: TreeNode, sourceFiles: ReadonlyArray<FileNode>): Array<FileNode> {
58
+ const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`))
59
+
60
+ if (leafPaths.length === 0) return []
61
+
62
+ const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`
63
+ const exports: ReturnType<typeof createExport>[] = []
64
+
65
+ for (const filePath of leafPaths) {
66
+ const sourceFile = sourceFiles.find((f) => f.path === filePath)
67
+ if (!sourceFile) {
68
+ // Fall back to wildcard if the source file is not in our set
69
+ exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))
70
+ continue
71
+ }
72
+
73
+ const indexableSources = sourceFile.sources.filter((s) => s.isIndexable && s.name)
74
+ if (indexableSources.length === 0) {
75
+ // No named exports: fall back to wildcard
76
+ exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))
77
+ continue
78
+ }
79
+
80
+ const names = indexableSources.map((s) => s.name as string)
81
+ exports.push(
82
+ createExport({
83
+ name: names,
84
+ path: toRelativeModulePath(treeNode.path, filePath),
85
+ }),
86
+ )
87
+ }
88
+
89
+ if (exports.length === 0) return []
90
+
91
+ return [
92
+ createFile({
93
+ baseName: BARREL_FILENAME,
94
+ path: barrelPath,
95
+ exports,
96
+ sources: [],
97
+ imports: [],
98
+ }),
99
+ ]
100
+ }
101
+
102
+ /**
103
+ * Generates barrel `FileNode[]` for a given directory tree node using the `'propagate'` strategy:
104
+ * like `'all'` but also generates intermediate barrel files for every sub-directory, so that
105
+ * consumers can import from any depth.
106
+ *
107
+ * Leaf barrels export directly from their files; parent barrels export from their sub-barrel files.
108
+ */
109
+ function getBarrelFilesPropagate(treeNode: TreeNode): Array<FileNode> {
110
+ return collectPropagatedBarrels(treeNode)
111
+ }
112
+
113
+ function collectPropagatedBarrels(node: TreeNode): Array<FileNode> {
114
+ const result: Array<FileNode> = []
115
+ const barrelExports: ReturnType<typeof createExport>[] = []
116
+
117
+ for (const child of node.children) {
118
+ if (child.isFile) {
119
+ if (!child.path.endsWith(`/${BARREL_FILENAME}`)) {
120
+ barrelExports.push(createExport({ path: toRelativeModulePath(node.path, child.path) }))
121
+ }
122
+ } else {
123
+ // Recurse into sub-directory
124
+ const subBarrels = collectPropagatedBarrels(child)
125
+ result.push(...subBarrels)
126
+
127
+ // Export the sub-directory's barrel (not individual files)
128
+ const subBarrelPath = `${child.path}/${BARREL_BASENAME}`
129
+ barrelExports.push(createExport({ path: toRelativeModulePath(node.path, subBarrelPath) }))
130
+ }
131
+ }
132
+
133
+ if (barrelExports.length > 0) {
134
+ result.push(
135
+ createFile({
136
+ baseName: BARREL_FILENAME,
137
+ path: `${node.path}/${BARREL_FILENAME}`,
138
+ exports: barrelExports,
139
+ sources: [],
140
+ imports: [],
141
+ }),
142
+ )
143
+ }
144
+
145
+ return result
146
+ }
147
+
148
+ /**
149
+ * Collects all leaf (file) paths within a tree node recursively.
150
+ */
151
+ function collectLeafPaths(node: TreeNode): Array<string> {
152
+ if (node.isFile) return [node.path]
153
+ return node.children.flatMap((c) => collectLeafPaths(c))
154
+ }
155
+
156
+ /**
157
+ * Generates barrel `FileNode[]` for a directory rooted at `outputPath`, given the full set of
158
+ * generated source `files`, using the specified `barrelType` strategy.
159
+ *
160
+ * Files not located inside `outputPath` are excluded automatically.
161
+ *
162
+ * @param outputPath Absolute path to the output directory.
163
+ * @param files All generated files (across all plugins).
164
+ * @param barrelType Barrel generation strategy.
165
+ */
166
+ export function getBarrelFiles(outputPath: string, files: ReadonlyArray<FileNode>, barrelType: BarrelType): Array<FileNode> {
167
+ // Only include files that live inside this outputPath
168
+ const relevantFiles = files.filter((f) => {
169
+ const normalizedFilePath = f.path.replace(/\\/g, '/')
170
+ const normalizedOutputPath = outputPath.replace(/\\/g, '/')
171
+ return normalizedFilePath.startsWith(normalizedOutputPath + '/') && !normalizedFilePath.endsWith(`/${BARREL_FILENAME}`)
172
+ })
173
+
174
+ if (relevantFiles.length === 0) return []
175
+
176
+ const tree = buildTree(outputPath, relevantFiles.map((f) => f.path))
177
+
178
+ switch (barrelType) {
179
+ case 'all':
180
+ return getBarrelFilesAll(tree, relevantFiles)
181
+ case 'named':
182
+ return getBarrelFilesNamed(tree, relevantFiles)
183
+ case 'propagate':
184
+ return getBarrelFilesPropagate(tree)
185
+ default:
186
+ return []
187
+ }
188
+ }