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

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
  }
@@ -730,6 +753,13 @@ interface KubbHooks {
730
753
  * The adapter has already parsed the source and `inputNode` is available.
731
754
  */
732
755
  'kubb:build:start': [ctx: KubbBuildStartContext];
756
+ /**
757
+ * Fired after all plugins have run and all per-plugin barrels have been generated,
758
+ * but BEFORE files are written to disk.
759
+ * Use this event to inject final files (e.g. a root barrel) that must be persisted
760
+ * in the same write pass as the plugin output.
761
+ */
762
+ 'kubb:plugins:end': [ctx: KubbPluginsEndContext];
733
763
  /**
734
764
  * Fired after all files have been written to disk.
735
765
  */
@@ -776,75 +806,155 @@ declare global {
776
806
  * ```
777
807
  */
778
808
  interface PluginRegistry {}
809
+ /**
810
+ * Extension point for root `Config['output']` options.
811
+ * Augment the `output` key in middleware or plugin packages to add extra fields
812
+ * to the global output configuration without touching core types.
813
+ *
814
+ * @example
815
+ * ```ts
816
+ * // packages/middleware-barrel/src/types.ts
817
+ * declare global {
818
+ * namespace Kubb {
819
+ * interface ConfigOptionsRegistry {
820
+ * output: {
821
+ * barrelType?: import('./types.ts').BarrelType | false
822
+ * }
823
+ * }
824
+ * }
825
+ * }
826
+ * ```
827
+ */
828
+ interface ConfigOptionsRegistry {}
829
+ /**
830
+ * Extension point for per-plugin `Output` options.
831
+ * Augment the `output` key in middleware or plugin packages to add extra fields
832
+ * to the per-plugin output configuration without touching core types.
833
+ *
834
+ * @example
835
+ * ```ts
836
+ * // packages/middleware-barrel/src/types.ts
837
+ * declare global {
838
+ * namespace Kubb {
839
+ * interface PluginOptionsRegistry {
840
+ * output: {
841
+ * barrelType?: import('./types.ts').BarrelType | false
842
+ * }
843
+ * }
844
+ * }
845
+ * }
846
+ * ```
847
+ */
848
+ interface PluginOptionsRegistry {}
779
849
  }
780
850
  }
781
851
  //#endregion
782
- //#region src/definePlugin.d.ts
852
+ //#region src/defineMiddleware.d.ts
783
853
  /**
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).
854
+ * A middleware observes and post-processes the build output produced by plugins.
855
+ * It attaches listeners to the shared `hooks` emitter before the plugin execution loop
856
+ * begins and reacts to lifecycle events (e.g. `kubb:plugin:end`, `kubb:build:end`) to
857
+ * inject barrel files or perform other cross-cutting concerns.
787
858
  *
788
- * @template TFactory - The plugin's `PluginFactoryOptions` type.
859
+ * Middleware listeners are always registered **after** all plugin listeners, because
860
+ * `createKubb` installs middleware only after the `PluginDriver` has registered every
861
+ * plugin's hooks. This means middleware hooks for any event always fire last.
862
+ *
863
+ * @example
864
+ * ```ts
865
+ * import { defineMiddleware } from '@kubb/core'
866
+ *
867
+ * export const myMiddleware = defineMiddleware({
868
+ * name: 'my-middleware',
869
+ * install(hooks) {
870
+ * hooks.on('kubb:build:end', ({ files }) => {
871
+ * console.log(`Build complete with ${files.length} files`)
872
+ * })
873
+ * },
874
+ * })
875
+ * ```
789
876
  */
790
- type Plugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions> = {
877
+ type Middleware = {
791
878
  /**
792
- * Unique name for the plugin, following the same naming convention as `createPlugin`.
879
+ * Unique identifier for this middleware.
793
880
  */
794
881
  name: string;
795
882
  /**
796
- * Plugins that must be registered before this plugin executes.
797
- * An error is thrown at startup when any listed dependency is missing.
883
+ * Called during `createKubb` after `setup()` but before the plugin
884
+ * execution loop starts. Attach listeners to `hooks` here.
798
885
  */
799
- dependencies?: Array<string>;
886
+ install(hooks: AsyncEventEmitter<KubbHooks>): void;
887
+ };
888
+ /**
889
+ * Identity factory for middleware.
890
+ * Returns the middleware object unchanged but provides a typed entry-point
891
+ * to define middleware with proper inference.
892
+ *
893
+ * @example
894
+ * ```ts
895
+ * export const myMiddleware = defineMiddleware({
896
+ * name: 'my-middleware',
897
+ * install(hooks) {
898
+ * hooks.on('kubb:build:end', ({ files }) => {
899
+ * console.log(`Build complete with ${files.length} files`)
900
+ * })
901
+ * },
902
+ * })
903
+ * ```
904
+ */
905
+ declare function defineMiddleware(middleware: Middleware): Middleware;
906
+ //#endregion
907
+ //#region src/defineParser.d.ts
908
+ type PrintOptions = {
909
+ extname?: FileNode['extname'];
910
+ };
911
+ type Parser<TMeta extends object = any> = {
912
+ name: string;
800
913
  /**
801
- * The options passed by the user when calling the plugin factory.
914
+ * File extensions this parser handles.
915
+ * Use `undefined` to create a catch-all fallback parser.
916
+ *
917
+ * @example Handled extensions
918
+ * `['.ts', '.js']`
802
919
  */
803
- options?: TFactory['options'];
920
+ extNames: Array<FileNode['extname']> | undefined;
804
921
  /**
805
- * Lifecycle event handlers for this plugin.
806
- * Any event from the global `KubbHooks` map can be subscribed to here.
922
+ * Convert a resolved file to a string.
807
923
  */
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
- };
924
+ parse(file: FileNode<TMeta>, options?: PrintOptions): Promise<string> | string;
811
925
  };
812
926
  /**
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`.
927
+ * Defines a parser with type safety.
818
928
  *
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.
929
+ * Use this function to create parsers that transform generated files to strings
930
+ * based on their extension.
821
931
  *
822
932
  * @example
823
933
  * ```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
- * },
934
+ * import { defineParser } from '@kubb/core'
935
+ *
936
+ * export const jsonParser = defineParser({
937
+ * name: 'json',
938
+ * extNames: ['.json'],
939
+ * parse(file) {
940
+ * const { extractStringsFromNodes } = await import('@kubb/ast')
941
+ * return file.sources.map((s) => extractStringsFromNodes(s.nodes ?? [])).join('\n')
831
942
  * },
832
- * }))
943
+ * })
833
944
  * ```
834
945
  */
835
- declare function definePlugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions>(factory: (options: TFactory['options']) => Plugin<TFactory>): (options?: TFactory['options']) => Plugin<TFactory>;
946
+ declare function defineParser<TMeta extends object = any>(parser: Parser<TMeta>): Parser<TMeta>;
836
947
  //#endregion
837
- //#region src/utils/getBarrelFiles.d.ts
948
+ //#region src/types.d.ts
838
949
  /**
839
- * Minimal file metadata attached to every generated file for barrel-file bookkeeping.
950
+ * Safely extracts the type of key `K` from `T`, returning `{}` when `K` is not a key of `T`.
951
+ * Used to implement optional interface augmentation for `Kubb.ConfigOptionsRegistry` and
952
+ * `Kubb.PluginOptionsRegistry` so that middleware and plugin packages can extend core types
953
+ * without requiring modifications to core.
840
954
  *
841
955
  * @internal
842
956
  */
843
- type FileMetaBase = {
844
- pluginName?: string;
845
- };
846
- //#endregion
847
- //#region src/types.d.ts
957
+ type ExtractRegistryKey<T, K extends PropertyKey> = K extends keyof T ? T[K] : {};
848
958
  type InputPath = {
849
959
  /**
850
960
  * Specify your Swagger/OpenAPI file, either as an absolute path or a path relative to the root.
@@ -936,13 +1046,6 @@ type Adapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions> = {
936
1046
  path: string;
937
1047
  }) => Array<ImportNode>;
938
1048
  };
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
1049
  type DevtoolsOptions = {
947
1050
  /**
948
1051
  * Open the AST inspector view (`/ast`) in Kubb Studio.
@@ -1058,11 +1161,6 @@ type Config<TInput = Input> = {
1058
1161
  * @default { '.ts': '.ts'}
1059
1162
  */
1060
1163
  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
1164
  /**
1067
1165
  * Adds a default banner to the start of every generated file indicating it was generated by Kubb.
1068
1166
  * - 'simple' adds banner with link to Kubb.
@@ -1078,7 +1176,7 @@ type Config<TInput = Input> = {
1078
1176
  * @default false
1079
1177
  */
1080
1178
  override?: boolean;
1081
- };
1179
+ } & ExtractRegistryKey<Kubb.ConfigOptionsRegistry, 'output'>;
1082
1180
  /**
1083
1181
  * An array of Kubb plugins used for code generation.
1084
1182
  * Each plugin may declare additional configurable options.
@@ -1086,6 +1184,22 @@ type Config<TInput = Input> = {
1086
1184
  * Use `dependencies` on the plugin to declare execution order.
1087
1185
  */
1088
1186
  plugins: Array<Plugin>;
1187
+ /**
1188
+ * Middleware instances that observe and post-process the build output.
1189
+ * Each middleware receives the `hooks` emitter and attaches its own listeners.
1190
+ * Middleware listeners are always registered after all plugin listeners,
1191
+ * so middleware hooks fire last for any given event.
1192
+ *
1193
+ * @example
1194
+ * ```ts
1195
+ * import { middlewareBarrel } from '@kubb/middleware-barrel'
1196
+ * export default defineConfig({
1197
+ * middleware: [middlewareBarrel],
1198
+ * plugins: [pluginTs(), pluginZod()],
1199
+ * })
1200
+ * ```
1201
+ */
1202
+ middleware?: Array<Middleware>;
1089
1203
  /**
1090
1204
  * Project-wide renderer factory. All plugins and generators that do not declare their own
1091
1205
  * `renderer` ultimately fall back to this value.
@@ -1360,11 +1474,6 @@ type Output<_TOptions = unknown> = {
1360
1474
  * Path to the output folder or file that will contain generated code.
1361
1475
  */
1362
1476
  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
1477
  /**
1369
1478
  * Text or function appended at the start of every generated file.
1370
1479
  * When a function, receives the current `InputNode` and must return a string.
@@ -1380,7 +1489,7 @@ type Output<_TOptions = unknown> = {
1380
1489
  * @default false
1381
1490
  */
1382
1491
  override?: boolean;
1383
- };
1492
+ } & ExtractRegistryKey<Kubb.PluginOptionsRegistry, 'output'>;
1384
1493
  type Group = {
1385
1494
  /**
1386
1495
  * Determines how files are grouped into subdirectories.
@@ -1481,6 +1590,37 @@ type KubbBuildStartContext = {
1481
1590
  */
1482
1591
  getPlugin<TName extends keyof Kubb.PluginRegistry>(name: TName): Plugin<Kubb.PluginRegistry[TName]> | undefined;
1483
1592
  getPlugin(name: string): Plugin | undefined;
1593
+ /**
1594
+ * Returns all files currently in the file manager.
1595
+ * Call this lazily (e.g. inside a `kubb:plugin:end` listener) to see files added by plugins
1596
+ * that have already run.
1597
+ */
1598
+ readonly files: ReadonlyArray<FileNode>;
1599
+ /**
1600
+ * Upsert one or more files into the file manager.
1601
+ * Files with the same path are merged; new files are appended.
1602
+ * Safe to call at any point during the plugin lifecycle, including inside `kubb:plugin:end`.
1603
+ */
1604
+ upsertFile: (...files: Array<FileNode>) => void;
1605
+ };
1606
+ /**
1607
+ * Context passed to `kubb:plugins:end` handlers.
1608
+ * Fires after all plugins have run and per-plugin barrels have been written,
1609
+ * but BEFORE files are written to disk.
1610
+ * Middleware that needs to inject final files (e.g. a root barrel) should use this event.
1611
+ */
1612
+ type KubbPluginsEndContext = {
1613
+ config: Config;
1614
+ /**
1615
+ * Returns all files currently in the file manager (lazy snapshot).
1616
+ * Includes files added by plugins and per-plugin barrel middleware.
1617
+ */
1618
+ readonly files: ReadonlyArray<FileNode>;
1619
+ /**
1620
+ * Upsert one or more files into the file manager.
1621
+ * Files added here will be included in the write pass that follows.
1622
+ */
1623
+ upsertFile: (...files: Array<FileNode>) => void;
1484
1624
  };
