Skip to content

Commit

Permalink
fix: better types for plugin api
Browse files Browse the repository at this point in the history
  • Loading branch information
stijnvanhulle committed Oct 21, 2023
1 parent 3fb7bdd commit a4303b7
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 66 deletions.
48 changes: 27 additions & 21 deletions packages/core/src/managers/pluginManager/PluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,21 @@ import { PluginError } from './PluginError.ts'
import { pluginParser } from './pluginParser.ts'

import type { CorePluginOptions } from '../../plugin.ts'
import type {KubbConfig, KubbPlugin, KubbUserPlugin,PluginContext, PluginLifecycle, PluginLifecycleHooks, PossiblePromise, ResolveNameParams, ResolvePathParams} from '../../types.ts';
import type {
KubbConfig,
KubbPlugin,
KubbUserPlugin,
PluginContext,
PluginLifecycle,
PluginLifecycleHooks,
PossiblePromise,
ResolveNameParams,
ResolvePathParams,
} from '../../types.ts'
import type { Logger } from '../../utils/logger.ts'
import type { QueueJob } from '../../utils/Queue.ts'
import type { KubbFile } from '../fileManager/types.ts'
import type { Argument0, Executer, ParseResult, SafeParseResult, Strategy } from './types.ts'
import type {Argument0, Executer, ParseResult, PluginParameter, RequiredPluginLifecycle, SafeParseResult, Strategy } from './types.ts'

// inspired by: https://github.com/rollup/rollup/blob/master/src/utils/PluginDriver.ts#

Expand Down Expand Up @@ -67,25 +77,22 @@ export class PluginManager {
resolvePath: this.resolvePath.bind(this),
resolveName: this.resolveName.bind(this),
getPlugins: this.#getSortedPlugins.bind(this),
plugin: undefined as unknown as KubbPlugin
}) as KubbPlugin<CorePluginOptions> & {
api: (this: Omit<PluginContext, 'addFile'>) => CorePluginOptions['api']
api: (this: Omit<PluginContext, 'addFile'>) => CorePluginOptions['api'];
plugin?: undefined
}

this.#core = pluginParser(core, core.api.call(null as any)) as KubbPlugin<CorePluginOptions>

this.plugins = [this.#core, ...(config.plugins || [])].reduce((prev, plugin) => {
// TODO HACK to be sure that this is equal to the `core.api` logic.

const convertedApi = pluginParser(plugin as KubbUserPlugin, this.#core?.api)

if (convertedApi) {
return [...prev, convertedApi]
}

return [...prev, plugin]
return [...prev, convertedApi]
}, [] as KubbPlugin[])

return this
return this
}

