@kubb/middleware-barrel 5.0.0-alpha.61 → 5.0.0-alpha.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10,16 +10,13 @@ const BARREL_FILENAME = "index.ts";
10
10
  //#endregion
11
11
  //#region src/utils/buildTree.ts
12
12
  /**
13
- * Builds a `TreeNode` directory tree from a list of absolute file paths.
14
- *
15
- * All `filePaths` must be inside `rootPath`. Paths that are outside
16
- * the root or that equal the root are silently ignored.
13
+ * Builds a directory tree rooted at `rootPath` from a list of absolute file paths.
14
+ * Paths outside `rootPath` are silently ignored.
17
15
  *
18
16
  * @example
19
17
  * ```ts
20
- * const tree = buildTree('/src/gen/types', [
18
+ * buildTree('/src/gen/types', [
21
19
  * '/src/gen/types/pet.ts',
22
- * '/src/gen/types/user.ts',
23
20
  * '/src/gen/types/pets/listPets.ts',
24
21
  * ])
25
22
  * ```
@@ -61,51 +58,48 @@ const SOURCE_EXTENSIONS = new Set([
61
58
  ".jsx"
62
59
  ]);
63
60
  /**
64
- * Derives a relative module specifier from an absolute `filePath` relative to an absolute `fromDir`.
65
- * The source extension is preserved so that `@kubb/parser-ts` can apply the `extNames` mapping
66
- * (e.g. `.ts` → `.js` for ESM output).
61
+ * Derives a relative module specifier from `filePath` relative to `fromDir`.
62
+ * The source extension is preserved so `@kubb/parser-ts` can apply its `extNames` mapping.
67
63
  *
68
64
  * @example
65
+ * ```ts
69
66
  * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet.ts'
70
67
  * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag.ts'
68
+ * ```
71
69
  */
72
70
  function toRelativeModulePath(fromDir, filePath) {
73
71
  return `./${filePath.slice(fromDir.length).replace(/^[/\\]/g, "")}`;
74
72
  }
75
- /**
76
- * Generates barrel `FileNode[]` for a given directory tree node using the `'all'` strategy:
77
- * each leaf file gets a `export * from './relPath'` in the barrel of its nearest ancestor directory.
78
- *
79
- * Only a single barrel file (at `treeNode.path`) is generated — sub-directory files are referenced
80
- * with their full relative path from `treeNode.path`.
81
- */
82
- function getBarrelFilesAll(treeNode, sourceFiles) {
73
+ function getBarrelFilesAll({ treeNode, sourceFiles, recursive = false }) {
83
74
  const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`));
84
75
  if (leafPaths.length === 0) return [];
85
- const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`;
86
76
  const exports = [];
87
77
  for (const filePath of leafPaths) {
88
78
  const sourceFile = sourceFiles.find((f) => f.path === filePath);
89
79
  if (sourceFile && sourceFile.sources.length > 0 && sourceFile.sources.every((s) => !s.isIndexable)) continue;
90
80
  exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }));
91
81
  }
92
- if (exports.length === 0) return [];
93
- return [createFile({
82
+ const result = [];
83
+ if (recursive) {
84
+ for (const child of treeNode.children) if (!child.isFile) result.push(...getBarrelFilesAll({
85
+ treeNode: child,
86
+ sourceFiles,
87
+ recursive: true
88
+ }));
89
+ }
90
+ if (exports.length === 0) return result;
91
+ result.push(createFile({
94
92
  baseName: BARREL_FILENAME,
95
- path: barrelPath,
93
+ path: `${treeNode.path}/${BARREL_FILENAME}`,
96
94
  exports,
97
95
  sources: [],
98
96
  imports: []
99
- })];
97
+ }));
98
+ return result;
100
99
  }
101
- /**
102
- * Generates barrel `FileNode[]` for a given directory tree node using the `'named'` strategy:
103
- * each indexable source in each leaf file gets an individual named `export { name } from '...'`.
104
- */
105
- function getBarrelFilesNamed(treeNode, sourceFiles) {
100
+ function getBarrelFilesNamed({ treeNode, sourceFiles, recursive = false }) {
106
101
  const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`));
107
102
  if (leafPaths.length === 0) return [];
108
- const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`;
109
103
  const exports = [];
110
104
  for (const filePath of leafPaths) {
111
105
  const sourceFile = sourceFiles.find((f) => f.path === filePath);
@@ -132,23 +126,24 @@ function getBarrelFilesNamed(treeNode, sourceFiles) {
132
126
  isTypeOnly: true
133
127
  }));
134
128
  }
135
- if (exports.length === 0) return [];
136
- return [createFile({
129
+ const result = [];
130
+ if (recursive) {
131
+ for (const child of treeNode.children) if (!child.isFile) result.push(...getBarrelFilesNamed({
132
+ treeNode: child,
133
+ sourceFiles,
134
+ recursive: true
135
+ }));
136
+ }
137
+ if (exports.length > 0) result.push(createFile({
137
138
  baseName: BARREL_FILENAME,
138
- path: barrelPath,
139
+ path: `${treeNode.path}/${BARREL_FILENAME}`,
139
140
  exports,
140
141
  sources: [],
141
142
  imports: []
142
- })];
143
+ }));
144
+ return result;
143
145
  }
