@kubb/core 2.0.0-canary.20231030T124958 → 2.0.1

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 (100) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-4A7WG6IA.js +128 -0
  3. package/dist/chunk-4A7WG6IA.js.map +1 -0
  4. package/dist/chunk-54P4AWHI.js +71 -0
  5. package/dist/chunk-54P4AWHI.js.map +1 -0
  6. package/dist/chunk-5TK7TMV6.cjs +131 -0
  7. package/dist/chunk-5TK7TMV6.cjs.map +1 -0
  8. package/dist/chunk-7S67BJXQ.js +85 -0
  9. package/dist/chunk-7S67BJXQ.js.map +1 -0
  10. package/dist/chunk-E3ANGQ5N.cjs +2290 -0
  11. package/dist/chunk-E3ANGQ5N.cjs.map +1 -0
  12. package/dist/chunk-H47IKRXJ.cjs +129 -0
  13. package/dist/chunk-H47IKRXJ.cjs.map +1 -0
  14. package/dist/chunk-HIE46T3F.js +129 -0
  15. package/dist/chunk-HIE46T3F.js.map +1 -0
  16. package/dist/chunk-K2H7BYQB.js +155 -0
  17. package/dist/chunk-K2H7BYQB.js.map +1 -0
  18. package/dist/chunk-NAWI7UXW.js +67 -0
  19. package/dist/chunk-NAWI7UXW.js.map +1 -0
  20. package/dist/chunk-PLVKILIY.cjs +162 -0
  21. package/dist/chunk-PLVKILIY.cjs.map +1 -0
  22. package/dist/chunk-W2FP7ZWW.cjs +71 -0
  23. package/dist/chunk-W2FP7ZWW.cjs.map +1 -0
  24. package/dist/chunk-WZQO3EPM.cjs +91 -0
  25. package/dist/chunk-WZQO3EPM.cjs.map +1 -0
  26. package/dist/chunk-XDHI63G7.cjs +104 -0
  27. package/dist/chunk-XDHI63G7.cjs.map +1 -0
  28. package/dist/chunk-XPOF4D5N.js +18 -0
  29. package/dist/chunk-XPOF4D5N.js.map +1 -0
  30. package/dist/fs.cjs +31 -0
  31. package/dist/fs.cjs.map +1 -0
  32. package/dist/fs.d.cts +5 -0
  33. package/dist/fs.d.ts +5 -0
  34. package/dist/fs.js +11 -0
  35. package/dist/fs.js.map +1 -0
  36. package/dist/index.cjs +1866 -977
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.cts +302 -319
  39. package/dist/index.d.ts +302 -319
  40. package/dist/index.js +1071 -846
  41. package/dist/index.js.map +1 -1
  42. package/dist/logger.cjs +26 -0
  43. package/dist/logger.cjs.map +1 -0
  44. package/dist/logger.d.cts +32 -0
  45. package/dist/logger.d.ts +32 -0
  46. package/dist/logger.js +8 -0
  47. package/dist/logger.js.map +1 -0
  48. package/dist/transformers.cjs +124 -0
  49. package/dist/transformers.cjs.map +1 -0
  50. package/dist/transformers.d.cts +55 -0
  51. package/dist/transformers.d.ts +55 -0
  52. package/dist/transformers.js +95 -0
  53. package/dist/transformers.js.map +1 -0
  54. package/dist/utils.cjs +23 -1163
  55. package/dist/utils.cjs.map +1 -1
  56. package/dist/utils.d.cts +2 -143
  57. package/dist/utils.d.ts +2 -143
  58. package/dist/utils.js +15 -1118
  59. package/dist/utils.js.map +1 -1
  60. package/dist/write-A6VgHkYA.d.cts +10 -0
  61. package/dist/write-A6VgHkYA.d.ts +10 -0
  62. package/package.json +40 -23
  63. package/src/BarrelManager.ts +113 -0
  64. package/src/FileManager.ts +581 -0
  65. package/src/Generator.ts +34 -0
  66. package/src/PackageManager.ts +178 -0
  67. package/src/PluginManager.ts +645 -0
  68. package/src/PromiseManager.ts +51 -0
  69. package/src/build.ts +221 -0
  70. package/src/config.ts +22 -0
  71. package/src/errors.ts +12 -0
  72. package/src/fs/clean.ts +5 -0
  73. package/src/fs/index.ts +3 -0
  74. package/src/fs/read.ts +68 -0
  75. package/src/fs/write.ts +79 -0
  76. package/src/index.ts +27 -0
  77. package/src/logger.ts +121 -0
  78. package/src/plugin.ts +80 -0
  79. package/src/transformers/casing.ts +9 -0
  80. package/src/transformers/combineCodes.ts +3 -0
  81. package/src/transformers/createJSDocBlockText.ts +9 -0
  82. package/src/transformers/escape.ts +31 -0
  83. package/src/transformers/indent.ts +3 -0
  84. package/src/transformers/index.ts +36 -0
  85. package/src/transformers/nameSorter.ts +9 -0
  86. package/src/transformers/searchAndReplace.ts +25 -0
  87. package/src/transformers/transformReservedWord.ts +97 -0
  88. package/src/transformers/trim.ts +7 -0
  89. package/src/types.ts +334 -0
  90. package/src/utils/EventEmitter.ts +24 -0
  91. package/src/utils/FunctionParams.ts +86 -0
  92. package/src/utils/TreeNode.ts +125 -0
  93. package/src/utils/URLPath.ts +133 -0
  94. package/src/utils/cache.ts +35 -0
  95. package/src/utils/executeStrategies.ts +83 -0
  96. package/src/utils/index.ts +8 -0
  97. package/src/utils/promise.ts +13 -0
  98. package/src/utils/renderTemplate.ts +31 -0
  99. package/src/utils/timeout.ts +7 -0
  100. package/src/utils/uniqueName.ts +20 -0
