@kubb/middleware-barrel 5.0.0-alpha.56 → 5.0.0-alpha.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -5,10 +5,6 @@ let node_path = require("node:path");
5
5
  let _kubb_ast = require("@kubb/ast");
6
6
  //#region src/constants.ts
7
7
  /**
8
- * Base file name for barrel files (without extension).
9
- */
10
- const BARREL_BASENAME = "index";
11
- /**
12
8
  * Full file name for barrel files (with extension).
13
9
  */
14
10
  const BARREL_FILENAME = "index.ts";
@@ -59,16 +55,23 @@ function buildTree(rootPath, filePaths) {
59
55
  }
60
56
  //#endregion
61
57
  //#region src/utils/getBarrelFiles.ts
58
+ const SOURCE_EXTENSIONS = new Set([
59
+ ".ts",
60
+ ".tsx",
61
+ ".js",
62
+ ".jsx"
63
+ ]);
62
64
  /**
63
- * Derives a relative module specifier (no extension) from an absolute `filePath`
64
- * relative to an absolute `fromDir`.
65
+ * Derives a relative module specifier from an absolute `filePath` relative to an absolute `fromDir`.
66
+ * The source extension is preserved so that `@kubb/parser-ts` can apply the `extNames` mapping
67
+ * (e.g. `.ts` → `.js` for ESM output).
65
68
  *
66
69
  * @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'
70
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet.ts'
71
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag.ts'
69
72
  */
70
73
  function toRelativeModulePath(fromDir, filePath) {
71
- return `./${filePath.slice(fromDir.length).replace(/^[/\\]/g, "").replace(/\.[^/.]+$/, "")}`;
74
+ return `./${filePath.slice(fromDir.length).replace(/^[/\\]/g, "")}`;
72
75
  }
73
76
  /**
74
77
  * Generates barrel `FileNode[]` for a given directory tree node using the `'all'` strategy:
@@ -77,13 +80,21 @@ function toRelativeModulePath(fromDir, filePath) {
77
80
  * Only a single barrel file (at `treeNode.path`) is generated — sub-directory files are referenced
78
81
  * with their full relative path from `treeNode.path`.
79
82
  */
