@kubb/cli 1.15.0-canary.20231112T135054 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/cli",
3
- "version": "1.15.0-canary.20231112T135054",
3
+ "version": "2.0.0-alpha.10",
4
4
  "description": "Generator cli",
5
5
  "keywords": [
6
6
  "typescript",
@@ -21,10 +21,18 @@
21
21
  "main": "dist/index.cjs",
22
22
  "module": "dist/index.js",
23
23
  "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "import": "./dist/index.js",
27
+ "require": "./dist/index.cjs",
28
+ "default": "./dist/index.cjs"
29
+ }
30
+ },
24
31
  "bin": {
25
32
  "kubb": "bin/kubb.js"
26
33
  },
27
34
  "files": [
35
+ "src",
28
36
  "dist",
29
37
  "bin",
30
38
  "!/**/**.test.**",
@@ -40,15 +48,15 @@
40
48
  "pretty-error": "^4.0.0",
41
49
  "string-argv": "^0.3.2",
42
50
  "ts-node": "^10.9.1",
43
- "@kubb/core": "1.15.0-canary.20231112T135011"
51
+ "@kubb/core": "2.0.0-alpha.10"
44
52
  },
45
53
  "devDependencies": {
46
- "@types/node": "^20.8.9",
54
+ "@types/node": "^20.9.3",
47
55
  "source-map-support": "^0.5.21",
48
- "tsup": "^7.2.0",
49
- "typescript": "^5.2.2",
50
- "@kubb/swagger": "1.15.0-canary.20231112T135040",
56
+ "tsup": "^8.0.1",
57
+ "typescript": "^5.3.2",
51
58
  "@kubb/ts-config": "0.1.0",
59
+ "@kubb/swagger": "2.0.0-alpha.10",
52
60
  "@kubb/tsup-config": "1.1.8"
53
61
  },
54
62
  "packageManager": "pnpm@8.3.0",
@@ -67,8 +75,8 @@
67
75
  "lint": "ESLINT_USE_FLAT_CONFIG=true eslint --format pretty .",
68
76
  "lint-fix": "bun run lint --quiet --fix",
69
77
  "release": "pnpm publish --no-git-check",
70
- "release:canary": "bash ../../.github/canary.sh && pnpm publish --no-git-check -tag canary",
71
- "release:alpha": "bash ../../.github/canary.sh && pnpm publish --no-git-check -tag alpha",
78
+ "release:canary": "bash ../../.github/canary.sh && node ../../scripts/build.js canary && pnpm publish --no-git-check -tag canary",
79
+ "release:alpha": "bash ../../.github/canary.sh && node ../../scripts/build.js alpha && pnpm publish --no-git-check -tag alpha",
72
80
  "start": "tsup --watch",
73
81
  "test": "vitest --passWithNoTests",
74
82
  "typecheck": "tsc -p ./tsconfig.json --noEmit --emitDeclarationOnly false"
