@kubb/cli 2.0.0-alpha.1 → 2.0.0-alpha.11
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 +39 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +40 -53
- package/dist/index.js.map +1 -1
- package/package.json +14 -6
- package/src/generate.ts +137 -0
- package/src/index.ts +96 -0
- package/src/init.ts +94 -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 +84 -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
|
@@ -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.cjs`,
|
|
52
|
+
`.${moduleName}rc.mjs`,
|
|
53
|
+
|
|
54
|
+
`${moduleName}.config.ts`,
|
|
55
|
+
`${moduleName}.config.js`,
|
|
56
|
+
`${moduleName}.config.cjs`,
|
|
57
|
+
`${moduleName}.config.mjs`,
|
|
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,84 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import { LogLevel, randomPicoColour } from '@kubb/core/utils'
|
|
4
|
+
|
|
5
|
+
import pc from 'picocolors'
|
|
6
|
+
|
|
7
|
+
import { parseHrtimeToSeconds } from './parseHrtimeToSeconds.ts'
|
|
8
|
+
|
|
9
|
+
import type { KubbConfig, PluginManager } from '@kubb/core'
|
|
10
|
+
|
|
11
|
+
type SummaryProps = {
|
|
12
|
+
pluginManager: PluginManager
|
|
13
|
+
status: 'success' | 'failed'
|
|
14
|
+
hrstart: [number, number]
|
|
15
|
+
config: KubbConfig
|
|
16
|
+
logLevel?: LogLevel
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getSummary({ pluginManager, status, hrstart, config, logLevel }: SummaryProps): string[] {
|
|
20
|
+
const logs: string[] = []
|
|
21
|
+
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(hrstart))
|
|
22
|
+
|
|
23
|
+
const buildStartPlugins = pluginManager.executed
|
|
24
|
+
.filter((item) => item.hookName === 'buildStart' && item.plugin.name !== 'core')
|
|
25
|
+
.map((item) => item.plugin.name)
|
|
26
|
+
|
|
27
|
+
const buildEndPlugins = pluginManager.executed
|
|
28
|
+
.filter((item) => item.hookName === 'buildEnd' && item.plugin.name !== 'core')
|
|
29
|
+
.map((item) => item.plugin.name)
|
|
30
|
+
|
|
31
|
+
const failedPlugins = config.plugins?.filter((plugin) => !buildEndPlugins.includes(plugin.name))?.map((plugin) => plugin.name)
|
|
32
|
+
const pluginsCount = config.plugins?.length || 0
|
|
33
|
+
const files = pluginManager.fileManager.files.sort((a, b) => {
|
|
34
|
+
if (!a.meta?.pluginKey?.[1] || !b.meta?.pluginKey?.[1]) {
|
|
35
|
+
return 0
|
|
36
|
+
}
|
|
37
|
+
if (a.meta?.pluginKey?.[1]?.length < b.meta?.pluginKey?.[1]?.length) {
|
|
38
|
+
return 1
|
|
39
|
+
}
|
|
40
|
+
if (a.meta?.pluginKey?.[1]?.length > b.meta?.pluginKey?.[1]?.length) {
|
|
41
|
+
return -1
|
|
42
|
+
}
|
|
43
|
+
return 0
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const meta = {
|
|
47
|
+
name: config.name,
|
|
48
|
+
plugins: status === 'success'
|
|
49
|
+
? `${pc.green(`${buildStartPlugins.length} successful`)}, ${pluginsCount} total`
|
|
50
|
+
: `${pc.red(`${failedPlugins?.length ?? 1} failed`)}, ${pluginsCount} total`,
|
|
51
|
+
pluginsFailed: status === 'failed' ? failedPlugins?.map((name) => randomPicoColour(name))?.join(', ') : undefined,
|
|
52
|
+
filesCreated: files.length,
|
|
53
|
+
time: pc.yellow(`${elapsedSeconds}s`),
|
|
54
|
+
output: path.resolve(config.root, config.output.path),
|
|
55
|
+
} as const
|
|
56
|
+
|
|
57
|
+
if (logLevel === LogLevel.debug) {
|
|
58
|
+
logs.push(pc.bold('\nGenerated files:\n'))
|
|
59
|
+
logs.push(files.map((file) => `${randomPicoColour(JSON.stringify(file.meta?.pluginKey))} ${file.path}`).join('\n'))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
logs.push(
|
|
63
|
+
[
|
|
64
|
+
[`\n`, true],
|
|
65
|
+
[` ${pc.bold('Name:')} ${meta.name}`, !!meta.name],
|
|
66
|
+
[` ${pc.bold('Plugins:')} ${meta.plugins}`, true],
|
|
67
|
+
[` ${pc.dim('Failed:')} ${meta.pluginsFailed || 'none'}`, !!meta.pluginsFailed],
|
|
68
|
+
[`${pc.bold('Generated:')} ${meta.filesCreated} files`, true],
|
|
69
|
+
[` ${pc.bold('Time:')} ${meta.time}`, true],
|
|
70
|
+
[` ${pc.bold('Output:')} ${meta.output}`, true],
|
|
71
|
+
[`\n`, true],
|
|
72
|
+
]
|
|
73
|
+
.map((item) => {
|
|
74
|
+
if (item.at(1)) {
|
|
75
|
+
return item.at(0)
|
|
76
|
+
}
|
|
77
|
+
return undefined
|
|
78
|
+
})
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.join('\n'),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return logs
|
|
84
|
+
}
|
|
@@ -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/utils'
|
|
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
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pc from 'picocolors'
|
|
2
|
+
|
|
3
|
+
import { spinner } from './spinner.ts'
|
|
4
|
+
|
|
5
|
+
export async function startWatcher(path: string[], cb: (path: string[]) => Promise<void>): Promise<void> {
|
|
6
|
+
const { watch } = await import('chokidar')
|
|
7
|
+
|
|
8
|
+
const ignored = ['**/{.git,node_modules}/**']
|
|
9
|
+
|
|
10
|
+
const watcher = watch(path, {
|
|
11
|
+
ignorePermissionErrors: true,
|
|
12
|
+
ignored,
|
|
13
|
+
})
|
|
14
|
+
watcher.on('all', (type, file) => {
|
|
15
|
+
spinner.succeed(pc.yellow(pc.bold(`Change detected: ${type} ${file}`)))
|
|
16
|
+
// revert back
|
|
17
|
+
spinner.spinner = 'clock'
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
cb(path)
|
|
21
|
+
} catch (e) {
|
|
22
|
+
spinner.warn(pc.red('Watcher failed'))
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return
|
|
27
|
+
}
|