1485
1625
  /**
1486
1626
  * Context passed to a hook-style plugin's `kubb:build:end` handler.
@@ -1757,5 +1897,5 @@ type CLIOptions = {
1757
1897
  */
1758
1898
  type PossibleConfig<TCliOptions = undefined> = PossiblePromise<Config | Config[]> | ((...args: [TCliOptions] extends [undefined] ? [] : [TCliOptions]) => PossiblePromise<Config | Config[]>);
1759
1899
  //#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
1900
+ export { defineParser as $, KubbPluginStartContext as A, Override as B, KubbGenerationSummaryContext as C, KubbLifecycleStartContext as D, KubbInfoContext 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, KubbPluginEndContext as O, KubbWarnContext as P, Parser as Q, NormalizedPlugin as R, KubbGenerationStartContext as S, KubbHookStartContext as T, ResolveBannerContext as U, PluginFactoryOptions as V, ResolveNameParams as W, UserConfig as X, ResolverPathParams as Y, UserLogger as Z, KubbErrorContext as _, logLevel as _t, Config as a, createKubb as at, KubbFilesProcessingStartContext as b, GeneratorContext as c, Plugin as ct, InputData as d, defineGenerator as dt, Middleware as et, InputPath as f, Storage as ft, KubbDebugContext as g, createRenderer as gt, KubbConfigEndContext as h, RendererFactory as ht, CLIOptions as i, BuildOutput as it, KubbPluginsEndContext as j, KubbPluginSetupContext as k, Group as l, definePlugin as lt, KubbBuildStartContext as m, Renderer as mt, AdapterFactoryOptions as n, Kubb$1 as nt, DevtoolsOptions as o, PluginDriver as ot, KubbBuildEndContext as p, createStorage as pt, ResolverContext as q, AdapterSource as r, KubbHooks as rt, Exclude$1 as s, FileManager as st, Adapter as t, defineMiddleware as tt, Include as u, Generator as ut, KubbFileProcessingUpdateContext as v, AsyncEventEmitter as vt, KubbHookEndContext as w, KubbGenerationEndContext as x, KubbFilesProcessingEndContext as y, Output as z };
1901
+ //# sourceMappingURL=types-BjL3cJ-T.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.56",
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.56"
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.56"
74
74
  },
