@kubb/plugin-barrel 5.0.0-beta.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stijn Van Hulle
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ <div align="center">
2
+ <a href="https://kubb.dev" target="_blank" rel="noopener noreferrer">
3
+ <img src="https://kubb.dev/og.png" alt="Kubb banner">
4
+ </a>
5
+
6
+ [![npm version][npm-version-src]][npm-version-href]
7
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
8
+ [![Stars][stars-src]][stars-href]
9
+ [![License][license-src]][license-href]
10
+ [![Node][node-src]][node-href]
11
+
12
+ <h4>
13
+ <a href="https://kubb.dev" target="_blank">Documentation</a>
14
+ <span> · </span>
15
+ <a href="https://github.com/kubb-labs/kubb/issues/" target="_blank">Report Bug</a>
16
+ <span> · </span>
17
+ <a href="https://github.com/kubb-labs/kubb/issues/" target="_blank">Request Feature</a>
18
+ </h4>
19
+ </div>
20
+
21
+ <br />
22
+
23
+ # @kubb/plugin-barrel
24
+
25
+ ### Barrel-file plugin for Kubb
26
+
27
+ Automatically generates `index.ts` re-export files for each plugin output directory.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ bun add @kubb/plugin-barrel
33
+ # or
34
+ pnpm add @kubb/plugin-barrel
35
+ # or
36
+ npm install @kubb/plugin-barrel
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ Add `pluginBarrel` to the `plugins` array in your `kubb.config.ts`:
42
+
43
+ ```typescript
44
+ import { defineConfig } from 'kubb'
45
+ import { pluginBarrel } from '@kubb/plugin-barrel'
46
+
47
+ export default defineConfig({
48
+ input: {
49
+ path: './openapi.yaml',
50
+ },
51
+ output: {
52
+ path: './src/gen',
53
+ },
54
+ plugins: [
55
+ pluginBarrel({
56
+ type: 'named',
57
+ }),
58
+ ],
59
+ })
60
+ ```
61
+
62
+ ## Options
63
+
64
+ | Option | Type | Default | Description |
65
+ | ------ | --------------------------------- | --------- | ------------------------------------------- |
66
+ | `type` | `'all' \| 'named' \| 'propagate'` | `'named'` | Export style for the generated barrel files |
67
+
68
+ ### Export types
69
+
70
+ | Value | Output |
71
+ | ------------- | ------------------------------------------------------------------------------- |
72
+ | `'all'` | `export * from './...'` — wildcard re-exports |
73
+ | `'named'` | `export { ... } from './...'` — named re-exports, tree-shaking friendly |
74
+ | `'propagate'` | `export * from './...'` on index files only, propagating up through directories |
75
+
76
+ ## How it works
77
+
78
+ After every plugin finishes generating files, `@kubb/plugin-barrel` walks the output tree and creates an `index.ts` in each directory, re-exporting everything inside. It then creates a root `index.ts` at the top of the output path that re-exports from all plugin directories.
79
+
80
+ `@kubb/plugin-barrel` uses `enforce: 'post'` so it always runs after all regular plugins complete, giving it access to the full set of generated files.
81
+
82
+ ## Supporting Kubb
83
+
84
+ Kubb is an open source project, and its development is funded entirely by sponsors. If you would like to become a sponsor, please consider:
85
+
86
+ - [Become a Sponsor on GitHub](https://github.com/sponsors/stijnvanhulle)
87
+ - [See sponsorship tiers and our sponsors](https://kubb.dev/sponsors)
88
+
89
+ <p align="center">
90
+ <a href="https://github.com/sponsors/stijnvanhulle">
91
+ <img src="https://raw.githubusercontent.com/stijnvanhulle/sponsors/main/sponsors.svg" alt="My sponsors" />
92
+ </a>
93
+ </p>
94
+
95
+ ## License
96
+
97
+ [MIT](https://github.com/kubb-labs/kubb/blob/main/licenses/LICENSE-MIT)
98
+
99
+ <!-- Badges -->
100
+
101
+ [npm-version-src]: https://shieldcn.dev/npm/v/@kubb/plugin-barrel.svg?variant=secondary&size=xs&theme=zinc&mode=dark
102
+ [npm-version-href]: https://npmx.dev/package/@kubb/plugin-barrel
103
+ [npm-downloads-src]: https://shieldcn.dev/npm/dm/@kubb/plugin-barrel.svg?variant=secondary&size=xs&theme=zinc&mode=dark
104
+ [npm-downloads-href]: https://npmx.dev/package/@kubb/plugin-barrel
105
+ [stars-src]: https://shieldcn.dev/github/stars/kubb-labs/kubb.svg?variant=secondary&size=xs&theme=zinc&mode=dark
106
+ [stars-href]: https://github.com/kubb-labs/kubb
107
+ [license-src]: https://shieldcn.dev/npm/license/@kubb/plugin-barrel.svg?variant=secondary&size=xs&theme=zinc
108
+ [license-href]: https://github.com/kubb-labs/kubb/blob/main/LICENSE
109
+ [node-src]: https://shieldcn.dev/npm/node/@kubb/plugin-barrel.svg?variant=secondary&size=xs&theme=zinc&mode=dark
110
+ [node-href]: https://npmx.dev/package/@kubb/plugin-barrel
package/dist/index.cjs ADDED
@@ -0,0 +1,423 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let node_path = require("node:path");
25
+ node_path = __toESM(node_path, 1);
26
+ let _kubb_core = require("@kubb/core");
27
+ let _kubb_ast = require("@kubb/ast");
28
+ //#region ../../internals/utils/src/fs.ts
29
+ /**
30
+ * Converts a filesystem path to use POSIX (`/`) separators.
31
+ *
32
+ * Most of the codebase compares and composes paths as strings (prefix matching, joining for
33
+ * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated
34
+ * paths, but on Windows it returns `\`-separated paths, which breaks every such comparison.
35
+ *
36
+ * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the
37
+ * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is
38
+ * exercisable from POSIX CI.
39
+ *
40
+ * @example
41
+ * toPosixPath('C:\\repo\\src\\pet.ts') // 'C:/repo/src/pet.ts'
42
+ */
43
+ function toPosixPath(filePath) {
44
+ return filePath.replaceAll("\\", "/");
45
+ }
46
+ //#endregion
47
+ //#region src/utils.ts
48
+ const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
49
+ ".ts",
50
+ ".tsx",
51
+ ".js",
52
+ ".jsx"
53
+ ]);
54
+ const BARREL_SUFFIX = `/index.ts`;
55
+ /**
56
+ * Builds a directory tree rooted at `rootPath` from a list of absolute file paths.
57
+ * Paths outside `rootPath` are silently ignored. Children are sorted alphabetically
58
+ * by path so consumers (barrel exports, propagated indexes) emit a deterministic order.
59
+ *
60
+ * Both POSIX (`/`) and Windows (`\`) separators are accepted in input paths; emitted node
61
+ * paths are always POSIX-normalized so downstream prefix/lookup operations behave the same
62
+ * across platforms.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * buildTree('/src/gen/types', [
67
+ * '/src/gen/types/pet.ts',
68
+ * '/src/gen/types/pets/listPets.ts',
69
+ * ])
70
+ * ```
71
+ */
72
+ function buildTree(rootPath, filePaths) {
73
+ const normalizedRoot = toPosixPath(rootPath);
74
+ const root = {
75
+ path: normalizedRoot,
76
+ children: [],
77
+ isFile: false
78
+ };
79
+ const childIndex = /* @__PURE__ */ new WeakMap();
80
+ childIndex.set(root, /* @__PURE__ */ new Map());
81
+ const rootPrefix = `${normalizedRoot}/`;
82
+ for (const filePath of filePaths) {
83
+ const normalized = toPosixPath(filePath);
84
+ if (!normalized.startsWith(rootPrefix)) continue;
85
+ const parts = normalized.slice(rootPrefix.length).split("/");
86
+ if (parts.length === 0) continue;
87
+ let current = root;
88
+ const lastIndex = parts.length - 1;
89
+ for (const [i, part] of parts.entries()) {
90
+ if (!part) continue;
91
+ const isLast = i === lastIndex;
92
+ const siblings = childIndex.get(current);
93
+ let child = siblings.get(part);
94
+ if (!child) {
95
+ child = {
96
+ path: `${current.path}/${part}`,
97
+ children: [],
98
+ isFile: isLast
99
+ };
100
+ current.children.push(child);
101
+ siblings.set(part, child);
102
+ if (!isLast) childIndex.set(child, /* @__PURE__ */ new Map());
103
+ }
104
+ current = child;
105
+ }
106
+ }
107
+ sortTree(root);
108
+ return root;
109
+ }
110
+ function sortTree(node) {
111
+ if (node.children.length === 0) return;
112
+ node.children.sort(compareByPath);
113
+ for (const child of node.children) if (!child.isFile) sortTree(child);
114
+ }
115
+ function compareByPath(a, b) {
116
+ return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
117
+ }
118
+ function toRelativeModulePath(fromDir, filePath) {
119
+ return `./${filePath.slice(fromDir.length + 1)}`;
120
+ }
121
+ function isBarrelPath(path) {
122
+ return path.endsWith(BARREL_SUFFIX);
123
+ }
124
+ function makeBarrel(dirPath, exports) {
125
+ return _kubb_ast.ast.factory.createFile({
126
+ baseName: "index.ts",
127
+ path: `${dirPath}${BARREL_SUFFIX}`,
128
+ exports,
129
+ sources: [],
130
+ imports: [],
131
+ banner: void 0,
132
+ footer: void 0
133
+ });
134
+ }
135
+ function hasOnlyNonIndexableSources(sources) {
136
+ if (sources.length === 0) return false;
137
+ for (const source of sources) if (source.isIndexable) return false;
138
+ return true;
139
+ }
140
+ function partitionIndexableNames(sources) {
141
+ const byTypeOnly = /* @__PURE__ */ new Map([[false, /* @__PURE__ */ new Set()], [true, /* @__PURE__ */ new Set()]]);
142
+ for (const source of sources) {
143
+ if (!source.isIndexable || !source.name) continue;
144
+ byTypeOnly.get(Boolean(source.isTypeOnly)).add(source.name);
145
+ }
146
+ return byTypeOnly;
147
+ }
148
+ const allStrategy = ({ dirPath, leafPath, sourceFile }) => {
149
+ if (sourceFile && hasOnlyNonIndexableSources(sourceFile.sources)) return [];
150
+ return [_kubb_ast.ast.factory.createExport({ path: toRelativeModulePath(dirPath, leafPath) })];
151
+ };
152
+ const namedStrategy = ({ dirPath, leafPath, sourceFile }) => {
153
+ const modulePath = toRelativeModulePath(dirPath, leafPath);
154
+ if (!sourceFile) return [_kubb_ast.ast.factory.createExport({ path: modulePath })];
155
+ const namesByTypeOnly = partitionIndexableNames(sourceFile.sources);
156
+ const valueNames = namesByTypeOnly.get(false);
157
+ const typeNames = namesByTypeOnly.get(true);
158
+ if (valueNames.size === 0 && typeNames.size === 0) {
159
+ if (sourceFile.sources.length > 0) return [];
160
+ return [_kubb_ast.ast.factory.createExport({ path: modulePath })];
161
+ }
162
+ const exports = [];
163
+ if (valueNames.size > 0) exports.push(_kubb_ast.ast.factory.createExport({
164
+ name: [...valueNames].sort(),
165
+ path: modulePath
166
+ }));
167
+ if (typeNames.size > 0) exports.push(_kubb_ast.ast.factory.createExport({
168
+ name: [...typeNames].sort(),
169
+ path: modulePath,
170
+ isTypeOnly: true
171
+ }));
172
+ return exports;
173
+ };
174
+ const LEAF_STRATEGIES = /* @__PURE__ */ new Map([["all", allStrategy], ["named", namedStrategy]]);
175
+ /**
176
+ * Post-order walk that yields a barrel per visited directory.
177
+ * Returns the list of leaf file paths collected in this subtree (used by the parent call).
178
+ */
179
+ function* walkAllOrNamed(node, params, isRoot) {
180
+ const subtreeLeaves = [];
181
+ for (const child of node.children) {
182
+ if (child.isFile) {
183
+ if (!isBarrelPath(child.path)) subtreeLeaves.push(child.path);
184
+ continue;
185
+ }
186
+ const childLeaves = yield* walkAllOrNamed(child, params, false);
187
+ for (const leaf of childLeaves) subtreeLeaves.push(leaf);
188
+ }
189
+ if (!isRoot && !params.recursive) return subtreeLeaves;
190
+ const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({
191
+ dirPath: node.path,
192
+ leafPath,
193
+ sourceFile: params.sourceFiles.get(leafPath) ?? null
194
+ }));
195
+ if (exports.length > 0) yield makeBarrel(node.path, exports);
196
+ return subtreeLeaves;
197
+ }
198
+ /**
199
+ * Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.
200
+ * Used when nested: true. Leaf files honor the barrel `strategy`, so `named` emits explicit
201
+ * named exports instead of wildcards. Sub-directory barrels are chained with a wildcard
202
+ * re-export, which forwards the names the child barrel already curated. Returns whether this
203
+ * node yielded a barrel, so a parent never re-exports a sub-directory that produced nothing.
204
+ */
205
+ function* walkNested(node, params) {
206
+ const exports = [];
207
+ for (const child of node.children) {
208
+ if (child.isFile) {
209
+ if (isBarrelPath(child.path)) continue;
210
+ const sourceFile = params.sourceFiles.get(child.path) ?? null;
211
+ exports.push(...params.strategy({
212
+ dirPath: node.path,
213
+ leafPath: child.path,
214
+ sourceFile
215
+ }));
216
+ continue;
217
+ }
218
+ if (yield* walkNested(child, params)) exports.push(_kubb_ast.ast.factory.createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }));
219
+ }
220
+ if (exports.length > 0) {
221
+ yield makeBarrel(node.path, exports);
222
+ return true;
223
+ }
224
+ return false;
225
+ }
226
+ function indexRelevantFiles(files, outputPath) {
227
+ const outputPrefix = `${toPosixPath(outputPath)}/`;
228
+ const sourceFiles = /* @__PURE__ */ new Map();
229
+ const paths = [];
230
+ for (const file of files) {
231
+ const normalized = toPosixPath(file.path);
232
+ if (!normalized.startsWith(outputPrefix)) continue;
233
+ if (isBarrelPath(normalized)) continue;
234
+ if (!SOURCE_EXTENSIONS.has((0, node_path.extname)(normalized))) continue;
235
+ sourceFiles.set(normalized, file);
236
+ paths.push(normalized);
237
+ }
238
+ return {
239
+ sourceFiles,
240
+ paths
241
+ };
242
+ }
243
+ /**
244
+ * Yields barrel `FileNode`s for the directory rooted at `outputPath`.
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * for (const file of getBarrelFiles({ outputPath, files, barrelType })) {
249
+ * upsertFile(file)
250
+ * }
251
+ * // or collect into an array
252
+ * const barrels = [...getBarrelFiles({ outputPath, files, barrelType })]
253
+ * ```
254
+ */
255
+ function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }) {
256
+ const { sourceFiles, paths } = indexRelevantFiles(files, outputPath);
257
+ if (paths.length === 0) return;
258
+ const tree = buildTree(outputPath, paths);
259
+ const strategy = LEAF_STRATEGIES.get(barrelType);
260
+ if (!strategy) return;
261
+ if (nested) {
262
+ yield* walkNested(tree, {
263
+ sourceFiles,
264
+ strategy
265
+ });
266
+ return;
267
+ }
268
+ yield* walkAllOrNamed(tree, {
269
+ sourceFiles,
270
+ strategy,
271
+ recursive
272
+ }, true);
273
+ }
274
+ /**
275
+ * Builds a POSIX-normalized prefix for a plugin's output. A directory output gets a trailing `/`,
276
+ * while a `mode: 'file'` output (the path is the file itself) gets the exact path with no trailing `/`.
277
+ *
278
+ * Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.
279
+ */
280
+ function getPluginOutputPrefix(plugin, config) {
281
+ const resolved = toPosixPath((0, node_path.resolve)(config.root, config.output.path, plugin.options.output.path));
282
+ return plugin.options.output.mode === "file" ? resolved : `${resolved}/`;
283
+ }
284
+ /**
285
+ * Returns `true` when `filePath` lives under any of the given excluded prefixes. A prefix with a
286
+ * trailing `/` matches a directory subtree, and a prefix without one matches that exact file
287
+ * (used for `mode: 'file'` outputs).
288
+ *
289
+ * Both sides are POSIX-normalized so Windows backslash paths match correctly.
290
+ */
291
+ function isExcludedPath(filePath, prefixes) {
292
+ const normalized = toPosixPath(filePath);
293
+ for (const prefix of prefixes) if (prefix.endsWith("/") ? normalized.startsWith(prefix) : normalized === prefix) return true;
294
+ return false;
295
+ }
296
+ //#endregion
297
+ //#region src/plugin.ts
298
+ /**
299
+ * Applies a plugin's configured `output.banner`/`footer` to a barrel file, flagged as `isBarrel`.
300
+ *
301
+ * Resolves through the plugin's own resolver, and only when the plugin explicitly sets a
302
+ * banner/footer, so barrels stay banner-free by default and never inherit the implicit
303
+ * "Generated by Kubb" notice.
304
+ */
305
+ function withBarrelBannerFooter({ file, plugin, config }) {
306
+ const output = plugin.options?.output;
307
+ const resolver = plugin.resolver;
308
+ if (!resolver) return file;
309
+ const hasBanner = output?.banner !== void 0;
310
+ const hasFooter = output?.footer !== void 0;
311
+ if (!hasBanner && !hasFooter) return file;
312
+ const context = {
313
+ output,
314
+ config,
315
+ file: {
316
+ path: file.path,
317
+ baseName: file.baseName,
318
+ isBarrel: true
319
+ }
320
+ };
321
+ return {
322
+ ...file,
323
+ banner: hasBanner ? resolver.default.banner(void 0, context) : file.banner,
324
+ footer: hasFooter ? resolver.default.footer(void 0, context) : file.footer
325
+ };
326
+ }
327
+ /**
328
+ * Canonical plugin name for `@kubb/plugin-barrel`. Used for driver lookups
329
+ * and to guard the `kubb:plugin:end` handler against reacting to its own lifecycle hook.
330
+ */
331
+ const pluginBarrelName = "plugin-barrel";
332
+ /**
333
+ * Generates an `index.ts` for every plugin output directory and one root
334
+ * barrel at `config.output.path/index.ts` after the build completes. Ships
335
+ * with Kubb and is registered by default in `defineConfig`.
336
+ *
337
+ * Each plugin inherits `output.barrel` from `config.output.barrel` (which
338
+ * defaults to `{ type: 'named' }`). Set `barrel: false` on a plugin to skip
339
+ * its barrel and also exclude its files from the root barrel.
340
+ *
341
+ * A plugin with `output.mode: 'file'` gets no per-plugin barrel, since its output
342
+ * is a single file. The root barrel re-exports that file directly.
343
+ *
344
+ * @example
345
+ * ```ts
346
+ * import { defineConfig } from '@kubb/core'
347
+ * import { pluginBarrel } from '@kubb/plugin-barrel'
348
+ * import { pluginTs } from '@kubb/plugin-ts'
349
+ * import { pluginZod } from '@kubb/plugin-zod'
350
+ *
351
+ * export default defineConfig({
352
+ * input: './petStore.yaml',
353
+ * output: { path: 'src/gen', barrel: { type: 'named' } },
354
+ * plugins: [
355
+ * pluginTs({ output: { path: 'types', barrel: { type: 'all' } } }),
356
+ * pluginZod({ output: { path: 'schemas' } }),
357
+ * pluginBarrel(),
358
+ * ],
359
+ * })
360
+ * ```
361
+ */
362
+ const pluginBarrel = (0, _kubb_core.definePlugin)(() => {
363
+ const excludedPrefixes = /* @__PURE__ */ new Set();
364
+ return {
365
+ name: pluginBarrelName,
366
+ enforce: "post",
367
+ hooks: {
368
+ "kubb:plugin:end"({ plugin, config, files, upsertFile }) {
369
+ if (plugin.name === "plugin-barrel") return;
370
+ const pluginBarrelOpt = plugin.options.output?.barrel;
371
+ const configBarrel = config.output.barrel;
372
+ const defaultBarrel = { type: "named" };
373
+ const barrelConfig = (() => {
374
+ if (pluginBarrelOpt !== void 0) return pluginBarrelOpt;
375
+ if (configBarrel !== void 0) return configBarrel === false ? false : {
376
+ ...configBarrel,
377
+ nested: false
378
+ };
379
+ return defaultBarrel;
380
+ })();
381
+ if (barrelConfig === false) {
382
+ excludedPrefixes.add(getPluginOutputPrefix(plugin, config));
383
+ return;
384
+ }
385
+ if (plugin.options.output.mode === "file") return;
386
+ const barrelType = barrelConfig.type;
387
+ const nested = barrelConfig.nested ?? false;
388
+ const base = node_path.default.resolve(config.root, config.output.path);
389
+ const target = node_path.default.resolve(base, plugin.options.output.path);
390
+ const relative = node_path.default.relative(base, target);
391
+ if (relative.startsWith("..") || node_path.default.isAbsolute(relative)) throw new Error("Invalid output path");
392
+ for (const file of getBarrelFiles({
393
+ outputPath: target,
394
+ files,
395
+ barrelType,
396
+ nested,
397
+ recursive: true
398
+ })) upsertFile(withBarrelBannerFooter({
399
+ file,
400
+ plugin,
401
+ config
402
+ }));
403
+ },
404
+ "kubb:plugins:end"({ files, config, upsertFile }) {
405
+ const barrelConfig = config.output.barrel ?? { type: "named" };
406
+ const filteredFiles = excludedPrefixes.size === 0 ? files : files.filter((f) => !isExcludedPath(f.path, excludedPrefixes));
407
+ excludedPrefixes.clear();
408
+ if (barrelConfig === false) return;
409
+ const barrelType = barrelConfig.type;
410
+ for (const file of getBarrelFiles({
411
+ outputPath: node_path.default.resolve(config.root, config.output.path),
412
+ files: filteredFiles,
413
+ barrelType
414
+ })) upsertFile(file);
415
+ }
416
+ }
417
+ };
418
+ });
419
+ //#endregion
420
+ exports.pluginBarrel = pluginBarrel;
421
+ exports.pluginBarrelName = pluginBarrelName;
422
+
423
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["ast","path"],"sources":["../../../internals/utils/src/fs.ts","../src/utils.ts","../src/plugin.ts"],"sourcesContent":["import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { camelCase } from './casing.ts'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Writes `data` to `path`, trimming leading/trailing whitespace before saving.\n * Skips the write when the trimmed content is empty or identical to what is already on disk.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns trimmed content\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const resolved = resolve(path)\n\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n const oldContent = (await file.exists()) ? await file.text() : null\n if (oldContent === trimmed) return null\n await Bun.write(resolved, trimmed)\n return trimmed\n }\n\n try {\n const oldContent = await readFile(resolved, { encoding: 'utf-8' })\n if (oldContent === trimmed) return null\n } catch {\n /* file doesn't exist yet */\n }\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, trimmed, { encoding: 'utf-8' })\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== trimmed) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return trimmed\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards destructive operations: before wiping an output directory, check that it does not contain\n * the project root, otherwise a `clean` would delete `kubb.config` and every source file.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) return true\n\n const rel = relative(resolvedParent, resolvedPath)\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Builds a nested file path from a dotted name. Splits on dots that precede a letter\n * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases\n * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.\n *\n * Empty segments are dropped before joining. They arise when the name starts with a dot\n * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to\n * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an\n * absolute path, letting generated files escape the configured output directory.\n *\n * @example Nested path from a dotted name\n * `toFilePath('pet.petId') // 'pet/petId'`\n *\n * @example PascalCase the final segment\n * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`\n *\n * @example Suffix applied to the final segment only\n * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`\n */\nexport function toFilePath(name: string, caseLast: (part: string) => string = camelCase): string {\n const parts = name.split(/\\.(?=[a-zA-Z])/)\n return parts\n .map((part, i) => (i === parts.length - 1 ? caseLast(part) : camelCase(part)))\n .filter(Boolean)\n .join('/')\n}\n","import { extname, resolve } from 'node:path'\nimport { ast, type ExportNode, type FileNode, type SourceNode } from '@kubb/ast'\nimport type { Config, NormalizedPlugin } from '@kubb/core'\nimport { toPosixPath } from '@internals/utils'\nimport type { BarrelType } from './types.ts'\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\nconst BARREL_SUFFIX = `/index.ts`\n\n/**\n * A node in the directory tree used to compute barrel file exports.\n * Either represents a directory (with `children`) or a file (`isFile: true`, empty `children`).\n */\ntype BuildTree = {\n /**\n * Absolute filesystem path of this directory or file. Always normalized to POSIX (`/`) separators.\n */\n path: string\n /**\n * Sub-directories and files contained within this directory.\n * Always empty for file nodes.\n */\n children: Array<BuildTree>\n /**\n * `true` when this node represents a file (leaf), `false` for directory nodes.\n */\n isFile: boolean\n}\n\n/**\n * Builds a directory tree rooted at `rootPath` from a list of absolute file paths.\n * Paths outside `rootPath` are silently ignored. Children are sorted alphabetically\n * by path so consumers (barrel exports, propagated indexes) emit a deterministic order.\n *\n * Both POSIX (`/`) and Windows (`\\`) separators are accepted in input paths; emitted node\n * paths are always POSIX-normalized so downstream prefix/lookup operations behave the same\n * across platforms.\n *\n * @example\n * ```ts\n * buildTree('/src/gen/types', [\n * '/src/gen/types/pet.ts',\n * '/src/gen/types/pets/listPets.ts',\n * ])\n * ```\n */\nexport function buildTree(rootPath: string, filePaths: ReadonlyArray<string>): BuildTree {\n const normalizedRoot = toPosixPath(rootPath)\n const root: BuildTree = { path: normalizedRoot, children: [], isFile: false }\n // Per-directory child lookup avoids the O(N) `Array.find` scan during insertion.\n // WeakMap keyed by object identity so directory nodes are GC-eligible once the tree is discarded.\n const childIndex = new WeakMap<BuildTree, Map<string, BuildTree>>()\n childIndex.set(root, new Map())\n\n const rootPrefix = `${normalizedRoot}/`\n\n for (const filePath of filePaths) {\n const normalized = toPosixPath(filePath)\n if (!normalized.startsWith(rootPrefix)) continue\n\n const parts = normalized.slice(rootPrefix.length).split('/')\n if (parts.length === 0) continue\n\n let current = root\n const lastIndex = parts.length - 1\n for (const [i, part] of parts.entries()) {\n if (!part) continue\n\n const isLast = i === lastIndex\n const siblings = childIndex.get(current)!\n let child = siblings.get(part)\n if (!child) {\n child = { path: `${current.path}/${part}`, children: [], isFile: isLast }\n current.children.push(child)\n siblings.set(part, child)\n if (!isLast) childIndex.set(child, new Map())\n }\n current = child\n }\n }\n\n sortTree(root)\n\n return root\n}\n\nfunction sortTree(node: BuildTree): void {\n if (node.children.length === 0) return\n node.children.sort(compareByPath)\n\n for (const child of node.children) {\n if (!child.isFile) sortTree(child)\n }\n}\n\nfunction compareByPath(a: BuildTree, b: BuildTree): number {\n return a.path < b.path ? -1 : a.path > b.path ? 1 : 0\n}\n\nfunction toRelativeModulePath(fromDir: string, filePath: string): string {\n return `./${filePath.slice(fromDir.length + 1)}`\n}\n\nfunction isBarrelPath(path: string): boolean {\n return path.endsWith(BARREL_SUFFIX)\n}\n\nfunction makeBarrel(dirPath: string, exports: Array<ExportNode>): FileNode {\n return ast.factory.createFile({\n baseName: 'index.ts',\n path: `${dirPath}${BARREL_SUFFIX}`,\n exports,\n sources: [],\n imports: [],\n // Default to no banner/footer. The barrel plugin resolves a configured plugin\n // banner/footer (with isBarrel: true) afterwards, so a `banner` function can\n // decide per file whether a barrel should carry a directive like \"use server\".\n banner: undefined,\n footer: undefined,\n })\n}\n\ntype LeafContext = {\n dirPath: string\n leafPath: string\n sourceFile: FileNode | null\n}\n\ntype LeafStrategy = (ctx: LeafContext) => Array<ExportNode>\n\nfunction hasOnlyNonIndexableSources(sources: ReadonlyArray<SourceNode>): boolean {\n if (sources.length === 0) return false\n for (const source of sources) {\n if (source.isIndexable) return false\n }\n return true\n}\n\nfunction partitionIndexableNames(sources: ReadonlyArray<SourceNode>): Map<boolean, Set<string>> {\n const byTypeOnly = new Map<boolean, Set<string>>([\n [false, new Set()],\n [true, new Set()],\n ])\n for (const source of sources) {\n if (!source.isIndexable || !source.name) continue\n byTypeOnly.get(Boolean(source.isTypeOnly))!.add(source.name)\n }\n return byTypeOnly\n}\n\nconst allStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n if (sourceFile && hasOnlyNonIndexableSources(sourceFile.sources)) return []\n return [ast.factory.createExport({ path: toRelativeModulePath(dirPath, leafPath) })]\n}\n\nconst namedStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const modulePath = toRelativeModulePath(dirPath, leafPath)\n\n if (!sourceFile) return [ast.factory.createExport({ path: modulePath })]\n\n const namesByTypeOnly = partitionIndexableNames(sourceFile.sources)\n const valueNames = namesByTypeOnly.get(false)!\n const typeNames = namesByTypeOnly.get(true)!\n\n if (valueNames.size === 0 && typeNames.size === 0) {\n if (sourceFile.sources.length > 0) return []\n return [ast.factory.createExport({ path: modulePath })]\n }\n\n const exports: Array<ExportNode> = []\n if (valueNames.size > 0) {\n exports.push(ast.factory.createExport({ name: [...valueNames].sort(), path: modulePath }))\n }\n if (typeNames.size > 0) {\n exports.push(ast.factory.createExport({ name: [...typeNames].sort(), path: modulePath, isTypeOnly: true }))\n }\n return exports\n}\n\nconst LEAF_STRATEGIES: ReadonlyMap<BarrelType, LeafStrategy> = new Map([\n ['all', allStrategy],\n ['named', namedStrategy],\n])\n\ntype LeafWalkParams = {\n sourceFiles: ReadonlyMap<string, FileNode>\n strategy: LeafStrategy\n recursive: boolean\n}\n\n/**\n * Post-order walk that yields a barrel per visited directory.\n * Returns the list of leaf file paths collected in this subtree (used by the parent call).\n */\nfunction* walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean): Generator<FileNode, Array<string>> {\n const subtreeLeaves: Array<string> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (!isBarrelPath(child.path)) subtreeLeaves.push(child.path)\n continue\n }\n\n const childLeaves = yield* walkAllOrNamed(child, params, false)\n for (const leaf of childLeaves) subtreeLeaves.push(leaf)\n }\n\n if (!isRoot && !params.recursive) return subtreeLeaves\n\n const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({ dirPath: node.path, leafPath, sourceFile: params.sourceFiles.get(leafPath) ?? null }))\n\n if (exports.length > 0) {\n yield makeBarrel(node.path, exports)\n }\n\n return subtreeLeaves\n}\n\ntype NestedWalkParams = {\n sourceFiles: ReadonlyMap<string, FileNode>\n strategy: LeafStrategy\n}\n\n/**\n * Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.\n * Used when nested: true. Leaf files honor the barrel `strategy`, so `named` emits explicit\n * named exports instead of wildcards. Sub-directory barrels are chained with a wildcard\n * re-export, which forwards the names the child barrel already curated. Returns whether this\n * node yielded a barrel, so a parent never re-exports a sub-directory that produced nothing.\n */\nfunction* walkNested(node: BuildTree, params: NestedWalkParams): Generator<FileNode, boolean> {\n const exports: Array<ExportNode> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (isBarrelPath(child.path)) continue\n const sourceFile = params.sourceFiles.get(child.path) ?? null\n exports.push(...params.strategy({ dirPath: node.path, leafPath: child.path, sourceFile }))\n continue\n }\n\n const childYieldedBarrel = yield* walkNested(child, params)\n if (childYieldedBarrel) {\n exports.push(ast.factory.createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }))\n }\n }\n\n if (exports.length > 0) {\n yield makeBarrel(node.path, exports)\n return true\n }\n\n return false\n}\n\ntype IndexedFiles = {\n sourceFiles: ReadonlyMap<string, FileNode>\n paths: ReadonlyArray<string>\n}\n\nfunction indexRelevantFiles(files: ReadonlyArray<FileNode>, outputPath: string): IndexedFiles {\n const outputPrefix = `${toPosixPath(outputPath)}/`\n const sourceFiles = new Map<string, FileNode>()\n const paths: Array<string> = []\n\n for (const file of files) {\n const normalized = toPosixPath(file.path)\n if (!normalized.startsWith(outputPrefix)) continue\n if (isBarrelPath(normalized)) continue\n if (!SOURCE_EXTENSIONS.has(extname(normalized))) continue\n\n sourceFiles.set(normalized, file)\n paths.push(normalized)\n }\n\n return { sourceFiles, paths }\n}\n\ntype GetBarrelFilesParams = {\n /**\n * Absolute directory the barrel(s) should be rooted at.\n * Only files living under this path are considered.\n */\n outputPath: string\n /**\n * Pool of generated files to scan for indexable sources.\n */\n files: ReadonlyArray<FileNode>\n /**\n * Export strategy used when emitting each barrel.\n * - `'all'` re-exports the whole module (`export * from './x'`)\n * - `'named'` re-exports only the indexable named symbols\n */\n barrelType: BarrelType\n /**\n * Generate an `index.ts` in every sub-directory, each re-exporting only what's directly inside it (hierarchical).\n * When false, uses flat generation strategy with optional recursive subdirectory barrels.\n */\n nested?: boolean\n /**\n * Also generate a barrel for each sub-directory when nested is false.\n * No effect when nested is true (always generates hierarchical structure).\n */\n recursive?: boolean\n}\n\n/**\n * Yields barrel `FileNode`s for the directory rooted at `outputPath`.\n *\n * @example\n * ```ts\n * for (const file of getBarrelFiles({ outputPath, files, barrelType })) {\n * upsertFile(file)\n * }\n * // or collect into an array\n * const barrels = [...getBarrelFiles({ outputPath, files, barrelType })]\n * ```\n */\nexport function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }: GetBarrelFilesParams): Generator<FileNode> {\n const { sourceFiles, paths } = indexRelevantFiles(files, outputPath)\n if (paths.length === 0) return\n\n const tree = buildTree(outputPath, paths)\n\n const strategy = LEAF_STRATEGIES.get(barrelType)\n if (!strategy) return\n\n if (nested) {\n yield* walkNested(tree, { sourceFiles, strategy })\n return\n }\n\n yield* walkAllOrNamed(tree, { sourceFiles, strategy, recursive }, true)\n}\n\n/**\n * Builds a POSIX-normalized prefix for a plugin's output. A directory output gets a trailing `/`,\n * while a `mode: 'file'` output (the path is the file itself) gets the exact path with no trailing `/`.\n *\n * Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.\n */\nexport function getPluginOutputPrefix(plugin: NormalizedPlugin, config: Config): string {\n const resolved = toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path))\n return plugin.options.output.mode === 'file' ? resolved : `${resolved}/`\n}\n\n/**\n * Returns `true` when `filePath` lives under any of the given excluded prefixes. A prefix with a\n * trailing `/` matches a directory subtree, and a prefix without one matches that exact file\n * (used for `mode: 'file'` outputs).\n *\n * Both sides are POSIX-normalized so Windows backslash paths match correctly.\n */\nexport function isExcludedPath(filePath: string, prefixes: ReadonlySet<string>): boolean {\n const normalized = toPosixPath(filePath)\n // Plain `for...of` over the Set rather than `.values().some()`: the iterator-helper `some`\n // allocates an iterator object per call, and this runs once per file during barrel generation.\n for (const prefix of prefixes) {\n const matched = prefix.endsWith('/') ? normalized.startsWith(prefix) : normalized === prefix\n if (matched) return true\n }\n return false\n}\n","import path from 'node:path'\nimport type { FileNode } from '@kubb/ast'\nimport { definePlugin } from '@kubb/core'\nimport type { Config, NormalizedPlugin, Plugin } from '@kubb/core'\nimport type { BarrelConfig, PluginBarrelConfig } from './types.ts'\nimport { getBarrelFiles, getPluginOutputPrefix, isExcludedPath } from './utils.ts'\n\n/**\n * Applies a plugin's configured `output.banner`/`footer` to a barrel file, flagged as `isBarrel`.\n *\n * Resolves through the plugin's own resolver, and only when the plugin explicitly sets a\n * banner/footer, so barrels stay banner-free by default and never inherit the implicit\n * \"Generated by Kubb\" notice.\n */\nfunction withBarrelBannerFooter({ file, plugin, config }: { file: FileNode; plugin: NormalizedPlugin; config: Config }): FileNode {\n const output = plugin.options?.output\n const resolver = plugin.resolver\n if (!resolver) return file\n\n const hasBanner = output?.banner !== undefined\n const hasFooter = output?.footer !== undefined\n if (!hasBanner && !hasFooter) return file\n\n const context = { output, config, file: { path: file.path, baseName: file.baseName, isBarrel: true } }\n return {\n ...file,\n banner: hasBanner ? resolver.default.banner(undefined, context) : file.banner,\n footer: hasFooter ? resolver.default.footer(undefined, context) : file.footer,\n }\n}\n\ndeclare global {\n namespace Kubb {\n interface PluginOptionsRegistry {\n output: {\n /**\n * Barrel configuration for this plugin's output.\n * Set to `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.barrel` when omitted.\n *\n * @default { type: 'named' }\n */\n barrel?: PluginBarrelConfig | false\n }\n }\n interface ConfigOptionsRegistry {\n output: {\n /**\n * Barrel configuration for the root barrel file at `config.output.path/index.ts`.\n * Set to `false` to disable root barrel generation. Individual plugins can override\n * this via their own `output.barrel`.\n *\n * @default { type: 'named' }\n */\n barrel?: BarrelConfig | false\n }\n }\n }\n}\n\n/**\n * Canonical plugin name for `@kubb/plugin-barrel`. Used for driver lookups\n * and to guard the `kubb:plugin:end` handler against reacting to its own lifecycle hook.\n */\nexport const pluginBarrelName = 'plugin-barrel' satisfies Plugin['name']\n\n/**\n * Generates an `index.ts` for every plugin output directory and one root\n * barrel at `config.output.path/index.ts` after the build completes. Ships\n * with Kubb and is registered by default in `defineConfig`.\n *\n * Each plugin inherits `output.barrel` from `config.output.barrel` (which\n * defaults to `{ type: 'named' }`). Set `barrel: false` on a plugin to skip\n * its barrel and also exclude its files from the root barrel.\n *\n * A plugin with `output.mode: 'file'` gets no per-plugin barrel, since its output\n * is a single file. The root barrel re-exports that file directly.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@kubb/core'\n * import { pluginBarrel } from '@kubb/plugin-barrel'\n * import { pluginTs } from '@kubb/plugin-ts'\n * import { pluginZod } from '@kubb/plugin-zod'\n *\n * export default defineConfig({\n * input: './petStore.yaml',\n * output: { path: 'src/gen', barrel: { type: 'named' } },\n * plugins: [\n * pluginTs({ output: { path: 'types', barrel: { type: 'all' } } }),\n * pluginZod({ output: { path: 'schemas' } }),\n * pluginBarrel(),\n * ],\n * })\n * ```\n */\nexport const pluginBarrel = definePlugin(() => {\n const excludedPrefixes = new Set<string>()\n\n return {\n name: pluginBarrelName,\n enforce: 'post' as const,\n hooks: {\n 'kubb:plugin:end'({ plugin, config, files, upsertFile }) {\n // Skip reactions to the barrel plugin's own lifecycle hook\n if (plugin.name === pluginBarrelName) return\n\n const pluginBarrelOpt = plugin.options.output?.barrel\n const configBarrel = config.output.barrel\n const defaultBarrel = { type: 'named' } as const\n\n // Root config barrel doesn't have nested, so we add it\n const barrelConfig: PluginBarrelConfig | false = (() => {\n if (pluginBarrelOpt !== undefined) return pluginBarrelOpt\n if (configBarrel !== undefined) return configBarrel === false ? false : { ...configBarrel, nested: false }\n return defaultBarrel\n })()\n\n if (barrelConfig === false) {\n excludedPrefixes.add(getPluginOutputPrefix(plugin, config))\n return\n }\n\n // `mode: 'file'` writes a single file, so there is no directory to barrel. The root barrel\n // re-exports that file as a direct leaf of `config.output.path`.\n if (plugin.options.output.mode === 'file') {\n return\n }\n\n const barrelType = barrelConfig.type\n const nested = barrelConfig.nested ?? false\n\n const base = path.resolve(config.root, config.output.path)\n const target = path.resolve(base, plugin.options.output.path)\n const relative = path.relative(base, target)\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error('Invalid output path')\n }\n for (const file of getBarrelFiles({ outputPath: target, files, barrelType, nested, recursive: true })) {\n upsertFile(withBarrelBannerFooter({ file, plugin, config }))\n }\n },\n 'kubb:plugins:end'({ files, config, upsertFile }) {\n const barrelConfig = config.output.barrel ?? { type: 'named' }\n\n const filteredFiles = excludedPrefixes.size === 0 ? files : files.filter((f) => !isExcludedPath(f.path, excludedPrefixes))\n excludedPrefixes.clear()\n\n if (barrelConfig === false) return\n\n const barrelType = barrelConfig.type\n\n for (const file of getBarrelFiles({ outputPath: path.resolve(config.root, config.output.path), files: filteredFiles, barrelType })) {\n upsertFile(file)\n }\n },\n },\n }\n})\n"],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
+ import { Plugin } from "@kubb/core";
3
+ //#region src/types.d.ts
4
+ /**
5
+ * Barrel export strategy.
6
+ *
7
+ * - `'all'` generates `export * from '...'` for every file
8
+ * - `'named'` generates `export { name1, name2 } from '...'` using each file's named exports
9
+ */
10
+ type BarrelType = 'all' | 'named';
11
+ /**
12
+ * Barrel configuration at the root config level.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * barrel: { type: 'named' } // default
17
+ * barrel: { type: 'all' }
18
+ * barrel: false // disable barrel generation
19
+ * ```
20
+ */
21
+ type BarrelConfig = {
22
+ /**
23
+ * Export strategy for the root barrel file.
24
+ * - `'all'` wildcard exports: `export * from './file'`
25
+ * - `'named'` explicit exports: `export { x, y } from './file'`
26
+ */
27
+ type: BarrelType;
28
+ };
29
+ /**
30
+ * Barrel configuration at the plugin level.
31
+ * Supports nested barrel generation in subdirectories.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * barrel: { type: 'named' } // single barrel with named exports
36
+ * barrel: { type: 'all', nested: true } // hierarchical barrels with wildcard exports
37
+ * barrel: { type: 'named', nested: true } // hierarchical barrels with named exports
38
+ * barrel: false // disable barrel generation
39
+ * ```
40
+ */
41
+ type PluginBarrelConfig = {
42
+ /**
43
+ * Export strategy for the plugin's barrel files.
44
+ * - `'all'` wildcard exports: `export * from './file'`
45
+ * - `'named'` explicit exports: `export { x, y } from './file'`
46
+ */
47
+ type: BarrelType;
48
+ /**
49
+ * Generate an `index.ts` in every sub-directory, each re-exporting only what's directly inside it.
50
+ * Creates a hierarchical barrel structure instead of flat exports from the root.
51
+ */
52
+ nested?: boolean;
53
+ };
54
+ //#endregion
55
+ //#region src/plugin.d.ts
56
+ declare global {
57
+ namespace Kubb {
58
+ interface PluginOptionsRegistry {
59
+ output: {
60
+ /**
61
+ * Barrel configuration for this plugin's output.
62
+ * Set to `false` to disable barrel generation for this plugin entirely. Doing so also
63
+ * excludes the plugin's files from the root barrel.
64
+ *
65
+ * Falls back to `config.output.barrel` when omitted.
66
+ *
67
+ * @default { type: 'named' }
68
+ */
69
+ barrel?: PluginBarrelConfig | false;
70
+ };
71
+ }
72
+ interface ConfigOptionsRegistry {
73
+ output: {
74
+ /**
75
+ * Barrel configuration for the root barrel file at `config.output.path/index.ts`.
76
+ * Set to `false` to disable root barrel generation. Individual plugins can override
77
+ * this via their own `output.barrel`.
78
+ *
79
+ * @default { type: 'named' }
80
+ */
81
+ barrel?: BarrelConfig | false;
82
+ };
83
+ }
84
+ }
85
+ }
86
+ /**
87
+ * Canonical plugin name for `@kubb/plugin-barrel`. Used for driver lookups
88
+ * and to guard the `kubb:plugin:end` handler against reacting to its own lifecycle hook.
89
+ */
90
+ declare const pluginBarrelName = "plugin-barrel";
91
+ /**
92
+ * Generates an `index.ts` for every plugin output directory and one root
93
+ * barrel at `config.output.path/index.ts` after the build completes. Ships
94
+ * with Kubb and is registered by default in `defineConfig`.
95
+ *
96
+ * Each plugin inherits `output.barrel` from `config.output.barrel` (which
97
+ * defaults to `{ type: 'named' }`). Set `barrel: false` on a plugin to skip
98
+ * its barrel and also exclude its files from the root barrel.
99
+ *
100
+ * A plugin with `output.mode: 'file'` gets no per-plugin barrel, since its output
101
+ * is a single file. The root barrel re-exports that file directly.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * import { defineConfig } from '@kubb/core'
106
+ * import { pluginBarrel } from '@kubb/plugin-barrel'
107
+ * import { pluginTs } from '@kubb/plugin-ts'
108
+ * import { pluginZod } from '@kubb/plugin-zod'
109
+ *
110
+ * export default defineConfig({
111
+ * input: './petStore.yaml',
112
+ * output: { path: 'src/gen', barrel: { type: 'named' } },
113
+ * plugins: [
114
+ * pluginTs({ output: { path: 'types', barrel: { type: 'all' } } }),
115
+ * pluginZod({ output: { path: 'schemas' } }),
116
+ * pluginBarrel(),
117
+ * ],
118
+ * })
119
+ * ```
120
+ */
121
+ declare const pluginBarrel: (options?: object | undefined) => Plugin<import("@kubb/core").PluginFactoryOptions<string, object, object, import("@kubb/core").Resolver>>;
122
+ //#endregion
123
+ export { type BarrelConfig, type BarrelType, type PluginBarrelConfig, pluginBarrel, pluginBarrelName };
124
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,399 @@
1
+ import "./rolldown-runtime-C0LytTxp.js";
2
+ import path, { extname, resolve } from "node:path";
3
+ import { definePlugin } from "@kubb/core";
4
+ import { ast } from "@kubb/ast";
5
+ //#region ../../internals/utils/src/fs.ts
6
+ /**
7
+ * Converts a filesystem path to use POSIX (`/`) separators.
8
+ *
9
+ * Most of the codebase compares and composes paths as strings (prefix matching, joining for
10
+ * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated
11
+ * paths, but on Windows it returns `\`-separated paths, which breaks every such comparison.
12
+ *
13
+ * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the
14
+ * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is
15
+ * exercisable from POSIX CI.
16
+ *
17
+ * @example
18
+ * toPosixPath('C:\\repo\\src\\pet.ts') // 'C:/repo/src/pet.ts'
19
+ */
20
+ function toPosixPath(filePath) {
21
+ return filePath.replaceAll("\\", "/");
22
+ }
23
+ //#endregion
24
+ //#region src/utils.ts
25
+ const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
26
+ ".ts",
27
+ ".tsx",
28
+ ".js",
29
+ ".jsx"
30
+ ]);
31
+ const BARREL_SUFFIX = `/index.ts`;
32
+ /**
33
+ * Builds a directory tree rooted at `rootPath` from a list of absolute file paths.
34
+ * Paths outside `rootPath` are silently ignored. Children are sorted alphabetically
35
+ * by path so consumers (barrel exports, propagated indexes) emit a deterministic order.
36
+ *
37
+ * Both POSIX (`/`) and Windows (`\`) separators are accepted in input paths; emitted node
38
+ * paths are always POSIX-normalized so downstream prefix/lookup operations behave the same
39
+ * across platforms.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * buildTree('/src/gen/types', [
44
+ * '/src/gen/types/pet.ts',
45
+ * '/src/gen/types/pets/listPets.ts',
46
+ * ])
47
+ * ```
48
+ */
49
+ function buildTree(rootPath, filePaths) {
50
+ const normalizedRoot = toPosixPath(rootPath);
51
+ const root = {
52
+ path: normalizedRoot,
53
+ children: [],
54
+ isFile: false
55
+ };
56
+ const childIndex = /* @__PURE__ */ new WeakMap();
57
+ childIndex.set(root, /* @__PURE__ */ new Map());
58
+ const rootPrefix = `${normalizedRoot}/`;
59
+ for (const filePath of filePaths) {
60
+ const normalized = toPosixPath(filePath);
61
+ if (!normalized.startsWith(rootPrefix)) continue;
62
+ const parts = normalized.slice(rootPrefix.length).split("/");
63
+ if (parts.length === 0) continue;
64
+ let current = root;
65
+ const lastIndex = parts.length - 1;
66
+ for (const [i, part] of parts.entries()) {
67
+ if (!part) continue;
68
+ const isLast = i === lastIndex;
69
+ const siblings = childIndex.get(current);
70
+ let child = siblings.get(part);
71
+ if (!child) {
72
+ child = {
73
+ path: `${current.path}/${part}`,
74
+ children: [],
75
+ isFile: isLast
76
+ };
77
+ current.children.push(child);
78
+ siblings.set(part, child);
79
+ if (!isLast) childIndex.set(child, /* @__PURE__ */ new Map());
80
+ }
81
+ current = child;
82
+ }
83
+ }
84
+ sortTree(root);
85
+ return root;
86
+ }
87
+ function sortTree(node) {
88
+ if (node.children.length === 0) return;
89
+ node.children.sort(compareByPath);
90
+ for (const child of node.children) if (!child.isFile) sortTree(child);
91
+ }
92
+ function compareByPath(a, b) {
93
+ return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
94
+ }
95
+ function toRelativeModulePath(fromDir, filePath) {
96
+ return `./${filePath.slice(fromDir.length + 1)}`;
97
+ }
98
+ function isBarrelPath(path) {
99
+ return path.endsWith(BARREL_SUFFIX);
100
+ }
101
+ function makeBarrel(dirPath, exports) {
102
+ return ast.factory.createFile({
103
+ baseName: "index.ts",
104
+ path: `${dirPath}${BARREL_SUFFIX}`,
105
+ exports,
106
+ sources: [],
107
+ imports: [],
108
+ banner: void 0,
109
+ footer: void 0
110
+ });
111
+ }
112
+ function hasOnlyNonIndexableSources(sources) {
113
+ if (sources.length === 0) return false;
114
+ for (const source of sources) if (source.isIndexable) return false;
115
+ return true;
116
+ }
117
+ function partitionIndexableNames(sources) {
118
+ const byTypeOnly = /* @__PURE__ */ new Map([[false, /* @__PURE__ */ new Set()], [true, /* @__PURE__ */ new Set()]]);
119
+ for (const source of sources) {
120
+ if (!source.isIndexable || !source.name) continue;
121
+ byTypeOnly.get(Boolean(source.isTypeOnly)).add(source.name);
122
+ }
123
+ return byTypeOnly;
124
+ }
125
+ const allStrategy = ({ dirPath, leafPath, sourceFile }) => {
126
+ if (sourceFile && hasOnlyNonIndexableSources(sourceFile.sources)) return [];
127
+ return [ast.factory.createExport({ path: toRelativeModulePath(dirPath, leafPath) })];
128
+ };
129
+ const namedStrategy = ({ dirPath, leafPath, sourceFile }) => {
130
+ const modulePath = toRelativeModulePath(dirPath, leafPath);
131
+ if (!sourceFile) return [ast.factory.createExport({ path: modulePath })];
132
+ const namesByTypeOnly = partitionIndexableNames(sourceFile.sources);
133
+ const valueNames = namesByTypeOnly.get(false);
134
+ const typeNames = namesByTypeOnly.get(true);
135
+ if (valueNames.size === 0 && typeNames.size === 0) {
136
+ if (sourceFile.sources.length > 0) return [];
137
+ return [ast.factory.createExport({ path: modulePath })];
138
+ }
139
+ const exports = [];
140
+ if (valueNames.size > 0) exports.push(ast.factory.createExport({
141
+ name: [...valueNames].sort(),
142
+ path: modulePath
143
+ }));
144
+ if (typeNames.size > 0) exports.push(ast.factory.createExport({
145
+ name: [...typeNames].sort(),
146
+ path: modulePath,
147
+ isTypeOnly: true
148
+ }));
149
+ return exports;
150
+ };
151
+ const LEAF_STRATEGIES = /* @__PURE__ */ new Map([["all", allStrategy], ["named", namedStrategy]]);
152
+ /**
153
+ * Post-order walk that yields a barrel per visited directory.
154
+ * Returns the list of leaf file paths collected in this subtree (used by the parent call).
155
+ */
156
+ function* walkAllOrNamed(node, params, isRoot) {
157
+ const subtreeLeaves = [];
158
+ for (const child of node.children) {
159
+ if (child.isFile) {
160
+ if (!isBarrelPath(child.path)) subtreeLeaves.push(child.path);
161
+ continue;
162
+ }
163
+ const childLeaves = yield* walkAllOrNamed(child, params, false);
164
+ for (const leaf of childLeaves) subtreeLeaves.push(leaf);
165
+ }
166
+ if (!isRoot && !params.recursive) return subtreeLeaves;
167
+ const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({
168
+ dirPath: node.path,
169
+ leafPath,
170
+ sourceFile: params.sourceFiles.get(leafPath) ?? null
171
+ }));
172
+ if (exports.length > 0) yield makeBarrel(node.path, exports);
173
+ return subtreeLeaves;
174
+ }
175
+ /**
176
+ * Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.
177
+ * Used when nested: true. Leaf files honor the barrel `strategy`, so `named` emits explicit
178
+ * named exports instead of wildcards. Sub-directory barrels are chained with a wildcard
179
+ * re-export, which forwards the names the child barrel already curated. Returns whether this
180
+ * node yielded a barrel, so a parent never re-exports a sub-directory that produced nothing.
181
+ */
182
+ function* walkNested(node, params) {
183
+ const exports = [];
184
+ for (const child of node.children) {
185
+ if (child.isFile) {
186
+ if (isBarrelPath(child.path)) continue;
187
+ const sourceFile = params.sourceFiles.get(child.path) ?? null;
188
+ exports.push(...params.strategy({
189
+ dirPath: node.path,
190
+ leafPath: child.path,
191
+ sourceFile
192
+ }));
193
+ continue;
194
+ }
195
+ if (yield* walkNested(child, params)) exports.push(ast.factory.createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }));
196
+ }
197
+ if (exports.length > 0) {
198
+ yield makeBarrel(node.path, exports);
199
+ return true;
200
+ }
201
+ return false;
202
+ }
203
+ function indexRelevantFiles(files, outputPath) {
204
+ const outputPrefix = `${toPosixPath(outputPath)}/`;
205
+ const sourceFiles = /* @__PURE__ */ new Map();
206
+ const paths = [];
207
+ for (const file of files) {
208
+ const normalized = toPosixPath(file.path);
209
+ if (!normalized.startsWith(outputPrefix)) continue;
210
+ if (isBarrelPath(normalized)) continue;
211
+ if (!SOURCE_EXTENSIONS.has(extname(normalized))) continue;
212
+ sourceFiles.set(normalized, file);
213
+ paths.push(normalized);
214
+ }
215
+ return {
216
+ sourceFiles,
217
+ paths
218
+ };
219
+ }
220
+ /**
221
+ * Yields barrel `FileNode`s for the directory rooted at `outputPath`.
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * for (const file of getBarrelFiles({ outputPath, files, barrelType })) {
226
+ * upsertFile(file)
227
+ * }
228
+ * // or collect into an array
229
+ * const barrels = [...getBarrelFiles({ outputPath, files, barrelType })]
230
+ * ```
231
+ */
232
+ function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }) {
233
+ const { sourceFiles, paths } = indexRelevantFiles(files, outputPath);
234
+ if (paths.length === 0) return;
235
+ const tree = buildTree(outputPath, paths);
236
+ const strategy = LEAF_STRATEGIES.get(barrelType);
237
+ if (!strategy) return;
238
+ if (nested) {
239
+ yield* walkNested(tree, {
240
+ sourceFiles,
241
+ strategy
242
+ });
243
+ return;
244
+ }
245
+ yield* walkAllOrNamed(tree, {
246
+ sourceFiles,
247
+ strategy,
248
+ recursive
249
+ }, true);
250
+ }
251
+ /**
252
+ * Builds a POSIX-normalized prefix for a plugin's output. A directory output gets a trailing `/`,
253
+ * while a `mode: 'file'` output (the path is the file itself) gets the exact path with no trailing `/`.
254
+ *
255
+ * Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.
256
+ */
257
+ function getPluginOutputPrefix(plugin, config) {
258
+ const resolved = toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path));
259
+ return plugin.options.output.mode === "file" ? resolved : `${resolved}/`;
260
+ }
261
+ /**
262
+ * Returns `true` when `filePath` lives under any of the given excluded prefixes. A prefix with a
263
+ * trailing `/` matches a directory subtree, and a prefix without one matches that exact file
264
+ * (used for `mode: 'file'` outputs).
265
+ *
266
+ * Both sides are POSIX-normalized so Windows backslash paths match correctly.
267
+ */
268
+ function isExcludedPath(filePath, prefixes) {
269
+ const normalized = toPosixPath(filePath);
270
+ for (const prefix of prefixes) if (prefix.endsWith("/") ? normalized.startsWith(prefix) : normalized === prefix) return true;
271
+ return false;
272
+ }
273
+ //#endregion
274
+ //#region src/plugin.ts
275
+ /**
276
+ * Applies a plugin's configured `output.banner`/`footer` to a barrel file, flagged as `isBarrel`.
277
+ *
278
+ * Resolves through the plugin's own resolver, and only when the plugin explicitly sets a
279
+ * banner/footer, so barrels stay banner-free by default and never inherit the implicit
280
+ * "Generated by Kubb" notice.
281
+ */
282
+ function withBarrelBannerFooter({ file, plugin, config }) {
283
+ const output = plugin.options?.output;
284
+ const resolver = plugin.resolver;
285
+ if (!resolver) return file;
286
+ const hasBanner = output?.banner !== void 0;
287
+ const hasFooter = output?.footer !== void 0;
288
+ if (!hasBanner && !hasFooter) return file;
289
+ const context = {
290
+ output,
291
+ config,
292
+ file: {
293
+ path: file.path,
294
+ baseName: file.baseName,
295
+ isBarrel: true
296
+ }
297
+ };
298
+ return {
299
+ ...file,
300
+ banner: hasBanner ? resolver.default.banner(void 0, context) : file.banner,
301
+ footer: hasFooter ? resolver.default.footer(void 0, context) : file.footer
302
+ };
303
+ }
304
+ /**
305
+ * Canonical plugin name for `@kubb/plugin-barrel`. Used for driver lookups
306
+ * and to guard the `kubb:plugin:end` handler against reacting to its own lifecycle hook.
307
+ */
308
+ const pluginBarrelName = "plugin-barrel";
309
+ /**
310
+ * Generates an `index.ts` for every plugin output directory and one root
311
+ * barrel at `config.output.path/index.ts` after the build completes. Ships
312
+ * with Kubb and is registered by default in `defineConfig`.
313
+ *
314
+ * Each plugin inherits `output.barrel` from `config.output.barrel` (which
315
+ * defaults to `{ type: 'named' }`). Set `barrel: false` on a plugin to skip
316
+ * its barrel and also exclude its files from the root barrel.
317
+ *
318
+ * A plugin with `output.mode: 'file'` gets no per-plugin barrel, since its output
319
+ * is a single file. The root barrel re-exports that file directly.
320
+ *
321
+ * @example
322
+ * ```ts
323
+ * import { defineConfig } from '@kubb/core'
324
+ * import { pluginBarrel } from '@kubb/plugin-barrel'
325
+ * import { pluginTs } from '@kubb/plugin-ts'
326
+ * import { pluginZod } from '@kubb/plugin-zod'
327
+ *
328
+ * export default defineConfig({
329
+ * input: './petStore.yaml',
330
+ * output: { path: 'src/gen', barrel: { type: 'named' } },
331
+ * plugins: [
332
+ * pluginTs({ output: { path: 'types', barrel: { type: 'all' } } }),
333
+ * pluginZod({ output: { path: 'schemas' } }),
334
+ * pluginBarrel(),
335
+ * ],
336
+ * })
337
+ * ```
338
+ */
339
+ const pluginBarrel = definePlugin(() => {
340
+ const excludedPrefixes = /* @__PURE__ */ new Set();
341
+ return {
342
+ name: pluginBarrelName,
343
+ enforce: "post",
344
+ hooks: {
345
+ "kubb:plugin:end"({ plugin, config, files, upsertFile }) {
346
+ if (plugin.name === "plugin-barrel") return;
347
+ const pluginBarrelOpt = plugin.options.output?.barrel;
348
+ const configBarrel = config.output.barrel;
349
+ const defaultBarrel = { type: "named" };
350
+ const barrelConfig = (() => {
351
+ if (pluginBarrelOpt !== void 0) return pluginBarrelOpt;
352
+ if (configBarrel !== void 0) return configBarrel === false ? false : {
353
+ ...configBarrel,
354
+ nested: false
355
+ };
356
+ return defaultBarrel;
357
+ })();
358
+ if (barrelConfig === false) {
359
+ excludedPrefixes.add(getPluginOutputPrefix(plugin, config));
360
+ return;
361
+ }
362
+ if (plugin.options.output.mode === "file") return;
363
+ const barrelType = barrelConfig.type;
364
+ const nested = barrelConfig.nested ?? false;
365
+ const base = path.resolve(config.root, config.output.path);
366
+ const target = path.resolve(base, plugin.options.output.path);
367
+ const relative = path.relative(base, target);
368
+ if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error("Invalid output path");
369
+ for (const file of getBarrelFiles({
370
+ outputPath: target,
371
+ files,
372
+ barrelType,
373
+ nested,
374
+ recursive: true
375
+ })) upsertFile(withBarrelBannerFooter({
376
+ file,
377
+ plugin,
378
+ config
379
+ }));
380
+ },
381
+ "kubb:plugins:end"({ files, config, upsertFile }) {
382
+ const barrelConfig = config.output.barrel ?? { type: "named" };
383
+ const filteredFiles = excludedPrefixes.size === 0 ? files : files.filter((f) => !isExcludedPath(f.path, excludedPrefixes));
384
+ excludedPrefixes.clear();
385
+ if (barrelConfig === false) return;
386
+ const barrelType = barrelConfig.type;
387
+ for (const file of getBarrelFiles({
388
+ outputPath: path.resolve(config.root, config.output.path),
389
+ files: filteredFiles,
390
+ barrelType
391
+ })) upsertFile(file);
392
+ }
393
+ }
394
+ };
395
+ });
396
+ //#endregion
397
+ export { pluginBarrel, pluginBarrelName };
398
+
399
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../internals/utils/src/fs.ts","../src/utils.ts","../src/plugin.ts"],"sourcesContent":["import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { camelCase } from './casing.ts'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Writes `data` to `path`, trimming leading/trailing whitespace before saving.\n * Skips the write when the trimmed content is empty or identical to what is already on disk.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns trimmed content\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const resolved = resolve(path)\n\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n const oldContent = (await file.exists()) ? await file.text() : null\n if (oldContent === trimmed) return null\n await Bun.write(resolved, trimmed)\n return trimmed\n }\n\n try {\n const oldContent = await readFile(resolved, { encoding: 'utf-8' })\n if (oldContent === trimmed) return null\n } catch {\n /* file doesn't exist yet */\n }\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, trimmed, { encoding: 'utf-8' })\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== trimmed) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return trimmed\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards destructive operations: before wiping an output directory, check that it does not contain\n * the project root, otherwise a `clean` would delete `kubb.config` and every source file.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) return true\n\n const rel = relative(resolvedParent, resolvedPath)\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Builds a nested file path from a dotted name. Splits on dots that precede a letter\n * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases\n * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.\n *\n * Empty segments are dropped before joining. They arise when the name starts with a dot\n * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to\n * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an\n * absolute path, letting generated files escape the configured output directory.\n *\n * @example Nested path from a dotted name\n * `toFilePath('pet.petId') // 'pet/petId'`\n *\n * @example PascalCase the final segment\n * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`\n *\n * @example Suffix applied to the final segment only\n * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`\n */\nexport function toFilePath(name: string, caseLast: (part: string) => string = camelCase): string {\n const parts = name.split(/\\.(?=[a-zA-Z])/)\n return parts\n .map((part, i) => (i === parts.length - 1 ? caseLast(part) : camelCase(part)))\n .filter(Boolean)\n .join('/')\n}\n","import { extname, resolve } from 'node:path'\nimport { ast, type ExportNode, type FileNode, type SourceNode } from '@kubb/ast'\nimport type { Config, NormalizedPlugin } from '@kubb/core'\nimport { toPosixPath } from '@internals/utils'\nimport type { BarrelType } from './types.ts'\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\nconst BARREL_SUFFIX = `/index.ts`\n\n/**\n * A node in the directory tree used to compute barrel file exports.\n * Either represents a directory (with `children`) or a file (`isFile: true`, empty `children`).\n */\ntype BuildTree = {\n /**\n * Absolute filesystem path of this directory or file. Always normalized to POSIX (`/`) separators.\n */\n path: string\n /**\n * Sub-directories and files contained within this directory.\n * Always empty for file nodes.\n */\n children: Array<BuildTree>\n /**\n * `true` when this node represents a file (leaf), `false` for directory nodes.\n */\n isFile: boolean\n}\n\n/**\n * Builds a directory tree rooted at `rootPath` from a list of absolute file paths.\n * Paths outside `rootPath` are silently ignored. Children are sorted alphabetically\n * by path so consumers (barrel exports, propagated indexes) emit a deterministic order.\n *\n * Both POSIX (`/`) and Windows (`\\`) separators are accepted in input paths; emitted node\n * paths are always POSIX-normalized so downstream prefix/lookup operations behave the same\n * across platforms.\n *\n * @example\n * ```ts\n * buildTree('/src/gen/types', [\n * '/src/gen/types/pet.ts',\n * '/src/gen/types/pets/listPets.ts',\n * ])\n * ```\n */\nexport function buildTree(rootPath: string, filePaths: ReadonlyArray<string>): BuildTree {\n const normalizedRoot = toPosixPath(rootPath)\n const root: BuildTree = { path: normalizedRoot, children: [], isFile: false }\n // Per-directory child lookup avoids the O(N) `Array.find` scan during insertion.\n // WeakMap keyed by object identity so directory nodes are GC-eligible once the tree is discarded.\n const childIndex = new WeakMap<BuildTree, Map<string, BuildTree>>()\n childIndex.set(root, new Map())\n\n const rootPrefix = `${normalizedRoot}/`\n\n for (const filePath of filePaths) {\n const normalized = toPosixPath(filePath)\n if (!normalized.startsWith(rootPrefix)) continue\n\n const parts = normalized.slice(rootPrefix.length).split('/')\n if (parts.length === 0) continue\n\n let current = root\n const lastIndex = parts.length - 1\n for (const [i, part] of parts.entries()) {\n if (!part) continue\n\n const isLast = i === lastIndex\n const siblings = childIndex.get(current)!\n let child = siblings.get(part)\n if (!child) {\n child = { path: `${current.path}/${part}`, children: [], isFile: isLast }\n current.children.push(child)\n siblings.set(part, child)\n if (!isLast) childIndex.set(child, new Map())\n }\n current = child\n }\n }\n\n sortTree(root)\n\n return root\n}\n\nfunction sortTree(node: BuildTree): void {\n if (node.children.length === 0) return\n node.children.sort(compareByPath)\n\n for (const child of node.children) {\n if (!child.isFile) sortTree(child)\n }\n}\n\nfunction compareByPath(a: BuildTree, b: BuildTree): number {\n return a.path < b.path ? -1 : a.path > b.path ? 1 : 0\n}\n\nfunction toRelativeModulePath(fromDir: string, filePath: string): string {\n return `./${filePath.slice(fromDir.length + 1)}`\n}\n\nfunction isBarrelPath(path: string): boolean {\n return path.endsWith(BARREL_SUFFIX)\n}\n\nfunction makeBarrel(dirPath: string, exports: Array<ExportNode>): FileNode {\n return ast.factory.createFile({\n baseName: 'index.ts',\n path: `${dirPath}${BARREL_SUFFIX}`,\n exports,\n sources: [],\n imports: [],\n // Default to no banner/footer. The barrel plugin resolves a configured plugin\n // banner/footer (with isBarrel: true) afterwards, so a `banner` function can\n // decide per file whether a barrel should carry a directive like \"use server\".\n banner: undefined,\n footer: undefined,\n })\n}\n\ntype LeafContext = {\n dirPath: string\n leafPath: string\n sourceFile: FileNode | null\n}\n\ntype LeafStrategy = (ctx: LeafContext) => Array<ExportNode>\n\nfunction hasOnlyNonIndexableSources(sources: ReadonlyArray<SourceNode>): boolean {\n if (sources.length === 0) return false\n for (const source of sources) {\n if (source.isIndexable) return false\n }\n return true\n}\n\nfunction partitionIndexableNames(sources: ReadonlyArray<SourceNode>): Map<boolean, Set<string>> {\n const byTypeOnly = new Map<boolean, Set<string>>([\n [false, new Set()],\n [true, new Set()],\n ])\n for (const source of sources) {\n if (!source.isIndexable || !source.name) continue\n byTypeOnly.get(Boolean(source.isTypeOnly))!.add(source.name)\n }\n return byTypeOnly\n}\n\nconst allStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n if (sourceFile && hasOnlyNonIndexableSources(sourceFile.sources)) return []\n return [ast.factory.createExport({ path: toRelativeModulePath(dirPath, leafPath) })]\n}\n\nconst namedStrategy: LeafStrategy = ({ dirPath, leafPath, sourceFile }) => {\n const modulePath = toRelativeModulePath(dirPath, leafPath)\n\n if (!sourceFile) return [ast.factory.createExport({ path: modulePath })]\n\n const namesByTypeOnly = partitionIndexableNames(sourceFile.sources)\n const valueNames = namesByTypeOnly.get(false)!\n const typeNames = namesByTypeOnly.get(true)!\n\n if (valueNames.size === 0 && typeNames.size === 0) {\n if (sourceFile.sources.length > 0) return []\n return [ast.factory.createExport({ path: modulePath })]\n }\n\n const exports: Array<ExportNode> = []\n if (valueNames.size > 0) {\n exports.push(ast.factory.createExport({ name: [...valueNames].sort(), path: modulePath }))\n }\n if (typeNames.size > 0) {\n exports.push(ast.factory.createExport({ name: [...typeNames].sort(), path: modulePath, isTypeOnly: true }))\n }\n return exports\n}\n\nconst LEAF_STRATEGIES: ReadonlyMap<BarrelType, LeafStrategy> = new Map([\n ['all', allStrategy],\n ['named', namedStrategy],\n])\n\ntype LeafWalkParams = {\n sourceFiles: ReadonlyMap<string, FileNode>\n strategy: LeafStrategy\n recursive: boolean\n}\n\n/**\n * Post-order walk that yields a barrel per visited directory.\n * Returns the list of leaf file paths collected in this subtree (used by the parent call).\n */\nfunction* walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean): Generator<FileNode, Array<string>> {\n const subtreeLeaves: Array<string> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (!isBarrelPath(child.path)) subtreeLeaves.push(child.path)\n continue\n }\n\n const childLeaves = yield* walkAllOrNamed(child, params, false)\n for (const leaf of childLeaves) subtreeLeaves.push(leaf)\n }\n\n if (!isRoot && !params.recursive) return subtreeLeaves\n\n const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({ dirPath: node.path, leafPath, sourceFile: params.sourceFiles.get(leafPath) ?? null }))\n\n if (exports.length > 0) {\n yield makeBarrel(node.path, exports)\n }\n\n return subtreeLeaves\n}\n\ntype NestedWalkParams = {\n sourceFiles: ReadonlyMap<string, FileNode>\n strategy: LeafStrategy\n}\n\n/**\n * Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.\n * Used when nested: true. Leaf files honor the barrel `strategy`, so `named` emits explicit\n * named exports instead of wildcards. Sub-directory barrels are chained with a wildcard\n * re-export, which forwards the names the child barrel already curated. Returns whether this\n * node yielded a barrel, so a parent never re-exports a sub-directory that produced nothing.\n */\nfunction* walkNested(node: BuildTree, params: NestedWalkParams): Generator<FileNode, boolean> {\n const exports: Array<ExportNode> = []\n\n for (const child of node.children) {\n if (child.isFile) {\n if (isBarrelPath(child.path)) continue\n const sourceFile = params.sourceFiles.get(child.path) ?? null\n exports.push(...params.strategy({ dirPath: node.path, leafPath: child.path, sourceFile }))\n continue\n }\n\n const childYieldedBarrel = yield* walkNested(child, params)\n if (childYieldedBarrel) {\n exports.push(ast.factory.createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }))\n }\n }\n\n if (exports.length > 0) {\n yield makeBarrel(node.path, exports)\n return true\n }\n\n return false\n}\n\ntype IndexedFiles = {\n sourceFiles: ReadonlyMap<string, FileNode>\n paths: ReadonlyArray<string>\n}\n\nfunction indexRelevantFiles(files: ReadonlyArray<FileNode>, outputPath: string): IndexedFiles {\n const outputPrefix = `${toPosixPath(outputPath)}/`\n const sourceFiles = new Map<string, FileNode>()\n const paths: Array<string> = []\n\n for (const file of files) {\n const normalized = toPosixPath(file.path)\n if (!normalized.startsWith(outputPrefix)) continue\n if (isBarrelPath(normalized)) continue\n if (!SOURCE_EXTENSIONS.has(extname(normalized))) continue\n\n sourceFiles.set(normalized, file)\n paths.push(normalized)\n }\n\n return { sourceFiles, paths }\n}\n\ntype GetBarrelFilesParams = {\n /**\n * Absolute directory the barrel(s) should be rooted at.\n * Only files living under this path are considered.\n */\n outputPath: string\n /**\n * Pool of generated files to scan for indexable sources.\n */\n files: ReadonlyArray<FileNode>\n /**\n * Export strategy used when emitting each barrel.\n * - `'all'` re-exports the whole module (`export * from './x'`)\n * - `'named'` re-exports only the indexable named symbols\n */\n barrelType: BarrelType\n /**\n * Generate an `index.ts` in every sub-directory, each re-exporting only what's directly inside it (hierarchical).\n * When false, uses flat generation strategy with optional recursive subdirectory barrels.\n */\n nested?: boolean\n /**\n * Also generate a barrel for each sub-directory when nested is false.\n * No effect when nested is true (always generates hierarchical structure).\n */\n recursive?: boolean\n}\n\n/**\n * Yields barrel `FileNode`s for the directory rooted at `outputPath`.\n *\n * @example\n * ```ts\n * for (const file of getBarrelFiles({ outputPath, files, barrelType })) {\n * upsertFile(file)\n * }\n * // or collect into an array\n * const barrels = [...getBarrelFiles({ outputPath, files, barrelType })]\n * ```\n */\nexport function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }: GetBarrelFilesParams): Generator<FileNode> {\n const { sourceFiles, paths } = indexRelevantFiles(files, outputPath)\n if (paths.length === 0) return\n\n const tree = buildTree(outputPath, paths)\n\n const strategy = LEAF_STRATEGIES.get(barrelType)\n if (!strategy) return\n\n if (nested) {\n yield* walkNested(tree, { sourceFiles, strategy })\n return\n }\n\n yield* walkAllOrNamed(tree, { sourceFiles, strategy, recursive }, true)\n}\n\n/**\n * Builds a POSIX-normalized prefix for a plugin's output. A directory output gets a trailing `/`,\n * while a `mode: 'file'` output (the path is the file itself) gets the exact path with no trailing `/`.\n *\n * Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.\n */\nexport function getPluginOutputPrefix(plugin: NormalizedPlugin, config: Config): string {\n const resolved = toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path))\n return plugin.options.output.mode === 'file' ? resolved : `${resolved}/`\n}\n\n/**\n * Returns `true` when `filePath` lives under any of the given excluded prefixes. A prefix with a\n * trailing `/` matches a directory subtree, and a prefix without one matches that exact file\n * (used for `mode: 'file'` outputs).\n *\n * Both sides are POSIX-normalized so Windows backslash paths match correctly.\n */\nexport function isExcludedPath(filePath: string, prefixes: ReadonlySet<string>): boolean {\n const normalized = toPosixPath(filePath)\n // Plain `for...of` over the Set rather than `.values().some()`: the iterator-helper `some`\n // allocates an iterator object per call, and this runs once per file during barrel generation.\n for (const prefix of prefixes) {\n const matched = prefix.endsWith('/') ? normalized.startsWith(prefix) : normalized === prefix\n if (matched) return true\n }\n return false\n}\n","import path from 'node:path'\nimport type { FileNode } from '@kubb/ast'\nimport { definePlugin } from '@kubb/core'\nimport type { Config, NormalizedPlugin, Plugin } from '@kubb/core'\nimport type { BarrelConfig, PluginBarrelConfig } from './types.ts'\nimport { getBarrelFiles, getPluginOutputPrefix, isExcludedPath } from './utils.ts'\n\n/**\n * Applies a plugin's configured `output.banner`/`footer` to a barrel file, flagged as `isBarrel`.\n *\n * Resolves through the plugin's own resolver, and only when the plugin explicitly sets a\n * banner/footer, so barrels stay banner-free by default and never inherit the implicit\n * \"Generated by Kubb\" notice.\n */\nfunction withBarrelBannerFooter({ file, plugin, config }: { file: FileNode; plugin: NormalizedPlugin; config: Config }): FileNode {\n const output = plugin.options?.output\n const resolver = plugin.resolver\n if (!resolver) return file\n\n const hasBanner = output?.banner !== undefined\n const hasFooter = output?.footer !== undefined\n if (!hasBanner && !hasFooter) return file\n\n const context = { output, config, file: { path: file.path, baseName: file.baseName, isBarrel: true } }\n return {\n ...file,\n banner: hasBanner ? resolver.default.banner(undefined, context) : file.banner,\n footer: hasFooter ? resolver.default.footer(undefined, context) : file.footer,\n }\n}\n\ndeclare global {\n namespace Kubb {\n interface PluginOptionsRegistry {\n output: {\n /**\n * Barrel configuration for this plugin's output.\n * Set to `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.barrel` when omitted.\n *\n * @default { type: 'named' }\n */\n barrel?: PluginBarrelConfig | false\n }\n }\n interface ConfigOptionsRegistry {\n output: {\n /**\n * Barrel configuration for the root barrel file at `config.output.path/index.ts`.\n * Set to `false` to disable root barrel generation. Individual plugins can override\n * this via their own `output.barrel`.\n *\n * @default { type: 'named' }\n */\n barrel?: BarrelConfig | false\n }\n }\n }\n}\n\n/**\n * Canonical plugin name for `@kubb/plugin-barrel`. Used for driver lookups\n * and to guard the `kubb:plugin:end` handler against reacting to its own lifecycle hook.\n */\nexport const pluginBarrelName = 'plugin-barrel' satisfies Plugin['name']\n\n/**\n * Generates an `index.ts` for every plugin output directory and one root\n * barrel at `config.output.path/index.ts` after the build completes. Ships\n * with Kubb and is registered by default in `defineConfig`.\n *\n * Each plugin inherits `output.barrel` from `config.output.barrel` (which\n * defaults to `{ type: 'named' }`). Set `barrel: false` on a plugin to skip\n * its barrel and also exclude its files from the root barrel.\n *\n * A plugin with `output.mode: 'file'` gets no per-plugin barrel, since its output\n * is a single file. The root barrel re-exports that file directly.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@kubb/core'\n * import { pluginBarrel } from '@kubb/plugin-barrel'\n * import { pluginTs } from '@kubb/plugin-ts'\n * import { pluginZod } from '@kubb/plugin-zod'\n *\n * export default defineConfig({\n * input: './petStore.yaml',\n * output: { path: 'src/gen', barrel: { type: 'named' } },\n * plugins: [\n * pluginTs({ output: { path: 'types', barrel: { type: 'all' } } }),\n * pluginZod({ output: { path: 'schemas' } }),\n * pluginBarrel(),\n * ],\n * })\n * ```\n */\nexport const pluginBarrel = definePlugin(() => {\n const excludedPrefixes = new Set<string>()\n\n return {\n name: pluginBarrelName,\n enforce: 'post' as const,\n hooks: {\n 'kubb:plugin:end'({ plugin, config, files, upsertFile }) {\n // Skip reactions to the barrel plugin's own lifecycle hook\n if (plugin.name === pluginBarrelName) return\n\n const pluginBarrelOpt = plugin.options.output?.barrel\n const configBarrel = config.output.barrel\n const defaultBarrel = { type: 'named' } as const\n\n // Root config barrel doesn't have nested, so we add it\n const barrelConfig: PluginBarrelConfig | false = (() => {\n if (pluginBarrelOpt !== undefined) return pluginBarrelOpt\n if (configBarrel !== undefined) return configBarrel === false ? false : { ...configBarrel, nested: false }\n return defaultBarrel\n })()\n\n if (barrelConfig === false) {\n excludedPrefixes.add(getPluginOutputPrefix(plugin, config))\n return\n }\n\n // `mode: 'file'` writes a single file, so there is no directory to barrel. The root barrel\n // re-exports that file as a direct leaf of `config.output.path`.\n if (plugin.options.output.mode === 'file') {\n return\n }\n\n const barrelType = barrelConfig.type\n const nested = barrelConfig.nested ?? false\n\n const base = path.resolve(config.root, config.output.path)\n const target = path.resolve(base, plugin.options.output.path)\n const relative = path.relative(base, target)\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error('Invalid output path')\n }\n for (const file of getBarrelFiles({ outputPath: target, files, barrelType, nested, recursive: true })) {\n upsertFile(withBarrelBannerFooter({ file, plugin, config }))\n }\n },\n 'kubb:plugins:end'({ files, config, upsertFile }) {\n const barrelConfig = config.output.barrel ?? { type: 'named' }\n\n const filteredFiles = excludedPrefixes.size === 0 ? files : files.filter((f) => !isExcludedPath(f.path, excludedPrefixes))\n excludedPrefixes.clear()\n\n if (barrelConfig === false) return\n\n const barrelType = barrelConfig.type\n\n for (const file of getBarrelFiles({ outputPath: path.resolve(config.root, config.output.path), files: filteredFiles, barrelType })) {\n upsertFile(file)\n }\n },\n },\n }\n})\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __name = (target, value) => __defProp(target, "name", {
4
+ value,
5
+ configurable: true
6
+ });
7
+ //#endregion
8
+ export { __name as t };
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@kubb/plugin-barrel",
3
+ "version": "5.0.0-beta.100",
4
+ "description": "Barrel-file plugin for Kubb. Automatically generates index.ts re-export files per plugin output directory.",
5
+ "keywords": [
6
+ "barrel",
7
+ "codegen",
8
+ "kubb",
9
+ "meta-framework",
10
+ "plugin",
11
+ "typescript"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "stijnvanhulle",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/kubb-labs/kubb.git",
18
+ "directory": "packages/plugin-barrel"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "*.d.ts",
23
+ "*.d.cts",
24
+ "!/**/**.test.**",
25
+ "!/**/__tests__/**",
26
+ "!/**/__snapshots__/**"
27
+ ],
28
+ "type": "module",
29
+ "sideEffects": false,
30
+ "main": "./dist/index.cjs",
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "import": "./dist/index.js",
36
+ "require": "./dist/index.cjs"
37
+ },
38
+ "./package.json": "./package.json"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public",
42
+ "registry": "https://registry.npmjs.org/"
43
+ },
44
+ "dependencies": {
45
+ "@kubb/ast": "5.0.0-beta.100",
46
+ "@kubb/core": "5.0.0-beta.100"
47
+ },
48
+ "devDependencies": {
49
+ "@internals/utils": "0.0.0"
50
+ },
51
+ "peerDependencies": {
52
+ "@kubb/core": "5.0.0-beta.100"
53
+ },
54
+ "engines": {
55
+ "node": ">=22"
56
+ },
57
+ "scripts": {
58
+ "build": "tsdown",
59
+ "clean": "node -e \"require('node:fs').rmSync('./dist', {recursive:true,force:true})\"",
60
+ "lint": "oxlint .",
61
+ "lint:fix": "oxlint --fix .",
62
+ "start": "tsdown --watch",
63
+ "test": "vitest --passWithNoTests",
64
+ "typecheck": "tsc -p ./tsconfig.json --noEmit --emitDeclarationOnly false"
65
+ }
66
+ }