@@ -0,0 +1,137 @@
1
+ import { safeBuild } from '@kubb/core'
2
+ import { createLogger, LogLevel, randomPicoColour } from '@kubb/core/utils'
3
+
4
+ import { execa } from 'execa'
5
+ import pc from 'picocolors'
6
+ import { parseArgsStringToArgv } from 'string-argv'
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' ? pc.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' ? pc.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 = randomPicoColour(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 = pc.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' ? pc.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, logLevel: CLIOptions.logLevel })
121
+
122
+ if (error) {
123
+ spinner.suffixText = ''
124
+ spinner.fail(`🚀 Build failed ${logLevel !== 'silent' ? pc.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' ? pc.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 pc from 'picocolors'
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(pc.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(${pc.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(pc.yellow(pc.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,94 @@
1
+ import path from 'node:path'
2
+
3
+ import { isPromiseFulfilledResult } from '@kubb/core/utils'
4
+ import { LogLevel, write } from '@kubb/core/utils'
5
+
6
+ import { $ } from 'execa'
7
+ import pc from 'picocolors'
8
+
9
+ import { spinner } from './utils/spinner.ts'
10
+
11
+ type Preset = 'simple'
12
+
13
+ type PackageManager = 'pnpm' | 'npm' | 'yarn'
14
+
15
+ type PresetMeta = {
16
+ 'kubb.config': string
17
+ packages: string[]
18
+ }
19
+
20
+ type InitProps = {
21
+ /**
22
+ * @default `'silent'`
23
+ */
24
+ logLevel?: LogLevel
25
+ /**
26
+ * @default `'simple'`
27
+ */
28
+ preset?: Preset
29
+ /**
30
+ * @default `'pnpm'`
31
+ */
32
+ packageManager?: PackageManager
33
+ }
34
+
35
+ const presets: Record<Preset, PresetMeta> = {
36
+ simple: {
37
+ 'kubb.config': `
38
+ import { defineConfig } from '@kubb/core'
39
+ import createSwagger from '@kubb/swagger'
40
+ import createSwaggerTS from '@kubb/swagger-ts'
41
+ import createSwaggerTanstackQuery from '@kubb/swagger-tanstack-query'
42
+
43
+ export default defineConfig({
44
+ root: '.',
45
+ input: {
46
+ path: 'https://petstore3.swagger.io/api/v3/openapi.json',
47
+ },
48
+ output: {
49
+ path: './src/gen',
50
+ clean: true,
51
+ },
52
+ hooks: {
53
+ done: ['echo "🎉 done"'],
54
+ },
55
+ plugins: [createSwagger({}), createSwaggerTS({ output: 'models', enumType: 'enum' }), createSwaggerTanstackQuery({ output: './hooks' })],
56
+ })
57
+ `,
58
+ packages: ['@kubb/core', '@kubb/cli', '@kubb/swagger', '@kubb/swagger-ts', '@kubb/swagger-tanstack-query'],
59
+ },
60
+ }
61
+
62
+ export async function init({ preset = 'simple', logLevel = LogLevel.silent, packageManager = 'pnpm' }: InitProps): Promise<undefined> {
63
+ spinner.start('📦 Initializing Kubb')
64
+
65
+ const presetMeta = presets[preset]
66
+ const configPath = path.resolve(process.cwd(), './kubb.config.js')
67
+ const installCommand = packageManager === 'npm' ? 'install' : 'add'
68
+
69
+ spinner.start(`📀 Writing \`kubb.config.js\` ${pc.dim(configPath)}`)
70
+ await write(presetMeta['kubb.config'], configPath)
71
+ spinner.succeed(`📀 Wrote \`kubb.config.js\` ${pc.dim(configPath)}`)
72
+
73
+ const results = await Promise.allSettled([
74
+ $`npm init es6 -y`,
75
+ ...presetMeta.packages.map(async (pack) => {
76
+ spinner.start(`📀 Installing ${pc.dim(pack)}`)
77
+ const { stdout } = await $({ preferLocal: false })`${packageManager} ${installCommand} ${pack}`
78
+ spinner.succeed(`📀 Installed ${pc.dim(pack)}`)
79
+
80
+ return stdout
81
+ }),
82
+ ])
83
+
84
+ if (logLevel === LogLevel.info) {
85
+ results.forEach((result) => {
86
+ if (isPromiseFulfilledResult(result)) {
87
+ console.log(result.value)
88
+ }
89
+ })
90
+ }
91
+ spinner.succeed(`📦 initialized Kubb`)
92
+
93
+ return
94
+ }
@@ -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 pc from 'picocolors'
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${pc.bold(pc.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.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,4 @@
1
+ export function parseHrtimeToSeconds(hrtime: [number, number]): string {
2
+ const seconds = (hrtime[0] + hrtime[1] / 1e9).toFixed(3)
3
+ return seconds
4
+ }