@kubb/core
Version:
Core functionality for Kubb's plugin-based code generation system, providing the foundation for transforming OpenAPI specifications.
393 lines (360 loc) • 13.3 kB
text/typescript
import type { KubbFile } from '@kubb/fabric-core/types'
import type { Fabric } from '@kubb/react-fabric'
import type { KubbEvents } from './Kubb.ts'
import type { PluginManager } from './PluginManager.ts'
import type { AsyncEventEmitter } from './utils/AsyncEventEmitter.ts'
import type { PossiblePromise } from './utils/types.ts'
declare global {
namespace Kubb {
interface PluginContext {}
}
}
/**
* Config used in `kubb.config.ts`
*
* @example
* import { defineConfig } from '@kubb/core'
* export default defineConfig({
* ...
* })
*/
export type UserConfig<TInput = Input> = Omit<Config<TInput>, 'root' | 'plugins'> & {
/**
* The project root directory, which can be either an absolute path or a path relative to the location of your `kubb.config.ts` file.
* @default process.cwd()
*/
root?: string
/**
* An array of Kubb plugins used for generation. Each plugin may have additional configurable options (defined within the plugin itself). If a plugin relies on another plugin, an error will occur if the required dependency is missing. Refer to “pre” for more details.
*/
// inject needs to be omitted because else we have a clash with the PluginManager instance
plugins?: Array<Omit<UnknownUserPlugin, 'inject'>>
}
export type InputPath = {
/**
* Specify your Swagger/OpenAPI file, either as an absolute path or a path relative to the root.
*/
path: string
}
export type InputData = {
/**
* A `string` or `object` that contains your Swagger/OpenAPI data.
*/
data: string | unknown
}
type Input = InputPath | InputData | Array<InputPath>
export type BarrelType = 'all' | 'named' | 'propagate'
/**
* @private
*/
export type Config<TInput = Input> = {
/**
* The name to display in the CLI output.
*/
name?: string
/**
* The project root directory, which can be either an absolute path or a path relative to the location of your `kubb.config.ts` file.
* @default process.cwd()
*/
root: string
/**
* You can use either `input.path` or `input.data`, depending on your specific needs.
*/
input: TInput
output: {
/**
* The path where all generated files will be exported.
* This can be an absolute path or a path relative to the specified root option.
*/
path: string
/**
* Clean the output directory before each build.
*/
clean?: boolean
/**
* Save files to the file system.
* @default true
*/
write?: boolean
/**
* Specifies the formatting tool to be used.
* @default prettier
*
* Possible values:
* - 'auto': Automatically detects and uses biome or prettier (in that order of preference).
* - 'prettier': Uses Prettier for code formatting.
* - 'biome': Uses Biome for code formatting.
*
*/
format?: 'auto' | 'prettier' | 'biome' | 'oxfmt' | false
/**
* Specifies the linter that should be used to analyze the code.
* The accepted values indicate different linting tools.
*
* Possible values:
* - 'auto': Automatically detects and uses biome, oxlint, or eslint (in that order of preference).
* - 'eslint': Represents the use of ESLint, a widely used JavaScript linter.
* - 'biome': Represents the Biome linter, a modern tool for code scanning.
* - 'oxlint': Represents the Oxlint tool for linting purposes.
*
*/
lint?: 'auto' | 'eslint' | 'biome' | 'oxlint' | false
/**
* Override the extension to the generated imports and exports, by default each plugin will add an extension
* @default { '.ts': '.ts'}
*/
extension?: Record<KubbFile.Extname, KubbFile.Extname | ''>
/**
* Specify how `index.ts` files should be created. You can also disable the generation of barrel files here. While each plugin has its own `barrelType` option, this setting controls the creation of the root barrel file, such as` src/gen/index.ts`.
* @default 'named'
*/
barrelType?: Exclude<BarrelType, 'propagate'> | false
/**
* Add a default banner to the beginning of every generated file. This makes it clear that the file was generated by Kubb.
* - 'simple': will only add banner with link to Kubb
* - 'full': will add source, title, description and the OpenAPI version used
* @default 'simple'
*/
defaultBanner?: 'simple' | 'full' | false
/**
* Whether to override existing external files if they already exist.
* When setting the option in the global configuration, all plugins inherit the same behavior by default.
* However, all plugins also have an `output.override` option, which can be used to override the behavior for a specific plugin.
* @default false
*/
override?: boolean
}
/**
* An array of Kubb plugins that will be used in the generation.
* Each plugin may include additional configurable options(defined in the plugin itself).
* If a plugin depends on another plugin, an error will be returned if the required dependency is missing. See pre for more details.
*/
plugins?: Array<Plugin>
/**
* Hooks that will be called when a specific action is triggered in Kubb.
*/
hooks?: {
/**
* Hook that will be triggered at the end of all executions.
* Useful for running Prettier or ESLint to format/lint your code.
*/
done?: string | Array<string>
}
}
// plugin
export type PluginFactoryOptions<
/**
* Name to be used for the plugin, this will also be used for they key.
*/
TName extends string = string,
/**
* Options of the plugin.
*/
TOptions extends object = object,
/**
* Options of the plugin that can be used later on, see `options` inside your plugin config.
*/
TResolvedOptions extends object = TOptions,
/**
* Context that you want to expose to other plugins.
*/
TContext = any,
/**
* When calling `resolvePath` you can specify better types.
*/
TResolvePathOptions extends object = object,
> = {
name: TName
/**
* Same behaviour like what has been done with `QueryKey` in `@tanstack/react-query`
*/
key: PluginKey<TName | string>
options: TOptions
resolvedOptions: TResolvedOptions
context: TContext
resolvePathOptions: TResolvePathOptions
}
export type PluginKey<TName> = [name: TName, identifier?: string | number]
export type GetPluginFactoryOptions<TPlugin extends UserPlugin> = TPlugin extends UserPlugin<infer X> ? X : never
export type UserPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = {
/**
* Unique name used for the plugin
* The name of the plugin follows the format scope:foo-bar or foo-bar, adding scope: can avoid naming conflicts with other plugins.
* @example @kubb/typescript
*/
name: TOptions['name']
/**
* Options set for a specific plugin(see kubb.config.js), passthrough of options.
*/
options: TOptions['resolvedOptions']
/**
* Specifies the preceding plugins for the current plugin. You can pass an array of preceding plugin names, and the current plugin will be executed after these plugins.
* Can be used to validate dependent plugins.
*/
pre?: Array<string>
/**
* Specifies the succeeding plugins for the current plugin. You can pass an array of succeeding plugin names, and the current plugin will be executed before these plugins.
*/
post?: Array<string>
inject?: (this: PluginContext<TOptions>, context: PluginContext<TOptions>) => TOptions['context']
}
export type UserPluginWithLifeCycle<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = UserPlugin<TOptions> & PluginLifecycle<TOptions>
type UnknownUserPlugin = UserPlugin<PluginFactoryOptions<any, any, any, any, any>>
export type Plugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = {
/**
* Unique name used for the plugin
* @example @kubb/typescript
*/
name: TOptions['name']
/**
* Internal key used when a developer uses more than one of the same plugin
* @private
*/
key: TOptions['key']
/**
* Specifies the preceding plugins for the current plugin. You can pass an array of preceding plugin names, and the current plugin will be executed after these plugins.
* Can be used to validate dependent plugins.
*/
pre?: Array<string>
/**
* Specifies the succeeding plugins for the current plugin. You can pass an array of succeeding plugin names, and the current plugin will be executed before these plugins.
*/
post?: Array<string>
/**
* Options set for a specific plugin(see kubb.config.js), passthrough of options.
*/
options: TOptions['resolvedOptions']
install: (this: PluginContext<TOptions>, context: PluginContext<TOptions>) => PossiblePromise<void>
/**
* Define a context that can be used by other plugins, see `PluginManager' where we convert from `UserPlugin` to `Plugin`(used when calling `definePlugin`).
*/
inject: (this: PluginContext<TOptions>, context: PluginContext<TOptions>) => TOptions['context']
}
export type PluginWithLifeCycle<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = Plugin<TOptions> & PluginLifecycle<TOptions>
export type PluginLifecycle<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = {
/**
* Start of the lifecycle of a plugin.
* @type hookParallel
*/
install?: (this: PluginContext<TOptions>, context: PluginContext<TOptions>) => PossiblePromise<void>
/**
* Resolve to a Path based on a baseName(example: `./Pet.ts`) and directory(example: `./models`).
* Options can als be included.
* @type hookFirst
* @example ('./Pet.ts', './src/gen/') => '/src/gen/Pet.ts'
*/
resolvePath?: (this: PluginContext<TOptions>, baseName: KubbFile.BaseName, mode?: KubbFile.Mode, options?: TOptions['resolvePathOptions']) => KubbFile.Path
/**
* Resolve to a name based on a string.
* Useful when converting to PascalCase or camelCase.
* @type hookFirst
* @example ('pet') => 'Pet'
*/
resolveName?: (this: PluginContext<TOptions>, name: ResolveNameParams['name'], type?: ResolveNameParams['type']) => string
}
export type PluginLifecycleHooks = keyof PluginLifecycle
export type PluginParameter<H extends PluginLifecycleHooks> = Parameters<Required<PluginLifecycle>[H]>
export type ResolvePathParams<TOptions = object> = {
pluginKey?: Plugin['key']
baseName: KubbFile.BaseName
mode?: KubbFile.Mode
/**
* Options to be passed to 'resolvePath' 3th parameter
*/
options?: TOptions
}
export type ResolveNameParams = {
name: string
pluginKey?: Plugin['key']
/**
* `file` will be used to customize the name of the created file(use of camelCase)
* `function` can be used to customize the exported functions(use of camelCase)
* `type` is a special type for TypeScript(use of PascalCase)
* `const` can be used for variables(use of camelCase)
*/
type?: 'file' | 'function' | 'type' | 'const'
}
export type PluginContext<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = {
fabric: Fabric
config: Config
pluginManager: PluginManager
/**
* Only add when the file does not exist yet
*/
addFile: (...file: Array<KubbFile.File>) => Promise<void>
/**
* merging multiple sources into the same output file
*/
upsertFile: (...file: Array<KubbFile.File>) => Promise<void>
events: AsyncEventEmitter<KubbEvents>
mode: KubbFile.Mode
/**
* Current plugin
*/
plugin: Plugin<TOptions>
} & Kubb.PluginContext
/**
* Specify the export location for the files and define the behavior of the output
*/
export type Output<TOptions> = {
/**
* Path to the output folder or file that will contain the generated code
*/
path: string
/**
* Define what needs to be exported, here you can also disable the export of barrel files
* @default 'named'
*/
barrelType?: BarrelType | false
/**
* Add a banner text in the beginning of every file
*/
banner?: string | ((options: TOptions) => string)
/**
* Add a footer text in the beginning of every file
*/
footer?: string | ((options: TOptions) => string)
/**
* Whether to override existing external files if they already exist.
* @default false
*/
override?: boolean
}
type GroupContext = {
group: string
}
export type Group = {
/**
* Define a type where to group the files on
*/
type: 'tag' | 'path'
/**
* Return the name of a group based on the group name, this will be used for the file and name generation
*/
name?: (context: GroupContext) => string
}
export const LogLevel = {
silent: Number.NEGATIVE_INFINITY,
error: 0,
warn: 1,
info: 3,
verbose: 4,
debug: 5,
} as const
export type LoggerOptions = {
/**
* @default 3
*/
logLevel: (typeof LogLevel)[keyof typeof LogLevel]
}
/**
* Shared context passed to all plugins, parsers, and Fabric internals.
*/
export interface LoggerContext extends AsyncEventEmitter<KubbEvents> {}
type Install<TOptions = unknown> = (context: LoggerContext, options?: TOptions) => void | Promise<void>
export type Logger<TOptions extends LoggerOptions = LoggerOptions> = {
name: string
install: Install<TOptions>
}
export type UserLogger<TOptions extends LoggerOptions = LoggerOptions> = Omit<Logger<TOptions>, 'logLevel'>
export type { KubbEvents } from './Kubb.ts'