@kubb/core 5.0.0-alpha.54 → 5.0.0-alpha.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -307,46 +307,70 @@ type Generator<TOptions extends PluginFactoryOptions = PluginFactoryOptions, TEl
307
307
  */
308
308
  declare function defineGenerator<TOptions extends PluginFactoryOptions = PluginFactoryOptions, TElement = unknown>(generator: Generator<TOptions, TElement>): Generator<TOptions, TElement>;
309
309
  //#endregion
310
- //#region src/defineParser.d.ts
311
- type PrintOptions = {
312
- extname?: FileNode['extname'];
313
- };
314
- type Parser<TMeta extends object = any> = {
310
+ //#region src/definePlugin.d.ts
311
+ /**
312
+ * A plugin object produced by `definePlugin`.
313
+ * Instead of flat lifecycle methods, it groups all handlers under a `hooks:` property
314
+ * (matching Astro's integration naming convention).
315
+ *
316
+ * @template TFactory - The plugin's `PluginFactoryOptions` type.
317
+ */
318
+ type Plugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions> = {
319
+ /**
320
+ * Unique name for the plugin, following the same naming convention as `createPlugin`.
321
+ */
315
322
  name: string;
316
323
  /**
317
- * File extensions this parser handles.
318
- * Use `undefined` to create a catch-all fallback parser.
324
+ * Plugins that must be registered before this plugin executes.
325
+ * An error is thrown at startup when any listed dependency is missing.
326
+ */
327
+ dependencies?: Array<string>;
328
+ /**
329
+ * Controls the execution order of this plugin relative to others.
319
330
  *
320
- * @example Handled extensions
321
- * `['.ts', '.js']`
331
+ * - `'pre'` — runs before all normal plugins.
332
+ * - `'post'` — runs after all normal plugins.
333
+ * - `undefined` (default) — runs in declaration order among normal plugins.
334
+ *
335
+ * Dependency constraints always take precedence over `enforce`.
322
336
  */
323
- extNames: Array<FileNode['extname']> | undefined;
337
+ enforce?: 'pre' | 'post';
324
338
  /**
325
- * Convert a resolved file to a string.
339
+ * The options passed by the user when calling the plugin factory.
326
340
  */
327
- parse(file: FileNode<TMeta>, options?: PrintOptions): Promise<string> | string;
341
+ options?: TFactory['options'];
342
+ /**
343
+ * Lifecycle event handlers for this plugin.
344
+ * Any event from the global `KubbHooks` map can be subscribed to here.
345
+ */
346
+ hooks: { [K in Exclude<keyof KubbHooks, 'kubb:plugin:setup'>]?: (...args: KubbHooks[K]) => void | Promise<void> } & {
347
+ 'kubb:plugin:setup'?(ctx: KubbPluginSetupContext<TFactory>): void | Promise<void>;
348
+ };
328
349
  };
329
350
  /**
330
- * Defines a parser with type safety.
351
+ * Creates a plugin factory using the hook-style (`hooks:`) API.
331
352
  *
332
- * Use this function to create parsers that transform generated files to strings
333
- * based on their extension.
353
+ * The returned factory is called with optional options and produces a `Plugin`
354
+ * that coexists with plugins created via the legacy `createPlugin` API in the same
355
+ * `kubb.config.ts`.
356
+ *
357
+ * Lifecycle handlers are registered on the `PluginDriver`'s `AsyncEventEmitter`, enabling
358
+ * both the plugin's own handlers and external tooling (CLI, devtools) to observe every event.
334
359
  *
335
360
  * @example
336
361
  * ```ts
337
- * import { defineParser } from '@kubb/core'
338
- *
339
- * export const jsonParser = defineParser({
340
- * name: 'json',
341
- * extNames: ['.json'],
342
- * parse(file) {
343
- * const { extractStringsFromNodes } = await import('@kubb/ast')
344
- * return file.sources.map((s) => extractStringsFromNodes(s.nodes ?? [])).join('\n')
362
+ * // With PluginFactoryOptions (recommended for real plugins)
363
+ * export const pluginTs = definePlugin<PluginTs>((options) => ({
364
+ * name: 'plugin-ts',
365
+ * hooks: {
366
+ * 'kubb:plugin:setup'(ctx) {
367
+ * ctx.setResolver(resolverTs) // typed as Partial<ResolverTs>
368
+ * },
345
369
  * },
346
- * })
370
+ * }))
347
371
  * ```
348
372
  */
349
- declare function defineParser<TMeta extends object = any>(parser: Parser<TMeta>): Parser<TMeta>;
373
+ declare function definePlugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions>(factory: (options: TFactory['options']) => Plugin<TFactory>): (options?: TFactory['options']) => Plugin<TFactory>;
350
374
  //#endregion
351
375
  //#region src/FileManager.d.ts
352
376
  /**
@@ -381,7 +405,6 @@ declare class FileManager {
381
405
  clear(): void;
382
406
  /**
383
407
  * All stored files, sorted by path length (shorter paths first).
384
- * Barrel/index files (e.g. index.ts) are sorted last within each length bucket.
385
408
  */
386
409
  get files(): Array<FileNode>;
387
410
  }
