@kubb/core 1.0.0-beta.3 → 1.0.0-beta.5

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.
@@ -7,7 +7,7 @@ import { Queue } from '../../utils/queue'
7
7
 
8
8
  import type { QueueTask } from '../../utils/queue'
9
9
  import type { Argument0, Strategy } from './types'
10
- import type { KubbConfig, KubbPlugin, PluginLifecycleHooks, PluginLifecycle, MaybePromise, ResolveIdParams } from '../../types'
10
+ import type { KubbConfig, KubbPlugin, PluginLifecycleHooks, PluginLifecycle, MaybePromise, ResolvePathParams, ResolveNameParams } from '../../types'
11
11
  import type { Logger } from '../../build'
12
12
  import type { CorePluginOptions } from '../../plugin'
13
13
 
@@ -19,7 +19,8 @@ const hookNames: {
19
19
  } = {
20
20
  validate: 1,
21
21
  buildStart: 1,
22
- resolveId: 1,
22
+ resolvePath: 1,
23
+ resolveName: 1,
23
24
  load: 1,
24
25
  transform: 1,
25
26
  writeFile: 1,
@@ -50,70 +51,174 @@ export class PluginManager {
50
51
  config,
51
52
  fileManager: this.fileManager,
52
53
  load: this.load,
53
- resolveId: this.resolveId,
54
+ resolvePath: this.resolvePath,
55
+ resolveName: this.resolveName,
54
56
  }) as KubbPlugin<CorePluginOptions> & {
55
57
  api: CorePluginOptions['api']
56
58
  }
57
59
  this.plugins = [this.core, ...(config.plugins || [])]
58
60
  }
59
61
 
