@kubb/core 1.15.0-canary.20231112T135011 → 2.0.0-alpha.10
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.
- package/README.md +1 -1
- package/dist/index.cjs +1253 -1088
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +396 -411
- package/dist/index.d.ts +396 -411
- package/dist/index.js +1194 -1018
- package/dist/index.js.map +1 -1
- package/dist/utils.cjs +1272 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +239 -0
- package/dist/utils.d.ts +239 -0
- package/dist/utils.js +1219 -0
- package/dist/utils.js.map +1 -0
- package/globals.d.ts +33 -16
- package/package.json +21 -14
- package/src/BarrelManager.ts +123 -0
- package/src/FileManager.ts +524 -0
- package/src/Generator.ts +34 -0
- package/src/PackageManager.ts +178 -0
- package/src/PluginManager.ts +629 -0
- package/src/PromiseManager.ts +51 -0
- package/src/SchemaGenerator.ts +8 -0
- package/src/build.ts +207 -0
- package/src/config.ts +22 -0
- package/src/errors.ts +12 -0
- package/src/index.ts +28 -0
- package/src/plugin.ts +80 -0
- package/src/types.ts +353 -0
- package/src/utils/EventEmitter.ts +24 -0
- package/src/utils/FunctionParams.ts +85 -0
- package/src/utils/Queue.ts +110 -0
- package/src/utils/TreeNode.ts +122 -0
- package/src/utils/URLPath.ts +133 -0
- package/src/utils/cache.ts +35 -0
- package/src/utils/clean.ts +5 -0
- package/src/utils/executeStrategies.ts +83 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/logger.ts +76 -0
- package/src/utils/promise.ts +13 -0
- package/src/utils/randomColour.ts +39 -0
- package/src/utils/read.ts +68 -0
- package/src/utils/renderTemplate.ts +31 -0
- package/src/utils/throttle.ts +30 -0
- package/src/utils/timeout.ts +7 -0
- package/src/utils/transformers/combineCodes.ts +3 -0
- package/src/utils/transformers/createJSDocBlockText.ts +15 -0
- package/src/utils/transformers/escape.ts +31 -0
- package/src/utils/transformers/indent.ts +3 -0
- package/src/utils/transformers/index.ts +22 -0
- package/src/utils/transformers/nameSorter.ts +9 -0
- package/src/utils/transformers/searchAndReplace.ts +25 -0
- package/src/utils/transformers/transformReservedWord.ts +97 -0
- package/src/utils/transformers/trim.ts +3 -0
- package/src/utils/uniqueName.ts +20 -0
- 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,133 @@
|
|
|
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
|
+
return JSON.stringify(object).replaceAll("'", '').replaceAll(`"`, '')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (object.params) {
|
|
70
|
+
return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", '').replaceAll(`"`, '')} }`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return `{ url: '${object.url}' }`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return object
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Convert Swagger path to template literals/ template strings(camelcase)
|
|
81
|
+
* @example /pet/{petId} => `/pet/${petId}`
|
|
82
|
+
* @example /account/monetary-accountID => `/account/${monetaryAccountId}`
|
|
83
|
+
* @example /account/userID => `/account/${userId}`
|
|
84
|
+
*/
|
|
85
|
+
toTemplateString(replacer?: (pathParam: string) => string): string {
|
|
86
|
+
const regex = /{(\w|-)*}/g
|
|
87
|
+
const found = this.path.match(regex)
|
|
88
|
+
let newPath = this.path.replaceAll('{', '${')
|
|
89
|
+
|
|
90
|
+
if (found) {
|
|
91
|
+
newPath = found.reduce((prev, curr) => {
|
|
92
|
+
const pathParam = replacer
|
|
93
|
+
? replacer(camelCase(curr, { delimiter: '', transform: camelCaseTransformMerge }))
|
|
94
|
+
: camelCase(curr, { delimiter: '', transform: camelCaseTransformMerge })
|
|
95
|
+
const replacement = `\${${pathParam}}`
|
|
96
|
+
|
|
97
|
+
return prev.replace(curr, replacement)
|
|
98
|
+
}, this.path)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return `\`${newPath}\``
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getParams(replacer?: (pathParam: string) => string): Record<string, string> | undefined {
|
|
105
|
+
const regex = /{(\w|-)*}/g
|
|
106
|
+
const found = this.path.match(regex)
|
|
107
|
+
|
|
108
|
+
if (!found) {
|
|
109
|
+
return undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const params: Record<string, string> = {}
|
|
113
|
+
found.forEach((item) => {
|
|
114
|
+
item = item.replaceAll('{', '').replaceAll('}', '')
|
|
115
|
+
|
|
116
|
+
const pathParam = replacer
|
|
117
|
+
? replacer(camelCase(item, { delimiter: '', transform: camelCaseTransformMerge }))
|
|
118
|
+
: camelCase(item, { delimiter: '', transform: camelCaseTransformMerge })
|
|
119
|
+
|
|
120
|
+
params[pathParam] = pathParam
|
|
121
|
+
}, this.path)
|
|
122
|
+
|
|
123
|
+
return params
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Convert Swagger path to URLPath(syntax of Express)
|
|
128
|
+
* @example /pet/{petId} => /pet/:petId
|
|
129
|
+
*/
|
|
130
|
+
toURLPath(): string {
|
|
131
|
+
return this.path.replaceAll('{', ':').replaceAll('}', '')
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -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,83 @@
|
|
|
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
|
+
type HookParallelOutput<TInput extends Array<PromiseFunc<TValue, null>>, TValue> = Promise<PromiseSettledResult<Awaited<ValueOfPromiseFuncArray<TInput>>>[]>
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Run promises in parallel with allSettled
|
|
65
|
+
*/
|
|
66
|
+
export function hookParallel<TInput extends Array<PromiseFunc<TValue, null>>, TValue = unknown, TOutput = HookParallelOutput<TInput, TValue>>(
|
|
67
|
+
promises: TInput,
|
|
68
|
+
): TOutput {
|
|
69
|
+
return Promise.allSettled(promises.filter(Boolean).map(promise => promise())) as TOutput
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type Strategy = 'seq' | 'first' | 'parallel'
|
|
73
|
+
|
|
74
|
+
export type StrategySwitch<TStrategy extends Strategy, TInput extends Array<PromiseFunc<TValue, null>>, TValue> = TStrategy extends 'first'
|
|
75
|
+
? HookFirstOutput<TInput, TValue>
|
|
76
|
+
: TStrategy extends 'seq' ? SeqOutput<TInput, TValue>
|
|
77
|
+
: TStrategy extends 'parallel' ? HookParallelOutput<TInput, TValue>
|
|
78
|
+
: never
|
|
79
|
+
|
|
80
|
+
// tests
|
|
81
|
+
|
|
82
|
+
type test = ValueOfPromiseFuncArray<Array<PromiseFunc<number, null>>>
|
|
83
|
+
// ^?
|
|
@@ -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'
|