@kubb/cli 2.0.0-canary.20231030T123808 → 2.0.0
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 +87 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +76 -86
- package/dist/index.js.map +1 -1
- package/package.json +20 -13
- package/src/generate.ts +137 -0
- package/src/index.ts +96 -0
- package/src/init.ts +95 -0
- package/src/utils/OraWritable.ts +27 -0
- package/src/utils/getConfig.ts +37 -0
- package/src/utils/getCosmiConfig.ts +82 -0
- package/src/utils/getPlugins.ts +41 -0
- package/src/utils/getSummary.ts +86 -0
- package/src/utils/parseHrtimeToSeconds.ts +4 -0
- package/src/utils/renderErrors.ts +55 -0
- package/src/utils/spinner.ts +5 -0
- package/src/utils/watcher.ts +27 -0
package/src/generate.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { safeBuild } from '@kubb/core'
|
|
2
|
+
import { createLogger, LogLevel, randomCliColour } from '@kubb/core/logger'
|
|
3
|
+
|
|
4
|
+
import { execa } from 'execa'
|
|
5
|
+
import { parseArgsStringToArgv } from 'string-argv'
|
|
6
|
+
import c from 'tinyrainbow'
|
|
7
|
+
|
|
8
|
+
import { getSummary } from './utils/getSummary.ts'
|
|
9
|
+
import { OraWritable } from './utils/OraWritable.ts'
|
|
10
|
+
import { spinner } from './utils/spinner.ts'
|
|
11
|
+
|
|
12
|
+
import type { Writable } from 'node:stream'
|
|
13
|
+
import type { CLIOptions, KubbConfig } from '@kubb/core'
|
|
14
|
+
import type { ExecaReturnValue } from 'execa'
|
|
15
|
+
|
|
16
|
+
type GenerateProps = {
|
|
17
|
+
input?: string
|
|
18
|
+
config: KubbConfig
|
|
19
|
+
CLIOptions: CLIOptions
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type ExecutingHooksProps = {
|
|
23
|
+
hooks: KubbConfig['hooks']
|
|
24
|
+
logLevel: LogLevel
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type Executer = { subProcess: ExecaReturnValue<string>; abort: AbortController['abort'] }
|
|
28
|
+
|
|
29
|
+
async function executeHooks({ hooks, logLevel }: ExecutingHooksProps): Promise<void> {
|
|
30
|
+
if (!hooks?.done) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const commands = Array.isArray(hooks.done) ? hooks.done : [hooks.done]
|
|
35
|
+
|
|
36
|
+
if (logLevel === LogLevel.silent) {
|
|
37
|
+
spinner.start(`Executing hooks`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const executers: Promise<Executer | null>[] = commands
|
|
41
|
+
.map(async (command) => {
|
|
42
|
+
const oraWritable = new OraWritable(spinner, command)
|
|
43
|
+
const abortController = new AbortController()
|
|
44
|
+
const [cmd, ..._args] = [...parseArgsStringToArgv(command)]
|
|
45
|
+
|
|
46
|
+
if (!cmd) {
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
spinner.start(`Executing hook ${logLevel !== 'silent' ? c.dim(command) : ''}`)
|
|
51
|
+
|
|
52
|
+
const subProcess = await execa(cmd, _args, { detached: true, signal: abortController.signal }).pipeStdout!(oraWritable as Writable)
|
|
53
|
+
spinner.suffixText = ''
|
|
54
|
+
|
|
55
|
+
if (logLevel === LogLevel.silent) {
|
|
56
|
+
spinner.succeed(`Executing hook ${logLevel !== 'silent' ? c.dim(command) : ''}`)
|
|
57
|
+
|
|
58
|
+
console.log(subProcess.stdout)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
oraWritable.destroy()
|
|
62
|
+
return { subProcess, abort: abortController.abort.bind(abortController) }
|
|
63
|
+
})
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
|
|
66
|
+
await Promise.all(executers)
|
|
67
|
+
|
|
68
|
+
if (logLevel === LogLevel.silent) {
|
|
69
|
+
spinner.succeed(`Executing hooks`)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function generate({ input, config, CLIOptions }: GenerateProps): Promise<void> {
|
|
74
|
+
const logger = createLogger({ logLevel: CLIOptions.logLevel || LogLevel.silent, name: config.name, spinner })
|
|
75
|
+
|
|
76
|
+
if (logger.name) {
|
|
77
|
+
spinner.prefixText = randomCliColour(logger.name)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const hrstart = process.hrtime()
|
|
81
|
+
|
|
82
|
+
if (CLIOptions.logLevel === LogLevel.debug) {
|
|
83
|
+
const { performance, PerformanceObserver } = await import('node:perf_hooks')
|
|
84
|
+
|
|
85
|
+
const performanceOpserver = new PerformanceObserver((items) => {
|
|
86
|
+
const message = `${items.getEntries()[0]?.duration.toFixed(0)}ms`
|
|
87
|
+
|
|
88
|
+
spinner.suffixText = c.yellow(message)
|
|
89
|
+
|
|
90
|
+
performance.clearMarks()
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
performanceOpserver.observe({ type: 'measure' })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const { root: _root, ...userConfig } = config
|
|
97
|
+
const logLevel = logger.logLevel
|
|
98
|
+
const inputPath = input ?? ('path' in userConfig.input ? userConfig.input.path : undefined)
|
|
99
|
+
|
|
100
|
+
spinner.start(`🚀 Building ${logLevel !== 'silent' ? c.dim(inputPath) : ''}`)
|
|
101
|
+
|
|
102
|
+
const { pluginManager, error } = await safeBuild({
|
|
103
|
+
config: {
|
|
104
|
+
root: process.cwd(),
|
|
105
|
+
...userConfig,
|
|
106
|
+
input: inputPath
|
|
107
|
+
? {
|
|
108
|
+
...userConfig.input,
|
|
109
|
+
path: inputPath,
|
|
110
|
+
}
|
|
111
|
+
: userConfig.input,
|
|
112
|
+
output: {
|
|
113
|
+
write: true,
|
|
114
|
+
...userConfig.output,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
logger,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const summary = getSummary({ pluginManager, config, status: error ? 'failed' : 'success', hrstart, logger })
|
|
121
|
+
|
|
122
|
+
if (error) {
|
|
123
|
+
spinner.suffixText = ''
|
|
124
|
+
spinner.fail(`🚀 Build failed ${logLevel !== 'silent' ? c.dim(inputPath) : ''}`)
|
|
125
|
+
|
|
126
|
+
console.log(summary.join(''))
|
|
127
|
+
|
|
128
|
+
throw error
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await executeHooks({ hooks: config.hooks, logLevel })
|
|
132
|
+
|
|
133
|
+
spinner.suffixText = ''
|
|
134
|
+
spinner.succeed(`🚀 Build completed ${logLevel !== 'silent' ? c.dim(inputPath) : ''}`)
|
|
135
|
+
|
|
136
|
+
console.log(summary.join(''))
|
|
137
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import { isInputPath, PromiseManager, Warning } from '@kubb/core'
|
|
4
|
+
|
|
5
|
+
import { cac } from 'cac'
|
|
6
|
+
import c from 'tinyrainbow'
|
|
7
|
+
|
|
8
|
+
import { version } from '../package.json'
|
|
9
|
+
import { getConfig } from './utils/getConfig.ts'
|
|
10
|
+
import { getCosmiConfig } from './utils/getCosmiConfig.ts'
|
|
11
|
+
import { renderErrors } from './utils/renderErrors.ts'
|
|
12
|
+
import { spinner } from './utils/spinner.ts'
|
|
13
|
+
import { startWatcher } from './utils/watcher.ts'
|
|
14
|
+
import { generate } from './generate.ts'
|
|
15
|
+
import { init } from './init.ts'
|
|
16
|
+
|
|
17
|
+
import type { CLIOptions } from '@kubb/core'
|
|
18
|
+
|
|
19
|
+
const moduleName = 'kubb'
|
|
20
|
+
|
|
21
|
+
function programCatcher(e: unknown, CLIOptions: CLIOptions): void {
|
|
22
|
+
const error = e as Error
|
|
23
|
+
const message = renderErrors(error, { logLevel: CLIOptions.logLevel })
|
|
24
|
+
|
|
25
|
+
if (error instanceof Warning) {
|
|
26
|
+
spinner.warn(c.yellow(error.message))
|
|
27
|
+
process.exit(0)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
spinner.fail(message)
|
|
31
|
+
process.exit(1)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function generateAction(input: string, CLIOptions: CLIOptions) {
|
|
35
|
+
spinner.start('🔍 Loading config')
|
|
36
|
+
const result = await getCosmiConfig(moduleName, CLIOptions.config)
|
|
37
|
+
spinner.succeed(`🔍 Config loaded(${c.dim(path.relative(process.cwd(), result.filepath))})`)
|
|
38
|
+
|
|
39
|
+
const config = await getConfig(result, CLIOptions)
|
|
40
|
+
|
|
41
|
+
if (CLIOptions.watch) {
|
|
42
|
+
if (Array.isArray(config)) {
|
|
43
|
+
throw new Error('Cannot use watcher with multiple KubbConfigs(array)')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (isInputPath(config)) {
|
|
47
|
+
return startWatcher([input || config.input.path], async (paths) => {
|
|
48
|
+
await generate({ config, CLIOptions })
|
|
49
|
+
spinner.spinner = 'simpleDotsScrolling'
|
|
50
|
+
spinner.start(c.yellow(c.bold(`Watching for changes in ${paths.join(' and ')}`)))
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (Array.isArray(config)) {
|
|
56
|
+
const promiseManager = new PromiseManager()
|
|
57
|
+
const promises = config.map((item) => () => generate({ input, config: item, CLIOptions }))
|
|
58
|
+
|
|
59
|
+
await promiseManager.run('seq', promises)
|
|
60
|
+
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await generate({ input, config, CLIOptions })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function run(argv?: string[]): Promise<void> {
|
|
68
|
+
const program = cac(moduleName)
|
|
69
|
+
|
|
70
|
+
program.command('[input]', 'Path of the input file(overrides the one in `kubb.config.js`)').action(generateAction)
|
|
71
|
+
|
|
72
|
+
program
|
|
73
|
+
.command('generate [input]', 'Path of the input file(overrides the one in `kubb.config.js`)')
|
|
74
|
+
.option('-c, --config <path>', 'Path to the Kubb config')
|
|
75
|
+
.option('-l, --log-level <type>', 'Info, silent or debug')
|
|
76
|
+
.option('-w, --watch', 'Watch mode based on the input file')
|
|
77
|
+
.action(generateAction)
|
|
78
|
+
|
|
79
|
+
program.command('init', 'Init Kubb').action(async () => {
|
|
80
|
+
return init({ logLevel: 'info' })
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
program.help()
|
|
84
|
+
program.version(version)
|
|
85
|
+
program.parse(argv, { run: false })
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
await program.runMatchedCommand()
|
|
89
|
+
|
|
90
|
+
process.exit(0)
|
|
91
|
+
} catch (e) {
|
|
92
|
+
programCatcher(e, program.options)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default run
|
package/src/init.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import { write } from '@kubb/core/fs'
|
|
4
|
+
import { LogLevel } from '@kubb/core/logger'
|
|
5
|
+
import { isPromiseFulfilledResult } from '@kubb/core/utils'
|
|
6
|
+
|
|
7
|
+
import { $ } from 'execa'
|
|
8
|
+
import c from 'tinyrainbow'
|
|
9
|
+
|
|
10
|
+
import { spinner } from './utils/spinner.ts'
|
|
11
|
+
|
|
12
|
+
type Preset = 'simple'
|
|
13
|
+
|
|
14
|
+
type PackageManager = 'pnpm' | 'npm' | 'yarn'
|
|
15
|
+
|
|
16
|
+
type PresetMeta = {
|
|
17
|
+
'kubb.config': string
|
|
18
|
+
packages: string[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type InitProps = {
|
|
22
|
+
/**
|
|
23
|
+
* @default `'silent'`
|
|
24
|
+
*/
|
|
25
|
+
logLevel?: LogLevel
|
|
26
|
+
/**
|
|
27
|
+
* @default `'simple'`
|
|
28
|
+
*/
|
|
29
|
+
preset?: Preset
|
|
30
|
+
/**
|
|
31
|
+
* @default `'pnpm'`
|
|
32
|
+
*/
|
|
33
|
+
packageManager?: PackageManager
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const presets: Record<Preset, PresetMeta> = {
|
|
37
|
+
simple: {
|
|
38
|
+
'kubb.config': `
|
|
39
|
+
import { defineConfig } from '@kubb/core'
|
|
40
|
+
import createSwagger from '@kubb/swagger'
|
|
41
|
+
import createSwaggerTS from '@kubb/swagger-ts'
|
|
42
|
+
import createSwaggerTanstackQuery from '@kubb/swagger-tanstack-query'
|
|
43
|
+
|
|
44
|
+
export default defineConfig({
|
|
45
|
+
root: '.',
|
|
46
|
+
input: {
|
|
47
|
+
path: 'https://petstore3.swagger.io/api/v3/openapi.json',
|
|
48
|
+
},
|
|
49
|
+
output: {
|
|
50
|
+
path: './src/gen',
|
|
51
|
+
clean: true,
|
|
52
|
+
},
|
|
53
|
+
hooks: {
|
|
54
|
+
done: ['echo "🎉 done"'],
|
|
55
|
+
},
|
|
56
|
+
plugins: [createSwagger({}), createSwaggerTS({ output: { path: 'models'}, enumType: 'enum' }), createSwaggerTanstackQuery({ output: { path: './hooks' } })],
|
|
57
|
+
})
|
|
58
|
+
`,
|
|
59
|
+
packages: ['@kubb/core', '@kubb/cli', '@kubb/swagger', '@kubb/swagger-ts', '@kubb/swagger-tanstack-query'],
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function init({ preset = 'simple', logLevel = LogLevel.silent, packageManager = 'pnpm' }: InitProps): Promise<undefined> {
|
|
64
|
+
spinner.start('📦 Initializing Kubb')
|
|
65
|
+
|
|
66
|
+
const presetMeta = presets[preset]
|
|
67
|
+
const configPath = path.resolve(process.cwd(), './kubb.config.js')
|
|
68
|
+
const installCommand = packageManager === 'npm' ? 'install' : 'add'
|
|
69
|
+
|
|
70
|
+
spinner.start(`📀 Writing \`kubb.config.js\` ${c.dim(configPath)}`)
|
|
71
|
+
await write(presetMeta['kubb.config'], configPath)
|
|
72
|
+
spinner.succeed(`📀 Wrote \`kubb.config.js\` ${c.dim(configPath)}`)
|
|
73
|
+
|
|
74
|
+
const results = await Promise.allSettled([
|
|
75
|
+
$`npm init es6 -y`,
|
|
76
|
+
...presetMeta.packages.map(async (pack) => {
|
|
77
|
+
spinner.start(`📀 Installing ${c.dim(pack)}`)
|
|
78
|
+
const { stdout } = await $({ preferLocal: false })`${packageManager} ${installCommand} ${pack}`
|
|
79
|
+
spinner.succeed(`📀 Installed ${c.dim(pack)}`)
|
|
80
|
+
|
|
81
|
+
return stdout
|
|
82
|
+
}),
|
|
83
|
+
])
|
|
84
|
+
|
|
85
|
+
if (logLevel === LogLevel.info) {
|
|
86
|
+
results.forEach((result) => {
|
|
87
|
+
if (isPromiseFulfilledResult(result)) {
|
|
88
|
+
console.log(result.value)
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
spinner.succeed(`📦 initialized Kubb`)
|
|
93
|
+
|
|
94
|
+
return
|
|
95
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
|
2
|
+
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
5
|
+
|
|
6
|
+
import { Writable } from 'node:stream'
|
|
7
|
+
|
|
8
|
+
import c from 'tinyrainbow'
|
|
9
|
+
|
|
10
|
+
import type { WritableOptions } from 'node:stream'
|
|
11
|
+
import type { Ora } from 'ora'
|
|
12
|
+
|
|
13
|
+
export class OraWritable extends Writable {
|
|
14
|
+
public command: string
|
|
15
|
+
public spinner: Ora
|
|
16
|
+
constructor(spinner: Ora, command: string, opts?: WritableOptions) {
|
|
17
|
+
super(opts)
|
|
18
|
+
|
|
19
|
+
this.command = command
|
|
20
|
+
this.spinner = spinner
|
|
21
|
+
}
|
|
22
|
+
_write(chunk: any, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
|
|
23
|
+
this.spinner.suffixText = `\n\n${c.bold(c.blue(this.command))}: ${chunk?.toString()}`
|
|
24
|
+
|
|
25
|
+
callback()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isPromise } from '@kubb/core/utils'
|
|
2
|
+
|
|
3
|
+
import { getPlugins } from './getPlugins.ts'
|
|
4
|
+
|
|
5
|
+
import type { CLIOptions, KubbConfig, KubbUserConfig } from '@kubb/core'
|
|
6
|
+
import type { CosmiconfigResult } from './getCosmiConfig.ts'
|
|
7
|
+
|
|
8
|
+
export async function getConfig(result: CosmiconfigResult, CLIOptions: CLIOptions): Promise<Array<KubbConfig> | KubbConfig> {
|
|
9
|
+
const config = result?.config
|
|
10
|
+
let kubbUserConfig = Promise.resolve(config) as Promise<KubbUserConfig | Array<KubbUserConfig>>
|
|
11
|
+
|
|
12
|
+
// for ts or js files
|
|
13
|
+
if (typeof config === 'function') {
|
|
14
|
+
const possiblePromise = config(CLIOptions)
|
|
15
|
+
if (isPromise(possiblePromise)) {
|
|
16
|
+
kubbUserConfig = possiblePromise
|
|
17
|
+
}
|
|
18
|
+
kubbUserConfig = Promise.resolve(possiblePromise)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let JSONConfig = await kubbUserConfig
|
|
22
|
+
|
|
23
|
+
if (Array.isArray(JSONConfig)) {
|
|
24
|
+
const promises = JSONConfig.map(async (item) => {
|
|
25
|
+
return { ...item, plugins: item.plugins ? await getPlugins(item.plugins) : undefined }
|
|
26
|
+
}) as unknown as Array<Promise<KubbConfig>>
|
|
27
|
+
|
|
28
|
+
return Promise.all(promises)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
JSONConfig = {
|
|
32
|
+
...JSONConfig,
|
|
33
|
+
plugins: JSONConfig.plugins ? await getPlugins(JSONConfig.plugins) : undefined,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return JSONConfig as KubbConfig
|
|
37
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
4
|
+
import { cosmiconfig } from 'cosmiconfig'
|
|
5
|
+
import tsNode from 'ts-node'
|
|
6
|
+
|
|
7
|
+
import type { defineConfig, KubbUserConfig } from '@kubb/core'
|
|
8
|
+
|
|
9
|
+
export type CosmiconfigResult = {
|
|
10
|
+
filepath: string
|
|
11
|
+
isEmpty?: boolean
|
|
12
|
+
config: ReturnType<typeof defineConfig> | KubbUserConfig
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const tsLoader = (configFile: string) => {
|
|
16
|
+
let registerer = { enabled() {} }
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
// Register TypeScript compiler instance
|
|
20
|
+
registerer = tsNode.register({
|
|
21
|
+
compilerOptions: { module: 'commonjs' },
|
|
22
|
+
typeCheck: false,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const module = require(configFile)
|
|
26
|
+
|
|
27
|
+
return module.default
|
|
28
|
+
} catch (err) {
|
|
29
|
+
const error = err as Error
|
|
30
|
+
|
|
31
|
+
if (error.name === 'MODULE_NOT_FOUND') {
|
|
32
|
+
throw new Error(`'ts-node' is required for the TypeScript configuration files. Make sure it is installed\nError: ${error.message}`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
throw error
|
|
36
|
+
} finally {
|
|
37
|
+
registerer.enabled()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function getCosmiConfig(moduleName: string, config?: string): Promise<CosmiconfigResult> {
|
|
42
|
+
const searchPlaces = [
|
|
43
|
+
'package.json',
|
|
44
|
+
`.${moduleName}rc`,
|
|
45
|
+
`.${moduleName}rc.json`,
|
|
46
|
+
`.${moduleName}rc.yaml`,
|
|
47
|
+
`.${moduleName}rc.yml`,
|
|
48
|
+
|
|
49
|
+
`.${moduleName}rc.ts`,
|
|
50
|
+
`.${moduleName}rc.js`,
|
|
51
|
+
`.${moduleName}rc.mjs`,
|
|
52
|
+
`.${moduleName}rc.cjs`,
|
|
53
|
+
|
|
54
|
+
`${moduleName}.config.ts`,
|
|
55
|
+
`${moduleName}.config.js`,
|
|
56
|
+
`${moduleName}.config.mjs`,
|
|
57
|
+
`${moduleName}.config.cjs`,
|
|
58
|
+
]
|
|
59
|
+
const explorer = cosmiconfig(moduleName, {
|
|
60
|
+
cache: false,
|
|
61
|
+
searchPlaces: [
|
|
62
|
+
...searchPlaces.map((searchPlace) => {
|
|
63
|
+
return `.config/${searchPlace}`
|
|
64
|
+
}),
|
|
65
|
+
...searchPlaces.map((searchPlace) => {
|
|
66
|
+
return `configs/${searchPlace}`
|
|
67
|
+
}),
|
|
68
|
+
...searchPlaces,
|
|
69
|
+
],
|
|
70
|
+
loaders: {
|
|
71
|
+
'.ts': tsLoader,
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const result = config ? await explorer.load(config) : await explorer.search()
|
|
76
|
+
|
|
77
|
+
if (result?.isEmpty || !result || !result.config) {
|
|
78
|
+
throw new Error('Config not defined, create a kubb.config.js or pass through your config with the option --config')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return result as CosmiconfigResult
|
|
82
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
2
|
+
|
|
3
|
+
import { PackageManager } from '@kubb/core'
|
|
4
|
+
|
|
5
|
+
import type { KubbUserConfig } from '@kubb/core'
|
|
6
|
+
|
|
7
|
+
function isJSONPlugins(plugins: KubbUserConfig['plugins']): plugins is Array<[name: string, options: object]> {
|
|
8
|
+
return !!(plugins as Array<[name: string, options: object]>[])?.some((plugin) => {
|
|
9
|
+
return Array.isArray(plugin) && typeof plugin?.at(0) === 'string'
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isObjectPlugins(plugins: KubbUserConfig['plugins']): plugins is any {
|
|
14
|
+
return plugins instanceof Object && !Array.isArray(plugins)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function importPlugin(name: string, options: object): Promise<KubbUserConfig['plugins']> {
|
|
18
|
+
const packageManager = new PackageManager(process.cwd())
|
|
19
|
+
|
|
20
|
+
const importedPlugin: any = process.env.NODE_ENV === 'test' ? await import(name) : await packageManager.import(name)
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line
|
|
23
|
+
return importedPlugin?.default ? importedPlugin.default(options) : importedPlugin(options)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getPlugins(plugins: KubbUserConfig['plugins']): Promise<KubbUserConfig['plugins']> {
|
|
27
|
+
if (isObjectPlugins(plugins)) {
|
|
28
|
+
throw new Error('Object plugins are not supported anymore, best to use http://kubb.dev/guide/configure#json')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isJSONPlugins(plugins)) {
|
|
32
|
+
const jsonPlugins = plugins as Array<[name: string, options: object]>
|
|
33
|
+
const promises = jsonPlugins.map((plugin) => {
|
|
34
|
+
const [name, options = {}] = plugin
|
|
35
|
+
return importPlugin(name, options)
|
|
36
|
+
})
|
|
37
|
+
return Promise.all(promises) as Promise<KubbUserConfig['plugins']>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return Promise.resolve(plugins)
|
|
41
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import { LogLevel, randomCliColour } from '@kubb/core/logger'
|
|
4
|
+
|
|
5
|
+
import c from 'tinyrainbow'
|
|
6
|
+
|
|
7
|
+
import { parseHrtimeToSeconds } from './parseHrtimeToSeconds.ts'
|
|
8
|
+
|
|
9
|
+
import type { KubbConfig, PluginManager } from '@kubb/core'
|
|
10
|
+
import type { Logger } from '@kubb/core/logger'
|
|
11
|
+
|
|
12
|
+
type SummaryProps = {
|
|
13
|
+
pluginManager: PluginManager
|
|
14
|
+
status: 'success' | 'failed'
|
|
15
|
+
hrstart: [number, number]
|
|
16
|
+
config: KubbConfig
|
|
17
|
+
logger: Logger
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getSummary({ pluginManager, status, hrstart, config, logger }: SummaryProps): string[] {
|
|
21
|
+
const { logLevel } = logger
|
|
22
|
+
const logs: string[] = []
|
|
23
|
+
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(hrstart))
|
|
24
|
+
|
|
25
|
+
const buildStartPlugins = pluginManager.executed
|
|
26
|
+
.filter((item) => item.hookName === 'buildStart' && item.plugin.name !== 'core')
|
|
27
|
+
.map((item) => item.plugin.name)
|
|
28
|
+
|
|
29
|
+
const buildEndPlugins = pluginManager.executed
|
|
30
|
+
.filter((item) => item.hookName === 'buildEnd' && item.plugin.name !== 'core')
|
|
31
|
+
.map((item) => item.plugin.name)
|
|
32
|
+
|
|
33
|
+
const failedPlugins = config.plugins?.filter((plugin) => !buildEndPlugins.includes(plugin.name))?.map((plugin) => plugin.name)
|
|
34
|
+
const pluginsCount = config.plugins?.length || 0
|
|
35
|
+
const files = pluginManager.fileManager.files.sort((a, b) => {
|
|
36
|
+
if (!a.meta?.pluginKey?.[0] || !b.meta?.pluginKey?.[0]) {
|
|
37
|
+
return 0
|
|
38
|
+
}
|
|
39
|
+
if (a.meta?.pluginKey?.[0]?.length < b.meta?.pluginKey?.[0]?.length) {
|
|
40
|
+
return 1
|
|
41
|
+
}
|
|
42
|
+
if (a.meta?.pluginKey?.[0]?.length > b.meta?.pluginKey?.[0]?.length) {
|
|
43
|
+
return -1
|
|
44
|
+
}
|
|
45
|
+
return 0
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const meta = {
|
|
49
|
+
name: config.name,
|
|
50
|
+
plugins: status === 'success'
|
|
51
|
+
? `${c.green(`${buildStartPlugins.length} successful`)}, ${pluginsCount} total`
|
|
52
|
+
: `${c.red(`${failedPlugins?.length ?? 1} failed`)}, ${pluginsCount} total`,
|
|
53
|
+
pluginsFailed: status === 'failed' ? failedPlugins?.map((name) => randomCliColour(name))?.join(', ') : undefined,
|
|
54
|
+
filesCreated: files.length,
|
|
55
|
+
time: c.yellow(`${elapsedSeconds}s`),
|
|
56
|
+
output: path.resolve(config.root, config.output.path),
|
|
57
|
+
} as const
|
|
58
|
+
|
|
59
|
+
if (logLevel === LogLevel.debug) {
|
|
60
|
+
logger.debug(c.bold('\nGenerated files:\n'))
|
|
61
|
+
logger.debug(files.map((file) => `${randomCliColour(JSON.stringify(file.meta?.pluginKey))} ${file.path}`).join('\n'))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
logs.push(
|
|
65
|
+
[
|
|
66
|
+
[`\n`, true],
|
|
67
|
+
[` ${c.bold('Name:')} ${meta.name}`, !!meta.name],
|
|
68
|
+
[` ${c.bold('Plugins:')} ${meta.plugins}`, true],
|
|
69
|
+
[` ${c.dim('Failed:')} ${meta.pluginsFailed || 'none'}`, !!meta.pluginsFailed],
|
|
70
|
+
[`${c.bold('Generated:')} ${meta.filesCreated} files`, true],
|
|
71
|
+
[` ${c.bold('Time:')} ${meta.time}`, true],
|
|
72
|
+
[` ${c.bold('Output:')} ${meta.output}`, true],
|
|
73
|
+
[`\n`, true],
|
|
74
|
+
]
|
|
75
|
+
.map((item) => {
|
|
76
|
+
if (item.at(1)) {
|
|
77
|
+
return item.at(0)
|
|
78
|
+
}
|
|
79
|
+
return undefined
|
|
80
|
+
})
|
|
81
|
+
.filter(Boolean)
|
|
82
|
+
.join('\n'),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return logs
|
|
86
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
4
|
+
|
|
5
|
+
import { LogLevel } from '@kubb/core/logger'
|
|
6
|
+
|
|
7
|
+
import PrettyError from 'pretty-error'
|
|
8
|
+
|
|
9
|
+
export const prettyError = new PrettyError()
|
|
10
|
+
.skipPackage('commander')
|
|
11
|
+
.skip(function callback(traceLine: any) {
|
|
12
|
+
// exclude renderErrors.ts
|
|
13
|
+
const pattern = new RegExp('renderErrors')
|
|
14
|
+
|
|
15
|
+
const hasMatch = traceLine?.file?.match(pattern)
|
|
16
|
+
|
|
17
|
+
if (typeof traceLine.packageName !== 'undefined' && hasMatch) {
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
20
|
+
} as PrettyError.Callback)
|
|
21
|
+
.start()
|
|
22
|
+
|
|
23
|
+
function getErrorCauses(errors: Error[]): string[] {
|
|
24
|
+
return errors
|
|
25
|
+
.reduce((prev, error) => {
|
|
26
|
+
const causedError = error?.cause as Error
|
|
27
|
+
if (causedError) {
|
|
28
|
+
prev = [...prev, ...getErrorCauses([causedError])]
|
|
29
|
+
}
|
|
30
|
+
prev = [...prev, prettyError.render(error)]
|
|
31
|
+
|
|
32
|
+
return prev
|
|
33
|
+
}, [] as string[])
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function renderErrors(error: Error | undefined, { logLevel = LogLevel.silent }: { logLevel?: LogLevel }): string {
|
|
38
|
+
if (!error) {
|
|
39
|
+
return ''
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (logLevel === LogLevel.silent) {
|
|
43
|
+
// skip when no debug is set
|
|
44
|
+
prettyError.skipNodeFiles()
|
|
45
|
+
prettyError.skip(function skip() {
|
|
46
|
+
return true
|
|
47
|
+
} as PrettyError.Callback)
|
|
48
|
+
|
|
49
|
+
return [prettyError.render(error)].filter(Boolean).join('\n')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const errors = getErrorCauses([error])
|
|
53
|
+
|
|
54
|
+
return errors.filter(Boolean).join('\n')
|
|
55
|
+
}
|