@kubb/core
Advanced tools
Comparing version
@@ -108,5 +108,5 @@ import { DirectoryTreeOptions } from 'directory-tree'; | ||
} & Partial<PluginLifecycle<TOptions>>; | ||
type PluginFactoryOptions<Options = unknown, Nested extends boolean = false, Api = any, ResolveIdOptions = Record<string, any>> = { | ||
type PluginFactoryOptions<Options = unknown, Nested extends boolean = false, Api = any, resolvePathOptions = Record<string, any>> = { | ||
options: Options; | ||
resolveIdOptions: ResolveIdOptions; | ||
resolvePathOptions: resolvePathOptions; | ||
nested: Nested; | ||
@@ -127,11 +127,19 @@ api: Api; | ||
/** | ||
* Resolve to an id based on importee(example: `./Pet.ts`) and directory(example: `./models`). | ||
* Resolve to a Path based on a fileName(example: `./Pet.ts`) and directory(example: `./models`). | ||
* Options can als be included. | ||
* @type hookFirst | ||
* @example ('./Pet.ts', './src/gen/') | ||
* @example ('./Pet.ts', './src/gen/') => '/src/gen/Pet.ts' | ||
*/ | ||
resolveId: (this: Omit<PluginContext, 'addFile'>, fileName: string, directory?: string, options?: TOptions['resolveIdOptions']) => OptionalPath; | ||
resolvePath: (this: Omit<PluginContext, 'addFile'>, fileName: string, directory?: string, options?: TOptions['resolvePathOptions']) => OptionalPath; | ||
/** | ||
* Makes it possible to run async logic to override the path defined previously by `resolveId`. | ||
* Resolve to a name based on a string. | ||
* Useful when converting to PascalCase or camelCase. | ||
* @type hookFirst | ||
* @example ('pet') => 'Pet' | ||
*/ | ||
resolveName: (this: Omit<PluginContext, 'addFile'>, name: string) => string | null; | ||
/** | ||
* Makes it possible to run async logic to override the path defined previously by `resolvePath`. | ||
* @type hookFirst | ||
*/ | ||
load: (this: Omit<PluginContext, 'addFile'>, path: Path) => MaybePromise<TransformResult | null>; | ||
@@ -144,3 +152,3 @@ /** | ||
/** | ||
* Write the result to the file-system based on the id(defined by `resolveId` or changed by `load`). | ||
* Write the result to the file-system based on the id(defined by `resolvePath` or changed by `load`). | ||
* @type hookParallel | ||
@@ -156,15 +164,23 @@ */ | ||
type PluginLifecycleHooks = keyof PluginLifecycle; | ||
type ResolveIdParams<TOptions = Record<string, any>> = { | ||
fileName: string; | ||
directory?: string | undefined; | ||
type ResolvePathParams<TOptions = Record<string, any>> = { | ||
/** | ||
* When set, resolveId will only call resolveId of the name of the plugin set here. | ||
* If not defined it will fall back on the resolveId of the core plugin. | ||
* When set, resolvePath will only call resolvePath of the name of the plugin set here. | ||
* If not defined it will fall back on the resolvePath of the core plugin. | ||
*/ | ||
pluginName?: string; | ||
fileName: string; | ||
directory?: string | undefined; | ||
/** | ||
* Options to be passed to 'resolveId' 3th parameter | ||
* Options to be passed to 'resolvePath' 3th parameter | ||
*/ | ||
options?: TOptions; | ||
}; | ||
type ResolveNameParams = { | ||
/** | ||
* When set, resolvePath will only call resolvePath of the name of the plugin set here. | ||
* If not defined it will fall back on the resolvePath of the core plugin. | ||
*/ | ||
pluginName?: string; | ||
name: string; | ||
}; | ||
type PluginContext<TOptions = Record<string, any>> = { | ||
@@ -175,3 +191,4 @@ config: KubbConfig; | ||
addFile: (file: File) => Promise<File>; | ||
resolveId: (params: ResolveIdParams<TOptions>) => MaybePromise<OptionalPath>; | ||
resolvePath: (params: ResolvePathParams<TOptions>) => OptionalPath; | ||
resolveName: (params: ResolveNameParams) => string | null; | ||
load: (id: string) => MaybePromise<TransformResult | void>; | ||
@@ -212,4 +229,4 @@ }; | ||
declare function createJSDocBlockText({ comments }: { | ||
comments: Array<string | undefined>; | ||
}): string | undefined; | ||
comments: Array<string>; | ||
}): string; | ||
@@ -331,3 +348,4 @@ declare function getUniqueName(originalName: string, data: Record<string, number>): string; | ||
fileManager: FileManager; | ||
resolveId: PluginContext['resolveId']; | ||
resolvePath: PluginContext['resolvePath']; | ||
resolveName: PluginContext['resolveName']; | ||
load: PluginContext['load']; | ||
@@ -357,10 +375,52 @@ }; | ||
}); | ||
resolveId: (params: ResolveIdParams) => Promise<OptionalPath>; | ||
resolvePath: (params: ResolvePathParams) => OptionalPath; | ||
resolveName: (params: ResolveNameParams) => string | null; | ||
load: (id: string) => Promise<TransformResult>; | ||
hookForPlugin<H extends PluginLifecycleHooks>(pluginName: string, hookName: H, parameters: Parameters<PluginLifecycle[H]>, skipped?: ReadonlySet<KubbPlugin> | null): Promise<ReturnType<PluginLifecycle[H]> | null>; | ||
hookFirst<H extends PluginLifecycleHooks>(hookName: H, parameters: Parameters<PluginLifecycle[H]>, skipped?: ReadonlySet<KubbPlugin> | null): Promise<ReturnType<PluginLifecycle[H]> | null>; | ||
hookParallel<H extends PluginLifecycleHooks, TOuput = void>(hookName: H, parameters?: Parameters<PluginLifecycle[H]> | undefined): Promise<Awaited<TOuput>[]>; | ||
hookReduceArg0<H extends PluginLifecycleHooks>(hookName: H, [argument0, ...rest]: Parameters<PluginLifecycle[H]>, reduce: (reduction: Argument0<H>, result: ReturnType<PluginLifecycle[H]>, plugin: KubbPlugin) => MaybePromise<Argument0<H> | null>): Promise<Argument0<H>>; | ||
hookSeq<H extends PluginLifecycleHooks>(hookName: H, parameters?: Parameters<PluginLifecycle[H]>): Promise<void>; | ||
/** | ||
* | ||
* Run only hook for a specific plugin name | ||
*/ | ||
hookForPlugin<H extends PluginLifecycleHooks>({ pluginName, hookName, parameters, }: { | ||
pluginName: string; | ||
hookName: H; | ||
parameters: Parameters<PluginLifecycle[H]>; | ||
}): Promise<ReturnType<PluginLifecycle[H]> | null>; | ||
hookForPluginSync<H extends PluginLifecycleHooks>({ pluginName, hookName, parameters, }: { | ||
pluginName: string; | ||
hookName: H; | ||
parameters: Parameters<PluginLifecycle[H]>; | ||
}): ReturnType<PluginLifecycle[H]> | null; | ||
/** | ||
* | ||
* Chains, first non-null result stops and returns | ||
*/ | ||
hookFirst<H extends PluginLifecycleHooks>({ hookName, parameters, skipped, }: { | ||
hookName: H; | ||
parameters: Parameters<PluginLifecycle[H]>; | ||
skipped?: ReadonlySet<KubbPlugin> | null; | ||
}): Promise<ReturnType<PluginLifecycle[H]> | null>; | ||
/** | ||
* | ||
* Chains, first non-null result stops and returns | ||
*/ | ||
hookFirstSync<H extends PluginLifecycleHooks>({ hookName, parameters, skipped, }: { | ||
hookName: H; | ||
parameters: Parameters<PluginLifecycle[H]>; | ||
skipped?: ReadonlySet<KubbPlugin> | null; | ||
}): ReturnType<PluginLifecycle[H]> | null; | ||
hookParallel<H extends PluginLifecycleHooks, TOuput = void>({ hookName, parameters, }: { | ||
hookName: H; | ||
parameters?: Parameters<PluginLifecycle[H]> | undefined; | ||
}): Promise<Awaited<TOuput>[]>; | ||
hookReduceArg0<H extends PluginLifecycleHooks>({ hookName, parameters, reduce, }: { | ||
hookName: H; | ||
parameters: Parameters<PluginLifecycle[H]>; | ||
reduce: (reduction: Argument0<H>, result: ReturnType<PluginLifecycle[H]>, plugin: KubbPlugin) => MaybePromise<Argument0<H> | null>; | ||
}): Promise<Argument0<H>>; | ||
hookSeq<H extends PluginLifecycleHooks>({ hookName, parameters }: { | ||
hookName: H; | ||
parameters?: Parameters<PluginLifecycle[H]>; | ||
}): Promise<void>; | ||
private getSortedPlugins; | ||
private getPlugin; | ||
/** | ||
@@ -406,2 +466,2 @@ * Run an async plugin hook and return the result. | ||
export { Argument0, CLIOptions, Cache, CacheStore, CorePluginOptions, File, FileManager, FileName, Generator, KubbBuild, KubbConfig, KubbJSONPlugin, KubbPlugin, KubbPluginKind, KubbUserConfig, LogLevel, LogType, Logger, MaybePromise, OptionalPath, Path, PathMode, PluginContext, PluginFactoryOptions, PluginLifecycle, PluginLifecycleHooks, PluginManager, Queue, QueueTask, ResolveIdParams, SchemaGenerator, Status, Strategy, TransformResult, TreeNode, TreeNodeOptions, UUID, ValidationPluginError, build, clean, combineFiles, createJSDocBlockText, createPlugin, createPluginCache, build as default, defineConfig, getFileSource, getPathMode, getRelativePath, getUniqueName, hooks, isPromise, isURL, name, nameSorter, objectToParameters, read, timeout, validatePlugins, write }; | ||
export { Argument0, CLIOptions, Cache, CacheStore, CorePluginOptions, File, FileManager, FileName, Generator, KubbBuild, KubbConfig, KubbJSONPlugin, KubbPlugin, KubbPluginKind, KubbUserConfig, LogLevel, LogType, Logger, MaybePromise, OptionalPath, Path, PathMode, PluginContext, PluginFactoryOptions, PluginLifecycle, PluginLifecycleHooks, PluginManager, Queue, QueueTask, ResolveNameParams, ResolvePathParams, SchemaGenerator, Status, Strategy, TransformResult, TreeNode, TreeNodeOptions, UUID, ValidationPluginError, build, clean, combineFiles, createJSDocBlockText, createPlugin, createPluginCache, build as default, defineConfig, getFileSource, getPathMode, getRelativePath, getUniqueName, hooks, isPromise, isURL, name, nameSorter, objectToParameters, read, timeout, validatePlugins, write }; |
@@ -142,3 +142,3 @@ import { createRequire } from 'module'; | ||
if (!filteredComments.length) { | ||
return void 0; | ||
return ""; | ||
} | ||
@@ -218,3 +218,3 @@ const text = filteredComments.reduce((acc, comment) => { | ||
var definePlugin = createPlugin((options) => { | ||
const { fileManager, resolveId, load } = options; | ||
const { fileManager, resolvePath, resolveName, load } = options; | ||
const api = { | ||
@@ -228,3 +228,4 @@ get config() { | ||
}, | ||
resolveId, | ||
resolvePath, | ||
resolveName, | ||
load, | ||
@@ -237,3 +238,3 @@ cache: createPluginCache(/* @__PURE__ */ Object.create(null)) | ||
api, | ||
resolveId(fileName, directory) { | ||
resolvePath(fileName, directory) { | ||
if (!directory) { | ||
@@ -243,2 +244,5 @@ return null; | ||
return pathParser2.resolve(directory, fileName); | ||
}, | ||
resolveName(name2) { | ||
return name2; | ||
} | ||
@@ -465,3 +469,4 @@ }; | ||
buildStart: 1, | ||
resolveId: 1, | ||
resolvePath: 1, | ||
resolveName: 1, | ||
load: 1, | ||
@@ -489,19 +494,80 @@ transform: 1, | ||
load: this.load, | ||
resolveId: this.resolveId | ||
resolvePath: this.resolvePath, | ||
resolveName: this.resolveName | ||
}); | ||
this.plugins = [this.core, ...config.plugins || []]; | ||
} | ||
resolveId = (params) => { | ||
resolvePath = (params) => { | ||
if (params.pluginName) { | ||
return this.hookForPlugin(params.pluginName, "resolveId", [params.fileName, params.directory, params.options]); | ||
return this.hookForPluginSync({ | ||
pluginName: params.pluginName, | ||
hookName: "resolvePath", | ||
parameters: [params.fileName, params.directory, params.options] | ||
}); | ||
} | ||
return this.hookFirst("resolveId", [params.fileName, params.directory, params.options]); | ||
return this.hookFirstSync({ | ||
hookName: "resolvePath", | ||
parameters: [params.fileName, params.directory, params.options] | ||
}); | ||
}; | ||
resolveName = (params) => { | ||
if (params.pluginName) { | ||
return this.hookForPluginSync({ | ||
pluginName: params.pluginName, | ||
hookName: "resolveName", | ||
parameters: [params.name] | ||
}); | ||
} | ||
return this.hookFirstSync({ | ||
hookName: "resolveName", | ||
parameters: [params.name] | ||
}); | ||
}; | ||
load = async (id) => { | ||
return this.hookFirst("load", [id]); | ||
return this.hookFirst({ | ||
hookName: "load", | ||
parameters: [id] | ||
}); | ||
}; | ||
// run only hook for a specific plugin name | ||
hookForPlugin(pluginName, hookName, parameters, skipped) { | ||
/** | ||
* | ||
* Run only hook for a specific plugin name | ||
*/ | ||
hookForPlugin({ | ||
pluginName, | ||
hookName, | ||
parameters | ||
}) { | ||
const plugin = this.getPlugin(hookName, pluginName); | ||
return this.run({ | ||
strategy: "hookFirst", | ||
hookName, | ||
parameters, | ||
plugin | ||
}); | ||
} | ||
hookForPluginSync({ | ||
pluginName, | ||
hookName, | ||
parameters | ||
}) { | ||
const plugin = this.getPlugin(hookName, pluginName); | ||
return this.runSync({ | ||
strategy: "hookFirst", | ||
hookName, | ||
parameters, | ||
plugin | ||
}); | ||
} | ||
/** | ||
* | ||
* Chains, first non-null result stops and returns | ||
*/ | ||
hookFirst({ | ||
hookName, | ||
parameters, | ||
skipped | ||
}) { | ||
let promise = Promise.resolve(null); | ||
for (const plugin of this.getSortedPlugins(hookName, pluginName)) { | ||
for (const plugin of this.getSortedPlugins(hookName)) { | ||
if (skipped && skipped.has(plugin)) | ||
@@ -512,3 +578,8 @@ continue; | ||
return result; | ||
return this.run("hookFirst", hookName, parameters, plugin); | ||
return this.run({ | ||
strategy: "hookFirst", | ||
hookName, | ||
parameters, | ||
plugin | ||
}); | ||
}); | ||
@@ -518,18 +589,32 @@ } | ||
} | ||
// chains, first non-null result stops and returns | ||
hookFirst(hookName, parameters, skipped) { | ||
let promise = Promise.resolve(null); | ||
/** | ||
* | ||
* Chains, first non-null result stops and returns | ||
*/ | ||
hookFirstSync({ | ||
hookName, | ||
parameters, | ||
skipped | ||
}) { | ||
let result = null; | ||
for (const plugin of this.getSortedPlugins(hookName)) { | ||
if (skipped && skipped.has(plugin)) | ||
continue; | ||
promise = promise.then((result) => { | ||
if (result != null) | ||
return result; | ||
return this.run("hookFirst", hookName, parameters, plugin); | ||
result = this.runSync({ | ||
strategy: "hookFirst", | ||
hookName, | ||
parameters, | ||
plugin | ||
}); | ||
if (result != null) { | ||
break; | ||
} | ||
} | ||
return promise; | ||
return result; | ||
} | ||
// parallel | ||
async hookParallel(hookName, parameters) { | ||
async hookParallel({ | ||
hookName, | ||
parameters | ||
}) { | ||
const parallelPromises = []; | ||
@@ -540,5 +625,10 @@ for (const plugin of this.getSortedPlugins(hookName)) { | ||
parallelPromises.length = 0; | ||
await this.run("hookParallel", hookName, parameters, plugin); | ||
await this.run({ | ||
strategy: "hookParallel", | ||
hookName, | ||
parameters, | ||
plugin | ||
}); | ||
} else { | ||
const promise = this.run("hookParallel", hookName, parameters, plugin); | ||
const promise = this.run({ strategy: "hookParallel", hookName, parameters, plugin }); | ||
parallelPromises.push(promise); | ||
@@ -550,9 +640,17 @@ } | ||
// chains, reduces returned value, handling the reduced value as the first hook argument | ||
hookReduceArg0(hookName, [argument0, ...rest], reduce) { | ||
hookReduceArg0({ | ||
hookName, | ||
parameters, | ||
reduce | ||
}) { | ||
const [argument0, ...rest] = parameters; | ||
let promise = Promise.resolve(argument0); | ||
for (const plugin of this.getSortedPlugins(hookName)) { | ||
promise = promise.then( | ||
(argument02) => this.run("hookReduceArg0", hookName, [argument02, ...rest], plugin).then( | ||
(result) => reduce.call(this.core.api, argument02, result, plugin) | ||
) | ||
(argument02) => this.run({ | ||
strategy: "hookReduceArg0", | ||
hookName, | ||
parameters: [argument02, ...rest], | ||
plugin | ||
}).then((result) => reduce.call(this.core.api, argument02, result, plugin)) | ||
); | ||
@@ -563,22 +661,30 @@ } | ||
// chains | ||
hookSeq(hookName, parameters) { | ||
hookSeq({ hookName, parameters }) { | ||
let promise = Promise.resolve(); | ||
for (const plugin of this.getSortedPlugins(hookName)) { | ||
promise = promise.then(() => this.run("hookSeq", hookName, parameters, plugin)); | ||
promise = promise.then( | ||
() => this.run({ | ||
strategy: "hookSeq", | ||
hookName, | ||
parameters, | ||
plugin | ||
}) | ||
); | ||
} | ||
return promise.then(noReturn); | ||
} | ||
getSortedPlugins(hookName, pluginName) { | ||
getSortedPlugins(_hookName) { | ||
const plugins = [...this.plugins]; | ||
if (pluginName) { | ||
const pluginsByPluginName = plugins.filter((item) => item.name === pluginName && item[hookName]); | ||
if (pluginsByPluginName.length === 0) { | ||
if (this.config.logLevel === "warn" && this.logger?.spinner) { | ||
this.logger.spinner.info(`Plugin hook with ${hookName} not found for plugin ${pluginName}`); | ||
} | ||
return [this.core]; | ||
return plugins; | ||
} | ||
getPlugin(hookName, pluginName) { | ||
const plugins = [...this.plugins]; | ||
const pluginByPluginName = plugins.find((item) => item.name === pluginName && item[hookName]); | ||
if (!pluginByPluginName) { | ||
if (this.config.logLevel === "warn" && this.logger?.spinner) { | ||
this.logger.spinner.info(`Plugin hook with ${hookName} not found for plugin ${pluginName}`); | ||
} | ||
return pluginsByPluginName; | ||
return this.core; | ||
} | ||
return plugins; | ||
return pluginByPluginName; | ||
} | ||
@@ -592,3 +698,8 @@ /** | ||
// Implementation signature | ||
run(strategy, hookName, parameters, plugin) { | ||
run({ | ||
strategy, | ||
hookName, | ||
parameters, | ||
plugin | ||
}) { | ||
const hook = plugin[hookName]; | ||
@@ -629,8 +740,17 @@ return Promise.resolve().then(() => { | ||
*/ | ||
runSync(hookName, parameters, plugin) { | ||
runSync({ | ||
strategy, | ||
hookName, | ||
parameters, | ||
plugin | ||
}) { | ||
const hook = plugin[hookName]; | ||
try { | ||
if (typeof hook !== "function") { | ||
return hook; | ||
} | ||
return hook.apply(this.core.api, parameters); | ||
} catch (error) { | ||
return error; | ||
} catch (e) { | ||
this.catcher(e, plugin, hookName); | ||
return null; | ||
} | ||
@@ -681,3 +801,6 @@ } | ||
let code = getFileSource(file); | ||
const loadedResult = await pluginManager.hookFirst("load", [path]); | ||
const loadedResult = await pluginManager.hookFirst({ | ||
hookName: "load", | ||
parameters: [path] | ||
}); | ||
if (loadedResult) { | ||
@@ -687,5 +810,12 @@ code = loadedResult; | ||
if (code) { | ||
const transformedCode = await pluginManager.hookReduceArg0("transform", [code, path], transformReducer); | ||
const transformedCode = await pluginManager.hookReduceArg0({ | ||
hookName: "transform", | ||
parameters: [code, path], | ||
reduce: transformReducer | ||
}); | ||
if (config.output.write || config.output.write === void 0) { | ||
await pluginManager.hookParallel("writeFile", [transformedCode, path]); | ||
await pluginManager.hookParallel({ | ||
hookName: "writeFile", | ||
parameters: [transformedCode, path] | ||
}); | ||
} | ||
@@ -696,5 +826,11 @@ } | ||
const { plugins, fileManager } = pluginManager; | ||
await pluginManager.hookParallel("validate", [plugins]); | ||
await pluginManager.hookParallel("buildStart", [config]); | ||
await pluginManager.hookParallel("buildEnd"); | ||
await pluginManager.hookParallel({ | ||
hookName: "validate", | ||
parameters: [plugins] | ||
}); | ||
await pluginManager.hookParallel({ | ||
hookName: "buildStart", | ||
parameters: [config] | ||
}); | ||
await pluginManager.hookParallel({ hookName: "buildEnd" }); | ||
setTimeout(() => { | ||
@@ -701,0 +837,0 @@ done({ files: fileManager.files.map((file) => ({ ...file, source: getFileSource(file) })) }); |
{ | ||
"name": "@kubb/core", | ||
"version": "1.0.0-beta.4", | ||
"version": "1.0.0-beta.5", | ||
"description": "Generator core", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -7,5 +7,6 @@ /* eslint-disable no-async-promise-executor */ | ||
import { clean, read } from './utils' | ||
import { getFileSource } from './managers/fileManager' | ||
import type { FileManager, File } from './managers/fileManager' | ||
import type { QueueTask } from './utils' | ||
import { FileManager, File, getFileSource } from './managers/fileManager' | ||
import type { PluginContext, TransformResult, LogLevel, KubbPlugin } from './types' | ||
@@ -61,3 +62,6 @@ | ||
const loadedResult = await pluginManager.hookFirst('load', [path]) | ||
const loadedResult = await pluginManager.hookFirst({ | ||
hookName: 'load', | ||
parameters: [path], | ||
}) | ||
if (loadedResult) { | ||
@@ -68,6 +72,13 @@ code = loadedResult | ||
if (code) { | ||
const transformedCode = await pluginManager.hookReduceArg0('transform', [code, path], transformReducer) | ||
const transformedCode = await pluginManager.hookReduceArg0({ | ||
hookName: 'transform', | ||
parameters: [code, path], | ||
reduce: transformReducer, | ||
}) | ||
if (config.output.write || config.output.write === undefined) { | ||
await pluginManager.hookParallel('writeFile', [transformedCode, path]) | ||
await pluginManager.hookParallel({ | ||
hookName: 'writeFile', | ||
parameters: [transformedCode, path], | ||
}) | ||
} | ||
@@ -80,7 +91,13 @@ } | ||
await pluginManager.hookParallel<'validate', true>('validate', [plugins]) | ||
await pluginManager.hookParallel<'validate', true>({ | ||
hookName: 'validate', | ||
parameters: [plugins], | ||
}) | ||
await pluginManager.hookParallel('buildStart', [config]) | ||
await pluginManager.hookParallel({ | ||
hookName: 'buildStart', | ||
parameters: [config], | ||
}) | ||
await pluginManager.hookParallel('buildEnd') | ||
await pluginManager.hookParallel({ hookName: 'buildEnd' }) | ||
setTimeout(() => { | ||
@@ -87,0 +104,0 @@ done({ files: fileManager.files.map((file) => ({ ...file, source: getFileSource(file) })) }) |
@@ -10,3 +10,3 @@ /* eslint-disable no-await-in-loop */ | ||
import type { Argument0, Strategy } from './types' | ||
import type { KubbConfig, KubbPlugin, PluginLifecycleHooks, PluginLifecycle, MaybePromise, ResolveIdParams } from '../../types' | ||
import type { KubbConfig, KubbPlugin, PluginLifecycleHooks, PluginLifecycle, MaybePromise, ResolvePathParams, ResolveNameParams } from '../../types' | ||
import type { Logger } from '../../build' | ||
@@ -23,3 +23,4 @@ import type { CorePluginOptions } from '../../plugin' | ||
buildStart: 1, | ||
resolveId: 1, | ||
resolvePath: 1, | ||
resolveName: 1, | ||
load: 1, | ||
@@ -55,3 +56,4 @@ transform: 1, | ||
load: this.load, | ||
resolveId: this.resolveId, | ||
resolvePath: this.resolvePath, | ||
resolveName: this.resolveName, | ||
}) as KubbPlugin<CorePluginOptions> & { | ||
@@ -63,26 +65,103 @@ api: CorePluginOptions['api'] | ||
resolveId = (params: ResolveIdParams) => { | ||
resolvePath = (params: ResolvePathParams) => { | ||
if (params.pluginName) { | ||
return this.hookForPlugin(params.pluginName, 'resolveId', [params.fileName, params.directory, params.options]) | ||
return this.hookForPluginSync({ | ||
pluginName: params.pluginName, | ||
hookName: 'resolvePath', | ||
parameters: [params.fileName, params.directory, params.options], | ||
}) | ||
} | ||
return this.hookFirst('resolveId', [params.fileName, params.directory, params.options]) | ||
return this.hookFirstSync({ | ||
hookName: 'resolvePath', | ||
parameters: [params.fileName, params.directory, params.options], | ||
}) | ||
} | ||
resolveName = (params: ResolveNameParams) => { | ||
if (params.pluginName) { | ||
return this.hookForPluginSync({ | ||
pluginName: params.pluginName, | ||
hookName: 'resolveName', | ||
parameters: [params.name], | ||
}) | ||
} | ||
return this.hookFirstSync({ | ||
hookName: 'resolveName', | ||
parameters: [params.name], | ||
}) | ||
} | ||
load = async (id: string) => { | ||
return this.hookFirst('load', [id]) | ||
return this.hookFirst({ | ||
hookName: 'load', | ||
parameters: [id], | ||
}) | ||
} | ||
// run only hook for a specific plugin name | ||
hookForPlugin<H extends PluginLifecycleHooks>( | ||
pluginName: string, | ||
hookName: H, | ||
parameters: Parameters<PluginLifecycle[H]>, | ||
/** | ||
* | ||
* Run only hook for a specific plugin name | ||
*/ | ||
hookForPlugin<H extends PluginLifecycleHooks>({ | ||
pluginName, | ||
hookName, | ||
parameters, | ||
}: { | ||
pluginName: string | ||
hookName: H | ||
parameters: Parameters<PluginLifecycle[H]> | ||
}): Promise<ReturnType<PluginLifecycle[H]> | null> { | ||
const plugin = this.getPlugin(hookName, pluginName) | ||
return this.run({ | ||
strategy: 'hookFirst', | ||
hookName, | ||
parameters, | ||
plugin, | ||
}) | ||
} | ||
hookForPluginSync<H extends PluginLifecycleHooks>({ | ||
pluginName, | ||
hookName, | ||
parameters, | ||
}: { | ||
pluginName: string | ||
hookName: H | ||
parameters: Parameters<PluginLifecycle[H]> | ||
}): ReturnType<PluginLifecycle[H]> | null { | ||
const plugin = this.getPlugin(hookName, pluginName) | ||
return this.runSync({ | ||
strategy: 'hookFirst', | ||
hookName, | ||
parameters, | ||
plugin, | ||
}) | ||
} | ||
/** | ||
* | ||
* Chains, first non-null result stops and returns | ||
*/ | ||
hookFirst<H extends PluginLifecycleHooks>({ | ||
hookName, | ||
parameters, | ||
skipped, | ||
}: { | ||
hookName: H | ||
parameters: Parameters<PluginLifecycle[H]> | ||
skipped?: ReadonlySet<KubbPlugin> | null | ||
): Promise<ReturnType<PluginLifecycle[H]> | null> { | ||
}): Promise<ReturnType<PluginLifecycle[H]> | null> { | ||
let promise: Promise<ReturnType<PluginLifecycle[H]> | null> = Promise.resolve(null) | ||
for (const plugin of this.getSortedPlugins(hookName, pluginName)) { | ||
for (const plugin of this.getSortedPlugins(hookName)) { | ||
if (skipped && skipped.has(plugin)) continue | ||
promise = promise.then((result) => { | ||
if (result != null) return result | ||
return this.run('hookFirst', hookName, parameters, plugin) as typeof result | ||
return this.run({ | ||
strategy: 'hookFirst', | ||
hookName, | ||
parameters, | ||
plugin, | ||
}) as typeof result | ||
}) | ||
@@ -93,21 +172,42 @@ } | ||
// chains, first non-null result stops and returns | ||
hookFirst<H extends PluginLifecycleHooks>( | ||
hookName: H, | ||
parameters: Parameters<PluginLifecycle[H]>, | ||
/** | ||
* | ||
* Chains, first non-null result stops and returns | ||
*/ | ||
hookFirstSync<H extends PluginLifecycleHooks>({ | ||
hookName, | ||
parameters, | ||
skipped, | ||
}: { | ||
hookName: H | ||
parameters: Parameters<PluginLifecycle[H]> | ||
skipped?: ReadonlySet<KubbPlugin> | null | ||
): Promise<ReturnType<PluginLifecycle[H]> | null> { | ||
let promise: Promise<ReturnType<PluginLifecycle[H]> | null> = Promise.resolve(null) | ||
}): ReturnType<PluginLifecycle[H]> | null { | ||
let result = null | ||
for (const plugin of this.getSortedPlugins(hookName)) { | ||
if (skipped && skipped.has(plugin)) continue | ||
promise = promise.then((result) => { | ||
if (result != null) return result | ||
return this.run('hookFirst', hookName, parameters, plugin) as typeof result | ||
result = this.runSync({ | ||
strategy: 'hookFirst', | ||
hookName, | ||
parameters, | ||
plugin, | ||
}) | ||
if (result != null) { | ||
break | ||
} | ||
} | ||
return promise | ||
return result | ||
} | ||
// parallel | ||
async hookParallel<H extends PluginLifecycleHooks, TOuput = void>(hookName: H, parameters?: Parameters<PluginLifecycle[H]> | undefined) { | ||
async hookParallel<H extends PluginLifecycleHooks, TOuput = void>({ | ||
hookName, | ||
parameters, | ||
}: { | ||
hookName: H | ||
parameters?: Parameters<PluginLifecycle[H]> | undefined | ||
}) { | ||
const parallelPromises: Promise<TOuput>[] = [] | ||
@@ -119,5 +219,10 @@ | ||
parallelPromises.length = 0 | ||
await this.run('hookParallel', hookName, parameters, plugin) | ||
await this.run({ | ||
strategy: 'hookParallel', | ||
hookName, | ||
parameters, | ||
plugin, | ||
}) | ||
} else { | ||
const promise: Promise<TOuput> = this.run('hookParallel', hookName, parameters, plugin) | ||
const promise: Promise<TOuput> = this.run({ strategy: 'hookParallel', hookName, parameters, plugin }) | ||
@@ -131,13 +236,22 @@ parallelPromises.push(promise) | ||
// chains, reduces returned value, handling the reduced value as the first hook argument | ||
hookReduceArg0<H extends PluginLifecycleHooks>( | ||
hookName: H, | ||
[argument0, ...rest]: Parameters<PluginLifecycle[H]>, | ||
hookReduceArg0<H extends PluginLifecycleHooks>({ | ||
hookName, | ||
parameters, | ||
reduce, | ||
}: { | ||
hookName: H | ||
parameters: Parameters<PluginLifecycle[H]> | ||
reduce: (reduction: Argument0<H>, result: ReturnType<PluginLifecycle[H]>, plugin: KubbPlugin) => MaybePromise<Argument0<H> | null> | ||
): Promise<Argument0<H>> { | ||
}): Promise<Argument0<H>> { | ||
const [argument0, ...rest] = parameters | ||
let promise: Promise<Argument0<H>> = Promise.resolve(argument0) | ||
for (const plugin of this.getSortedPlugins(hookName)) { | ||
promise = promise.then((argument0) => | ||
this.run('hookReduceArg0', hookName, [argument0, ...rest] as Parameters<PluginLifecycle[H]>, plugin).then((result) => | ||
reduce.call(this.core.api, argument0, result as ReturnType<PluginLifecycle[H]>, plugin) | ||
) | ||
this.run({ | ||
strategy: 'hookReduceArg0', | ||
hookName, | ||
parameters: [argument0, ...rest] as Parameters<PluginLifecycle[H]>, | ||
plugin, | ||
}).then((result) => reduce.call(this.core.api, argument0, result as ReturnType<PluginLifecycle[H]>, plugin)) | ||
) as Promise<Argument0<H>> | ||
@@ -150,6 +264,13 @@ } | ||
hookSeq<H extends PluginLifecycleHooks>(hookName: H, parameters?: Parameters<PluginLifecycle[H]>) { | ||
hookSeq<H extends PluginLifecycleHooks>({ hookName, parameters }: { hookName: H; parameters?: Parameters<PluginLifecycle[H]> }) { | ||
let promise: Promise<void> = Promise.resolve() | ||
for (const plugin of this.getSortedPlugins(hookName)) { | ||
promise = promise.then(() => this.run('hookSeq', hookName, parameters, plugin)) | ||
promise = promise.then(() => | ||
this.run({ | ||
strategy: 'hookSeq', | ||
hookName, | ||
parameters, | ||
plugin, | ||
}) | ||
) | ||
} | ||
@@ -159,18 +280,20 @@ return promise.then(noReturn) | ||
private getSortedPlugins(hookName: keyof PluginLifecycle, pluginName?: string): KubbPlugin[] { | ||
private getSortedPlugins(_hookName: keyof PluginLifecycle): KubbPlugin[] { | ||
const plugins = [...this.plugins] | ||
if (pluginName) { | ||
const pluginsByPluginName = plugins.filter((item) => item.name === pluginName && item[hookName]) | ||
if (pluginsByPluginName.length === 0) { | ||
// fallback on the core plugin when there is no match | ||
if (this.config.logLevel === 'warn' && this.logger?.spinner) { | ||
this.logger.spinner.info(`Plugin hook with ${hookName} not found for plugin ${pluginName}`) | ||
} | ||
return [this.core] | ||
return plugins | ||
} | ||
private getPlugin(hookName: keyof PluginLifecycle, pluginName: string): KubbPlugin { | ||
const plugins = [...this.plugins] | ||
const pluginByPluginName = plugins.find((item) => item.name === pluginName && item[hookName]) | ||
if (!pluginByPluginName) { | ||
// fallback on the core plugin when there is no match | ||
if (this.config.logLevel === 'warn' && this.logger?.spinner) { | ||
this.logger.spinner.info(`Plugin hook with ${hookName} not found for plugin ${pluginName}`) | ||
} | ||
return pluginsByPluginName | ||
return this.core | ||
} | ||
return plugins | ||
return pluginByPluginName | ||
} | ||
@@ -185,8 +308,13 @@ | ||
// Implementation signature | ||
private run<H extends PluginLifecycleHooks, TResult = void>( | ||
strategy: Strategy, | ||
hookName: H, | ||
parameters: unknown[] | undefined, | ||
private run<H extends PluginLifecycleHooks, TResult = void>({ | ||
strategy, | ||
hookName, | ||
parameters, | ||
plugin, | ||
}: { | ||
strategy: Strategy | ||
hookName: H | ||
parameters: unknown[] | undefined | ||
plugin: KubbPlugin | ||
): Promise<TResult> { | ||
}): Promise<TResult> { | ||
const hook = plugin[hookName]! | ||
@@ -234,7 +362,13 @@ | ||
*/ | ||
private runSync<H extends PluginLifecycleHooks>( | ||
hookName: H, | ||
parameters: Parameters<PluginLifecycle[H]>, | ||
private runSync<H extends PluginLifecycleHooks>({ | ||
strategy, | ||
hookName, | ||
parameters, | ||
plugin, | ||
}: { | ||
strategy: Strategy | ||
hookName: H | ||
parameters: Parameters<PluginLifecycle[H]> | ||
plugin: KubbPlugin | ||
): ReturnType<PluginLifecycle[H]> | Error { | ||
}): ReturnType<PluginLifecycle[H]> { | ||
const hook = plugin[hookName]! | ||
@@ -246,5 +380,10 @@ | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
if (typeof hook !== 'function') { | ||
return hook | ||
} | ||
return (hook as Function).apply(this.core.api, parameters) | ||
} catch (error) { | ||
return error as Error | ||
} catch (e) { | ||
this.catcher<H>(e as Error, plugin, hookName) | ||
return null as ReturnType<PluginLifecycle[H]> | ||
} | ||
@@ -251,0 +390,0 @@ } |
@@ -33,3 +33,4 @@ import pathParser from 'path' | ||
fileManager: FileManager | ||
resolveId: PluginContext['resolveId'] | ||
resolvePath: PluginContext['resolvePath'] | ||
resolveName: PluginContext['resolveName'] | ||
load: PluginContext['load'] | ||
@@ -44,3 +45,3 @@ } | ||
export const definePlugin = createPlugin<CorePluginOptions>((options) => { | ||
const { fileManager, resolveId, load } = options | ||
const { fileManager, resolvePath, resolveName, load } = options | ||
@@ -55,3 +56,4 @@ const api: PluginContext = { | ||
}, | ||
resolveId, | ||
resolvePath, | ||
resolveName, | ||
load, | ||
@@ -65,3 +67,3 @@ cache: createPluginCache(Object.create(null)), | ||
api, | ||
resolveId(fileName, directory) { | ||
resolvePath(fileName, directory) { | ||
if (!directory) { | ||
@@ -72,3 +74,6 @@ return null | ||
}, | ||
resolveName(name) { | ||
return name | ||
}, | ||
} | ||
}) |
@@ -111,5 +111,5 @@ import type { FileManager, File } from './managers/fileManager' | ||
// use of type objects | ||
export type PluginFactoryOptions<Options = unknown, Nested extends boolean = false, Api = any, ResolveIdOptions = Record<string, any>> = { | ||
export type PluginFactoryOptions<Options = unknown, Nested extends boolean = false, Api = any, resolvePathOptions = Record<string, any>> = { | ||
options: Options | ||
resolveIdOptions: ResolveIdOptions | ||
resolvePathOptions: resolvePathOptions | ||
nested: Nested | ||
@@ -131,11 +131,19 @@ api: Api | ||
/** | ||
* Resolve to an id based on importee(example: `./Pet.ts`) and directory(example: `./models`). | ||
* Resolve to a Path based on a fileName(example: `./Pet.ts`) and directory(example: `./models`). | ||
* Options can als be included. | ||
* @type hookFirst | ||
* @example ('./Pet.ts', './src/gen/') | ||
* @example ('./Pet.ts', './src/gen/') => '/src/gen/Pet.ts' | ||
*/ | ||
resolveId: (this: Omit<PluginContext, 'addFile'>, fileName: string, directory?: string, options?: TOptions['resolveIdOptions']) => OptionalPath | ||
resolvePath: (this: Omit<PluginContext, 'addFile'>, fileName: string, directory?: string, options?: TOptions['resolvePathOptions']) => OptionalPath | ||
/** | ||
* Makes it possible to run async logic to override the path defined previously by `resolveId`. | ||
* Resolve to a name based on a string. | ||
* Useful when converting to PascalCase or camelCase. | ||
* @type hookFirst | ||
* @example ('pet') => 'Pet' | ||
*/ | ||
resolveName: (this: Omit<PluginContext, 'addFile'>, name: string) => string | null | ||
/** | ||
* Makes it possible to run async logic to override the path defined previously by `resolvePath`. | ||
* @type hookFirst | ||
*/ | ||
load: (this: Omit<PluginContext, 'addFile'>, path: Path) => MaybePromise<TransformResult | null> | ||
@@ -148,3 +156,3 @@ /** | ||
/** | ||
* Write the result to the file-system based on the id(defined by `resolveId` or changed by `load`). | ||
* Write the result to the file-system based on the id(defined by `resolvePath` or changed by `load`). | ||
* @type hookParallel | ||
@@ -162,12 +170,12 @@ */ | ||
export type ResolveIdParams<TOptions = Record<string, any>> = { | ||
fileName: string | ||
directory?: string | undefined | ||
export type ResolvePathParams<TOptions = Record<string, any>> = { | ||
/** | ||
* When set, resolveId will only call resolveId of the name of the plugin set here. | ||
* If not defined it will fall back on the resolveId of the core plugin. | ||
* When set, resolvePath will only call resolvePath of the name of the plugin set here. | ||
* If not defined it will fall back on the resolvePath of the core plugin. | ||
*/ | ||
pluginName?: string | ||
fileName: string | ||
directory?: string | undefined | ||
/** | ||
* Options to be passed to 'resolveId' 3th parameter | ||
* Options to be passed to 'resolvePath' 3th parameter | ||
*/ | ||
@@ -177,2 +185,11 @@ options?: TOptions | ||
export type ResolveNameParams = { | ||
/** | ||
* When set, resolvePath will only call resolvePath of the name of the plugin set here. | ||
* If not defined it will fall back on the resolvePath of the core plugin. | ||
*/ | ||
pluginName?: string | ||
name: string | ||
} | ||
export type PluginContext<TOptions = Record<string, any>> = { | ||
@@ -183,3 +200,4 @@ config: KubbConfig | ||
addFile: (file: File) => Promise<File> | ||
resolveId: (params: ResolveIdParams<TOptions>) => MaybePromise<OptionalPath> | ||
resolvePath: (params: ResolvePathParams<TOptions>) => OptionalPath | ||
resolveName: (params: ResolveNameParams) => string | null | ||
load: (id: string) => MaybePromise<TransformResult | void> | ||
@@ -186,0 +204,0 @@ } |
@@ -1,6 +0,6 @@ | ||
export function createJSDocBlockText({ comments }: { comments: Array<string | undefined> }) { | ||
export function createJSDocBlockText({ comments }: { comments: Array<string> }): string { | ||
const filteredComments = comments.filter(Boolean) | ||
if (!filteredComments.length) { | ||
return undefined | ||
return '' | ||
} | ||
@@ -7,0 +7,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
208087
9.43%3476
16.76%