@kubb/core 2.0.0-alpha.3 → 2.0.0-alpha.4
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/dist/index.cjs +62 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -38
- package/dist/index.d.ts +18 -38
- package/dist/index.js +62 -72
- package/dist/index.js.map +1 -1
- package/dist/utils.cjs +12 -7
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +2 -1
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +12 -7
- package/dist/utils.js.map +1 -1
- package/package.json +8 -7
- package/src/BarrelManager.ts +123 -0
- package/src/FileManager.ts +482 -0
- package/src/Generator.ts +34 -0
- package/src/PackageManager.ts +163 -0
- package/src/PluginManager.ts +640 -0
- package/src/PromiseManager.ts +48 -0
- package/src/SchemaGenerator.ts +8 -0
- package/src/build.ts +198 -0
- package/src/config.ts +21 -0
- package/src/errors.ts +12 -0
- package/src/index.ts +28 -0
- package/src/plugin.ts +80 -0
- package/src/types.ts +370 -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 +128 -0
- package/src/utils/cache.ts +35 -0
- package/src/utils/clean.ts +5 -0
- package/src/utils/executeStrategies.ts +71 -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 +20 -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/uniqueName.ts +20 -0
- package/src/utils/write.ts +63 -0
|
@@ -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,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) {
|
|
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
|
+
type PossiblePromise<T> = Promise<T> | T
|
|
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
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import pc from 'picocolors'
|
|
2
|
+
import seedrandom from 'seedrandom'
|
|
3
|
+
|
|
4
|
+
import type { Formatter } from 'picocolors/types.ts'
|
|
5
|
+
|
|
6
|
+
const defaultColours = ['black', 'blue', 'darkBlue', 'cyan', 'gray', 'green', 'darkGreen', 'magenta', 'red', 'darkRed', 'yellow', 'darkYellow'] as const
|
|
7
|
+
|
|
8
|
+
export function randomColour(text?: string, colours = defaultColours): string {
|
|
9
|
+
if (!text) {
|
|
10
|
+
return 'white'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const random = seedrandom(text)
|
|
14
|
+
const colour = colours.at(Math.floor(random() * colours.length)) || 'white'
|
|
15
|
+
|
|
16
|
+
return colour
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function randomPicoColour(text?: string, colors = defaultColours): string {
|
|
20
|
+
const colours = pc.createColors(true)
|
|
21
|
+
|
|
22
|
+
if (!text) {
|
|
23
|
+
return colours.white(text)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const colour = randomColour(text, colors)
|
|
27
|
+
const isDark = colour.includes('dark')
|
|
28
|
+
const key = colour.replace('dark', '').toLowerCase() as keyof typeof colours
|
|
29
|
+
const formatter: Formatter = colours[key] as Formatter
|
|
30
|
+
|
|
31
|
+
if (isDark) {
|
|
32
|
+
return pc.bold(formatter(text))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof formatter !== 'function') {
|
|
36
|
+
throw new Error('Formatter for picoColor is not of type function/Formatter')
|
|
37
|
+
}
|
|
38
|
+
return formatter(text)
|
|
39
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { basename, extname, relative } from 'node:path'
|
|
2
|
+
|
|
3
|
+
import fs from 'fs-extra'
|
|
4
|
+
import { switcher } from 'js-runtime'
|
|
5
|
+
|
|
6
|
+
function slash(path: string, platform: 'windows' | 'mac' | 'linux' = 'linux') {
|
|
7
|
+
const isWindowsPath = /^\\\\\?\\/.test(path)
|
|
8
|
+
|
|
9
|
+
if (['linux', 'mac'].includes(platform) && !isWindowsPath) {
|
|
10
|
+
// linux and mac
|
|
11
|
+
return path.replaceAll(/\\/g, '/').replace('../', '').trimEnd()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// windows
|
|
15
|
+
return path.replaceAll(/\\/g, '/').replace('../', '').trimEnd()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getRelativePath(rootDir?: string | null, filePath?: string | null, platform: 'windows' | 'mac' | 'linux' = 'linux'): string {
|
|
19
|
+
if (!rootDir || !filePath) {
|
|
20
|
+
throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ''} ${filePath || ''}`)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const relativePath = relative(rootDir, filePath)
|
|
24
|
+
|
|
25
|
+
// On Windows, paths are separated with a "\"
|
|
26
|
+
// However, web browsers use "/" no matter the platform
|
|
27
|
+
const slashedPath = slash(relativePath, platform)
|
|
28
|
+
|
|
29
|
+
if (slashedPath.startsWith('../')) {
|
|
30
|
+
return slashedPath.replace(basename(slashedPath), basename(slashedPath, extname(filePath)))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return `./${slashedPath.replace(basename(slashedPath), basename(slashedPath, extname(filePath)))}`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const reader = switcher(
|
|
37
|
+
{
|
|
38
|
+
node: async (path: string) => {
|
|
39
|
+
return fs.readFile(path, { encoding: 'utf8' })
|
|
40
|
+
},
|
|
41
|
+
bun: async (path: string) => {
|
|
42
|
+
const file = Bun.file(path)
|
|
43
|
+
|
|
44
|
+
return file.text()
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
'node',
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const syncReader = switcher(
|
|
51
|
+
{
|
|
52
|
+
node: (path: string) => {
|
|
53
|
+
return fs.readFileSync(path, { encoding: 'utf8' })
|
|
54
|
+
},
|
|
55
|
+
bun: () => {
|
|
56
|
+
throw new Error('Bun cannot read sync')
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
'node',
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
export async function read(path: string): Promise<string> {
|
|
63
|
+
return reader(path)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function readSync(path: string): string {
|
|
67
|
+
return syncReader(path)
|
|
68
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function renderTemplate<TData extends Record<string, unknown> = Record<string, unknown>>(template: string, data: TData | undefined = undefined): string {
|
|
2
|
+
if (!data || !Object.keys(data).length) {
|
|
3
|
+
return template.replace(/{{(.*?)}}/g, '')
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const matches = template.match(/{{(.*?)}}/g)
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
matches?.reduce((prev, curr) => {
|
|
10
|
+
const index = curr.split(/{{|}}/).filter(Boolean)[0]?.trim()
|
|
11
|
+
if (index === undefined) {
|
|
12
|
+
return prev
|
|
13
|
+
}
|
|
14
|
+
const value = data[index]
|
|
15
|
+
|
|
16
|
+
if (value === undefined) {
|
|
17
|
+
return prev
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return prev
|
|
21
|
+
.replace(curr, () => {
|
|
22
|
+
if (typeof value === 'boolean') {
|
|
23
|
+
return `${value.toString()}` || 'false'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (value as string) || ''
|
|
27
|
+
})
|
|
28
|
+
.trim()
|
|
29
|
+
}, template) || ''
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const throttle = <R, A extends any[]>(fn: (...args: A) => R, delay: number): [(...args: A) => R | undefined, () => void] => {
|
|
2
|
+
let wait = false
|
|
3
|
+
let timeout: NodeJS.Timeout
|
|
4
|
+
let cancelled = false
|
|
5
|
+
|
|
6
|
+
return [
|
|
7
|
+
(...args: A) => {
|
|
8
|
+
if (cancelled) {
|
|
9
|
+
return undefined
|
|
10
|
+
}
|
|
11
|
+
if (wait) {
|
|
12
|
+
return undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const val = fn(...args)
|
|
16
|
+
|
|
17
|
+
wait = true
|
|
18
|
+
|
|
19
|
+
timeout = setTimeout(() => {
|
|
20
|
+
wait = false
|
|
21
|
+
}, delay)
|
|
22
|
+
|
|
23
|
+
return val
|
|
24
|
+
},
|
|
25
|
+
() => {
|
|
26
|
+
cancelled = true
|
|
27
|
+
clearTimeout(timeout)
|
|
28
|
+
},
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function createJSDocBlockText({ comments, newLine }: { comments: Array<string>; newLine?: boolean }): string {
|
|
2
|
+
const filteredComments = comments.filter(Boolean)
|
|
3
|
+
|
|
4
|
+
if (!filteredComments.length) {
|
|
5
|
+
return ''
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const source = `/**\n * ${filteredComments.join('\n * ')}\n */`
|
|
9
|
+
|
|
10
|
+
if (newLine) {
|
|
11
|
+
return `${source}\n`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return source
|
|
15
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function escape(text?: string): string {
|
|
2
|
+
return text ? text.replaceAll('`', '\\`') : ''
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Escape all characters not included in SingleStringCharacters and DoubleStringCharacters on
|
|
7
|
+
* @link http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
|
|
8
|
+
* @link https://github.com/joliss/js-string-escape/blob/master/index.js
|
|
9
|
+
*/
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
11
|
+
export function jsStringEscape(input: any): string {
|
|
12
|
+
return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
|
|
13
|
+
switch (character) {
|
|
14
|
+
case '"':
|
|
15
|
+
case "'":
|
|
16
|
+
case '\\':
|
|
17
|
+
return '\\' + character
|
|
18
|
+
// Four possible LineTerminator characters need to be escaped:
|
|
19
|
+
case '\n':
|
|
20
|
+
return '\\n'
|
|
21
|
+
case '\r':
|
|
22
|
+
return '\\r'
|
|
23
|
+
case '\u2028':
|
|
24
|
+
return '\\u2028'
|
|
25
|
+
case '\u2029':
|
|
26
|
+
return '\\u2029'
|
|
27
|
+
default:
|
|
28
|
+
return ''
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { combineCodes } from './combineCodes.ts'
|
|
2
|
+
import { createJSDocBlockText } from './createJSDocBlockText.ts'
|
|
3
|
+
import { escape, jsStringEscape } from './escape.ts'
|
|
4
|
+
import { createIndent } from './indent.ts'
|
|
5
|
+
import { nameSorter } from './nameSorter.ts'
|
|
6
|
+
import { searchAndReplace } from './searchAndReplace.ts'
|
|
7
|
+
import { transformReservedWord } from './transformReservedWord.ts'
|
|
8
|
+
|
|
9
|
+
export const transformers = {
|
|
10
|
+
combineCodes,
|
|
11
|
+
escape,
|
|
12
|
+
jsStringEscape,
|
|
13
|
+
createIndent,
|
|
14
|
+
transformReservedWord,
|
|
15
|
+
nameSorter,
|
|
16
|
+
searchAndReplace,
|
|
17
|
+
JSDoc: {
|
|
18
|
+
createJSDocBlockText,
|
|
19
|
+
},
|
|
20
|
+
} as const
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type Options = {
|
|
2
|
+
text: string
|
|
3
|
+
replaceBy: string
|
|
4
|
+
prefix?: string
|
|
5
|
+
key: string
|
|
6
|
+
searchValues?: (prefix: string, key: string) => Array<RegExp | string>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function searchAndReplace(options: Options): string {
|
|
10
|
+
const { text, replaceBy, prefix = '', key } = options
|
|
11
|
+
|
|
12
|
+
const searchValues = options.searchValues?.(prefix, key) || [
|
|
13
|
+
`${prefix}["${key}"]`,
|
|
14
|
+
`${prefix}['${key}']`,
|
|
15
|
+
`${prefix}[\`${key}\`]`,
|
|
16
|
+
`${prefix}"${key}"`,
|
|
17
|
+
`${prefix}'${key}'`,
|
|
18
|
+
`${prefix}\`${key}\``,
|
|
19
|
+
new RegExp(`${prefix}${key}`, 'g'),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
return searchValues.reduce((prev, searchValue) => {
|
|
23
|
+
return prev.toString().replaceAll(searchValue, replaceBy)
|
|
24
|
+
}, text) as string
|
|
25
|
+
}
|