60
- resolveId = (params: ResolveIdParams) => {
62
+ resolvePath = (params: ResolvePathParams) => {
61
63
  if (params.pluginName) {
62
- return this.hookForPlugin(params.pluginName, 'resolveId', [params.fileName, params.directory, params.options])
64
+ return this.hookForPluginSync({
65
+ pluginName: params.pluginName,
66
+ hookName: 'resolvePath',
67
+ parameters: [params.fileName, params.directory, params.options],
68
+ })
69
+ }
70
+ return this.hookFirstSync({
71
+ hookName: 'resolvePath',
72
+ parameters: [params.fileName, params.directory, params.options],
73
+ })
74
+ }
75
+
76
+ resolveName = (params: ResolveNameParams) => {
77
+ if (params.pluginName) {
78
+ return this.hookForPluginSync({
79
+ pluginName: params.pluginName,
80
+ hookName: 'resolveName',
81
+ parameters: [params.name],
82
+ })
63
83
  }
64
- return this.hookFirst('resolveId', [params.fileName, params.directory, params.options])
84
+ return this.hookFirstSync({
85
+ hookName: 'resolveName',
86
+ parameters: [params.name],
87
+ })
65
88
  }
66
89
 
67
90
  load = async (id: string) => {
68
- return this.hookFirst('load', [id])
91
+ return this.hookFirst({
92
+ hookName: 'load',
93
+ parameters: [id],
94
+ })
95
+ }
96
+
97
+ /**
98
+ *
99
+ * Run only hook for a specific plugin name
100
+ */
101
+ hookForPlugin<H extends PluginLifecycleHooks>({
102
+ pluginName,
103
+ hookName,
104
+ parameters,
105
+ }: {
106
+ pluginName: string
107
+ hookName: H
108
+ parameters: Parameters<PluginLifecycle[H]>
109
+ }): Promise<ReturnType<PluginLifecycle[H]> | null> {
110
+ const plugin = this.getPlugin(hookName, pluginName)
111
+
112
+ return this.run({
113
+ strategy: 'hookFirst',
114
+ hookName,
115
+ parameters,
116
+ plugin,
117
+ })
69
118
  }
70
119
 
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]>,
120
+ hookForPluginSync<H extends PluginLifecycleHooks>({
121
+ pluginName,
122
+ hookName,
123
+ parameters,
124
+ }: {
125
+ pluginName: string
126
+ hookName: H
127
+ parameters: Parameters<PluginLifecycle[H]>
128
+ }): ReturnType<PluginLifecycle[H]> | null {
129
+ const plugin = this.getPlugin(hookName, pluginName)
130
+
131
+ return this.runSync({
132
+ strategy: 'hookFirst',
133
+ hookName,
134
+ parameters,
135
+ plugin,
136
+ })
137
+ }
138
+
139
+ /**
140
+ *
141
+ * Chains, first non-null result stops and returns
142
+ */
143
+ hookFirst<H extends PluginLifecycleHooks>({
144
+ hookName,
145
+ parameters,
146
+ skipped,
147
+ }: {
148
+ hookName: H
149
+ parameters: Parameters<PluginLifecycle[H]>
76
150
  skipped?: ReadonlySet<KubbPlugin> | null
77
- ): Promise<ReturnType<PluginLifecycle[H]> | null> {
151
+ }): Promise<ReturnType<PluginLifecycle[H]> | null> {
78
152
  let promise: Promise<ReturnType<PluginLifecycle[H]> | null> = Promise.resolve(null)
79
- for (const plugin of this.getSortedPlugins(hookName, pluginName)) {
153
+ for (const plugin of this.getSortedPlugins(hookName)) {
80
154
  if (skipped && skipped.has(plugin)) continue
81
155
  promise = promise.then((result) => {
82
156
  if (result != null) return result
83
- return this.run('hookFirst', hookName, parameters, plugin) as typeof result
157
+ return this.run({
158
+ strategy: 'hookFirst',
159
+ hookName,
160
+ parameters,
161
+ plugin,
162
+ }) as typeof result
84
163
  })
85
164
  }
86
165
  return promise
87
166
  }
88
167
 
89
- // chains, first non-null result stops and returns
90
- hookFirst<H extends PluginLifecycleHooks>(
91
- hookName: H,
92
- parameters: Parameters<PluginLifecycle[H]>,
168
+ /**
169
+ *
170
+ * Chains, first non-null result stops and returns
171
+ */
172
+ hookFirstSync<H extends PluginLifecycleHooks>({
173
+ hookName,
174
+ parameters,
175
+ skipped,
176
+ }: {
177
+ hookName: H
178
+ parameters: Parameters<PluginLifecycle[H]>
93
179
  skipped?: ReadonlySet<KubbPlugin> | null
94
- ): Promise<ReturnType<PluginLifecycle[H]> | null> {
95
- let promise: Promise<ReturnType<PluginLifecycle[H]> | null> = Promise.resolve(null)
180
+ }): ReturnType<PluginLifecycle[H]> | null {
181
+ let result = null
182
+
96
183
  for (const plugin of this.getSortedPlugins(hookName)) {
97
184
  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
185
+
186
+ result = this.runSync({
187
+ strategy: 'hookFirst',
188
+ hookName,
189
+ parameters,
190
+ plugin,
101
191
  })
192
+
193
+ if (result != null) {
194
+ break
195
+ }
102
196
  }
103
- return promise
197
+ return result
104
198
  }
105
199
 
106
200
  // parallel
107
- async hookParallel<H extends PluginLifecycleHooks, TOuput = void>(hookName: H, parameters?: Parameters<PluginLifecycle[H]> | undefined) {
201
+ async hookParallel<H extends PluginLifecycleHooks, TOuput = void>({
202
+ hookName,
203
+ parameters,
204
+ }: {
205
+ hookName: H
206
+ parameters?: Parameters<PluginLifecycle[H]> | undefined
207
+ }) {
108
208
  const parallelPromises: Promise<TOuput>[] = []
109
209
 
110
210
  for (const plugin of this.getSortedPlugins(hookName)) {
111
211
  if ((plugin[hookName] as { sequential?: boolean })?.sequential) {
112
212
  await Promise.all(parallelPromises)
113
213
  parallelPromises.length = 0
114
- await this.run('hookParallel', hookName, parameters, plugin)
214
+ await this.run({
215
+ strategy: 'hookParallel',
216
+ hookName,
217
+ parameters,
218
+ plugin,
219
+ })
115
220
  } else {
116
- const promise: Promise<TOuput> = this.run('hookParallel', hookName, parameters, plugin)
221
+ const promise: Promise<TOuput> = this.run({ strategy: 'hookParallel', hookName, parameters, plugin })
117
222
 
118
223
  parallelPromises.push(promise)
119
224
  }
@@ -122,17 +227,26 @@ export class PluginManager {
122
227
  }
123
228
 
124
229
  // 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]>,
230
+ hookReduceArg0<H extends PluginLifecycleHooks>({
231
+ hookName,
232
+ parameters,
233
+ reduce,
234
+ }: {
235
+ hookName: H
236
+ parameters: Parameters<PluginLifecycle[H]>
128
237
  reduce: (reduction: Argument0<H>, result: ReturnType<PluginLifecycle[H]>, plugin: KubbPlugin) => MaybePromise<Argument0<H> | null>
129
- ): Promise<Argument0<H>> {
238
+ }): Promise<Argument0<H>> {
239
+ const [argument0, ...rest] = parameters
240
+
130
241
  let promise: Promise<Argument0<H>> = Promise.resolve(argument0)
131
242
  for (const plugin of this.getSortedPlugins(hookName)) {
132
243
  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
- )
244
+ this.run({
245
+ strategy: 'hookReduceArg0',
246
+ hookName,
247
+ parameters: [argument0, ...rest] as Parameters<PluginLifecycle[H]>,
248
+ plugin,
249
+ }).then((result) => reduce.call(this.core.api, argument0, result as ReturnType<PluginLifecycle[H]>, plugin))
136
250
  ) as Promise<Argument0<H>>
137
251
  }
138
252
  return promise
@@ -140,30 +254,39 @@ export class PluginManager {
140
254
 
141
255
  // chains
142
256
 
143
- hookSeq<H extends PluginLifecycleHooks>(hookName: H, parameters?: Parameters<PluginLifecycle[H]>) {
257
+ hookSeq<H extends PluginLifecycleHooks>({ hookName, parameters }: { hookName: H; parameters?: Parameters<PluginLifecycle[H]> }) {
144
258
  let promise: Promise<void> = Promise.resolve()
145
259
  for (const plugin of this.getSortedPlugins(hookName)) {
146
- promise = promise.then(() => this.run('hookSeq', hookName, parameters, plugin))
260
+ promise = promise.then(() =>
261
+ this.run({
262
+ strategy: 'hookSeq',
263
+ hookName,
264
+ parameters,
265
+ plugin,
266
+ })
267
+ )
147
268
  }
148
269
  return promise.then(noReturn)
149
270
  }
150
271
 
151
- private getSortedPlugins(hookName: keyof PluginLifecycle, pluginName?: string): KubbPlugin[] {
272
+ private getSortedPlugins(_hookName: keyof PluginLifecycle): KubbPlugin[] {
152
273
  const plugins = [...this.plugins]
153
274
 
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]
275
+ return plugins
276
+ }
277
+
278
+ private getPlugin(hookName: keyof PluginLifecycle, pluginName: string): KubbPlugin {
279
+ const plugins = [...this.plugins]
280
+
281
+ const pluginByPluginName = plugins.find((item) => item.name === pluginName && item[hookName])
282
+ if (!pluginByPluginName) {
283
+ // fallback on the core plugin when there is no match
284
+ if (this.config.logLevel === 'warn' && this.logger?.spinner) {
285
+ this.logger.spinner.info(`Plugin hook with ${hookName} not found for plugin ${pluginName}`)
162
286
  }
163
- return pluginsByPluginName
287
+ return this.core
164
288
  }
165
-
166
- return plugins
289
+ return pluginByPluginName
167
290
  }
168
291
 
169
292
  /**
@@ -173,12 +296,17 @@ export class PluginManager {
173
296
  * @param plugin The actual pluginObject to run.
174
297
  */
175
298
  // Implementation signature
176
- private run<H extends PluginLifecycleHooks, TResult = void>(
177
- strategy: Strategy,
178
- hookName: H,
179
- parameters: unknown[] | undefined,
299
+ private run<H extends PluginLifecycleHooks, TResult = void>({
300
+ strategy,
301
+ hookName,
302
+ parameters,
303
+ plugin,
304
+ }: {
305
+ strategy: Strategy
306
+ hookName: H
307
+ parameters: unknown[] | undefined
180
308
  plugin: KubbPlugin
181
- ): Promise<TResult> {
309
+ }): Promise<TResult> {
182
310
  const hook = plugin[hookName]!
183
311
 
184
312
  return Promise.resolve()
@@ -221,20 +349,31 @@ export class PluginManager {
221
349
  * @param plugin The acutal plugin
222
350
  * @param replaceContext When passed, the plugin context can be overridden.
223
351
  */
224
- private runSync<H extends PluginLifecycleHooks>(
225
- hookName: H,
226
- parameters: Parameters<PluginLifecycle[H]>,
352
+ private runSync<H extends PluginLifecycleHooks>({
353
+ strategy,
354
+ hookName,
355
+ parameters,
356
+ plugin,
357
+ }: {
358
+ strategy: Strategy
359
+ hookName: H
360
+ parameters: Parameters<PluginLifecycle[H]>
227
361
  plugin: KubbPlugin
228
- ): ReturnType<PluginLifecycle[H]> | Error {
362
+ }): ReturnType<PluginLifecycle[H]> {
229
363
  const hook = plugin[hookName]!
230
364
 
231
365
  // const context = this.pluginContexts.get(plugin)!;
232
366
 
233
367
  try {
234
368
  // eslint-disable-next-line @typescript-eslint/ban-types
369
+ if (typeof hook !== 'function') {
370
+ return hook
371
+ }
372
+
235
373
  return (hook as Function).apply(this.core.api, parameters)
236
- } catch (error) {
237
- return error as Error
374
+ } catch (e) {
375
+ this.catcher<H>(e as Error, plugin, hookName)
376
+ return null as ReturnType<PluginLifecycle[H]>
238
377
  }
239
378
  }
240
379
 
package/src/plugin.ts CHANGED
@@ -30,7 +30,8 @@ export function createPlugin<T extends PluginFactoryOptions = PluginFactoryOptio
30
30
  type Options = {
31
31
  config: PluginContext['config']
32
32
  fileManager: FileManager
33
- resolveId: PluginContext['resolveId']
33
+ resolvePath: PluginContext['resolvePath']
34
+ resolveName: PluginContext['resolveName']
34
35
  load: PluginContext['load']
35
36
  }
36
37
 
@@ -40,7 +41,7 @@ export type CorePluginOptions = PluginFactoryOptions<Options, false, PluginConte
40
41
  export const name = 'core' as const
41
42
 
42
43
  export const definePlugin = createPlugin<CorePluginOptions>((options) => {
43
- const { fileManager, resolveId, load } = options
44
+ const { fileManager, resolvePath, resolveName, load } = options
44
45
 
45
46
  const api: PluginContext = {
46
47
  get config() {
@@ -50,7 +51,8 @@ export const definePlugin = createPlugin<CorePluginOptions>((options) => {
50
51
  async addFile(file) {
51
52
  return fileManager.addOrAppend(file)
52
53
  },
53
- resolveId,
54
+ resolvePath,
55
+ resolveName,
54
56
  load,
55
57
  cache: createPluginCache(Object.create(null)),
56
58
  }
@@ -59,11 +61,14 @@ export const definePlugin = createPlugin<CorePluginOptions>((options) => {
59
61
  name,
60
62
  options,
61
63
  api,
62
- resolveId(fileName, directory) {
64
+ resolvePath(fileName, directory) {
63
65
  if (!directory) {
64
66
  return null
65
67
  }
66
68
  return pathParser.resolve(directory, fileName)
67
69
  },
70
+ resolveName(name) {
71
+ return name
72
+ },
68
73
  }
69
74
  })
package/src/types.ts CHANGED
@@ -108,9 +108,9 @@ export type KubbPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOpti
108
108
  } & Partial<PluginLifecycle<TOptions>>
109
109
 
110
110
  // use of type objects
111
- export type PluginFactoryOptions<Options = unknown, Nested extends boolean = false, Api = any, ResolveIdOptions = Record<string, any>> = {
111
+ export type PluginFactoryOptions<Options = unknown, Nested extends boolean = false, Api = any, resolvePathOptions = Record<string, any>> = {
112
112
  options: Options
113
- resolveIdOptions: ResolveIdOptions
113
+ resolvePathOptions: resolvePathOptions
114
114
  nested: Nested
115
115
  api: Api
116
116
  }
@@ -127,13 +127,21 @@ export type PluginLifecycle<TOptions extends PluginFactoryOptions = PluginFactor
127
127
  */
128
128
  buildStart: (this: PluginContext, kubbConfig: KubbConfig) => MaybePromise<void>
129
129
  /**
130
- * Resolve to an id based on importee(example: `./Pet.ts`) and directory(example: `./models`).
130
+ * Resolve to a Path based on a fileName(example: `./Pet.ts`) and directory(example: `./models`).
131
+ * Options can als be included.
131
132
  * @type hookFirst
132
- * @example ('./Pet.ts', './src/gen/')
133
+ * @example ('./Pet.ts', './src/gen/') => '/src/gen/Pet.ts'
133
134
  */
134
- resolveId: (this: Omit<PluginContext, 'addFile'>, fileName: string, directory?: string, options?: TOptions['resolveIdOptions']) => OptionalPath
135
+ resolvePath: (this: Omit<PluginContext, 'addFile'>, fileName: string, directory?: string, options?: TOptions['resolvePathOptions']) => OptionalPath
135
136
  /**
136
- * Makes it possible to run async logic to override the path defined previously by `resolveId`.
137
+ * Resolve to a name based on a string.
138
+ * Useful when converting to PascalCase or camelCase.
139
+ * @type hookFirst
140
+ * @example ('pet') => 'Pet'
141
+ */
142
+ resolveName: (this: Omit<PluginContext, 'addFile'>, name: string) => string | null
143
+ /**
144
+ * Makes it possible to run async logic to override the path defined previously by `resolvePath`.
137
145
  * @type hookFirst
138
146
  */
139
147
  load: (this: Omit<PluginContext, 'addFile'>, path: Path) => MaybePromise<TransformResult | null>
@@ -143,7 +151,7 @@ export type PluginLifecycle<TOptions extends PluginFactoryOptions = PluginFactor
143
151
  */
144
152
  transform: (this: Omit<PluginContext, 'addFile'>, source: string, path: Path) => MaybePromise<TransformResult>
145
153
  /**
146
- * Write the result to the file-system based on the id(defined by `resolveId` or changed by `load`).
154
+ * Write the result to the file-system based on the id(defined by `resolvePath` or changed by `load`).
147
155
  * @type hookParallel
148
156
  */
149
157
  writeFile: (this: Omit<PluginContext, 'addFile'>, source: string | undefined, path: Path) => MaybePromise<void>
@@ -156,26 +164,36 @@ export type PluginLifecycle<TOptions extends PluginFactoryOptions = PluginFactor
156
164
 
157
165
  export type PluginLifecycleHooks = keyof PluginLifecycle
158
166
 
159
- export type ResolveIdParams<TOptions = Record<string, any>> = {
160
- fileName: string
161
- directory?: string | undefined
167
+ export type ResolvePathParams<TOptions = Record<string, any>> = {
162
168
  /**
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.
169
+ * When set, resolvePath will only call resolvePath of the name of the plugin set here.
170
+ * If not defined it will fall back on the resolvePath of the core plugin.
165
171
  */
166
172
  pluginName?: string
173
+ fileName: string
174
+ directory?: string | undefined
167
175
  /**
168
- * Options to be passed to 'resolveId' 3th parameter
176
+ * Options to be passed to 'resolvePath' 3th parameter
169
177
  */
170
178
  options?: TOptions
171
179
  }
172
180
 
181
+ export type ResolveNameParams = {
182
+ /**
183
+ * When set, resolvePath will only call resolvePath of the name of the plugin set here.
184
+ * If not defined it will fall back on the resolvePath of the core plugin.
185
+ */
186
+ pluginName?: string
187
+ name: string
188
+ }
189
+
173
190
  export type PluginContext<TOptions = Record<string, any>> = {
174
191
  config: KubbConfig
175
192
  cache: Cache
176
193
  fileManager: FileManager
177
194
  addFile: (file: File) => Promise<File>
178
- resolveId: (params: ResolveIdParams<TOptions>) => MaybePromise<OptionalPath>
195
+ resolvePath: (params: ResolvePathParams<TOptions>) => OptionalPath
196
+ resolveName: (params: ResolveNameParams) => string | null
179
197
  load: (id: string) => MaybePromise<TransformResult | void>
180
198
  }
181
199
 
@@ -1,8 +1,8 @@
1
- export function createJSDocBlockText({ comments }: { comments: Array<string | undefined> }) {
1
+ export function createJSDocBlockText({ comments }: { comments: Array<string> }): string {
2
2
  const filteredComments = comments.filter(Boolean)
3
3
 
4
4
  if (!filteredComments.length) {
5
- return undefined
5
+ return ''
6
6
  }
7
7
 
8
8
  const text = filteredComments.reduce((acc, comment) => {