@@ -776,75 +799,155 @@ declare global {
776
799
  * ```
777
800
  */
778
801
  interface PluginRegistry {}
802
+ /**
803
+ * Extension point for root `Config['output']` options.
804
+ * Augment the `output` key in middleware or plugin packages to add extra fields
805
+ * to the global output configuration without touching core types.
806
+ *
807
+ * @example
808
+ * ```ts
809
+ * // packages/middleware-barrel/src/types.ts
810
+ * declare global {
811
+ * namespace Kubb {
812
+ * interface ConfigOptionsRegistry {
813
+ * output: {
814
+ * barrelType?: import('./types.ts').BarrelType | false
815
+ * }
816
+ * }
817
+ * }
818
+ * }
819
+ * ```
820
+ */
821
+ interface ConfigOptionsRegistry {}
822
+ /**
823
+ * Extension point for per-plugin `Output` options.
824
+ * Augment the `output` key in middleware or plugin packages to add extra fields
825
+ * to the per-plugin output configuration without touching core types.
826
+ *
827
+ * @example
828
+ * ```ts
829
+ * // packages/middleware-barrel/src/types.ts
830
+ * declare global {
831
+ * namespace Kubb {
832
+ * interface PluginOptionsRegistry {
833
+ * output: {
834
+ * barrelType?: import('./types.ts').BarrelType | false
835
+ * }
836
+ * }
837
+ * }
838
+ * }
839
+ * ```
840
+ */
841
+ interface PluginOptionsRegistry {}
779
842
  }
780
843
  }
781
844
  //#endregion
782
- //#region src/definePlugin.d.ts
845
+ //#region src/defineMiddleware.d.ts
783
846
  /**
784
- * A plugin object produced by `definePlugin`.
785
- * Instead of flat lifecycle methods, it groups all handlers under a `hooks:` property
786
- * (matching Astro's integration naming convention).
847
+ * A middleware observes and post-processes the build output produced by plugins.
848
+ * It attaches listeners to the shared `hooks` emitter before the plugin execution loop
849
+ * begins and reacts to lifecycle events (e.g. `kubb:plugin:end`, `kubb:build:end`) to
850
+ * inject barrel files or perform other cross-cutting concerns.
787
851
  *
788
- * @template TFactory - The plugin's `PluginFactoryOptions` type.
852
+ * Middleware listeners are always registered **after** all plugin listeners, because
853
+ * `createKubb` installs middleware only after the `PluginDriver` has registered every
854
+ * plugin's hooks. This means middleware hooks for any event always fire last.
855
+ *
856
+ * @example
857
+ * ```ts
858
+ * import { defineMiddleware } from '@kubb/core'
859
+ *
860
+ * export const myMiddleware = defineMiddleware({
861
+ * name: 'my-middleware',
862
+ * install(hooks) {
863
+ * hooks.on('kubb:build:end', ({ files }) => {
864
+ * console.log(`Build complete with ${files.length} files`)
865
+ * })
866
+ * },
867
+ * })
868
+ * ```
789
869
  */
790
- type Plugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions> = {
870
+ type Middleware = {
791
871
  /**
792
- * Unique name for the plugin, following the same naming convention as `createPlugin`.
872
+ * Unique identifier for this middleware.
793
873
  */
794
874
  name: string;
795
875
  /**
796
- * Plugins that must be registered before this plugin executes.
797
- * An error is thrown at startup when any listed dependency is missing.
876
+ * Called during `createKubb` after `setup()` but before the plugin
877
+ * execution loop starts. Attach listeners to `hooks` here.
798
878
  */
799
- dependencies?: Array<string>;
879
+ install(hooks: AsyncEventEmitter<KubbHooks>): void;
880
+ };
881
+ /**
882
+ * Identity factory for middleware.
883
+ * Returns the middleware object unchanged but provides a typed entry-point
884
+ * to define middleware with proper inference.
885
+ *
886
+ * @example
887
+ * ```ts
888
+ * export const myMiddleware = defineMiddleware({
889
+ * name: 'my-middleware',
890
+ * install(hooks) {
891
+ * hooks.on('kubb:build:end', ({ files }) => {
892
+ * console.log(`Build complete with ${files.length} files`)
893
+ * })
894
+ * },
895
+ * })
896
+ * ```
897
+ */
898
+ declare function defineMiddleware(middleware: Middleware): Middleware;
899
+ //#endregion
900
+ //#region src/defineParser.d.ts
901
+ type PrintOptions = {
902
+ extname?: FileNode['extname'];
903
+ };
904
+ type Parser<TMeta extends object = any> = {
905
+ name: string;
800
906
  /**
801
- * The options passed by the user when calling the plugin factory.
907
+ * File extensions this parser handles.
908
+ * Use `undefined` to create a catch-all fallback parser.
909
+ *
910
+ * @example Handled extensions
911
+ * `['.ts', '.js']`
802
912
  */
803
- options?: TFactory['options'];
913
+ extNames: Array<FileNode['extname']> | undefined;
804
914
  /**
805
- * Lifecycle event handlers for this plugin.
806
- * Any event from the global `KubbHooks` map can be subscribed to here.
915
+ * Convert a resolved file to a string.
807
916
  */
808
- hooks: { [K in Exclude<keyof KubbHooks, 'kubb:plugin:setup'>]?: (...args: KubbHooks[K]) => void | Promise<void> } & {
809
- 'kubb:plugin:setup'?(ctx: KubbPluginSetupContext<TFactory>): void | Promise<void>;
810
- };
917
+ parse(file: FileNode<TMeta>, options?: PrintOptions): Promise<string> | string;
811
918
  };
812
919
  /**
813
- * Creates a plugin factory using the hook-style (`hooks:`) API.
814
- *
815
- * The returned factory is called with optional options and produces a `Plugin`
816
- * that coexists with plugins created via the legacy `createPlugin` API in the same
817
- * `kubb.config.ts`.
920
+ * Defines a parser with type safety.
818
921
  *
819
- * Lifecycle handlers are registered on the `PluginDriver`'s `AsyncEventEmitter`, enabling
820
- * both the plugin's own handlers and external tooling (CLI, devtools) to observe every event.
922
+ * Use this function to create parsers that transform generated files to strings
923
+ * based on their extension.
821
924
  *
822
925
  * @example
823
926
  * ```ts
824
- * // With PluginFactoryOptions (recommended for real plugins)
825
- * export const pluginTs = definePlugin<PluginTs>((options) => ({
826
- * name: 'plugin-ts',
827
- * hooks: {
828
- * 'kubb:plugin:setup'(ctx) {
829
- * ctx.setResolver(resolverTs) // typed as Partial<ResolverTs>
830
- * },
927
+ * import { defineParser } from '@kubb/core'
928
+ *
929
+ * export const jsonParser = defineParser({
930
+ * name: 'json',
931
+ * extNames: ['.json'],
932
+ * parse(file) {
933
+ * const { extractStringsFromNodes } = await import('@kubb/ast')
934
+ * return file.sources.map((s) => extractStringsFromNodes(s.nodes ?? [])).join('\n')
831
935
  * },
832
- * }))
936
+ * })
833
937
  * ```
834
938
  */
835
- declare function definePlugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions>(factory: (options: TFactory['options']) => Plugin<TFactory>): (options?: TFactory['options']) => Plugin<TFactory>;
939
+ declare function defineParser<TMeta extends object = any>(parser: Parser<TMeta>): Parser<TMeta>;
836
940
  //#endregion
837
- //#region src/utils/getBarrelFiles.d.ts
941
+ //#region src/types.d.ts
838
942
  /**
839
- * Minimal file metadata attached to every generated file for barrel-file bookkeeping.
943
+ * Safely extracts the type of key `K` from `T`, returning `{}` when `K` is not a key of `T`.
944
+ * Used to implement optional interface augmentation for `Kubb.ConfigOptionsRegistry` and
945
+ * `Kubb.PluginOptionsRegistry` so that middleware and plugin packages can extend core types
946
+ * without requiring modifications to core.
840
947
  *
841
948
  * @internal
842
949
  */
843
- type FileMetaBase = {
844
- pluginName?: string;
845
- };
846
- //#endregion
847
- //#region src/types.d.ts
950
+ type ExtractRegistryKey<T, K extends PropertyKey> = K extends keyof T ? T[K] : {};
848
951
  type InputPath = {
849
952
  /**
850
953
  * Specify your Swagger/OpenAPI file, either as an absolute path or a path relative to the root.
@@ -936,13 +1039,6 @@ type Adapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions> = {
936
1039
  path: string;
937
1040
  }) => Array<ImportNode>;
938
1041
  };
939
- /**
940
- * Controls how `index.ts` barrel files are generated.
941
- * - `'all'` — exports every generated symbol from every file.
942
- * - `'named'` — exports only explicitly named exports.
943
- * - `'propagate'` — propagates re-exports from nested barrel files upward.
944
- */
945
- type BarrelType = 'all' | 'named' | 'propagate';
946
1042
  type DevtoolsOptions = {
947
1043
  /**
948
1044
  * Open the AST inspector view (`/ast`) in Kubb Studio.
@@ -1058,11 +1154,6 @@ type Config<TInput = Input> = {
1058
1154
  * @default { '.ts': '.ts'}
1059
1155
  */
1060
1156
  extension?: Record<FileNode['extname'], FileNode['extname'] | ''>;
1061
- /**
1062
- * Configures how `index.ts` files are created, including disabling barrel file generation. Each plugin has its own `barrelType` option; this setting controls the root barrel file (e.g., `src/gen/index.ts`).
1063
- * @default 'named'
1064
- */
1065
- barrelType?: 'all' | 'named' | false;
1066
1157
  /**
1067
1158
  * Adds a default banner to the start of every generated file indicating it was generated by Kubb.
1068
1159
  * - 'simple' adds banner with link to Kubb.
@@ -1078,7 +1169,7 @@ type Config<TInput = Input> = {
1078
1169
  * @default false
1079
1170
  */
1080
1171
  override?: boolean;
1081
- };
1172
+ } & ExtractRegistryKey<Kubb.ConfigOptionsRegistry, 'output'>;
1082
1173
  /**
1083
1174
  * An array of Kubb plugins used for code generation.
1084
1175
  * Each plugin may declare additional configurable options.
@@ -1086,6 +1177,22 @@ type Config<TInput = Input> = {
1086
1177
  * Use `dependencies` on the plugin to declare execution order.
1087
1178
  */
1088
1179
  plugins: Array<Plugin>;
1180
+ /**
1181
+ * Middleware instances that observe and post-process the build output.
1182
+ * Each middleware receives the `hooks` emitter and attaches its own listeners.
1183
+ * Middleware listeners are always registered after all plugin listeners,
1184
+ * so middleware hooks fire last for any given event.
1185
+ *
1186
+ * @example
1187
+ * ```ts
1188
+ * import { middlewareBarrel } from '@kubb/middleware-barrel'
1189
+ * export default defineConfig({
1190
+ * middleware: [middlewareBarrel],
1191
+ * plugins: [pluginTs(), pluginZod()],
1192
+ * })
1193
+ * ```
1194
+ */
1195
+ middleware?: Array<Middleware>;
1089
1196
  /**
1090
1197
  * Project-wide renderer factory. All plugins and generators that do not declare their own
1091
1198
  * `renderer` ultimately fall back to this value.
@@ -1360,11 +1467,6 @@ type Output<_TOptions = unknown> = {
1360
1467
  * Path to the output folder or file that will contain generated code.
1361
1468
  */
1362
1469
  path: string;
1363
- /**
1364
- * Define what needs to be exported, here you can also disable the export of barrel files
1365
- * @default 'named'
1366
- */
1367
- barrelType?: BarrelType | false;
1368
1470
  /**
1369
1471
  * Text or function appended at the start of every generated file.
1370
1472
  * When a function, receives the current `InputNode` and must return a string.
@@ -1380,7 +1482,7 @@ type Output<_TOptions = unknown> = {
1380
1482
  * @default false
1381
1483
  */
1382
1484
  override?: boolean;
1383
- };
1485
+ } & ExtractRegistryKey<Kubb.PluginOptionsRegistry, 'output'>;
1384
1486
  type Group = {
1385
1487
  /**
1386
1488
  * Determines how files are grouped into subdirectories.
@@ -1481,6 +1583,18 @@ type KubbBuildStartContext = {
1481
1583
  */
1482
1584
  getPlugin<TName extends keyof Kubb.PluginRegistry>(name: TName): Plugin<Kubb.PluginRegistry[TName]> | undefined;
1483
1585
  getPlugin(name: string): Plugin | undefined;
1586
+ /**
1587
+ * Returns all files currently in the file manager.
1588
+ * Call this lazily (e.g. inside a `kubb:plugin:end` listener) to see files added by plugins
1589
+ * that have already run.
1590
+ */
1591
+ readonly files: ReadonlyArray<FileNode>;
1592
+ /**
1593
+ * Upsert one or more files into the file manager.
1594
+ * Files with the same path are merged; new files are appended.
1595
+ * Safe to call at any point during the plugin lifecycle, including inside `kubb:plugin:end`.
1596
+ */
1597
+ upsertFile: (...files: Array<FileNode>) => void;
1484
1598
  };
1485
1599
  /**
1486
1600
  * Context passed to a hook-style plugin's `kubb:build:end` handler.
@@ -1757,5 +1871,5 @@ type CLIOptions = {
1757
1871
  */
1758
1872
  type PossibleConfig<TCliOptions = undefined> = PossiblePromise<Config | Config[]> | ((...args: [TCliOptions] extends [undefined] ? [] : [TCliOptions]) => PossiblePromise<Config | Config[]>);
1759
1873
  //#endregion
1760
- export { Plugin as $, KubbPluginSetupContext as A, Override as B, KubbGenerationStartContext as C, KubbInfoContext as D, KubbHookStartContext as E, Logger as F, ResolveOptionsContext as G, PossibleConfig as H, LoggerContext as I, ResolverFileParams as J, Resolver as K, LoggerOptions as L, KubbSuccessContext as M, KubbVersionNewContext as N, KubbLifecycleStartContext as O, KubbWarnContext as P, FileMetaBase as Q, NormalizedPlugin as R, KubbGenerationEndContext as S, KubbHookEndContext as T, ResolveBannerContext as U, PluginFactoryOptions as V, ResolveNameParams as W, UserConfig as X, ResolverPathParams as Y, UserLogger as Z, KubbDebugContext as _, AsyncEventEmitter as _t, CLIOptions as a, PluginDriver as at, KubbFilesProcessingEndContext as b, Exclude$1 as c, defineParser as ct, Include as d, Storage as dt, definePlugin as et, InputData as f, createStorage as ft, KubbConfigEndContext as g, logLevel as gt, KubbBuildStartContext as h, createRenderer as ht, BarrelType as i, createKubb as it, KubbPluginStartContext as j, KubbPluginEndContext as k, GeneratorContext as l, Generator as lt, KubbBuildEndContext as m, RendererFactory as mt, AdapterFactoryOptions as n, KubbHooks as nt, Config as o, FileManager as ot, InputPath as p, Renderer as pt, ResolverContext as q, AdapterSource as r, BuildOutput as rt, DevtoolsOptions as s, Parser as st, Adapter as t, Kubb$1 as tt, Group as u, defineGenerator as ut, KubbErrorContext as v, KubbGenerationSummaryContext as w, KubbFilesProcessingStartContext as x, KubbFileProcessingUpdateContext as y, Output as z };
1761
- //# sourceMappingURL=types-CeLqzzAf.d.ts.map
1874
+ export { Middleware as $, KubbPluginStartContext as A, PluginFactoryOptions as B, KubbGenerationSummaryContext as C, KubbLifecycleStartContext as D, KubbInfoContext as E, LoggerContext as F, Resolver as G, ResolveBannerContext as H, LoggerOptions as I, ResolverPathParams as J, ResolverContext as K, NormalizedPlugin as L, KubbVersionNewContext as M, KubbWarnContext as N, KubbPluginEndContext as O, Logger as P, defineParser as Q, Output as R, KubbGenerationStartContext as S, KubbHookStartContext as T, ResolveNameParams as U, PossibleConfig as V, ResolveOptionsContext as W, UserLogger as X, UserConfig as Y, Parser as Z, KubbErrorContext as _, AsyncEventEmitter as _t, Config as a, PluginDriver as at, KubbFilesProcessingStartContext as b, GeneratorContext as c, definePlugin as ct, InputData as d, Storage as dt, defineMiddleware as et, InputPath as f, createStorage as ft, KubbDebugContext as g, logLevel as gt, KubbConfigEndContext as h, createRenderer as ht, CLIOptions as i, createKubb as it, KubbSuccessContext as j, KubbPluginSetupContext as k, Group as l, Generator as lt, KubbBuildStartContext as m, RendererFactory as mt, AdapterFactoryOptions as n, KubbHooks as nt, DevtoolsOptions as o, FileManager as ot, KubbBuildEndContext as p, Renderer as pt, ResolverFileParams as q, AdapterSource as r, BuildOutput as rt, Exclude$1 as s, Plugin as st, Adapter as t, Kubb$1 as tt, Include as u, defineGenerator as ut, KubbFileProcessingUpdateContext as v, KubbHookEndContext as w, KubbGenerationEndContext as x, KubbFilesProcessingEndContext as y, Override as z };
1875
+ //# sourceMappingURL=types-CgSRMX_8.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/core",
3
- "version": "5.0.0-alpha.54",
3
+ "version": "5.0.0-alpha.55",
4
4
  "description": "Core functionality for Kubb's plugin-based code generation system, providing the foundation for transforming OpenAPI specifications.",
5
5
  "keywords": [
6
6
  "ast",
@@ -65,15 +65,15 @@
65
65
  "dependencies": {
66
66
  "fflate": "^0.8.2",
67
67
  "tinyexec": "^1.1.1",
68
- "@kubb/ast": "5.0.0-alpha.54"
68
+ "@kubb/ast": "5.0.0-alpha.55"
69
69
  },
70
70
  "devDependencies": {
71
71
  "p-limit": "^7.3.0",
72
72
  "@internals/utils": "0.0.0",
73
- "@kubb/renderer-jsx": "5.0.0-alpha.54"
73
+ "@kubb/renderer-jsx": "5.0.0-alpha.55"
74
74
  },
75
75
  "peerDependencies": {
76
- "@kubb/renderer-jsx": "5.0.0-alpha.54"
76
+ "@kubb/renderer-jsx": "5.0.0-alpha.55"
77
77
  },
78
78
  "size-limit": [
79
79
  {
@@ -1,7 +1,5 @@
1
- import { trimExtName } from '@internals/utils'
2
1
  import type { FileNode } from '@kubb/ast'
3
2
  import { createFile } from '@kubb/ast'
4
- import { BARREL_BASENAME } from './constants.ts'
5
3
 
6
4
  function mergeFile<TMeta extends object = object>(a: FileNode<TMeta>, b: FileNode<TMeta>): FileNode<TMeta> {
7
5
  return {
@@ -95,39 +93,23 @@ export class FileManager {
95
93
 
96
94
  /**
97
95
  * All stored files, sorted by path length (shorter paths first).
98
- * Barrel/index files (e.g. index.ts) are sorted last within each length bucket.
99
96
  */
100
97
  get files(): Array<FileNode> {
101
98
  if (this.#filesCache) {
102
99
  return this.#filesCache
103
100
  }
104
101
 
105
- // Precompute the barrel-file flag per key so the comparator avoids repeated string work.
106
- const keys = [...this.#cache.keys()]
107
- const meta = new Map<string, { length: number; isIndex: boolean }>()
108
- for (const key of keys) {
109
- meta.set(key, {
110
- length: key.length,
111
- isIndex: trimExtName(key).endsWith(BARREL_BASENAME),
112
- })
113
- }
114
- keys.sort((a, b) => {
115
- const ma = meta.get(a)!
116
- const mb = meta.get(b)!
117
- if (ma.length !== mb.length) return ma.length - mb.length
118
- if (ma.isIndex !== mb.isIndex) return ma.isIndex ? 1 : -1
102
+ this.#filesCache = [...this.#cache.values()].sort((a, b) => {
103
+ const lenDiff = a.path.length - b.path.length
104
+ if (lenDiff !== 0) return lenDiff
105
+ // Within the same length bucket, index.ts barrel files go last so other
106
+ // files are always processed before their barrel file.
107
+ const aIsIndex = a.path.endsWith('/index.ts') || a.path === 'index.ts'
108
+ const bIsIndex = b.path.endsWith('/index.ts') || b.path === 'index.ts'
109
+ if (aIsIndex && !bIsIndex) return 1
110
+ if (!aIsIndex && bIsIndex) return -1
119
111
  return 0
120
112
  })
121
-
122
- const files: Array<FileNode> = []
123
- for (const key of keys) {
124
- const file = this.#cache.get(key)
125
- if (file) {
126
- files.push(file)
127
- }
128
- }
129
-
130
- this.#filesCache = files
131
- return files
113
+ return this.#filesCache
132
114
  }
133
115
  }
package/src/Kubb.ts CHANGED
@@ -273,5 +273,47 @@ declare global {
273
273
  * ```
274
274
  */
275
275
  interface PluginRegistry {}
276
+
277
+ /**
278
+ * Extension point for root `Config['output']` options.
279
+ * Augment the `output` key in middleware or plugin packages to add extra fields
280
+ * to the global output configuration without touching core types.
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * // packages/middleware-barrel/src/types.ts
285
+ * declare global {
286
+ * namespace Kubb {
287
+ * interface ConfigOptionsRegistry {
288
+ * output: {
289
+ * barrelType?: import('./types.ts').BarrelType | false
290
+ * }
291
+ * }
292
+ * }
293
+ * }
294
+ * ```
295
+ */
296
+ interface ConfigOptionsRegistry {}
297
+
298
+ /**
299
+ * Extension point for per-plugin `Output` options.
300
+ * Augment the `output` key in middleware or plugin packages to add extra fields
301
+ * to the per-plugin output configuration without touching core types.
302
+ *
303
+ * @example
304
+ * ```ts
305
+ * // packages/middleware-barrel/src/types.ts
306
+ * declare global {
307
+ * namespace Kubb {
308
+ * interface PluginOptionsRegistry {
309
+ * output: {
310
+ * barrelType?: import('./types.ts').BarrelType | false
311
+ * }
312
+ * }
313
+ * }
314
+ * }
315
+ * ```
316
+ */
317
+ interface PluginOptionsRegistry {}
276
318
  }
277
319
  }