75
75
  "peerDependencies": {
76
- "@kubb/renderer-jsx": "5.0.0-alpha.54"
76
+ "@kubb/renderer-jsx": "5.0.0-alpha.56"
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
@@ -23,6 +23,7 @@ import type {
23
23
  KubbPluginEndContext,
24
24
  KubbPluginSetupContext,
25
25
  KubbPluginStartContext,
26
+ KubbPluginsEndContext,
26
27
  KubbSuccessContext,
27
28
  KubbVersionNewContext,
28
29
  KubbWarnContext,
@@ -225,6 +226,13 @@ export interface KubbHooks {
225
226
  * The adapter has already parsed the source and `inputNode` is available.
226
227
  */
227
228
  'kubb:build:start': [ctx: KubbBuildStartContext]
229
+ /**
230
+ * Fired after all plugins have run and all per-plugin barrels have been generated,
231
+ * but BEFORE files are written to disk.
232
+ * Use this event to inject final files (e.g. a root barrel) that must be persisted
233
+ * in the same write pass as the plugin output.
234
+ */
235
+ 'kubb:plugins:end': [ctx: KubbPluginsEndContext]
228
236
  /**
229
237
  * Fired after all files have been written to disk.
230
238
  */
@@ -273,5 +281,47 @@ declare global {
273
281
  * ```
274
282
  */
275
283
  interface PluginRegistry {}
284
+
285
+ /**
286
+ * Extension point for root `Config['output']` options.
287
+ * Augment the `output` key in middleware or plugin packages to add extra fields
288
+ * to the global output configuration without touching core types.
289
+ *
290
+ * @example
291
+ * ```ts
292
+ * // packages/middleware-barrel/src/types.ts
293
+ * declare global {
294
+ * namespace Kubb {
295
+ * interface ConfigOptionsRegistry {
296
+ * output: {
297
+ * barrelType?: import('./types.ts').BarrelType | false
298
+ * }
299
+ * }
300
+ * }
301
+ * }
302
+ * ```
303
+ */
304
+ interface ConfigOptionsRegistry {}
305
+
306
+ /**
307
+ * Extension point for per-plugin `Output` options.
308
+ * Augment the `output` key in middleware or plugin packages to add extra fields
309
+ * to the per-plugin output configuration without touching core types.
310
+ *
311
+ * @example
312
+ * ```ts
313
+ * // packages/middleware-barrel/src/types.ts
314
+ * declare global {
315
+ * namespace Kubb {
316
+ * interface PluginOptionsRegistry {
317
+ * output: {
318
+ * barrelType?: import('./types.ts').BarrelType | false
319
+ * }
320
+ * }
321
+ * }
322
+ * }
323
+ * ```
324
+ */
325
+ interface PluginOptionsRegistry {}
276
326
  }
277
327
  }
@@ -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
  */