@kubb/core 1.0.0-alpha.6 → 1.0.0-alpha.8

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.
@@ -0,0 +1,111 @@
1
+ import dirTree from 'directory-tree'
2
+
3
+ import type { DirectoryTree, DirectoryTreeOptions } from 'directory-tree'
4
+
5
+ export type TreeNodeOptions = DirectoryTreeOptions
6
+
7
+ export class TreeNode<T = unknown> {
8
+ public data: T
9
+
10
+ public parent?: TreeNode<T>
11
+
12
+ public children: Array<TreeNode<T>> = []
13
+
14
+ constructor(data: T, parent?: TreeNode<T>) {
15
+ this.data = data
16
+ this.parent = parent
17
+ return this
18
+ }
19
+
20
+ addChild(data: T): TreeNode<T> {
21
+ const child = new TreeNode(data, this)
22
+ if (!this.children) {
23
+ this.children = []
24
+ }
25
+ this.children.push(child)
26
+ return child
27
+ }
28
+
29
+ find(data: T) {
30
+ if (data === this.data) {
31
+ return this
32
+ }
33
+
34
+ if (this.children) {
35
+ for (let i = 0, { length } = this.children, target: unknown = null; i < length; i++) {
36
+ target = this.children[i].find(data)
37
+ if (target) {
38
+ return target
39
+ }
40
+ }
41
+ }
42
+
43
+ return null
44
+ }
45
+
46
+ leaves(): TreeNode<T>[] {
47
+ if (!this.children || this.children.length === 0) {
48
+ // this is a leaf
49
+ return [this]
50
+ }
51
+
52
+ // if not a leaf, return all children's leaves recursively
53
+ const leaves: TreeNode<T>[] = []
54
+ if (this.children) {
55
+ for (let i = 0, { length } = this.children; i < length; i++) {
56
+ // eslint-disable-next-line prefer-spread
57
+ leaves.push.apply(leaves, this.children[i].leaves())
58
+ }
59
+ }
60
+ return leaves
61
+ }
62
+
63
+ root(): TreeNode<T> {
64
+ if (!this.parent) {
65
+ return this
66
+ }
67
+ return this.parent.root()
68
+ }
69
+
70
+ forEach(callback: (treeNode: TreeNode<T>) => void): this {
71
+ if (typeof callback !== 'function') {
72
+ throw new TypeError('forEach() callback must be a function')
73
+ }
74
+
75
+ // run this node through function
76
+ callback(this)
77
+
78
+ // do the same for all children
79
+ if (this.children) {
80
+ for (let i = 0, { length } = this.children; i < length; i++) {
81
+ this.children[i].forEach(callback)
82
+ }
83
+ }
84
+
85
+ return this
86
+ }
87
+
88
+ public static build<T = unknown>(path: string, options: TreeNodeOptions = {}): TreeNode<T> | null {
89
+ const filteredTree = dirTree(path, { extensions: options?.extensions, exclude: options.exclude })
90
+
91
+ if (!filteredTree) {
92
+ return null
93
+ }
94
+
95
+ const treeNode = new TreeNode({ name: filteredTree.name, path: filteredTree.path, type: filteredTree.type })
96
+
97
+ const recurse = (node: typeof treeNode, item: DirectoryTree) => {
98
+ const subNode = node.addChild({ name: item.name, path: item.path, type: item.type })
99
+
100
+ if (item.children?.length) {
101
+ item.children?.forEach((child) => {
102
+ recurse(subNode, child)
103
+ })
104
+ }
105
+ }
106
+
107
+ filteredTree.children?.forEach((child) => recurse(treeNode, child))
108
+
109
+ return treeNode as TreeNode<T>
110
+ }
111
+ }
@@ -0,0 +1,3 @@
1
+ export * from './FileManager'
2
+ export * from './types'
3
+ export * from './TreeNode'
@@ -0,0 +1,24 @@
1
+ type Import = {
2
+ name: string | string[]
3
+ path: string
4
+ isTypeOnly?: boolean
5
+ }
6
+
7
+ export type File = {
8
+ /**
9
+ * Name to be used to dynamicly create the fileName(based on input.path)
10
+ */
11
+ fileName: string
12
+ /**
13
+ * Path will be full qualified path to a specified file
14
+ */
15
+ path: string
16
+ source: string
17
+ imports?: Import[]
18
+ }
19
+
20
+ export type UUID = string
21
+
22
+ export type CacheStore = { id: UUID; file: File; status: Status }
23
+
24
+ export type Status = 'new' | 'success' | 'removed'
@@ -0,0 +1,2 @@
1
+ export * from './fileManager'
2
+ export * from './pluginManager'
@@ -0,0 +1,249 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ /* eslint-disable no-restricted-syntax */
3
+
4
+ import { definePlugin } from '../../plugin'
5
+ import { FileManager } from '../fileManager'
6
+ import { Queue } from '../../utils/queue'
7
+
8
+ import type { QueueTask } from '../../utils/queue'
9
+ import type { Argument0, Strategy } from './types'
10
+ import type { KubbConfig, KubbPlugin, PluginLifecycleHooks, PluginLifecycle, MaybePromise, ResolveIdParams } from '../../types'
11
+ import type { Logger } from '../../build'
12
+ import type { CorePluginOptions } from '../../plugin'
13
+
14
+ // inspired by: https://github.com/rollup/rollup/blob/master/src/utils/PluginDriver.ts#
15
+
16
+ // This will make sure no input hook is omitted
17
+ const hookNames: {
18
+ [P in PluginLifecycleHooks]: 1
19
+ } = {
20
+ validate: 1,
21
+ buildStart: 1,
22
+ resolveId: 1,
23
+ load: 1,
24
+ transform: 1,
25
+ writeFile: 1,
26
+ buildEnd: 1,
27
+ }
28
+ export const hooks = Object.keys(hookNames) as [PluginLifecycleHooks]
29
+
30
+ export class PluginManager {
31
+ public plugins: KubbPlugin[]
32
+
33
+ public readonly fileManager: FileManager
34
+
35
+ private readonly logger?: Logger
36
+
37
+ private readonly config: KubbConfig
38
+
39
+ public readonly core: KubbPlugin<CorePluginOptions>
40
+
41
+ public queue: Queue
42
+
43
+ constructor(config: KubbConfig, options: { logger?: Logger; task: QueueTask }) {
44
+ this.logger = options.logger
45
+ this.config = config
46
+ this.queue = new Queue(10)
47
+
48
+ this.fileManager = new FileManager({ task: options.task, queue: this.queue })
49
+ this.core = definePlugin({
50
+ config,
51
+ fileManager: this.fileManager,
52
+ load: this.load,
53
+ resolveId: this.resolveId,
54
+ }) as KubbPlugin<CorePluginOptions> & {
55
+ api: CorePluginOptions['api']
56
+ }
57
+ this.plugins = [this.core, ...(config.plugins || [])]
58
+ }
59
+
60
+ resolveId = (params: ResolveIdParams) => {
61
+ if (params.pluginName) {
62
+ return this.hookForPlugin(params.pluginName, 'resolveId', [params.fileName, params.directory, params.options])
63
+ }
64
+ return this.hookFirst('resolveId', [params.fileName, params.directory, params.options])
65
+ }
66
+
67
+ load = async (id: string) => {
68
+ return this.hookFirst('load', [id])
69
+ }
70
+
71
+ // run only hook for a specific plugin name
72
+ hookForPlugin<H extends PluginLifecycleHooks>(
73
+ pluginName: string,
74
+ hookName: H,
75
+ parameters: Parameters<PluginLifecycle[H]>,
76
+ skipped?: ReadonlySet<KubbPlugin> | null
77
+ ): Promise<ReturnType<PluginLifecycle[H]> | null> {
78
+ let promise: Promise<ReturnType<PluginLifecycle[H]> | null> = Promise.resolve(null)
79
+ for (const plugin of this.getSortedPlugins(hookName, pluginName)) {
80
+ if (skipped && skipped.has(plugin)) continue
81
+ promise = promise.then((result) => {
82
+ if (result != null) return result
83
+ return this.run('hookFirst', hookName, parameters, plugin) as typeof result
84
+ })
85
+ }
86
+ return promise
87
+ }
88
+
89
+ // chains, first non-null result stops and returns
90
+ hookFirst<H extends PluginLifecycleHooks>(
91
+ hookName: H,
92
+ parameters: Parameters<PluginLifecycle[H]>,
93
+ skipped?: ReadonlySet<KubbPlugin> | null
94
+ ): Promise<ReturnType<PluginLifecycle[H]> | null> {
95
+ let promise: Promise<ReturnType<PluginLifecycle[H]> | null> = Promise.resolve(null)
96
+ for (const plugin of this.getSortedPlugins(hookName)) {
97
+ if (skipped && skipped.has(plugin)) continue
98
+ promise = promise.then((result) => {
99
+ if (result != null) return result
100
+ return this.run('hookFirst', hookName, parameters, plugin) as typeof result
101
+ })
102
+ }
103
+ return promise
104
+ }
105
+
106
+ // parallel
107
+ async hookParallel<H extends PluginLifecycleHooks, TOuput = void>(hookName: H, parameters?: Parameters<PluginLifecycle[H]> | undefined) {
108
+ const parallelPromises: Promise<TOuput>[] = []
109
+
110
+ for (const plugin of this.getSortedPlugins(hookName)) {
111
+ if ((plugin[hookName] as { sequential?: boolean })?.sequential) {
112
+ await Promise.all(parallelPromises)
113
+ parallelPromises.length = 0
114
+ await this.run('hookParallel', hookName, parameters, plugin)
115
+ } else {
116
+ const promise: Promise<TOuput> = this.run('hookParallel', hookName, parameters, plugin)
117
+
118
+ parallelPromises.push(promise)
119
+ }
120
+ }
121
+ return Promise.all(parallelPromises)
122
+ }
123
+
124
+ // chains, reduces returned value, handling the reduced value as the first hook argument
125
+ hookReduceArg0<H extends PluginLifecycleHooks>(
126
+ hookName: H,
127
+ [argument0, ...rest]: Parameters<PluginLifecycle[H]>,
128
+ reduce: (reduction: Argument0<H>, result: ReturnType<PluginLifecycle[H]>, plugin: KubbPlugin) => MaybePromise<Argument0<H> | null>
129
+ ): Promise<Argument0<H>> {
130
+ let promise: Promise<Argument0<H>> = Promise.resolve(argument0)
131
+ for (const plugin of this.getSortedPlugins(hookName)) {
132
+ promise = promise.then((argument0) =>
133
+ this.run('hookReduceArg0', hookName, [argument0, ...rest] as Parameters<PluginLifecycle[H]>, plugin).then((result) =>
134
+ reduce.call(this.core.api, argument0, result as ReturnType<PluginLifecycle[H]>, plugin)
135
+ )
136
+ ) as Promise<Argument0<H>>
137
+ }
138
+ return promise
139
+ }
140
+
141
+ // chains
142
+
143
+ hookSeq<H extends PluginLifecycleHooks>(hookName: H, parameters?: Parameters<PluginLifecycle[H]>) {
144
+ let promise: Promise<void> = Promise.resolve()
145
+ for (const plugin of this.getSortedPlugins(hookName)) {
146
+ promise = promise.then(() => this.run('hookSeq', hookName, parameters, plugin))
147
+ }
148
+ return promise.then(noReturn)
149
+ }
150
+
151
+ private getSortedPlugins(hookName: keyof PluginLifecycle, pluginName?: string): KubbPlugin[] {
152
+ const plugins = [...this.plugins]
153
+
154
+ if (pluginName) {
155
+ const pluginsByPluginName = plugins.filter((item) => item.name === pluginName && item[hookName])
156
+ if (pluginsByPluginName.length === 0) {
157
+ // fallback on the core plugin when there is no match
158
+ if (this.config.logLevel === 'warn' && this.logger?.spinner) {
159
+ this.logger.spinner.info(`Plugin hook with ${hookName} not found for plugin ${pluginName}`)
160
+ }
161
+ return [this.core]
162
+ }
163
+ return pluginsByPluginName
164
+ }
165
+
166
+ return plugins
167
+ }
168
+
169
+ /**
170
+ * Run an async plugin hook and return the result.
171
+ * @param hookName Name of the plugin hook. Must be either in `PluginHooks` or `OutputPluginValueHooks`.
172
+ * @param args Arguments passed to the plugin hook.
173
+ * @param plugin The actual pluginObject to run.
174
+ */
175
+ // Implementation signature
176
+ private run<H extends PluginLifecycleHooks, TResult = void>(
177
+ strategy: Strategy,
178
+ hookName: H,
179
+ parameters: unknown[] | undefined,
180
+ plugin: KubbPlugin
181
+ ): Promise<TResult> {
182
+ const hook = plugin[hookName]!
183
+
184
+ return Promise.resolve()
185
+ .then(() => {
186
+ if (typeof hook !== 'function') {
187
+ return hook
188
+ }
189
+
190
+ if (this.config.logLevel === 'info' && this.logger?.spinner) {
191
+ this.logger.spinner.text = `[${strategy}] ${hookName}: Excecuting task for plugin ${plugin.name} \n`
192
+ }
193
+
194
+ const hookResult = (hook as Function).apply(this.core.api, parameters)
195
+
196
+ if (!(hookResult as Promise<unknown>)?.then) {
197
+ // short circuit for non-thenables and non-Promises
198
+ if (this.config.logLevel === 'info' && this.logger?.spinner) {
199
+ this.logger.spinner.succeed(`[${strategy}] ${hookName}: Excecuting task for plugin ${plugin.name} \n`)
200
+ }
201
+ return hookResult
202
+ }
203
+
204
+ return Promise.resolve(hookResult).then((result) => {
205
+ // action was fulfilled
206
+ if (this.config.logLevel === 'info' && this.logger?.spinner) {
207
+ this.logger.spinner.succeed(`[${strategy}] ${hookName}: Excecuting task for plugin ${plugin.name} \n`)
208
+ }
209
+ return result
210
+ })
211
+ })
212
+ .catch((e: Error) => {
213
+ this.catcher<H>(e, plugin, hookName)
214
+ }) as Promise<TResult>
215
+ }
216
+
217
+ /**
218
+ * Run a sync plugin hook and return the result.
219
+ * @param hookName Name of the plugin hook. Must be in `PluginHooks`.
220
+ * @param args Arguments passed to the plugin hook.
221
+ * @param plugin The acutal plugin
222
+ * @param replaceContext When passed, the plugin context can be overridden.
223
+ */
224
+ private runSync<H extends PluginLifecycleHooks>(
225
+ hookName: H,
226
+ parameters: Parameters<PluginLifecycle[H]>,
227
+ plugin: KubbPlugin
228
+ ): ReturnType<PluginLifecycle[H]> | Error {
229
+ const hook = plugin[hookName]!
230
+
231
+ // const context = this.pluginContexts.get(plugin)!;
232
+
233
+ try {
234
+ // eslint-disable-next-line @typescript-eslint/ban-types
235
+ return (hook as Function).apply(this.core.api, parameters)
236
+ } catch (error) {
237
+ return error as Error
238
+ }
239
+ }
240
+
241
+ private catcher<H extends PluginLifecycleHooks>(e: Error, plugin: KubbPlugin, hookName: H) {
242
+ const text = `${e.message} (plugin: ${plugin.name}, hook: ${hookName})\n`
243
+
244
+ throw new Error(text, { cause: e })
245
+ }
246
+ }
247
+
248
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
249
+ function noReturn() {}
@@ -0,0 +1,3 @@
1
+ export * from './PluginManager'
2
+ export * from './types'
3
+ export * from './validate'
@@ -0,0 +1,8 @@
1
+ import type { PluginLifecycle } from '../../types'
2
+ /**
3
+ * Get the type of the first argument in a function.
4
+ * @example Arg0<(a: string, b: number) => void> -> string
5
+ */
6
+ export type Argument0<H extends keyof PluginLifecycle> = Parameters<PluginLifecycle[H]>[0]
7
+
8
+ export type Strategy = 'hookFirst' | 'hookForPlugin' | 'hookParallel' | 'hookReduceArg0' | 'hookSeq'
@@ -0,0 +1,21 @@
1
+ import type { KubbPlugin } from '../../types'
2
+
3
+ export class ValidationPluginError extends Error {}
4
+
5
+ export function validatePlugins(plugins: KubbPlugin[], dependedPluginNames: string | string[]): true {
6
+ let pluginNames: string[] = []
7
+ if (typeof dependedPluginNames === 'string') {
8
+ pluginNames = [dependedPluginNames]
9
+ } else {
10
+ pluginNames = dependedPluginNames
11
+ }
12
+
13
+ pluginNames.forEach((pluginName) => {
14
+ const exists = plugins.some((plugin) => plugin.name === pluginName)
15
+ if (!exists) {
16
+ throw new ValidationPluginError(`This plugin depends on the ${pluginName} plugin.`)
17
+ }
18
+ })
19
+
20
+ return true
21
+ }
package/src/plugin.ts ADDED
@@ -0,0 +1,69 @@
1
+ import pathParser from 'path'
2
+
3
+ import { createPluginCache } from './utils'
4
+
5
+ import type { FileManager } from './managers/fileManager'
6
+ import type { PluginContext, KubbPlugin, PluginFactoryOptions } from './types'
7
+
8
+ type KubbPluginFactory<T extends PluginFactoryOptions = PluginFactoryOptions> = (
9
+ options: T['options']
10
+ ) => T['nested'] extends true ? Array<KubbPlugin<T>> : KubbPlugin<T>
11
+
12
+ export function createPlugin<T extends PluginFactoryOptions = PluginFactoryOptions>(factory: KubbPluginFactory<T>) {
13
+ return (options: T['options']) => {
14
+ const plugin = factory(options)
15
+ if (Array.isArray(plugin)) {
16
+ throw new Error('Not implemented')
17
+ }
18
+
19
+ // default transform
20
+ if (!plugin.transform) {
21
+ plugin.transform = function transform(code) {
22
+ return code
23
+ }
24
+ }
25
+
26
+ return plugin
27
+ }
28
+ }
29
+
30
+ type Options = {
31
+ config: PluginContext['config']
32
+ fileManager: FileManager
33
+ resolveId: PluginContext['resolveId']
34
+ load: PluginContext['load']
35
+ }
36
+
37
+ // not publicly exported
38
+ export type CorePluginOptions = PluginFactoryOptions<Options, false, PluginContext>
39
+
40
+ export const name = 'core' as const
41
+
42
+ export const definePlugin = createPlugin<CorePluginOptions>((options) => {
43
+ const { fileManager, resolveId, load } = options
44
+
45
+ const api: PluginContext = {
46
+ get config() {
47
+ return options.config
48
+ },
49
+ fileManager,
50
+ async addFile(file) {
51
+ return fileManager.addOrAppend(file)
52
+ },
53
+ resolveId,
54
+ load,
55
+ cache: createPluginCache(Object.create(null)),
56
+ }
57
+
58
+ return {
59
+ name,
60
+ options,
61
+ api,
62
+ resolveId(fileName, directory) {
63
+ if (!directory) {
64
+ return null
65
+ }
66
+ return pathParser.resolve(directory, fileName)
67
+ },
68
+ }
69
+ })
package/src/types.ts ADDED
@@ -0,0 +1,193 @@
1
+ import type { FileManager, File } from './managers/fileManager'
2
+ import type { Cache } from './utils/cache'
3
+
4
+ export type MaybePromise<T> = Promise<T> | T
5
+
6
+ export type KubbUserConfig = Omit<KubbConfig, 'root'> & {
7
+ /**
8
+ * Project root directory. Can be an absolute path, or a path relative from
9
+ * the location of the config file itself.
10
+ * @default process.cwd()
11
+ */
12
+ root?: string
13
+ /**
14
+ * Plugin type can be KubbJSONPlugin or KubbPlugin
15
+ * Example: ['@kubb/swagger', { output: false }]
16
+ * Or: createSwagger({ output: false })
17
+ */
18
+ plugins?: Array<unknown>
19
+ }
20
+
21
+ /**
22
+ * Global/internal config used through out the full generation.
23
+ */
24
+ export type KubbConfig = {
25
+ /**
26
+ * Project root directory. Can be an absolute path, or a path relative from
27
+ * the location of the config file itself.
28
+ * @default process.cwd()
29
+ */
30
+ root: string
31
+ input: {
32
+ /**
33
+ * Path to be used as the input. Can be an absolute path, or a path relative from
34
+ * the defined root option.
35
+ */
36
+ path: string
37
+ }
38
+ output: {
39
+ /**
40
+ * Path to be used to export all generated files. Can be an absolute path, or a path relative based of the defined root option.
41
+ */
42
+ path: string
43
+ /**
44
+ * Remove previous generated files and folders.
45
+ */
46
+ clean?: boolean
47
+ /**
48
+ * Enabled or disable the writing to the filesystem. This is being used for the playground.
49
+ * @default true
50
+ */
51
+ write?: boolean
52
+ }
53
+ /**
54
+ * Array of Kubb plugins to use.
55
+ * The plugin/package can forsee some options that you need to pass through.
56
+ * Sometimes a plugin is depended on another plugin, if that's the case you will get an error back from the plugin you installed.
57
+ */
58
+ plugins?: KubbPlugin[]
59
+ /**
60
+ * Hooks that will be called when a specific action is triggered in Kubb.
61
+ */
62
+ hooks?: {
63
+ /**
64
+ * Hook that will be triggerend at the end of all executions.
65
+ * Useful for running Prettier or Eslint to use your own linting structure.
66
+ */
67
+ done?: string | string[]
68
+ }
69
+ /**
70
+ * Log level to report when using the CLI
71
+ */
72
+ logLevel?: LogLevel
73
+ }
74
+
75
+ export type CLIOptions = {
76
+ config?: string
77
+ debug?: boolean
78
+ watch?: string
79
+ }
80
+
81
+ // plugin
82
+
83
+ export type KubbPluginKind = 'schema' | 'controller'
84
+
85
+ export type KubbJSONPlugin = [string, Record<string, any>]
86
+
87
+ export type KubbPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = {
88
+ /**
89
+ * Unique name used for the plugin
90
+ * @example @kubb/typescript
91
+ */
92
+ name: string
93
+ /**
94
+ * Kind/type for the plugin
95
+ * Type 'schema' can be used for JSON schema's, TypeScript types, ...
96
+ * Type 'controller' can be used to create generate API calls, React-Query hooks, Axios controllers, ...
97
+ * @default undefined
98
+ */
99
+ kind?: KubbPluginKind
100
+ /**
101
+ * Defined an api that can be used by other plugins
102
+ */
103
+ api?: TOptions['api']
104
+ /**
105
+ * Options set for a specific plugin(see kubb.config.ts)
106
+ */
107
+ options?: TOptions['options']
108
+ } & Partial<PluginLifecycle<TOptions>>
109
+
110
+ // use of type objects
111
+ export type PluginFactoryOptions<Options = unknown, Nested extends boolean = false, Api = any, ResolveIdOptions = Record<string, any>> = {
112
+ options: Options
113
+ resolveIdOptions: ResolveIdOptions
114
+ nested: Nested
115
+ api: Api
116
+ }
117
+
118
+ export type PluginLifecycle<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = {
119
+ /**
120
+ * Valdiate all plugins to see if their depended plugins are installed and configured.
121
+ * @type hookParallel
122
+ */
123
+ validate: (this: Omit<PluginContext, 'addFile'>, plugins: KubbPlugin[]) => MaybePromise<true>
124
+ /**
125
+ * Start of the lifecycle of a plugin.
126
+ * @type hookParallel
127
+ */
128
+ buildStart: (this: PluginContext, kubbConfig: KubbConfig) => MaybePromise<void>
129
+ /**
130
+ * Resolve to an id based on importee(example: `./Pet.ts`) and directory(example: `./models`).
131
+ * @type hookFirst
132
+ * @example ('./Pet.ts', './src/gen/')
133
+ */
134
+ resolveId: (this: Omit<PluginContext, 'addFile'>, fileName: string, directory?: string, options?: TOptions['resolveIdOptions']) => OptionalPath
135
+ /**
136
+ * Makes it possible to run async logic to override the path defined previously by `resolveId`.
137
+ * @type hookFirst
138
+ */
139
+ load: (this: Omit<PluginContext, 'addFile'>, path: Path) => MaybePromise<TransformResult | null>
140
+ /**
141
+ * Transform the source-code.
142
+ * @type hookReduceArg0
143
+ */
144
+ transform: (this: Omit<PluginContext, 'addFile'>, source: string, path: Path) => MaybePromise<TransformResult>
145
+ /**
146
+ * Write the result to the file-system based on the id(defined by `resolveId` or changed by `load`).
147
+ * @type hookParallel
148
+ */
149
+ writeFile: (this: Omit<PluginContext, 'addFile'>, source: string | undefined, path: Path) => MaybePromise<void>
150
+ /**
151
+ * End of the plugin lifecycle.
152
+ * @type hookParallel
153
+ */
154
+ buildEnd: (this: Omit<PluginContext, 'addFile'>) => MaybePromise<void>
155
+ }
156
+
157
+ export type PluginLifecycleHooks = keyof PluginLifecycle
158
+
159
+ export type ResolveIdParams<TOptions = Record<string, any>> = {
160
+ fileName: string
161
+ directory?: string | undefined
162
+ /**
163
+ * When set, resolveId will only call resolveId of the name of the plugin set here.
164
+ * If not defined it will fall back on the resolveId of the core plugin.
165
+ */
166
+ pluginName?: string
167
+ /**
168
+ * Options to be passed to 'resolveId' 3th parameter
169
+ */
170
+ options?: TOptions
171
+ }
172
+
173
+ export type PluginContext<TOptions = Record<string, any>> = {
174
+ config: KubbConfig
175
+ cache: Cache
176
+ fileManager: FileManager
177
+ addFile: (file: File) => Promise<File>
178
+ resolveId: (params: ResolveIdParams<TOptions>) => MaybePromise<OptionalPath>
179
+ load: (id: string) => MaybePromise<TransformResult | void>
180
+ }
181
+
182
+ // null will mean clear the watcher for this key
183
+ export type TransformResult = string | null
184
+
185
+ /**
186
+ * @description Computing the name of a file or directory together with its position in relation to other directories traced back in a line to the root
187
+ */
188
+ export type Path = string
189
+ export type OptionalPath = Path | null | undefined
190
+ export type FileName = string | null | undefined
191
+
192
+ export type LogType = 'error' | 'warn' | 'info'
193
+ export type LogLevel = LogType | 'silent'