144
- /**
145
- * Generates barrel `FileNode[]` for a given directory tree node using the `'propagate'` strategy:
146
- * like `'all'` but also generates intermediate barrel files for every sub-directory, so that
147
- * consumers can import from any depth.
148
- *
149
- * Leaf barrels export directly from their files; parent barrels export from their sub-barrel files.
150
- */
151
- function getBarrelFilesPropagate(treeNode) {
146
+ function getBarrelFilesPropagate({ treeNode }) {
152
147
  return collectPropagatedBarrels(treeNode);
153
148
  }
154
149
  function collectPropagatedBarrels(node) {
@@ -171,24 +166,17 @@ function collectPropagatedBarrels(node) {
171
166
  }));
172
167
  return result;
173
168
  }
174
- /**
175
- * Collects all leaf (file) paths within a tree node recursively.
176
- */
177
169
  function collectLeafPaths(node) {
178
170
  if (node.isFile) return [node.path];
179
171
  return node.children.flatMap((c) => collectLeafPaths(c));
180
172
  }
181
173
  /**
182
- * Generates barrel `FileNode[]` for a directory rooted at `outputPath`, given the full set of
183
- * generated source `files`, using the specified `barrelType` strategy.
184
- *
185
- * Files not located inside `outputPath` are excluded automatically.
174
+ * Generates barrel `FileNode`s for the directory rooted at `outputPath`.
186
175
  *
187
- * @param outputPath Absolute path to the output directory.
188
- * @param files All generated files (across all plugins).
189
- * @param barrelType Barrel generation strategy.
176
+ * Files outside `outputPath`, existing barrel files, and non-source extensions are filtered out
177
+ * before the tree is built.
190
178
  */
