@kubb/core 2.0.0-alpha.3 → 2.0.0-alpha.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.
Files changed (53) hide show
  1. package/dist/index.cjs +70 -75
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +198 -215
  4. package/dist/index.d.ts +198 -215
  5. package/dist/index.js +70 -73
  6. package/dist/index.js.map +1 -1
  7. package/dist/utils.cjs +19 -8
  8. package/dist/utils.cjs.map +1 -1
  9. package/dist/utils.d.cts +6 -2
  10. package/dist/utils.d.ts +6 -2
  11. package/dist/utils.js +19 -8
  12. package/dist/utils.js.map +1 -1
  13. package/package.json +7 -5
  14. package/src/BarrelManager.ts +123 -0
  15. package/src/FileManager.ts +483 -0
  16. package/src/Generator.ts +34 -0
  17. package/src/PackageManager.ts +163 -0
  18. package/src/PluginManager.ts +644 -0
  19. package/src/PromiseManager.ts +47 -0
  20. package/src/SchemaGenerator.ts +8 -0
  21. package/src/build.ts +207 -0
  22. package/src/config.ts +22 -0
  23. package/src/errors.ts +12 -0
  24. package/src/index.ts +28 -0
  25. package/src/plugin.ts +80 -0
  26. package/src/types.ts +346 -0
  27. package/src/utils/EventEmitter.ts +24 -0
  28. package/src/utils/FunctionParams.ts +85 -0
  29. package/src/utils/Queue.ts +110 -0
  30. package/src/utils/TreeNode.ts +122 -0
  31. package/src/utils/URLPath.ts +128 -0
  32. package/src/utils/cache.ts +35 -0
  33. package/src/utils/clean.ts +5 -0
  34. package/src/utils/executeStrategies.ts +71 -0
  35. package/src/utils/index.ts +19 -0
  36. package/src/utils/logger.ts +76 -0
  37. package/src/utils/promise.ts +13 -0
  38. package/src/utils/randomColour.ts +39 -0
  39. package/src/utils/read.ts +68 -0
  40. package/src/utils/renderTemplate.ts +31 -0
  41. package/src/utils/throttle.ts +30 -0
  42. package/src/utils/timeout.ts +7 -0
  43. package/src/utils/transformers/combineCodes.ts +3 -0
  44. package/src/utils/transformers/createJSDocBlockText.ts +15 -0
  45. package/src/utils/transformers/escape.ts +31 -0
  46. package/src/utils/transformers/indent.ts +3 -0
  47. package/src/utils/transformers/index.ts +22 -0
  48. package/src/utils/transformers/nameSorter.ts +9 -0
  49. package/src/utils/transformers/searchAndReplace.ts +25 -0
  50. package/src/utils/transformers/transformReservedWord.ts +97 -0
  51. package/src/utils/transformers/trim.ts +3 -0
  52. package/src/utils/uniqueName.ts +20 -0
  53. package/src/utils/write.ts +63 -0