resolvePath = (params: ResolvePathParams): KubbFile.OptionalPath => {
Expand Down Expand Up @@ -133,7 +140,7 @@ export class PluginManager {
}: {
pluginName: string
hookName: H
parameters: Parameters<PluginLifecycle[H]>
parameters: PluginParameter<H>
}): Promise<ReturnType<ParseResult<H>> | null> | null {
const plugin = this.getPlugin(hookName, pluginName)

Expand All @@ -152,7 +159,7 @@ export class PluginManager {
}: {
pluginName: string
hookName: H
parameters: Parameters<PluginLifecycle[H]>
parameters: PluginParameter<H>
}): ReturnType<ParseResult<H>> | null {
const plugin = this.getPlugin(hookName, pluginName)

Expand All @@ -173,7 +180,7 @@ export class PluginManager {
skipped,
}: {
hookName: H
parameters: Parameters<PluginLifecycle[H]>
parameters: PluginParameter<H>
skipped?: ReadonlySet<KubbPlugin> | null
}): Promise<SafeParseResult<H>> {
let promise: Promise<SafeParseResult<H>> = Promise.resolve(null as unknown as SafeParseResult<H>)
Expand Down Expand Up @@ -214,7 +221,7 @@ export class PluginManager {
skipped,
}: {
hookName: H
parameters: Parameters<PluginLifecycle[H]>
parameters: PluginParameter<H>
skipped?: ReadonlySet<KubbPlugin> | null
}): SafeParseResult<H> {
let parseResult: SafeParseResult<H> = null as unknown as SafeParseResult<H>
Expand Down Expand Up @@ -249,7 +256,7 @@ export class PluginManager {
parameters,
}: {
hookName: H
parameters?: Parameters<PluginLifecycle[H]> | undefined
parameters?: Parameters<RequiredPluginLifecycle[H]> | undefined
}): Promise<Awaited<TOuput>[]> {
const parallelPromises: Promise<TOuput>[] = []

Expand Down Expand Up @@ -299,7 +306,7 @@ export class PluginManager {
reduce,
}: {
hookName: H
parameters: Parameters<PluginLifecycle[H]>
parameters: PluginParameter<H>
reduce: (reduction: Argument0<H>, result: ReturnType<ParseResult<H>>, plugin: KubbPlugin) => PossiblePromise<Argument0<H> | null>
}): Promise<Argument0<H>> {
const [argument0, ...rest] = parameters
Expand All @@ -311,7 +318,7 @@ export class PluginManager {
const value = this.#execute({
strategy: 'hookReduceArg0',
hookName,
parameters: [arg0, ...rest] as Parameters<PluginLifecycle[H]>,
parameters: [arg0, ...rest] as PluginParameter<H>,
plugin,
})
return value
Expand All @@ -324,7 +331,7 @@ export class PluginManager {
/**
* Chains plugins
*/
hookSeq<H extends PluginLifecycleHooks>({ hookName, parameters }: { hookName: H; parameters?: Parameters<PluginLifecycle[H]> }): Promise<void> {
hookSeq<H extends PluginLifecycleHooks>({ hookName, parameters }: { hookName: H; parameters?: PluginParameter<H> }): Promise<void> {
let promise: Promise<void | null> = Promise.resolve()
for (const plugin of this.#getSortedPlugins()) {
promise = promise.then(() => {
Expand All @@ -342,7 +349,6 @@ export class PluginManager {
#getSortedPlugins(hookName?: keyof PluginLifecycle): KubbPlugin[] {
const plugins = [...this.plugins].filter((plugin) => plugin.name !== 'core')


if (hookName) {
return plugins.filter((item) => item[hookName])
}
Expand Down Expand Up @@ -399,7 +405,7 @@ export class PluginManager {
const task = Promise.resolve()
.then(() => {
if (typeof hook === 'function') {
const possiblePromiseResult = (hook as Function).apply(this.#core.api, parameters) as TResult
const possiblePromiseResult = (hook as Function).apply({ ...this.#core.api, plugin }, parameters) as TResult

if (isPromise(possiblePromiseResult)) {
return Promise.resolve(possiblePromiseResult)
Expand Down Expand Up @@ -447,7 +453,7 @@ export class PluginManager {
}: {
strategy: Strategy
hookName: H
parameters: Parameters<PluginLifecycle[H]>
parameters: PluginParameter<H>
plugin: KubbPlugin
}): ReturnType<ParseResult<H>> | null {
const hook = plugin[hookName]
Expand Down
17 changes: 13 additions & 4 deletions packages/core/src/managers/pluginManager/pluginParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,28 @@ import { getUniqueName } from '../../utils/getUniqueName.ts'
import type { CorePluginOptions } from '../../plugin.ts'
import type { KubbPlugin, KubbUserPlugin } from '../../types.ts'

const usedPluginNames: Record<string, number>= {}
const usedPluginNames: Record<string, number> = {}

export function pluginParser<TPlugin extends KubbUserPlugin>(plugin: TPlugin, context: CorePluginOptions['api'] | undefined): KubbPlugin {
const key = [plugin.kind, plugin.name, getUniqueName(plugin.name, usedPluginNames).split(plugin.name).at(1)] as [
typeof plugin.kind,
typeof plugin.name,
string,
]

export function pluginParser<TPlugin extends KubbUserPlugin>(plugin: TPlugin, context: CorePluginOptions['api'] | undefined): KubbPlugin | null {
if (plugin.api && typeof plugin.api === 'function') {
// eslint-disable-next-line @typescript-eslint/ban-types
const api = (plugin.api as Function).call(context) as typeof plugin.api

return {
...plugin,
key: [plugin.kind,...getUniqueName(plugin.name, usedPluginNames).split(plugin.name)] as [typeof plugin.kind, typeof plugin.name, string],
key,
api,
}
}

return null
return {
...plugin,
key,
} as KubbPlugin
}
8 changes: 6 additions & 2 deletions packages/core/src/managers/pluginManager/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { KubbPlugin, PluginLifecycle, PluginLifecycleHooks } from '../../types.ts'

export type RequiredPluginLifecycle =Required<PluginLifecycle>

/**
* Get the type of the first argument in a function.
* @example Arg0<(a: string, b: number) => void> -> string
*/
export type Argument0<H extends keyof PluginLifecycle> = Parameters<PluginLifecycle[H]>[0]
export type Argument0<H extends keyof PluginLifecycle> = Parameters<RequiredPluginLifecycle[H]>[0]

export type Strategy = 'hookFirst' | 'hookForPlugin' | 'hookParallel' | 'hookReduceArg0' | 'hookSeq'

Expand All @@ -16,9 +18,11 @@ export type Executer<H extends PluginLifecycleHooks = PluginLifecycleHooks> = {
output?: unknown
}

export type ParseResult<H extends PluginLifecycleHooks> = PluginLifecycle[H]
export type ParseResult<H extends PluginLifecycleHooks> = RequiredPluginLifecycle[H]

export type SafeParseResult<H extends PluginLifecycleHooks, Result = ReturnType<ParseResult<H>>> = {
result: Result
plugin: KubbPlugin
}

export type PluginParameter<H extends PluginLifecycleHooks> = Parameters<RequiredPluginLifecycle[H]>
13 changes: 9 additions & 4 deletions packages/core/src/managers/pluginManager/validate.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import type { KubbPlugin,PluginFactoryOptions } from '../../types.ts'

import type { KubbPlugin, PluginFactoryOptions } from '../../types.ts'

export class ValidationPluginError extends Error {}

// export function getDependedPlugins<T1 extends PluginFactoryOptions>(plugins:Array<KubbPlugin>, dependedPluginNames: string | string[]): [KubbPlugin<T1>]
// export function getDependedPlugins<T1 extends PluginFactoryOptions, T2 extends PluginFactoryOptions>(plugins:Array<KubbPlugin>, dependedPluginNames: string | string[]): [KubbPlugin<T1>, KubbPlugin<T2>]
export function getDependedPlugins<T1 extends PluginFactoryOptions, T2 extends PluginFactoryOptions=never,T3 extends PluginFactoryOptions=never, TOutput= T3 extends never? T2 extends never ? [KubbPlugin<T1>] : [KubbPlugin<T1>, KubbPlugin<T2>]: [KubbPlugin<T1>, KubbPlugin<T2>, KubbPlugin<T3>]>(plugins:Array<KubbPlugin>, dependedPluginNames: string | string[]): TOutput{
export function getDependedPlugins<
T1 extends PluginFactoryOptions,
T2 extends PluginFactoryOptions = never,
T3 extends PluginFactoryOptions = never,
TOutput = T3 extends never ? T2 extends never ? [T1: KubbPlugin<T1>] : [T1: KubbPlugin<T1>, T2: KubbPlugin<T2>]
: [T1: KubbPlugin<T1>, T2: KubbPlugin<T2>, T3: KubbPlugin<T3>],
>(plugins: Array<KubbPlugin>, dependedPluginNames: string | string[]): TOutput {
let pluginNames: string[] = []
if (typeof dependedPluginNames === 'string') {
pluginNames = [dependedPluginNames]
Expand All @@ -19,5 +24,5 @@ export function getDependedPlugins<T1 extends PluginFactoryOptions, T2 extends P
throw new ValidationPluginError(`This plugin depends on the ${pluginName} plugin.`)
}
return plugin
}) as TOutput
}) as TOutput
}
1 change: 1 addition & 0 deletions packages/core/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Options = {
resolveName: PluginContext['resolveName']
logger: PluginContext['logger']
getPlugins: () => KubbPlugin[]
plugin: KubbPlugin
}

// not publicly exported
Expand Down
Loading

1 comment on commit a4303b7

@vercel
Copy link

@vercel vercel bot commented on a4303b7 Oct 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

kubb – ./

www.kubb.dev
kubb-git-main-kubb.vercel.app
kubb-kubb.vercel.app
kubb.dev

Please sign in to comment.