191
- function getBarrelFiles(outputPath, files, barrelType) {
179
+ function getBarrelFiles({ outputPath, files, barrelType, recursive = false }) {
192
180
  const relevantFiles = files.filter((f) => {
193
181
  const normalizedFilePath = f.path.replace(/\\/g, "/");
194
182
  const normalizedOutputPath = outputPath.replace(/\\/g, "/");
@@ -201,9 +189,17 @@ function getBarrelFiles(outputPath, files, barrelType) {
201
189
  if (relevantFiles.length === 0) return [];
202
190
  const tree = buildTree(outputPath, relevantFiles.map((f) => f.path));
203
191
  switch (barrelType) {
204
- case "all": return getBarrelFilesAll(tree, relevantFiles);
205
- case "named": return getBarrelFilesNamed(tree, relevantFiles);
206
- case "propagate": return getBarrelFilesPropagate(tree);
192
+ case "all": return getBarrelFilesAll({
193
+ treeNode: tree,
194
+ sourceFiles: relevantFiles,
195
+ recursive
196
+ });
197
+ case "named": return getBarrelFilesNamed({
198
+ treeNode: tree,
199
+ sourceFiles: relevantFiles,
200
+ recursive
201
+ });
202
+ case "propagate": return getBarrelFilesPropagate({ treeNode: tree });
207
203
  default: return [];
208
204
  }
209
205
  }
@@ -212,47 +208,46 @@ function getBarrelFiles(outputPath, files, barrelType) {
212
208
  /**
213
209
  * Generates barrel files for a single plugin's output directory.
214
210
  *
215
- * The barrel file is placed at `resolve(config.root, config.output.path, plugin.options.output.path)/index.ts`
216
- * and re-exports all files generated by that plugin, using the given `barrelType` strategy.
217
- *
218
- * Note: `plugin.options.output.path` is relative to `config.output.path`, not to `config.root`.
219
- * Files generated by plugins land at `config.root + config.output.path + plugin.output.path`,
220
- * so the barrel directory must be resolved with all three segments.
211
+ * The barrel is placed at `<config.root>/<config.output.path>/<plugin.options.output.path>/index.ts`.
212
+ * When the plugin uses `group`, additional sub-directory barrels are generated so that grouped
213
+ * output (e.g. `petController/index.ts`) gets its own re-export entry point.
221
214
  */
222
215
  function generatePerPluginBarrel({ barrelType, plugin, files, config }) {
223
- return getBarrelFiles(resolve(config.root, config.output.path, plugin.options.output.path), files, barrelType);
216
+ return getBarrelFiles({
217
+ outputPath: resolve(config.root, config.output.path, plugin.options.output.path),
218
+ files,
219
+ barrelType,
220
+ recursive: true
221
+ });
224
222
  }
225
223
  //#endregion
226
224
  //#region src/utils/generateRootBarrel.ts
227
225
  /**
228
- * Generates a root barrel file at `resolve(config.root, config.output.path)/index.ts`.
226
+ * Generates the root barrel file at `<config.root>/<config.output.path>/index.ts`.
229
227
  *
230
- * The root barrel re-exports from all files across all plugins that are located
231
- * inside the root output directory, using the given `barrelType` strategy.
232
- *
233
- * In practice this re-exports the per-plugin barrels when `barrelType = 'propagate'`,
234
- * or all individual files when `barrelType = 'all'` or `'named'`.
228
+ * Unlike `generatePerPluginBarrel`, this does not recurse into sub-directories each
229
+ * plugin is responsible for its own per-plugin barrels.
235
230
  */
236
231
  function generateRootBarrel({ barrelType, files, config }) {
237
- return getBarrelFiles(resolve(config.root, config.output.path), files, barrelType);
232
+ return getBarrelFiles({
233
+ outputPath: resolve(config.root, config.output.path),
234
+ files,
235
+ barrelType
236
+ });
238
237
  }
239
238
  //#endregion
240
239
  //#region src/middleware.ts
241
240
  /**
242
- * Barrel-file generation middleware.
243
- *
244
- * When added to `config.middleware`, generates an `index.ts` barrel file for each
245
- * plugin. When `output.barrelType` is omitted on a plugin it inherits from `config.output.barrelType`,
246
- * and when that is also omitted both default to `'all'`. Set `barrelType: false` to disable
247
- * barrel generation for a specific plugin or for the root entirely.
241
+ * Generates `index.ts` barrel files for each plugin's output directory and one root barrel
242
+ * at `config.output.path/index.ts`.
248
243
  *
249
- * The `barrelType` option controls the re-export style:
250
- * - `'all'` — `export * from '...'` for each generated file
251
- * - `'named'` — `export { name1, name2 } from '...'` using each file's indexable sources
252
- * - `'propagate'` — like `'all'` with intermediate barrel files for sub-directories
244
+ * Plugins inherit `output.barrelType` from `config.output.barrelType` (which itself defaults to `'named'`).
245
+ * Setting `barrelType: false` on a plugin disables its barrel and excludes the plugin's files from the
246
+ * root barrel as well.
253
247
  *
254
248
  * @example
255
249
  * ```ts
250
+ * import { defineConfig } from '@kubb/core'
256
251
  * import { middlewareBarrel } from '@kubb/middleware-barrel'
257
252
  *
258
253
  * export default defineConfig({
@@ -269,17 +264,15 @@ const middlewareBarrel = defineMiddleware({
269
264
  name: "middleware-barrel",
270
265
  install(hooks) {
271
266
  let ctx;
272
- const excludedOutputPaths = [];
267
+ const excludedPaths = /* @__PURE__ */ new Set();
273
268
  hooks.on("kubb:build:start", (buildCtx) => {
274
269
  ctx = buildCtx;
275
270
  });
276
271
  hooks.on("kubb:plugin:end", ({ plugin }) => {
277
272
  if (!ctx) return;
278
- const pluginOutput = plugin.options.output;
279
- const barrelType = pluginOutput?.barrelType !== void 0 ? pluginOutput.barrelType : ctx.config.output.barrelType ?? "all";
273
+ const barrelType = plugin.options.output?.barrelType ?? ctx.config.output.barrelType ?? "all";
280
274
  if (!barrelType) {
281
- const outputPath = plugin.options.output.path;
282
- excludedOutputPaths.push(outputPath);
275
+ excludedPaths.add(resolve(ctx.config.root, ctx.config.output.path, plugin.options.output.path));
283
276
  return;
284
277
  }
285
278
  const barrelFiles = generatePerPluginBarrel({
@@ -291,14 +284,11 @@ const middlewareBarrel = defineMiddleware({
291
284
  if (barrelFiles.length > 0) ctx.upsertFile(...barrelFiles);
292
285
  });
293
286
  hooks.on("kubb:plugins:end", ({ files, config, upsertFile }) => {
294
- const rootBarrelType = config.output.barrelType ?? "all";
287
+ const rootBarrelType = config.output.barrelType ?? "named";
295
288
  if (!rootBarrelType) return;
296
289
  const rootBarrelFiles = generateRootBarrel({
297
290
  barrelType: rootBarrelType,
298
- files: excludedOutputPaths.length > 0 ? files.filter((f) => {
299
- const normalizedPath = f.path.replace(/\\/g, "/");
300
- return !excludedOutputPaths.some((excluded) => normalizedPath.startsWith(excluded.replace(/\\/g, "/") + "/"));
301
- }) : files,
291
+ files: excludedPaths.size > 0 ? files.filter((f) => ![...excludedPaths].some((excluded) => f.path.startsWith(excluded + "/"))) : files,
302
292
  config
303
293
  });
304
294
  if (rootBarrelFiles.length > 0) upsertFile(...rootBarrelFiles);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/constants.ts","../src/utils/buildTree.ts","../src/utils/getBarrelFiles.ts","../src/utils/generatePerPluginBarrel.ts","../src/utils/generateRootBarrel.ts","../src/middleware.ts"],"sourcesContent":["\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 BuildTree = {\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<BuildTree>\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>): BuildTree {\n const root: BuildTree = { 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_FILENAME } from '../constants.ts'\nimport type { BarrelType } from '../types.ts'\nimport { buildTree, type BuildTree } from './buildTree.ts'\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\n\n/**\n * Derives a relative module specifier from an absolute `filePath` relative to an absolute `fromDir`.\n * The source extension is preserved so that `@kubb/parser-ts` can apply the `extNames` mapping\n * (e.g. `.ts` → `.js` for ESM output).\n *\n * @example\n * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet.ts'\n * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag.ts'\n */\nfunction toRelativeModulePath(fromDir: string, filePath: string): string {\n const relative = filePath.slice(fromDir.length).replace(/^[/\\\\]/g, '')\n return `./${relative}`\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: BuildTree, 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: ReturnType<typeof createExport>[] = []\n\n for (const filePath of leafPaths) {\n const sourceFile = sourceFiles.find((f) => f.path === filePath)\n // Skip files whose sources all have isIndexable: false (e.g. internal injected files)\n if (sourceFile && sourceFile.sources.length > 0 && sourceFile.sources.every((s) => !s.isIndexable)) {\n continue\n }\n exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))\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 `'named'` strategy:\n * each indexable source in each leaf file gets an individual named `export { name } from '...'`.\n */\nfunction getBarrelFilesNamed(treeNode: BuildTree, 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 // If the file has explicit sources but none are indexable, skip it entirely.\n // Only fall back to wildcard when there are no sources at all (unknown exports).\n if (sourceFile.sources.length > 0) continue\n exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))\n continue\n }\n\n const valueNames = indexableSources.filter((s) => !s.isTypeOnly).map((s) => s.name as string)\n const typeNames = indexableSources.filter((s) => s.isTypeOnly).map((s) => s.name as string)\n const modulePath = toRelativeModulePath(treeNode.path, filePath)\n\n if (valueNames.length > 0) {\n exports.push(createExport({ name: valueNames, path: modulePath }))\n }\n if (typeNames.length > 0) {\n exports.push(createExport({ name: typeNames, path: modulePath, isTypeOnly: true }))\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: BuildTree): Array<FileNode> {\n return collectPropagatedBarrels(treeNode)\n}\n\nfunction collectPropagatedBarrels(node: BuildTree): 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_FILENAME}`\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: BuildTree): 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 and have a recognised source extension\n const relevantFiles = files.filter((f) => {\n const normalizedFilePath = f.path.replace(/\\\\/g, '/')\n const normalizedOutputPath = outputPath.replace(/\\\\/g, '/')\n if (!normalizedFilePath.startsWith(normalizedOutputPath + '/')) return false\n if (normalizedFilePath.endsWith(`/${BARREL_FILENAME}`)) return false\n const dotIndex = normalizedFilePath.lastIndexOf('.')\n const ext = dotIndex === -1 ? '' : normalizedFilePath.slice(dotIndex)\n return SOURCE_EXTENSIONS.has(ext)\n })\n\n if (relevantFiles.length === 0) return []\n\n const tree = buildTree(\n outputPath,\n relevantFiles.map((f) => f.path),\n )\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, config.output.path, plugin.options.output.path)/index.ts`\n * and re-exports all files generated by that plugin, using the given `barrelType` strategy.\n *\n * Note: `plugin.options.output.path` is relative to `config.output.path`, not to `config.root`.\n * Files generated by plugins land at `config.root + config.output.path + plugin.output.path`,\n * so the barrel directory must be resolved with all three segments.\n */\nexport function generatePerPluginBarrel({ barrelType, plugin, files, config }: GeneratePerPluginBarrelParams): Array<FileNode> {\n const outputPath = resolve(config.root, config.output.path, plugin.options.output.path)\n\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\n return getBarrelFiles(outputPath, files, barrelType)\n}\n","import { defineMiddleware } from '@kubb/core'\nimport type { KubbBuildStartContext } 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\ndeclare global {\n namespace Kubb {\n interface PluginOptionsRegistry {\n output: {\n /**\n * Controls which barrel file (index.ts) is generated for this plugin's output directory.\n *\n * - `'all'` — `export * from '...'` for every generated file.\n * - `'named'` — `export { … } from '...'` using the file's named exports.\n * - `'propagate'` — like `'all'` but also generates intermediate barrel files.\n * - `false` — disable barrel generation for this plugin.\n *\n * When omitted, the root `config.output.barrelType` is used as the default.\n */\n barrelType?: BarrelType | false\n }\n }\n interface ConfigOptionsRegistry {\n output: {\n /**\n * Controls the root barrel file (index.ts) generated at `config.output.path`.\n *\n * - `'all'` — `export * from '...'` for every plugin's barrel.\n * - `'named'` — `export { … } from '...'` using the barrel's named exports.\n * - `'propagate'` — like `'all'` but also generates intermediate barrel files.\n * - `false` — disable root barrel generation.\n *\n * Individual plugins can override this via their own `output.barrelType`.\n */\n barrelType?: BarrelType | false\n }\n }\n }\n}\n\n/**\n * Barrel-file generation middleware.\n *\n * When added to `config.middleware`, generates an `index.ts` barrel file for each\n * plugin. When `output.barrelType` is omitted on a plugin it inherits from `config.output.barrelType`,\n * and when that is also omitted both default to `'all'`. Set `barrelType: false` to disable\n * barrel generation for a specific plugin or for the root entirely.\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 install(hooks) {\n let ctx: KubbBuildStartContext\n // Track output paths of plugins with barrelType: false so the root barrel\n // can exclude their files.\n const excludedOutputPaths: string[] = []\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 const pluginOutput = plugin.options.output\n const barrelType = pluginOutput?.barrelType !== undefined ? pluginOutput.barrelType : (ctx.config.output.barrelType ?? 'all')\n\n if (!barrelType) {\n // Remember this plugin's output path so we can exclude it from the root barrel\n const outputPath = plugin.options.output.path\n excludedOutputPaths.push(outputPath)\n return\n }\n\n const barrelFiles = generatePerPluginBarrel({\n barrelType,\n plugin,\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:plugins:end', ({ files, config, upsertFile }) => {\n const rootBarrelType = config.output.barrelType ?? 'all'\n\n if (!rootBarrelType) return\n\n // Exclude files that belong to plugins with barrelType: false\n const filteredFiles =\n excludedOutputPaths.length > 0\n ? files.filter((f) => {\n const normalizedPath = f.path.replace(/\\\\/g, '/')\n return !excludedOutputPaths.some((excluded) => normalizedPath.startsWith(excluded.replace(/\\\\/g, '/') + '/'))\n })\n : files\n\n const rootBarrelFiles = generateRootBarrel({\n barrelType: rootBarrelType,\n files: filteredFiles,\n config,\n })\n\n if (rootBarrelFiles.length > 0) {\n upsertFile(...rootBarrelFiles)\n }\n })\n },\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/constants.ts","../src/utils/buildTree.ts","../src/utils/getBarrelFiles.ts","../src/utils/generatePerPluginBarrel.ts","../src/utils/generateRootBarrel.ts","../src/middleware.ts"],"sourcesContent":["/**\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 the directory tree used to compute barrel file exports.\n * Either represents a directory (with `children`) or a file (`isFile: true`, empty `children`).\n */\nexport type BuildTree = {\n /**\n * Absolute filesystem path of this directory or file.\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.\n *\n * @example\n * ```ts\n * buildTree('/src/gen/types', [\n * '/src/gen/types/pet.ts',\n * '/src/gen/types/pets/listPets.ts',\n * ])\n * ```\n */\nexport function buildTree(rootPath: string, filePaths: ReadonlyArray<string>): BuildTree {\n const root: BuildTree = { 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_FILENAME } from '../constants.ts'\nimport type { BarrelType } from '../types.ts'\nimport { buildTree, type BuildTree } from './buildTree.ts'\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\n\n/**\n * Derives a relative module specifier from `filePath` relative to `fromDir`.\n * The source extension is preserved so `@kubb/parser-ts` can apply its `extNames` mapping.\n *\n * @example\n * ```ts\n * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet.ts'\n * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag.ts'\n * ```\n */\nfunction toRelativeModulePath(fromDir: string, filePath: string): string {\n const relative = filePath.slice(fromDir.length).replace(/^[/\\\\]/g, '')\n return `./${relative}`\n}\n\ntype BarrelFilesParams = {\n treeNode: BuildTree\n sourceFiles: ReadonlyArray<FileNode>\n recursive?: boolean\n}\n\nfunction getBarrelFilesAll({ treeNode, sourceFiles, recursive = false }: BarrelFilesParams): Array<FileNode> {\n const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`))\n\n if (leafPaths.length === 0) return []\n\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 && sourceFile.sources.length > 0 && sourceFile.sources.every((s) => !s.isIndexable)) {\n continue\n }\n exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))\n }\n\n const result: Array<FileNode> = []\n\n if (recursive) {\n for (const child of treeNode.children) {\n if (!child.isFile) {\n result.push(...getBarrelFilesAll({ treeNode: child, sourceFiles, recursive: true }))\n }\n }\n }\n\n if (exports.length === 0) return result\n\n result.push(\n createFile({\n baseName: BARREL_FILENAME,\n path: `${treeNode.path}/${BARREL_FILENAME}`,\n exports,\n sources: [],\n imports: [],\n }),\n )\n\n return result\n}\n\nfunction getBarrelFilesNamed({ treeNode, sourceFiles, recursive = false }: BarrelFilesParams): Array<FileNode> {\n const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`))\n\n if (leafPaths.length === 0) return []\n\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 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 if (sourceFile.sources.length > 0) continue\n exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))\n continue\n }\n\n const valueNames = indexableSources.filter((s) => !s.isTypeOnly).map((s) => s.name as string)\n const typeNames = indexableSources.filter((s) => s.isTypeOnly).map((s) => s.name as string)\n const modulePath = toRelativeModulePath(treeNode.path, filePath)\n\n if (valueNames.length > 0) {\n exports.push(createExport({ name: valueNames, path: modulePath }))\n }\n if (typeNames.length > 0) {\n exports.push(createExport({ name: typeNames, path: modulePath, isTypeOnly: true }))\n }\n }\n\n const result: Array<FileNode> = []\n\n if (recursive) {\n for (const child of treeNode.children) {\n if (!child.isFile) {\n result.push(...getBarrelFilesNamed({ treeNode: child, sourceFiles, recursive: true }))\n }\n }\n }\n\n if (exports.length > 0) {\n result.push(\n createFile({\n baseName: BARREL_FILENAME,\n path: `${treeNode.path}/${BARREL_FILENAME}`,\n exports,\n sources: [],\n imports: [],\n }),\n )\n }\n\n return result\n}\n\nfunction getBarrelFilesPropagate({ treeNode }: Pick<BarrelFilesParams, 'treeNode'>): Array<FileNode> {\n return collectPropagatedBarrels(treeNode)\n}\n\nfunction collectPropagatedBarrels(node: BuildTree): 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 const subBarrels = collectPropagatedBarrels(child)\n result.push(...subBarrels)\n\n const subBarrelPath = `${child.path}/${BARREL_FILENAME}`\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\nfunction collectLeafPaths(node: BuildTree): Array<string> {\n if (node.isFile) return [node.path]\n return node.children.flatMap((c) => collectLeafPaths(c))\n}\n\nexport type GetBarrelFilesParams = {\n /**\n * Absolute path to the directory the barrel(s) should be rooted at.\n * Files outside this directory are ignored.\n */\n outputPath: string\n /**\n * Full set of generated files across all plugins.\n * Used both to discover what to re-export and to read each file's indexable sources.\n */\n files: ReadonlyArray<FileNode>\n /**\n * Re-export style used in the generated barrel(s).\n */\n barrelType: BarrelType\n /**\n * When `true`, also generate a barrel for each sub-directory of `outputPath`.\n * Used by per-plugin barrels so that grouped output (e.g. `petController/`) gets its own `index.ts`.\n *\n * Has no effect for `barrelType: 'propagate'`, which always recurses by design.\n *\n * @default false\n */\n recursive?: boolean\n}\n\n/**\n * Generates barrel `FileNode`s for the directory rooted at `outputPath`.\n *\n * Files outside `outputPath`, existing barrel files, and non-source extensions are filtered out\n * before the tree is built.\n */\nexport function getBarrelFiles({ outputPath, files, barrelType, recursive = false }: GetBarrelFilesParams): Array<FileNode> {\n const relevantFiles = files.filter((f) => {\n const normalizedFilePath = f.path.replace(/\\\\/g, '/')\n const normalizedOutputPath = outputPath.replace(/\\\\/g, '/')\n if (!normalizedFilePath.startsWith(normalizedOutputPath + '/')) return false\n if (normalizedFilePath.endsWith(`/${BARREL_FILENAME}`)) return false\n const dotIndex = normalizedFilePath.lastIndexOf('.')\n const ext = dotIndex === -1 ? '' : normalizedFilePath.slice(dotIndex)\n return SOURCE_EXTENSIONS.has(ext)\n })\n\n if (relevantFiles.length === 0) return []\n\n const tree = buildTree(\n outputPath,\n relevantFiles.map((f) => f.path),\n )\n\n switch (barrelType) {\n case 'all':\n return getBarrelFilesAll({ treeNode: tree, sourceFiles: relevantFiles, recursive })\n case 'named':\n return getBarrelFilesNamed({ treeNode: tree, sourceFiles: relevantFiles, recursive })\n case 'propagate':\n return getBarrelFilesPropagate({ treeNode: 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 /**\n * Re-export style used in the plugin's barrel file(s).\n */\n barrelType: BarrelType\n /**\n * Plugin whose `output.path` determines the barrel directory.\n */\n plugin: NormalizedPlugin\n /**\n * Full set of generated files across all plugins.\n * Files outside the plugin's output directory are filtered out automatically.\n */\n files: ReadonlyArray<FileNode>\n /**\n * Resolved Kubb config; used to compute the absolute output directory.\n */\n config: Config\n}\n\n/**\n * Generates barrel files for a single plugin's output directory.\n *\n * The barrel is placed at `<config.root>/<config.output.path>/<plugin.options.output.path>/index.ts`.\n * When the plugin uses `group`, additional sub-directory barrels are generated so that grouped\n * output (e.g. `petController/index.ts`) gets its own re-export entry point.\n */\nexport function generatePerPluginBarrel({ barrelType, plugin, files, config }: GeneratePerPluginBarrelParams): Array<FileNode> {\n const outputPath = resolve(config.root, config.output.path, plugin.options.output.path)\n\n return getBarrelFiles({ outputPath, files, barrelType, recursive: true })\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 /**\n * Re-export style used in the root barrel file.\n */\n barrelType: BarrelType\n /**\n * Files eligible for re-export. The middleware filters out files belonging to plugins\n * with `barrelType: false` before passing them in.\n */\n files: ReadonlyArray<FileNode>\n /**\n * Resolved Kubb config; used to compute the root output directory.\n */\n config: Config\n}\n\n/**\n * Generates the root barrel file at `<config.root>/<config.output.path>/index.ts`.\n *\n * Unlike `generatePerPluginBarrel`, this does not recurse into sub-directories — each\n * plugin is responsible for its own per-plugin barrels.\n */\nexport function generateRootBarrel({ barrelType, files, config }: GenerateRootBarrelParams): Array<FileNode> {\n const outputPath = resolve(config.root, config.output.path)\n\n return getBarrelFiles({ outputPath, files, barrelType })\n}\n","import { defineMiddleware } from '@kubb/core'\nimport type { KubbBuildStartContext } from '@kubb/core'\nimport type { BarrelType } from './types.ts'\nimport { generatePerPluginBarrel } from './utils/generatePerPluginBarrel.ts'\nimport { generateRootBarrel } from './utils/generateRootBarrel.ts'\nimport { resolve } from 'node:path'\n\ndeclare global {\n namespace Kubb {\n interface PluginOptionsRegistry {\n output: {\n /**\n * Re-export style for this plugin's barrel file.\n * Set to `false` to disable barrel generation for this plugin entirely; doing so also\n * excludes the plugin's files from the root barrel.\n *\n * Falls back to `config.output.barrelType` when omitted.\n */\n barrelType?: BarrelType | false\n }\n }\n interface ConfigOptionsRegistry {\n output: {\n /**\n * Re-export style for the root barrel file at `config.output.path/index.ts`.\n * Set to `false` to disable root barrel generation. Individual plugins can override\n * this via their own `output.barrelType`.\n *\n * @default 'named'\n */\n barrelType?: BarrelType | false\n }\n }\n }\n}\n\n/**\n * Generates `index.ts` barrel files for each plugin's output directory and one root barrel\n * at `config.output.path/index.ts`.\n *\n * Plugins inherit `output.barrelType` from `config.output.barrelType` (which itself defaults to `'named'`).\n * Setting `barrelType: false` on a plugin disables its barrel and excludes the plugin's files from the\n * root barrel as well.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@kubb/core'\n * import { middlewareBarrel } from '@kubb/middleware-barrel'\n *\n * export default defineConfig({\n * output: { path: 'src/gen', 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 install(hooks) {\n let ctx: KubbBuildStartContext\n const excludedPaths = new Set<string>()\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 const barrelType = plugin.options.output?.barrelType ?? ctx.config.output.barrelType ?? 'all'\n\n if (!barrelType) {\n excludedPaths.add(resolve(ctx.config.root, ctx.config.output.path, plugin.options.output.path))\n return\n }\n\n const barrelFiles = generatePerPluginBarrel({\n barrelType,\n plugin,\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:plugins:end', ({ files, config, upsertFile }) => {\n const rootBarrelType = config.output.barrelType ?? 'named'\n if (!rootBarrelType) return\n\n const filteredFiles = excludedPaths.size > 0 ? files.filter((f) => ![...excludedPaths].some((excluded) => f.path.startsWith(excluded + '/'))) : files\n\n const rootBarrelFiles = generateRootBarrel({\n barrelType: rootBarrelType,\n files: filteredFiles,\n config,\n })\n\n if (rootBarrelFiles.length > 0) {\n upsertFile(...rootBarrelFiles)\n }\n })\n },\n})\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/middleware-barrel",
3
- "version": "5.0.0-alpha.61",
3
+ "version": "5.0.0-alpha.63",
4
4
  "description": "Barrel-file generation middleware for Kubb. Generates index.ts barrel files for each plugin's output directory and a root index.ts after all plugins have run.",
5
5
  "keywords": [
6
6
  "barrel",
@@ -44,7 +44,7 @@
44
44
  "registry": "https://registry.npmjs.org/"
45
45
  },
46
46
  "peerDependencies": {
47
- "@kubb/core": "5.0.0-alpha.61"
47
+ "@kubb/core": "5.0.0-alpha.63"
48
48
  },
49
49
  "engines": {
50
50
  "node": ">=22"
package/src/constants.ts CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * Full file name for barrel files (with extension).
4
3
  */
package/src/middleware.ts CHANGED
@@ -1,24 +1,20 @@
1
1
  import { defineMiddleware } from '@kubb/core'
2
2
  import type { KubbBuildStartContext } from '@kubb/core'
3
- import './types.ts'
4
3
  import type { BarrelType } from './types.ts'
5
4
  import { generatePerPluginBarrel } from './utils/generatePerPluginBarrel.ts'
6
5
  import { generateRootBarrel } from './utils/generateRootBarrel.ts'
7
-
6
+ import { resolve } from 'node:path'
8
7
 
9
8
  declare global {
10
9
  namespace Kubb {
11
10
  interface PluginOptionsRegistry {
12
11
  output: {
13
12
  /**
14
- * Controls which barrel file (index.ts) is generated for this plugin's output directory.
15
- *
16
- * - `'all'` — `export * from '...'` for every generated file.
17
- * - `'named'` — `export { … } from '...'` using the file's named exports.
18
- * - `'propagate'` — like `'all'` but also generates intermediate barrel files.
19
- * - `false` — disable barrel generation for this plugin.
13
+ * Re-export style for this plugin's barrel file.
14
+ * Set to `false` to disable barrel generation for this plugin entirely; doing so also
15
+ * excludes the plugin's files from the root barrel.
20
16
  *
21
- * When omitted, the root `config.output.barrelType` is used as the default.
17
+ * Falls back to `config.output.barrelType` when omitted.
22
18
  */
23
19
  barrelType?: BarrelType | false
24
20
  }
@@ -26,14 +22,11 @@ declare global {
26
22
  interface ConfigOptionsRegistry {
27
23
  output: {
28
24
  /**
29
- * Controls the root barrel file (index.ts) generated at `config.output.path`.
30
- *
31
- * - `'all'` — `export * from '...'` for every plugin's barrel.
32
- * - `'named'` — `export { … } from '...'` using the barrel's named exports.
33
- * - `'propagate'` — like `'all'` but also generates intermediate barrel files.
34
- * - `false` — disable root barrel generation.
25
+ * Re-export style for the root barrel file at `config.output.path/index.ts`.
26
+ * Set to `false` to disable root barrel generation. Individual plugins can override
27
+ * this via their own `output.barrelType`.
35
28
  *
36
- * Individual plugins can override this via their own `output.barrelType`.
29
+ * @default 'named'
37
30
  */
38
31
  barrelType?: BarrelType | false
39
32
  }
@@ -42,20 +35,16 @@ declare global {
42
35
  }
43
36
 
44
37
  /**
45
- * Barrel-file generation middleware.
38
+ * Generates `index.ts` barrel files for each plugin's output directory and one root barrel
39
+ * at `config.output.path/index.ts`.
46
40
  *
47
- * When added to `config.middleware`, generates an `index.ts` barrel file for each
48
- * plugin. When `output.barrelType` is omitted on a plugin it inherits from `config.output.barrelType`,
49
- * and when that is also omitted both default to `'all'`. Set `barrelType: false` to disable
50
- * barrel generation for a specific plugin or for the root entirely.
51
- *
52
- * The `barrelType` option controls the re-export style:
53
- * - `'all'` — `export * from '...'` for each generated file
54
- * - `'named'` — `export { name1, name2 } from '...'` using each file's indexable sources
55
- * - `'propagate'` — like `'all'` with intermediate barrel files for sub-directories
41
+ * Plugins inherit `output.barrelType` from `config.output.barrelType` (which itself defaults to `'named'`).
42
+ * Setting `barrelType: false` on a plugin disables its barrel and excludes the plugin's files from the
43
+ * root barrel as well.
56
44
  *
57
45
  * @example
58
46
  * ```ts
47
+ * import { defineConfig } from '@kubb/core'
59
48
  * import { middlewareBarrel } from '@kubb/middleware-barrel'
60
49
  *
61
50
  * export default defineConfig({
@@ -72,9 +61,7 @@ export const middlewareBarrel = defineMiddleware({
72
61
  name: 'middleware-barrel',
73
62
  install(hooks) {
74
63
  let ctx: KubbBuildStartContext
75
- // Track output paths of plugins with barrelType: false so the root barrel
76
- // can exclude their files.
77
- const excludedOutputPaths: string[] = []
64
+ const excludedPaths = new Set<string>()
78
65
 
79
66
  hooks.on('kubb:build:start', (buildCtx) => {
80
67
  ctx = buildCtx
@@ -83,13 +70,10 @@ export const middlewareBarrel = defineMiddleware({
83
70
  hooks.on('kubb:plugin:end', ({ plugin }) => {
84
71
  if (!ctx) return
85
72
 
86
- const pluginOutput = plugin.options.output
87
- const barrelType = pluginOutput?.barrelType !== undefined ? pluginOutput.barrelType : (ctx.config.output.barrelType ?? 'all')
73
+ const barrelType = plugin.options.output?.barrelType ?? ctx.config.output.barrelType ?? 'all'
88
74
 
89
75
  if (!barrelType) {
90
- // Remember this plugin's output path so we can exclude it from the root barrel
91
- const outputPath = plugin.options.output.path
92
- excludedOutputPaths.push(outputPath)
76
+ excludedPaths.add(resolve(ctx.config.root, ctx.config.output.path, plugin.options.output.path))
93
77
  return
94
78
  }
95
79
 
@@ -106,18 +90,10 @@ export const middlewareBarrel = defineMiddleware({
106
90
  })
107
91
 
108
92
  hooks.on('kubb:plugins:end', ({ files, config, upsertFile }) => {
109
- const rootBarrelType = config.output.barrelType ?? 'all'
110
-
93
+ const rootBarrelType = config.output.barrelType ?? 'named'
111
94
  if (!rootBarrelType) return
112
95
 
113
- // Exclude files that belong to plugins with barrelType: false
114
- const filteredFiles =
115
- excludedOutputPaths.length > 0
116
- ? files.filter((f) => {
117
- const normalizedPath = f.path.replace(/\\/g, '/')
118
- return !excludedOutputPaths.some((excluded) => normalizedPath.startsWith(excluded.replace(/\\/g, '/') + '/'))
119
- })
120
- : files
96
+ const filteredFiles = excludedPaths.size > 0 ? files.filter((f) => ![...excludedPaths].some((excluded) => f.path.startsWith(excluded + '/'))) : files
121
97
 
122
98
  const rootBarrelFiles = generateRootBarrel({
123
99
  barrelType: rootBarrelType,
package/src/types.ts CHANGED
@@ -1,10 +1,8 @@
1
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.
2
+ * Re-export style used when generating barrel `index.ts` files.
3
+ * - `'all'` emits `export * from '...'` for every file.
4
+ * - `'named'` emits `export { name1, name2 } from '...'` from each file's indexable sources.
5
+ * - `'propagate'` behaves like `'all'` and also generates an intermediate barrel for every
6
+ * sub-directory so consumers can import from any depth.
8
7
  */
9
8
  export type BarrelType = 'all' | 'named' | 'propagate'
10
-
@@ -1,37 +1,33 @@
1
1
  import { posix } from 'node:path'
2
2
 
3
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.
4
+ * A node in the directory tree used to compute barrel file exports.
5
+ * Either represents a directory (with `children`) or a file (`isFile: true`, empty `children`).
8
6
  */
9
7
  export type BuildTree = {
10
8
  /**
11
- * Absolute path of the directory (root of this subtree) or file.
9
+ * Absolute filesystem path of this directory or file.
12
10
  */
13
11
  path: string
14
12
  /**
15
- * Child nodes (sub-directories and files) within this directory.
13
+ * Sub-directories and files contained within this directory.
14
+ * Always empty for file nodes.
16
15
  */
17
16
  children: Array<BuildTree>
18
17
  /**
19
- * `true` when this node represents a file (leaf node).
18
+ * `true` when this node represents a file (leaf), `false` for directory nodes.
20
19
  */
21
20
  isFile: boolean
22
21
  }
23
22
 
24
23
  /**
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.
24
+ * Builds a directory tree rooted at `rootPath` from a list of absolute file paths.
25
+ * Paths outside `rootPath` are silently ignored.
29
26
  *
30
27
  * @example
31
28
  * ```ts
32
- * const tree = buildTree('/src/gen/types', [
29
+ * buildTree('/src/gen/types', [
33
30
  * '/src/gen/types/pet.ts',
34
- * '/src/gen/types/user.ts',
35
31
  * '/src/gen/types/pets/listPets.ts',
36
32
  * ])
37
33
  * ```