@@ -28,6 +28,10 @@ type Options = {
28
28
  hooks: AsyncEventEmitter<KubbHooks>
29
29
  }
30
30
 
31
+ function enforceOrder(enforce: 'pre' | 'post' | undefined): number {
32
+ return enforce === 'pre' ? -1 : enforce === 'post' ? 1 : 0
33
+ }
34
+
31
35
  export class PluginDriver {
32
36
  readonly config: Config
33
37
  readonly options: Options
@@ -88,7 +92,8 @@ export class PluginDriver {
88
92
  .sort((a, b) => {
89
93
  if (b.dependencies?.includes(a.name)) return -1
90
94
  if (a.dependencies?.includes(b.name)) return 1
91
- return 0
95
+ // enforce: 'pre' plugins run first, 'post' plugins run last
96
+ return enforceOrder(a.enforce) - enforceOrder(b.enforce)
92
97
  })
93
98
  .forEach((plugin) => {
94
99
  this.plugins.set(plugin.name, plugin)
@@ -107,6 +112,7 @@ export class PluginDriver {
107
112
  const normalizedPlugin = {
108
113
  name: hookPlugin.name,
109
114
  dependencies: hookPlugin.dependencies,
115
+ enforce: hookPlugin.enforce,
110
116
  options: { output: { path: '.' }, exclude: [], override: [] },
111
117
  } as unknown as NormalizedPlugin
112
118
 
package/src/constants.ts CHANGED
@@ -15,19 +15,6 @@ export const DEFAULT_CONCURRENCY = 15
15
15
  */
16
16
  export const PARALLEL_CONCURRENCY_LIMIT = 100
17
17
 
18
- /**
19
- * Basename (without extension) of generated barrel files.
20
- *
21
- * Used to detect whether a path already points at a barrel so the generator
22
- * avoids re-creating one on top of it.
23
- */
24
- export const BARREL_BASENAME = 'index' as const
25
-
26
- /**
27
- * File name used for generated barrel (index) files.
28
- */
29
- export const BARREL_FILENAME = `${BARREL_BASENAME}.ts` as const
30
-
31
18
  /**
32
19
  * Default banner style written at the top of every generated file.
33
20
  */