80
- function getBarrelFilesAll(treeNode, _sourceFiles) {
83
+ function getBarrelFilesAll(treeNode, sourceFiles) {
81
84
  const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`));
82
85
  if (leafPaths.length === 0) return [];
86
+ const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`;
87
+ const exports = [];
88
+ for (const filePath of leafPaths) {
89
+ const sourceFile = sourceFiles.find((f) => f.path === filePath);
90
+ if (sourceFile && sourceFile.sources.length > 0 && sourceFile.sources.every((s) => !s.isIndexable)) continue;
91
+ exports.push((0, _kubb_ast.createExport)({ path: toRelativeModulePath(treeNode.path, filePath) }));
92
+ }
93
+ if (exports.length === 0) return [];
83
94
  return [(0, _kubb_ast.createFile)({
84
95
  baseName: BARREL_FILENAME,
85
- path: `${treeNode.path}/${BARREL_FILENAME}`,
86
- exports: leafPaths.map((filePath) => (0, _kubb_ast.createExport)({ path: toRelativeModulePath(treeNode.path, filePath) })),
96
+ path: barrelPath,
97
+ exports,
87
98
  sources: [],
88
99
  imports: []
89
100
  })];
@@ -105,6 +116,7 @@ function getBarrelFilesNamed(treeNode, sourceFiles) {
105
116
  }
106
117
  const indexableSources = sourceFile.sources.filter((s) => s.isIndexable && s.name);
107
118
  if (indexableSources.length === 0) {
119
+ if (sourceFile.sources.length > 0) continue;
108
120
  exports.push((0, _kubb_ast.createExport)({ path: toRelativeModulePath(treeNode.path, filePath) }));
109
121
  continue;
110
122
  }
@@ -141,7 +153,7 @@ function collectPropagatedBarrels(node) {
141
153
  } else {
142
154
  const subBarrels = collectPropagatedBarrels(child);
143
155
  result.push(...subBarrels);
144
- const subBarrelPath = `${child.path}/${BARREL_BASENAME}`;
156
+ const subBarrelPath = `${child.path}/${BARREL_FILENAME}`;
145
157
  barrelExports.push((0, _kubb_ast.createExport)({ path: toRelativeModulePath(node.path, subBarrelPath) }));
146
158
  }
147
159
  if (barrelExports.length > 0) result.push((0, _kubb_ast.createFile)({
@@ -174,7 +186,11 @@ function getBarrelFiles(outputPath, files, barrelType) {
174
186
  const relevantFiles = files.filter((f) => {
175
187
  const normalizedFilePath = f.path.replace(/\\/g, "/");
176
188
  const normalizedOutputPath = outputPath.replace(/\\/g, "/");
177
- return normalizedFilePath.startsWith(normalizedOutputPath + "/") && !normalizedFilePath.endsWith(`/index.ts`);
189
+ if (!normalizedFilePath.startsWith(normalizedOutputPath + "/")) return false;
190
+ if (normalizedFilePath.endsWith(`/index.ts`)) return false;
191
+ const dotIndex = normalizedFilePath.lastIndexOf(".");
192
+ const ext = dotIndex === -1 ? "" : normalizedFilePath.slice(dotIndex);
193
+ return SOURCE_EXTENSIONS.has(ext);
178
194
  });
179
195
  if (relevantFiles.length === 0) return [];
180
196
  const tree = buildTree(outputPath, relevantFiles.map((f) => f.path));
@@ -1 +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(\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 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:plugins:end', ({ files, config, upsertFile }) => {\n const rootOutput = 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,\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.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_FILENAME } from '../constants.ts'\nimport type { BarrelType } from '../types.ts'\nimport { buildTree, type TreeNode } from './TreeNode.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: 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: 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: 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 // 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 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_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: 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 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 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:plugins:end', ({ files, config, upsertFile }) => {\n const rootOutput = 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,\n config,\n })\n\n if (rootBarrelFiles.length > 0) {\n upsertFile(...rootBarrelFiles)\n }\n })\n },\n})\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -4,10 +4,6 @@ import { posix, resolve } from "node:path";
4
4
  import { createExport, createFile } from "@kubb/ast";
5
5
  //#region src/constants.ts
6
6
  /**
7
- * Base file name for barrel files (without extension).
8
- */
9
- const BARREL_BASENAME = "index";
10
- /**
11
7
  * Full file name for barrel files (with extension).
12
8
  */
13
9
  const BARREL_FILENAME = "index.ts";
@@ -58,16 +54,23 @@ function buildTree(rootPath, filePaths) {
58
54
  }
59
55
  //#endregion
60
56
  //#region src/utils/getBarrelFiles.ts
57
+ const SOURCE_EXTENSIONS = new Set([
58
+ ".ts",
59
+ ".tsx",
60
+ ".js",
61
+ ".jsx"
62
+ ]);
61
63
  /**
62
- * Derives a relative module specifier (no extension) from an absolute `filePath`
63
- * relative to an absolute `fromDir`.
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).
64
67
  *
65
68
  * @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'
69
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet.ts'
70
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag.ts'
68
71
  */
69
72
  function toRelativeModulePath(fromDir, filePath) {
70
- return `./${filePath.slice(fromDir.length).replace(/^[/\\]/g, "").replace(/\.[^/.]+$/, "")}`;
73
+ return `./${filePath.slice(fromDir.length).replace(/^[/\\]/g, "")}`;
71
74
  }
72
75
  /**
73
76
  * Generates barrel `FileNode[]` for a given directory tree node using the `'all'` strategy:
@@ -76,13 +79,21 @@ function toRelativeModulePath(fromDir, filePath) {
76
79
  * Only a single barrel file (at `treeNode.path`) is generated — sub-directory files are referenced
77
80
  * with their full relative path from `treeNode.path`.
78
81
  */
79
- function getBarrelFilesAll(treeNode, _sourceFiles) {
82
+ function getBarrelFilesAll(treeNode, sourceFiles) {
80
83
  const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`));
81
84
  if (leafPaths.length === 0) return [];
85
+ const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`;
86
+ const exports = [];
87
+ for (const filePath of leafPaths) {
88
+ const sourceFile = sourceFiles.find((f) => f.path === filePath);
89
+ if (sourceFile && sourceFile.sources.length > 0 && sourceFile.sources.every((s) => !s.isIndexable)) continue;
90
+ exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }));
91
+ }
92
+ if (exports.length === 0) return [];
82
93
  return [createFile({
83
94
  baseName: BARREL_FILENAME,
84
- path: `${treeNode.path}/${BARREL_FILENAME}`,
85
- exports: leafPaths.map((filePath) => createExport({ path: toRelativeModulePath(treeNode.path, filePath) })),
95
+ path: barrelPath,
96
+ exports,
86
97
  sources: [],
87
98
  imports: []
88
99
  })];
@@ -104,6 +115,7 @@ function getBarrelFilesNamed(treeNode, sourceFiles) {
104
115
  }
105
116
  const indexableSources = sourceFile.sources.filter((s) => s.isIndexable && s.name);
106
117
  if (indexableSources.length === 0) {
118
+ if (sourceFile.sources.length > 0) continue;
107
119
  exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }));
108
120
  continue;
109
121
  }
@@ -140,7 +152,7 @@ function collectPropagatedBarrels(node) {
140
152
  } else {
141
153
  const subBarrels = collectPropagatedBarrels(child);
142
154
  result.push(...subBarrels);
143
- const subBarrelPath = `${child.path}/${BARREL_BASENAME}`;
155
+ const subBarrelPath = `${child.path}/${BARREL_FILENAME}`;
144
156
  barrelExports.push(createExport({ path: toRelativeModulePath(node.path, subBarrelPath) }));
145
157
  }
146
158
  if (barrelExports.length > 0) result.push(createFile({
@@ -173,7 +185,11 @@ function getBarrelFiles(outputPath, files, barrelType) {
173
185
  const relevantFiles = files.filter((f) => {
174
186
  const normalizedFilePath = f.path.replace(/\\/g, "/");
175
187
  const normalizedOutputPath = outputPath.replace(/\\/g, "/");
176
- return normalizedFilePath.startsWith(normalizedOutputPath + "/") && !normalizedFilePath.endsWith(`/index.ts`);
188
+ if (!normalizedFilePath.startsWith(normalizedOutputPath + "/")) return false;
189
+ if (normalizedFilePath.endsWith(`/index.ts`)) return false;
190
+ const dotIndex = normalizedFilePath.lastIndexOf(".");
191
+ const ext = dotIndex === -1 ? "" : normalizedFilePath.slice(dotIndex);
192
+ return SOURCE_EXTENSIONS.has(ext);
177
193
  });
178
194
  if (relevantFiles.length === 0) return [];
179
195
  const tree = buildTree(outputPath, relevantFiles.map((f) => f.path));
package/dist/index.js.map CHANGED
@@ -1 +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(\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 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:plugins:end', ({ files, config, upsertFile }) => {\n const rootOutput = 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,\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/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_FILENAME } from '../constants.ts'\nimport type { BarrelType } from '../types.ts'\nimport { buildTree, type TreeNode } from './TreeNode.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: 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: 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: 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 // 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 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_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: 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 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 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:plugins:end', ({ files, config, upsertFile }) => {\n const rootOutput = 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,\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.56",
3
+ "version": "5.0.0-alpha.58",
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.56"
47
+ "@kubb/core": "5.0.0-alpha.58"
48
48
  },
49
49
  "engines": {
50
50
  "node": ">=22"
@@ -1,22 +1,23 @@
1
1
  import { createExport, createFile } from '@kubb/ast'
2
2
  import type { FileNode } from '@kubb/ast'
3
- import { BARREL_BASENAME, BARREL_FILENAME } from '../constants.ts'
3
+ import { BARREL_FILENAME } from '../constants.ts'
4
4
  import type { BarrelType } from '../types.ts'
5
5
  import { buildTree, type TreeNode } from './TreeNode.ts'
6
6
 
7
+ const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])
8
+
7
9
  /**
8
- * Derives a relative module specifier (no extension) from an absolute `filePath`
9
- * relative to an absolute `fromDir`.
10
+ * Derives a relative module specifier from an absolute `filePath` relative to an absolute `fromDir`.
11
+ * The source extension is preserved so that `@kubb/parser-ts` can apply the `extNames` mapping
12
+ * (e.g. `.ts` → `.js` for ESM output).
10
13
  *
11
14
  * @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'
15
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/pet.ts') // './pet.ts'
16
+ * toRelativeModulePath('/src/gen/types', '/src/gen/types/tags/tag.ts') // './tags/tag.ts'
14
17
  */
15
18
  function toRelativeModulePath(fromDir: string, filePath: string): string {
16
19
  const relative = filePath.slice(fromDir.length).replace(/^[/\\]/g, '')
17
- // Strip extension
18
- const withoutExt = relative.replace(/\.[^/.]+$/, '')
19
- return `./${withoutExt}`
20
+ return `./${relative}`
20
21
  }
21
22
 
22
23
  /**
@@ -26,18 +27,25 @@ function toRelativeModulePath(fromDir: string, filePath: string): string {
26
27
  * Only a single barrel file (at `treeNode.path`) is generated — sub-directory files are referenced
27
28
  * with their full relative path from `treeNode.path`.
28
29
  */
29
- function getBarrelFilesAll(treeNode: TreeNode, _sourceFiles: ReadonlyArray<FileNode>): Array<FileNode> {
30
+ function getBarrelFilesAll(treeNode: TreeNode, sourceFiles: ReadonlyArray<FileNode>): Array<FileNode> {
30
31
  // Collect all source file paths under this node (excluding barrel files themselves)
31
32
  const leafPaths = collectLeafPaths(treeNode).filter((p) => !p.endsWith(`/${BARREL_FILENAME}`))
32
33
 
33
34
  if (leafPaths.length === 0) return []
34
35
 
35
36
  const barrelPath = `${treeNode.path}/${BARREL_FILENAME}`
36
- const exports = leafPaths.map((filePath) =>
37
- createExport({
38
- path: toRelativeModulePath(treeNode.path, filePath),
39
- }),
40
- )
37
+ const exports: ReturnType<typeof createExport>[] = []
38
+
39
+ for (const filePath of leafPaths) {
40
+ const sourceFile = sourceFiles.find((f) => f.path === filePath)
41
+ // Skip files whose sources all have isIndexable: false (e.g. internal injected files)
42
+ if (sourceFile && sourceFile.sources.length > 0 && sourceFile.sources.every((s) => !s.isIndexable)) {
43
+ continue
44
+ }
45
+ exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))
46
+ }
47
+
48
+ if (exports.length === 0) return []
41
49
 
42
50
  return [
43
51
  createFile({
@@ -72,7 +80,9 @@ function getBarrelFilesNamed(treeNode: TreeNode, sourceFiles: ReadonlyArray<File
72
80
 
73
81
  const indexableSources = sourceFile.sources.filter((s) => s.isIndexable && s.name)
74
82
  if (indexableSources.length === 0) {
75
- // No named exports: fall back to wildcard
83
+ // If the file has explicit sources but none are indexable, skip it entirely.
84
+ // Only fall back to wildcard when there are no sources at all (unknown exports).
85
+ if (sourceFile.sources.length > 0) continue
76
86
  exports.push(createExport({ path: toRelativeModulePath(treeNode.path, filePath) }))
77
87
  continue
78
88
  }
@@ -125,7 +135,7 @@ function collectPropagatedBarrels(node: TreeNode): Array<FileNode> {
125
135
  result.push(...subBarrels)
126
136
 
127
137
  // Export the sub-directory's barrel (not individual files)
128
- const subBarrelPath = `${child.path}/${BARREL_BASENAME}`
138
+ const subBarrelPath = `${child.path}/${BARREL_FILENAME}`
129
139
  barrelExports.push(createExport({ path: toRelativeModulePath(node.path, subBarrelPath) }))
130
140
  }
131
141
  }
@@ -164,11 +174,15 @@ function collectLeafPaths(node: TreeNode): Array<string> {
164
174
  * @param barrelType Barrel generation strategy.
165
175
  */
166
176
  export function getBarrelFiles(outputPath: string, files: ReadonlyArray<FileNode>, barrelType: BarrelType): Array<FileNode> {
167
- // Only include files that live inside this outputPath
177
+ // Only include files that live inside this outputPath and have a recognised source extension
168
178
  const relevantFiles = files.filter((f) => {
169
179
  const normalizedFilePath = f.path.replace(/\\/g, '/')
170
180
  const normalizedOutputPath = outputPath.replace(/\\/g, '/')
171
- return normalizedFilePath.startsWith(normalizedOutputPath + '/') && !normalizedFilePath.endsWith(`/${BARREL_FILENAME}`)
181
+ if (!normalizedFilePath.startsWith(normalizedOutputPath + '/')) return false
182
+ if (normalizedFilePath.endsWith(`/${BARREL_FILENAME}`)) return false
183
+ const dotIndex = normalizedFilePath.lastIndexOf('.')
184
+ const ext = dotIndex === -1 ? '' : normalizedFilePath.slice(dotIndex)
185
+ return SOURCE_EXTENSIONS.has(ext)
172
186
  })
173
187
 
174
188
  if (relevantFiles.length === 0) return []