package/src/build.ts ADDED
@@ -0,0 +1,221 @@
1
+ import c from 'tinyrainbow'
2
+
3
+ import { clean } from './fs/clean.ts'
4
+ import { read } from './fs/read.ts'
5
+ import { URLPath } from './utils/URLPath.ts'
6
+ import { isInputPath } from './config.ts'
7
+ import { FileManager } from './FileManager.ts'
8
+ import { createLogger, LogLevel, randomCliColour } from './logger.ts'
9
+ import { PluginManager } from './PluginManager.ts'
10
+ import { isPromise } from './PromiseManager.ts'
11
+
12
+ import type { KubbFile } from './FileManager.ts'
13
+ import type { Logger } from './logger.ts'
14
+ import type { KubbPlugin, PluginContext, PluginParameter, TransformResult } from './types.ts'
15
+
16
+ type BuildOptions = {
17
+ config: PluginContext['config']
18
+ /**
19
+ * @default Logger without the spinner
20
+ */
21
+ logger?: Logger
22
+ }
23
+
24
+ type BuildOutput = {
25
+ files: FileManager['files']
26
+ pluginManager: PluginManager
27
+ /**
28
+ * Only for safeBuild
29
+ */
30
+ error?: Error
31
+ }
32
+
33
+ async function transformReducer(
34
+ this: PluginContext,
35
+ _previousCode: string,
36
+ result: TransformResult | Promise<TransformResult>,
37
+ _plugin: KubbPlugin,
38
+ ): Promise<string | null> {
39
+ return result
40
+ }
41
+
42
+ async function setup(options: BuildOptions): Promise<PluginManager> {
43
+ const { config, logger = createLogger({ logLevel: LogLevel.silent }) } = options
44
+ let count = 0
45
+
46
+ try {
47
+ if (isInputPath(config) && !new URLPath(config.input.path).isURL) {
48
+ await read(config.input.path)
49
+ }
50
+ } catch (e) {
51
+ if (isInputPath(config)) {
52
+ throw new Error(
53
+ 'Cannot read file/URL defined in `input.path` or set with `kubb generate PATH` in the CLI of your Kubb config ' + c.dim(config.input.path),
54
+ {
55
+ cause: e,
56
+ },
57
+ )
58
+ }
59
+ }
60
+
61
+ if (config.output.clean) {
62
+ await clean(config.output.path)
63
+ }
64
+
65
+ const task = async (file: KubbFile.ResolvedFile): Promise<KubbFile.ResolvedFile> => {
66
+ const { path } = file
67
+
68
+ let source: string | null = FileManager.getSource(file)
69
+
70
+ const { result: loadedResult } = await pluginManager.hookFirst({
71
+ hookName: 'load',
72
+ parameters: [path],
73
+ })
74
+ if (loadedResult && isPromise(loadedResult)) {
75
+ source = await loadedResult
76
+ }
77
+ if (loadedResult && !isPromise(loadedResult)) {
78
+ source = loadedResult
79
+ }
80
+
81
+ if (source) {
82
+ source = await pluginManager.hookReduceArg0({
83
+ hookName: 'transform',
84
+ parameters: [source, path],
85
+ reduce: transformReducer,
86
+ })
87
+
88
+ if (config.output.write || config.output.write === undefined) {
89
+ if (file.meta?.pluginKey) {
90
+ // run only for pluginKey defined in the meta of the file
91
+ await pluginManager.hookForPlugin({
92
+ pluginKey: file.meta?.pluginKey,
93
+ hookName: 'writeFile',
94
+ parameters: [source, path],
95
+ })
96
+ }
97
+
98
+ await pluginManager.hookFirst({
99
+ hookName: 'writeFile',
100
+ parameters: [source, path],
101
+ })
102
+ }
103
+ }
104
+
105
+ return {
106
+ ...file,
107
+ source: source || '',
108
+ }
109
+ }
110
+
111
+ const pluginManager = new PluginManager(config, { logger, task })
112
+
113
+ pluginManager.on('execute', (executer) => {
114
+ const { hookName, parameters, plugin } = executer
115
+
116
+ if (hookName === 'writeFile' && logger.spinner) {
117
+ const [code] = parameters as PluginParameter<'writeFile'>
118
+
119
+ if (logger.logLevel === LogLevel.debug) {
120
+ logger.debug(`PluginKey ${c.dim(JSON.stringify(plugin.key))} \nwith source\n\n${code}`)
121
+ }
122
+ }
123
+ })
124
+
125
+ pluginManager.queue.on('add', () => {
126
+ if (logger.logLevel !== LogLevel.info) {
127
+ return
128
+ }
129
+
130
+ if (logger.spinner && count === 0) {
131
+ logger.spinner?.start(`💾 Writing`)
132
+ }
133
+ })
134
+
135
+ pluginManager.queue.on('active', () => {
136
+ if (logger.logLevel !== LogLevel.info) {
137
+ return
138
+ }
139
+
140
+ if (logger.spinner && pluginManager.queue.size > 0) {
141
+ const text = `Item: ${count} Size: ${pluginManager.queue.size} Pending: ${pluginManager.queue.pending}`
142
+
143
+ logger.spinner.suffixText = c.dim(text)
144
+ }
145
+ ;++count
146
+ })
147
+
148
+ pluginManager.queue.on('completed', () => {
149
+ if (logger.logLevel !== LogLevel.info) {
150
+ return
151
+ }
152
+
153
+ if (logger.spinner) {
154
+ const text = `Item: ${count} Size: ${pluginManager.queue.size} Pending: ${pluginManager.queue.pending}`
155
+
156
+ logger.spinner.suffixText = c.dim(text)
157
+ }
158
+ })
159
+
160
+ pluginManager.on('executed', (executer) => {
161
+ const { hookName, plugin, output, parameters } = executer
162
+
163
+ if (logger.logLevel === LogLevel.debug) {
164
+ const logs = [
165
+ `${randomCliColour(plugin.name)} Executing ${hookName}`,
166
+ parameters && `${c.bgWhite(`Parameters`)} ${randomCliColour(plugin.name)} ${hookName}`,
167
+ JSON.stringify(parameters, undefined, 2),
168
+ output && `${c.bgWhite('Output')} ${randomCliColour(plugin.name)} ${hookName}`,
169
+ output,
170
+ ].filter(Boolean)
171
+
172
+ logger.debug(logs.join('\n'))
173
+ }
174
+ })
175
+
176
+ return pluginManager
177
+ }
178
+
179
+ export async function build(options: BuildOptions): Promise<BuildOutput> {
180
+ const pluginManager = await setup(options)
181
+
182
+ const { fileManager, logger } = pluginManager
183
+
184
+ await pluginManager.hookParallel({
185
+ hookName: 'buildStart',
186
+ parameters: [options.config],
187
+ })
188
+
189
+ await pluginManager.hookParallel({ hookName: 'buildEnd' })
190
+
191
+ if (logger.logLevel === LogLevel.info && logger.spinner) {
192
+ logger.spinner.suffixText = ''
193
+ logger.spinner.succeed(`💾 Writing completed`)
194
+ }
195
+
196
+ return { files: fileManager.files.map((file) => ({ ...file, source: FileManager.getSource(file) })), pluginManager }
197
+ }
198
+
199
+ export async function safeBuild(options: BuildOptions): Promise<BuildOutput> {
200
+ const pluginManager = await setup(options)
201
+
202
+ const { fileManager, logger } = pluginManager
203
+
204
+ try {
205
+ await pluginManager.hookParallel({
206
+ hookName: 'buildStart',
207
+ parameters: [options.config],
208
+ })
209
+
210
+ await pluginManager.hookParallel({ hookName: 'buildEnd' })
211
+
212
+ if (logger.logLevel === LogLevel.info && logger.spinner) {
213
+ logger.spinner.suffixText = ''
214
+ logger.spinner.succeed(`💾 Writing completed`)
215
+ }
216
+ } catch (e) {
217
+ return { files: fileManager.files.map((file) => ({ ...file, source: FileManager.getSource(file) })), pluginManager, error: e as Error }
218
+ }
219
+
220
+ return { files: fileManager.files.map((file) => ({ ...file, source: FileManager.getSource(file) })), pluginManager }
221
+ }
package/src/config.ts ADDED
@@ -0,0 +1,22 @@
1
+ import type { PossiblePromise } from '@kubb/types'
2
+ import type { CLIOptions, InputPath, KubbConfig, KubbUserConfig } from './types.ts'
3
+
4
+ /**
5
+ * Type helper to make it easier to use kubb.config.js
6
+ * accepts a direct {@link KubbConfig} object, or a function that returns it.
7
+ * The function receives a {@link ConfigEnv} object that exposes two properties:
8
+ */
9
+ export function defineConfig(
10
+ options:
11
+ | PossiblePromise<KubbUserConfig | Array<KubbUserConfig>>
12
+ | ((
13
+ /** The options derived from the CLI flags */
14
+ cliOptions: CLIOptions,
15
+ ) => PossiblePromise<KubbUserConfig | Array<KubbUserConfig>>),
16
+ ): typeof options {
17
+ return options
18
+ }
19
+
20
+ export function isInputPath(result: KubbConfig | undefined): result is KubbConfig<InputPath> {
21
+ return !!result && 'path' in (result as any)
22
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Behaves as an Error to log a warning in the console(still stops the execution)
3
+ */
4
+ export class Warning extends Error {
5
+ constructor(message?: string, options?: { cause: Error }) {
6
+ super(message, { cause: options?.cause })
7
+
8
+ this.name = 'Warning'
9
+ }
10
+ }
11
+
12
+ export class ValidationPluginError extends Error {}
@@ -0,0 +1,5 @@
1
+ import fs from 'fs-extra'
2
+
3
+ export async function clean(path: string): Promise<void> {
4
+ return fs.remove(path)
5
+ }
@@ -0,0 +1,3 @@
1
+ export { clean } from './clean.ts'
2
+ export { getRelativePath, read, readSync } from './read.ts'
3
+ export { write } from './write.ts'
package/src/fs/read.ts ADDED
@@ -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,79 @@
1
+ import { resolve } from 'node:path'
2
+
3
+ import fs from 'fs-extra'
4
+ import { switcher } from 'js-runtime'
5
+
6
+ type Options = { sanity?: boolean }
7
+
8
+ const writer = switcher(
9
+ {
10
+ node: async (path: string, data: string, { sanity }: Options) => {
11
+ try {
12
+ const oldContent = await fs.readFile(resolve(path), { encoding: 'utf-8' })
13
+ if (oldContent?.toString() === data?.toString()) {
14
+ return
15
+ }
16
+ } catch (_err) {
17
+ /* empty */
18
+ }
19
+
20
+ await fs.outputFile(resolve(path), data, { encoding: 'utf-8' })
21
+
22
+ if (sanity) {
23
+ const savedData = await fs.readFile(resolve(path), { encoding: 'utf-8' })
24
+
25
+ if (savedData?.toString() !== data?.toString()) {
26
+ throw new Error(`Sanity check failed for ${path}\n\nData[${data.length}]:\n${data}\n\nSaved[${savedData.length}]:\n${savedData}\n`)
27
+ }
28
+
29
+ return savedData
30
+ }
31
+
32
+ return data
33
+ },
34
+ bun: async (path: string, data: string, { sanity }: Options) => {
35
+ try {
36
+ await Bun.write(resolve(path), data)
37
+
38
+ if (sanity) {
39
+ const file = Bun.file(resolve(path))
40
+ const savedData = await file.text()
41
+
42
+ if (savedData?.toString() !== data?.toString()) {
43
+ throw new Error(`Sanity check failed for ${path}\n\nData[${data.length}]:\n${data}\n\nSaved[${savedData.length}]:\n${savedData}\n`)
44
+ }
45
+
46
+ return savedData
47
+ }
48
+
49
+ return data
50
+ } catch (e) {
51
+ console.log(e, resolve(path))
52
+ }
53
+ },
54
+ },
55
+ 'node',
56
+ )
57
+
58
+ export async function write(data: string, path: string, options: Options = {}): Promise<string | undefined> {
59
+ if (data.trim() === '') {
60
+ return undefined
61
+ }
62
+ return writer(path, data.trim(), options)
63
+ }
64
+
65
+ export async function writeLog(data: string): Promise<string | undefined> {
66
+ if (data.trim() === '') {
67
+ return undefined
68
+ }
69
+ const path = resolve(process.cwd(), 'kubb-log.log')
70
+ let previousLogs = ''
71
+
72
+ try {
73
+ previousLogs = await fs.readFile(resolve(path), { encoding: 'utf-8' })
74
+ } catch (_err) {
75
+ /* empty */
76
+ }
77
+
78
+ return writer(path, [previousLogs, data.trim()].filter(Boolean).join('\n\n\n'), { sanity: false })
79
+ }
package/src/index.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { build } from './build.ts'
2
+
3
+ import type { ObjValueTuple, TupleToUnion } from '@kubb/types'
4
+
5
+ export { build, safeBuild } from './build.ts'
6
+ export { defineConfig, isInputPath } from './config.ts'
7
+ export { Warning } from './errors.ts'
8
+ export { FileManager, KubbFile } from './FileManager.ts'
9
+ export { Generator } from './Generator.ts'
10
+ export { PackageManager } from './PackageManager.ts'
11
+ // dprint-ignore
12
+ export { createPlugin, pluginName as name, pluginName } from './plugin.ts'
13
+ export { PluginManager } from './PluginManager.ts'
14
+ export { PromiseManager } from './PromiseManager.ts'
15
+ export type * from './types.ts'
16
+
17
+ export interface _Register {}
18
+ export type Plugins = _Register
19
+ export type OptionsPlugins = { [K in keyof Plugins]: Plugins[K]['options'] }
20
+
21
+ export type OptionsOfPlugin<K extends keyof Plugins> = Plugins[K]['options']
22
+
23
+ export type PluginUnion = TupleToUnion<ObjValueTuple<OptionsPlugins>>
24
+
25
+ export type Plugin = keyof Plugins
26
+
27
+ export default build
package/src/logger.ts ADDED
@@ -0,0 +1,121 @@
1
+ import seedrandom from 'seedrandom'
2
+ import c, { createColors } from 'tinyrainbow'
3
+
4
+ import { writeLog } from './fs/write.ts'
5
+
6
+ import type { Ora } from 'ora'
7
+ import type { Formatter } from 'tinyrainbow'
8
+
9
+ export const LogLevel = {
10
+ silent: 'silent',
11
+ info: 'info',
12
+ debug: 'debug',
13
+ } as const
14
+
15
+ export type LogLevel = keyof typeof LogLevel
16
+
17
+ export type Logger = {
18
+ /**
19
+ * Optional config name to show in CLI output
20
+ */
21
+ name?: string
22
+ logLevel: LogLevel
23
+ log: (message: string | null) => void
24
+ error: (message: string | null) => void
25
+ info: (message: string | null) => void
26
+ warn: (message: string | null) => void
27
+ debug: (message: string | null) => Promise<void>
28
+ spinner?: Ora
29
+ logs: string[]
30
+ }
31
+
32
+ type Props = {
33
+ name?: string
34
+ logLevel: LogLevel
35
+ spinner?: Ora
36
+ }
37
+
38
+ export function createLogger({ logLevel, name, spinner }: Props): Logger {
39
+ const logs: string[] = []
40
+ const log: Logger['log'] = (message) => {
41
+ if (message && spinner) {
42
+ spinner.text = message
43
+ logs.push(message)
44
+ }
45
+ }
46
+
47
+ const error: Logger['error'] = (message) => {
48
+ if (message) {
49
+ throw new Error(message || 'Something went wrong')
50
+ }
51
+ }
52
+
53
+ const warn: Logger['warn'] = (message) => {
54
+ if (message && spinner) {
55
+ spinner.warn(c.yellow(message))
56
+ logs.push(message)
57
+ }
58
+ }
59
+
60
+ const info: Logger['warn'] = (message) => {
61
+ if (message && spinner && logLevel !== LogLevel.silent) {
62
+ spinner.info(message)
63
+ logs.push(message)
64
+ }
65
+ }
66
+
67
+ const debug: Logger['debug'] = async (message) => {
68
+ if (message) {
69
+ await writeLog(message)
70
+ }
71
+ }
72
+
73
+ const logger: Logger = {
74
+ name,
75
+ logLevel,
76
+ log,
77
+ error,
78
+ warn,
79
+ info,
80
+ debug,
81
+ spinner,
82
+ logs,
83
+ }
84
+
85
+ return logger
86
+ }
87
+
88
+ const defaultColours = ['black', 'blue', 'darkBlue', 'cyan', 'gray', 'green', 'darkGreen', 'magenta', 'red', 'darkRed', 'yellow', 'darkYellow'] as const
89
+
90
+ export function randomColour(text?: string, colours = defaultColours): string {
91
+ if (!text) {
92
+ return 'white'
93
+ }
94
+
95
+ const random = seedrandom(text)
96
+ const colour = colours.at(Math.floor(random() * colours.length)) || 'white'
97
+
98
+ return colour
99
+ }
100
+
101
+ export function randomCliColour(text?: string, colors = defaultColours): string {
102
+ const colours = createColors(true)
103
+
104
+ if (!text) {
105
+ return colours.white(text)
106
+ }
107
+
108
+ const colour = randomColour(text, colors)
109
+ const isDark = colour.includes('dark')
110
+ const key = colour.replace('dark', '').toLowerCase() as keyof typeof colours
111
+ const formatter: Formatter = colours[key] as Formatter
112
+
113
+ if (isDark) {
114
+ return c.bold(formatter(text))
115
+ }
116
+
117
+ if (typeof formatter !== 'function') {
118
+ throw new Error('Formatter for picoColor is not of type function/Formatter')
119
+ }
120
+ return formatter(text)
121
+ }
package/src/plugin.ts ADDED
@@ -0,0 +1,80 @@
1
+ import path from 'node:path'
2
+
3
+ import { createPluginCache } from './utils/cache.ts'
4
+
5
+ import type { FileManager } from './FileManager.ts'
6
+ import type { PluginManager } from './PluginManager.ts'
7
+ import type { KubbPlugin, KubbUserPluginWithLifeCycle, PluginContext, PluginFactoryOptions } from './types.ts'
8
+
9
+ type KubbPluginFactory<T extends PluginFactoryOptions = PluginFactoryOptions> = (options: T['options']) => KubbUserPluginWithLifeCycle<T>
10
+
11
+ export function createPlugin<T extends PluginFactoryOptions = PluginFactoryOptions>(factory: KubbPluginFactory<T>) {
12
+ return (options: T['options']): ReturnType<KubbPluginFactory<T>> => {
13
+ return factory(options)
14
+ }
15
+ }
16
+
17
+ type Options = {
18
+ config: PluginContext['config']
19
+ fileManager: FileManager
20
+ pluginManager: PluginManager
21
+ resolvePath: PluginContext['resolvePath']
22
+ resolveName: PluginContext['resolveName']
23
+ logger: PluginContext['logger']
24
+ getPlugins: () => Array<KubbPlugin>
25
+ plugin?: PluginContext['plugin']
26
+ }
27
+
28
+ // not publicly exported
29
+ export type CorePluginOptions = PluginFactoryOptions<'core', Options, Options, PluginContext, never>
30
+
31
+ export const pluginName = 'core' satisfies CorePluginOptions['name']
32
+ export const pluginKey: CorePluginOptions['key'] = [pluginName] satisfies CorePluginOptions['key']
33
+
34
+ export const definePlugin = createPlugin<CorePluginOptions>((options) => {
35
+ const { fileManager, pluginManager, resolvePath, resolveName, logger } = options
36
+
37
+ return {
38
+ name: pluginName,
39
+ options,
40
+ key: ['core'],
41
+
42
+ api() {
43
+ return {
44
+ get config() {
45
+ return options.config
46
+ },
47
+ get plugins() {
48
+ return options.getPlugins()
49
+ },
50
+ get plugin() {
51
+ // see pluginManger.#execute where we override with `.call` the this with the correct plugin
52
+ return options.plugin as NonNullable<Options['plugin']>
53
+ },
54
+ logger,
55
+ fileManager,
56
+ pluginManager,
57
+ async addFile(...files) {
58
+ const resolvedFiles = await fileManager.add(...files)
59
+
60
+ if (!Array.isArray(resolvedFiles)) {
61
+ return [resolvedFiles]
62
+ }
63
+
64
+ return resolvedFiles
65
+ },
66
+ resolvePath,
67
+ resolveName,
68
+ cache: createPluginCache(),
69
+ }
70
+ },
71
+ resolvePath(baseName) {
72
+ const root = path.resolve(this.config.root, this.config.output.path)
73
+
74
+ return path.resolve(root, baseName)
75
+ },
76
+ resolveName(name) {
77
+ return name
78
+ },
79
+ }
80
+ })
@@ -0,0 +1,9 @@
1
+ import { camelCase as changeCaseCamel, pascalCase as changePascalCase } from 'change-case'
2
+
3
+ export function camelCase(text: string): string {
4
+ return changeCaseCamel(text, { delimiter: '', mergeAmbiguousCharacters: true })
5
+ }
6
+
7
+ export function pascalCase(text: string): string {
8
+ return changePascalCase(text, { delimiter: '', mergeAmbiguousCharacters: true })
9
+ }
@@ -0,0 +1,3 @@
1
+ export function combineCodes(codes: string[]): string {
2
+ return codes.join('\n')
3
+ }
@@ -0,0 +1,9 @@
1
+ export function createJSDocBlockText({ comments }: { comments: Array<string> }): string {
2
+ const filteredComments = comments.filter(Boolean)
3
+
4
+ if (!filteredComments.length) {
5
+ return ''
6
+ }
7
+
8
+ return `/**\n * ${filteredComments.join('\n * ')}\n */`
9
+ }