@@ -0,0 +1,24 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
2
+ import { EventEmitter as NodeEventEmitter } from 'node:events'
3
+
4
+ export class EventEmitter<TEvents extends Record<string, any>> {
5
+ constructor() {
6
+ this.#emitter.setMaxListeners(100)
7
+ }
8
+ #emitter = new NodeEventEmitter()
9
+
10
+ emit<TEventName extends keyof TEvents & string>(eventName: TEventName, ...eventArg: TEvents[TEventName]): void {
11
+ this.#emitter.emit(eventName, ...(eventArg as []))
12
+ }
13
+
14
+ on<TEventName extends keyof TEvents & string>(eventName: TEventName, handler: (...eventArg: TEvents[TEventName]) => void): void {
15
+ this.#emitter.on(eventName, handler as any)
16
+ }
17
+
18
+ off<TEventName extends keyof TEvents & string>(eventName: TEventName, handler: (...eventArg: TEvents[TEventName]) => void): void {
19
+ this.#emitter.off(eventName, handler as any)
20
+ }
21
+ removeAll(): void {
22
+ this.#emitter.removeAllListeners()
23
+ }
24
+ }
@@ -0,0 +1,85 @@
1
+ import { camelCase, camelCaseTransformMerge } from 'change-case'
2
+ import { orderBy } from 'natural-orderby'
3
+
4
+ type FunctionParamsASTWithoutType = {
5
+ name?: string
6
+ type?: string
7
+ /**
8
+ * @default true
9
+ */
10
+ required?: boolean
11
+ /**
12
+ * @default true
13
+ */
14
+ enabled?: boolean
15
+ default?: string
16
+ }
17
+
18
+ type FunctionParamsASTWithType = {
19
+ name?: never
20
+ type: string
21
+ /**
22
+ * @default true
23
+ */
24
+ required?: boolean
25
+ /**
26
+ * @default true
27
+ */
28
+ enabled?: boolean
29
+ default?: string
30
+ }
31
+
32
+ export type FunctionParamsAST = FunctionParamsASTWithoutType | FunctionParamsASTWithType
33
+ export class FunctionParams {
34
+ public type?: 'generics' | 'typed'
35
+ public items: FunctionParamsAST[] = []
36
+ constructor(type?: 'generics' | 'typed') {
37
+ this.type = type
38
+
39
+ return this
40
+ }
41
+
42
+ add(item: FunctionParamsAST | Array<FunctionParamsAST | undefined> | undefined): FunctionParams {
43
+ if (!item) {
44
+ return this
45
+ }
46
+
47
+ if (Array.isArray(item)) {
48
+ item.filter(Boolean).forEach((it) => this.items.push(it))
49
+ return this
50
+ }
51
+ this.items.push(item)
52
+
53
+ return this
54
+ }
55
+
56
+ toString(): string {
57
+ const sortedData = orderBy(this.items.filter(Boolean), [(v) => !v.default, (v) => v.required ?? true], ['desc', 'desc'])
58
+
59
+ return sortedData
60
+ .filter(({ enabled = true }) => enabled)
61
+ .reduce((acc, { name, type, required = true, ...rest }) => {
62
+ if (!name) {
63
+ // when name is not se we will use TypeScript generics
64
+ acc.push(`${type}${rest.default ? ` = ${rest.default}` : ''}`)
65
+
66
+ return acc
67
+ }
68
+ // TODO check whey we still need the camelcase here
69
+ const parameterName = name.startsWith('{') ? name : camelCase(name, { delimiter: '', transform: camelCaseTransformMerge })
70
+
71
+ if (type) {
72
+ if (required) {
73
+ acc.push(`${parameterName}: ${type}${rest.default ? ` = ${rest.default}` : ''}`)
74
+ } else {
75
+ acc.push(`${parameterName}?: ${type}`)
76
+ }
77
+ } else {
78
+ acc.push(`${parameterName}`)
79
+ }
80
+
81
+ return acc
82
+ }, [] as string[])
83
+ .join(', ')
84
+ }
85
+ }
@@ -0,0 +1,110 @@
1
+ import crypto from 'node:crypto'
2
+ import { performance } from 'node:perf_hooks'
3
+
4
+ import { EventEmitter } from './EventEmitter.ts'
5
+
6
+ export type QueueJob<T = unknown> = {
7
+ (...args: unknown[]): Promise<T | void>
8
+ }
9
+
10
+ type RunOptions = {
11
+ controller?: AbortController
12
+ name?: string
13
+ description?: string
14
+ }
15
+
16
+ type QueueItem = {
17
+ reject: <T>(reason?: T) => void
18
+ resolve: <T>(value: T | PromiseLike<T>) => void
19
+ job: QueueJob<unknown>
20
+ } & Required<RunOptions>
21
+
22
+ type Events = {
23
+ jobDone: [result: unknown]
24
+ jobFailed: [error: Error]
25
+ }
26
+
27
+ export class Queue {
28
+ #queue: QueueItem[] = []
29
+ readonly eventEmitter: EventEmitter<Events> = new EventEmitter()
30
+
31
+ #workerCount = 0
32
+
33
+ #maxParallel: number
34
+ #debug = false
35
+
36
+ constructor(maxParallel: number, debug = false) {
37
+ this.#maxParallel = maxParallel
38
+ this.#debug = debug
39
+ }
40
+
41
+ run<T>(job: QueueJob<T>, options: RunOptions = { controller: new AbortController(), name: crypto.randomUUID(), description: '' }): Promise<T> {
42
+ return new Promise<T>((resolve, reject) => {
43
+ const item = { reject, resolve, job, name: options.name, description: options.description || options.name } as QueueItem
44
+
45
+ options.controller?.signal.addEventListener('abort', () => {
46
+ this.#queue = this.#queue.filter((queueItem) => queueItem.name === item.name)
47
+
48
+ reject('Aborted')
49
+ })
50
+
51
+ this.#queue.push(item)
52
+ this.#work()
53
+ })
54
+ }
55
+
56
+ runSync<T>(job: QueueJob<T>, options: RunOptions = { controller: new AbortController(), name: crypto.randomUUID(), description: '' }): void {
57
+ new Promise<T>((resolve, reject) => {
58
+ const item = { reject, resolve, job, name: options.name, description: options.description || options.name } as QueueItem
59
+
60
+ options.controller?.signal.addEventListener('abort', () => {
61
+ this.#queue = this.#queue.filter((queueItem) => queueItem.name === item.name)
62
+ })
63
+
64
+ this.#queue.push(item)
65
+ this.#work()
66
+ })
67
+ }
68
+
69
+ get hasJobs(): boolean {
70
+ return this.#workerCount > 0 || this.#queue.length > 0
71
+ }
72
+
73
+ get count(): number {
74
+ return this.#workerCount
75
+ }
76
+
77
+ #work(): void {
78
+ if (this.#workerCount >= this.#maxParallel) {
79
+ return
80
+ }
81
+
82
+ this.#workerCount++
83
+
84
+ let entry: QueueItem | undefined
85
+ while ((entry = this.#queue.shift())) {
86
+ const { reject, resolve, job, name, description } = entry
87
+
88
+ if (this.#debug) {
89
+ performance.mark(name + '_start')
90
+ }
91
+
92
+ job()
93
+ .then((result) => {
94
+ this.eventEmitter.emit('jobDone', result)
95
+
96
+ resolve(result)
97
+
98
+ if (this.#debug) {
99
+ performance.mark(name + '_stop')
100
+ performance.measure(description, name + '_start', name + '_stop')
101
+ }
102
+ })
103
+ .catch((err) => {
104
+ this.eventEmitter.emit('jobFailed', err as Error)
105
+ reject(err)
106
+ })
107
+ }
108
+ this.#workerCount--
109
+ }
110
+ }
@@ -0,0 +1,122 @@
1
+ import dirTree from 'directory-tree'
2
+
3
+ import { FileManager } from '../FileManager.ts'
4
+
5
+ import type { DirectoryTree, DirectoryTreeOptions } from 'directory-tree'
6
+
7
+ export type TreeNodeOptions = DirectoryTreeOptions
8
+
9
+ export class TreeNode<T = unknown> {
10
+ public data: T
11
+
12
+ public parent?: TreeNode<T>
13
+
14
+ public children: Array<TreeNode<T>> = []
15
+
16
+ constructor(data: T, parent?: TreeNode<T>) {
17
+ this.data = data
18
+ this.parent = parent
19
+ return this
20
+ }
21
+
22
+ addChild(data: T): TreeNode<T> {
23
+ const child = new TreeNode(data, this)
24
+ if (!this.children) {
25
+ this.children = []
26
+ }
27
+ this.children.push(child)
28
+ return child
29
+ }
30
+
31
+ find(data?: T): TreeNode<T> | null {
32
+ if (!data) {
33
+ return null
34
+ }
35
+
36
+ if (data === this.data) {
37
+ return this
38
+ }
39
+
40
+ if (this.children?.length) {
41
+ for (let i = 0, { length } = this.children, target: TreeNode<T> | null = null; i < length; i++) {
42
+ target = this.children[i]!.find(data)
43
+ if (target) {
44
+ return target
45
+ }
46
+ }
47
+ }
48
+
49
+ return null
50
+ }
51
+
52
+ get leaves(): TreeNode<T>[] {
53
+ if (!this.children || this.children.length === 0) {
54
+ // this is a leaf
55
+ return [this]
56
+ }
57
+
58
+ // if not a leaf, return all children's leaves recursively
59
+ const leaves: TreeNode<T>[] = []
60
+ if (this.children) {
61
+ for (let i = 0, { length } = this.children; i < length; i++) {
62
+ // eslint-disable-next-line prefer-spread
63
+ leaves.push.apply(leaves, this.children[i]!.leaves)
64
+ }
65
+ }
66
+ return leaves
67
+ }
68
+
69
+ get root(): TreeNode<T> {
70
+ if (!this.parent) {
71
+ return this
72
+ }
73
+ return this.parent.root
74
+ }
75
+
76
+ forEach(callback: (treeNode: TreeNode<T>) => void): this {
77
+ if (typeof callback !== 'function') {
78
+ throw new TypeError('forEach() callback must be a function')
79
+ }
80
+
81
+ // run this node through function
82
+ callback(this)
83
+
84
+ // do the same for all children
85
+ if (this.children) {
86
+ for (let i = 0, { length } = this.children; i < length; i++) {
87
+ this.children[i]!.forEach(callback)
88
+ }
89
+ }
90
+
91
+ return this
92
+ }
93
+
94
+ public static build<T = unknown>(path: string, options: TreeNodeOptions = {}): TreeNode<T> | null {
95
+ try {
96
+ const exclude = Array.isArray(options.exclude) ? options.exclude : [options.exclude].filter(Boolean)
97
+ const filteredTree = dirTree(path, { extensions: options.extensions, exclude: [/node_modules/, ...exclude] })
98
+
99
+ if (!filteredTree) {
100
+ return null
101
+ }
102
+
103
+ const treeNode = new TreeNode({ name: filteredTree.name, path: filteredTree.path, type: filteredTree.type || FileManager.getMode(filteredTree.path) })
104
+
105
+ const recurse = (node: typeof treeNode, item: DirectoryTree) => {
106
+ const subNode = node.addChild({ name: item.name, path: item.path, type: item.type || FileManager.getMode(item.path) })
107
+
108
+ if (item.children?.length) {
109
+ item.children?.forEach((child) => {
110
+ recurse(subNode, child)
111
+ })
112
+ }
113
+ }
114
+
115
+ filteredTree.children?.forEach((child) => recurse(treeNode, child))
116
+
117
+ return treeNode as TreeNode<T>
118
+ } catch (e) {
119
+ throw new Error('Something went wrong with creating index files with the TreehNode class', { cause: e })
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,128 @@
1
+ import { camelCase, camelCaseTransformMerge } from 'change-case'
2
+
3
+ export type URLObject = {
4
+ url: string
5
+ params?: Record<string, string>
6
+ }
7
+
8
+ type ObjectOptions = {
9
+ type?: 'path' | 'template'
10
+ replacer?: (pathParam: string) => string
11
+ stringify?: boolean
12
+ }
13
+
14
+ export class URLPath {
15
+ path: string
16
+
17
+ constructor(path: string) {
18
+ this.path = path
19
+
20
+ return this
21
+ }
22
+
23
+ /**
24
+ * Convert Swagger path to URLPath(syntax of Express)
25
+ * @example /pet/{petId} => /pet/:petId
26
+ */
27
+ get URL(): string {
28
+ return this.toURLPath()
29
+ }
30
+ get isURL(): boolean {
31
+ try {
32
+ const url = new URL(this.path)
33
+ if (url?.href) {
34
+ return true
35
+ }
36
+ } catch (error) {
37
+ return false
38
+ }
39
+ return false
40
+ }
41
+
42
+ /**
43
+ * Convert Swagger path to template literals/ template strings(camelcase)
44
+ * @example /pet/{petId} => `/pet/${petId}`
45
+ * @example /account/monetary-accountID => `/account/${monetaryAccountId}`
46
+ * @example /account/userID => `/account/${userId}`
47
+ */
48
+ get template(): string {
49
+ return this.toTemplateString()
50
+ }
51
+ get object(): URLObject | string {
52
+ return this.toObject()
53
+ }
54
+ get params(): Record<string, string> | undefined {
55
+ return this.getParams()
56
+ }
57
+
58
+ toObject({ type = 'path', replacer, stringify }: ObjectOptions = {}): URLObject | string {
59
+ const object = {
60
+ url: type === 'path' ? this.toURLPath() : this.toTemplateString(replacer),
61
+ params: this.getParams(),
62
+ }
63
+
64
+ if (stringify) {
65
+ if (type !== 'template') {
66
+ throw new Error('Type should be `template` when using stringiyf')
67
+ }
68
+ return JSON.stringify(object).replaceAll("'", '').replaceAll(`"`, '')
69
+ }
70
+
71
+ return object
72
+ }
73
+
74
+ /**
75
+ * Convert Swagger path to template literals/ template strings(camelcase)
76
+ * @example /pet/{petId} => `/pet/${petId}`
77
+ * @example /account/monetary-accountID => `/account/${monetaryAccountId}`
78
+ * @example /account/userID => `/account/${userId}`
79
+ */
80
+ toTemplateString(replacer?: (pathParam: string) => string): string {
81
+ const regex = /{(\w|-)*}/g
82
+ const found = this.path.match(regex)
83
+ let newPath = this.path.replaceAll('{', '${')
84
+
85
+ if (found) {
86
+ newPath = found.reduce((prev, curr) => {
87
+ const pathParam = replacer
88
+ ? replacer(camelCase(curr, { delimiter: '', transform: camelCaseTransformMerge }))
89
+ : camelCase(curr, { delimiter: '', transform: camelCaseTransformMerge })
90
+ const replacement = `\${${pathParam}}`
91
+
92
+ return prev.replace(curr, replacement)
93
+ }, this.path)
94
+ }
95
+
96
+ return `\`${newPath}\``
97
+ }
98
+
99
+ getParams(replacer?: (pathParam: string) => string): Record<string, string> | undefined {
100
+ const regex = /{(\w|-)*}/g
101
+ const found = this.path.match(regex)
102
+
103
+ if (!found) {
104
+ return undefined
105
+ }
106
+
107
+ const params: Record<string, string> = {}
108
+ found.forEach((item) => {
109
+ item = item.replaceAll('{', '').replaceAll('}', '')
110
+
111
+ const pathParam = replacer
112
+ ? replacer(camelCase(item, { delimiter: '', transform: camelCaseTransformMerge }))
113
+ : camelCase(item, { delimiter: '', transform: camelCaseTransformMerge })
114
+
115
+ params[pathParam] = pathParam
116
+ }, this.path)
117
+
118
+ return params
119
+ }
120
+
121
+ /**
122
+ * Convert Swagger path to URLPath(syntax of Express)
123
+ * @example /pet/{petId} => /pet/:petId
124
+ */
125
+ toURLPath(): string {
126
+ return this.path.replaceAll('{', ':').replaceAll('}', '')
127
+ }
128
+ }
@@ -0,0 +1,35 @@
1
+ import type { PluginCache } from '../types.ts'
2
+
3
+ export interface Cache<TStore extends object = object> {
4
+ delete(id: keyof TStore): boolean
5
+ get(id: keyof TStore): TStore[keyof TStore] | null
6
+ has(id: keyof TStore): boolean
7
+ set(id: keyof TStore, value: unknown): void
8
+ }
9
+
10
+ export function createPluginCache<TStore extends PluginCache>(Store: TStore = Object.create(null) as TStore): Cache<TStore> {
11
+ return {
12
+ set(id, value): void {
13
+ Store[id] = [0, value] as TStore[keyof TStore]
14
+ },
15
+ get(id): TStore[keyof TStore] | null {
16
+ const item = Store[id]
17
+ if (!item) {
18
+ return null
19
+ }
20
+ item[0] = 0
21
+ return item[1] as TStore[keyof TStore]
22
+ },
23
+ has(id): boolean {
24
+ const item = Store[id]
25
+ if (!item) {
26
+ return false
27
+ }
28
+ item[0] = 0
29
+ return true
30
+ },
31
+ delete(id: keyof TStore): boolean {
32
+ return delete Store[id]
33
+ },
34
+ }
35
+ }
@@ -0,0 +1,5 @@
1
+ import { remove } from 'fs-extra'
2
+
3
+ export async function clean(path: string): Promise<void> {
4
+ return remove(path)
5
+ }
@@ -0,0 +1,71 @@
1
+ /* eslint-disable unused-imports/no-unused-vars */
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
3
+ type PromiseFunc<T = unknown, T2 = never> = (state: T) => T2 extends never ? Promise<T> : Promise<T> | T2
4
+
5
+ export type ValueOfPromiseFuncArray<TInput extends Array<unknown>> = TInput extends Array<PromiseFunc<infer X, infer Y>> ? X | Y : never
6
+
7
+ export function noReturn(): void {}
8
+
9
+ type SeqOutput<TInput extends Array<PromiseFunc<TValue, null>>, TValue> = Array<Awaited<ValueOfPromiseFuncArray<TInput>>>
10
+
11
+ /**
12
+ * Chains promises
13
+ */
14
+ export function hookSeq<TInput extends Array<PromiseFunc<TValue, null>>, TValue, TOutput = SeqOutput<TInput, TValue>>(
15
+ promises: TInput,
16
+ ): TOutput {
17
+ return promises.filter(Boolean).reduce(
18
+ (promise, func) => {
19
+ if (typeof func !== 'function') {
20
+ throw new Error('HookSeq needs a function that returns a promise `() => Promise<unknown>`')
21
+ }
22
+
23
+ return promise.then((state) => {
24
+ const calledFunc = func(state as TValue)
25
+
26
+ if (calledFunc) {
27
+ return calledFunc.then(Array.prototype.concat.bind(state))
28
+ }
29
+ })
30
+ },
31
+ Promise.resolve([] as unknown),
32
+ ) as TOutput
33
+ }
34
+
35
+ type HookFirstOutput<TInput extends Array<PromiseFunc<TValue, null>>, TValue = unknown> = ValueOfPromiseFuncArray<TInput>
36
+
37
+ /**
38
+ * Chains promises, first non-null result stops and returns
39
+ */
40
+ export function hookFirst<TInput extends Array<PromiseFunc<TValue, null>>, TValue = unknown, TOutput = HookFirstOutput<TInput, TValue>>(
41
+ promises: TInput,
42
+ nullCheck = (state: any) => state !== null,
43
+ ): TOutput {
44
+ let promise: Promise<unknown> = Promise.resolve(null) as Promise<unknown>
45
+
46
+ for (const func of promises.filter(Boolean)) {
47
+ promise = promise.then((state) => {
48
+ if (nullCheck(state)) {
49
+ return state
50
+ }
51
+
52
+ const calledFunc = func(state as TValue)
53
+
54
+ return calledFunc
55
+ })
56
+ }
57
+
58
+ return promise as TOutput
59
+ }
60
+
61
+ export type Strategy = 'seq' | 'first'
62
+
63
+ export type StrategySwitch<TStrategy extends Strategy, TInput extends Array<PromiseFunc<TValue, null>>, TValue> = TStrategy extends 'first'
64
+ ? HookFirstOutput<TInput, TValue>
65
+ : TStrategy extends 'seq' ? SeqOutput<TInput, TValue>
66
+ : never
67
+
68
+ // tests
69
+
70
+ type test = ValueOfPromiseFuncArray<Array<PromiseFunc<number, null>>>
71
+ // ^?
@@ -0,0 +1,19 @@
1
+ export * from './cache.ts'
2
+ export * from './cache.ts'
3
+ export * from './clean.ts'
4
+ export * from './FunctionParams.ts'
5
+ export * from './FunctionParams.ts'
6
+ export * from './logger.ts'
7
+ export * from './promise.ts'
8
+ export * from './Queue.ts'
9
+ export * from './Queue.ts'
10
+ export * from './randomColour.ts'
11
+ export * from './read.ts'
12
+ export * from './renderTemplate.ts'
13
+ export * from './throttle.ts'
14
+ export * from './timeout.ts'
15
+ export * from './transformers/index.ts'
16
+ export * from './TreeNode.ts'
17
+ export * from './uniqueName.ts'
18
+ export * from './URLPath.ts'
19
+ export * from './write.ts'
@@ -0,0 +1,76 @@
1
+ import pc from 'picocolors'
2
+
3
+ import type { Ora } from 'ora'
4
+
5
+ export const LogLevel = {
6
+ silent: 'silent',
7
+ info: 'info',
8
+ debug: 'debug',
9
+ } as const
10
+
11
+ export type LogLevel = keyof typeof LogLevel
12
+
13
+ export type Logger = {
14
+ /**
15
+ * Optional config name to show in CLI output
16
+ */
17
+ name?: string
18
+ logLevel: LogLevel
19
+ log: (message: string | null) => void
20
+ error: (message: string | null) => void
21
+ info: (message: string | null) => void
22
+ warn: (message: string | null) => void
23
+ spinner?: Ora
24
+ logs: string[]
25
+ }
26
+
27
+ type Props = {
28
+ name?: string
29
+ logLevel: LogLevel
30
+ spinner?: Ora
31
+ }
32
+
33
+ export function createLogger({ logLevel, name, spinner }: Props): Logger {
34
+ const logs: string[] = []
35
+ const log: Logger['log'] = (message) => {
36
+ if (message && spinner) {
37
+ spinner.text = message
38
+ logs.push(message)
39
+ }
40
+ }
41
+
42
+ const error: Logger['error'] = (message) => {
43
+ if (message) {
44
+ throw new Error(message || 'Something went wrong')
45
+ }
46
+ }
47
+
48
+ const warn: Logger['warn'] = (message) => {
49
+ if (message && spinner) {
50
+ spinner.warn(pc.yellow(message))
51
+ logs.push(message)
52
+ }
53
+ }
54
+
55
+ const info: Logger['warn'] = (message) => {
56
+ if (message && spinner && logLevel !== LogLevel.silent) {
57
+ spinner.info(message)
58
+ logs.push(message)
59
+ }
60
+ }
61
+
62
+ const logger: Logger = {
63
+ name,
64
+ logLevel,
65
+ log,
66
+ error,
67
+ warn,
68
+ info,
69
+ spinner,
70
+ logs,
71
+ }
72
+
73
+ return logger
74
+ }
75
+
76
+ export { default as pc } from 'picocolors'
@@ -0,0 +1,13 @@
1
+ import type { PossiblePromise } from '@kubb/types'
2
+
3
+ export function isPromise<T>(result: PossiblePromise<T>): result is Promise<T> {
4
+ return !!result && typeof (result as Promise<unknown>)?.then === 'function'
5
+ }
6
+
7
+ export function isPromiseFulfilledResult<T = unknown>(result: PromiseSettledResult<unknown>): result is PromiseFulfilledResult<T> {
8
+ return result.status === 'fulfilled'
9
+ }
10
+
11
+ export function isPromiseRejectedResult<T>(result: PromiseSettledResult<unknown>): result is Omit<PromiseRejectedResult, 'reason'> & { reason: T } {
12
+ return result.status === 'rejected'
13
+ }