@kubb/cli 4.28.0 → 4.29.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.
Files changed (64) hide show
  1. package/dist/{agent-DrV6PuuK.js → agent-BzD6f3mV.js} +4 -5
  2. package/dist/{agent-DrV6PuuK.js.map → agent-BzD6f3mV.js.map} +1 -1
  3. package/dist/{agent-BHBMSAC7.cjs → agent-NAhMf2ot.cjs} +4 -5
  4. package/dist/{agent-BHBMSAC7.cjs.map → agent-NAhMf2ot.cjs.map} +1 -1
  5. package/dist/{chunk-C1_xRkKa.cjs → chunk-CNbaEX1y.cjs} +1 -1
  6. package/dist/{chunk-jHaXqnEa.js → chunk-DKWOrOAv.js} +1 -1
  7. package/dist/{generate-CQYwekvF.cjs → generate-C8gS4Z2F.cjs} +190 -188
  8. package/dist/generate-C8gS4Z2F.cjs.map +1 -0
  9. package/dist/{generate-Cj8cU5XB.js → generate-CnKaIwc7.js} +191 -185
  10. package/dist/generate-CnKaIwc7.js.map +1 -0
  11. package/dist/index.cjs +9 -8
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +8 -8
  15. package/dist/{init-BVwhJb7n.js → init-B5qnw1XS.js} +22 -23
  16. package/dist/init-B5qnw1XS.js.map +1 -0
  17. package/dist/{init-Ckt9G-Yf.cjs → init-BDWQO7I8.cjs} +22 -24
  18. package/dist/init-BDWQO7I8.cjs.map +1 -0
  19. package/dist/{mcp-DGyip5BX.cjs → mcp-97TXkJVX.cjs} +5 -7
  20. package/dist/mcp-97TXkJVX.cjs.map +1 -0
  21. package/dist/{mcp-C-e4yatL.js → mcp-Jboea6xH.js} +5 -6
  22. package/dist/mcp-Jboea6xH.js.map +1 -0
  23. package/dist/{package-CeCGexzv.cjs → package-oo3QhWS5.cjs} +2 -2
  24. package/dist/package-oo3QhWS5.cjs.map +1 -0
  25. package/dist/package-veMf5zNr.js +6 -0
  26. package/dist/package-veMf5zNr.js.map +1 -0
  27. package/dist/{start-jaV7_Xss.js → start-CYuU23PX.js} +27 -24
  28. package/dist/start-CYuU23PX.js.map +1 -0
  29. package/dist/{start-DqKYNdp1.cjs → start-lrn1-P52.cjs} +27 -25
  30. package/dist/start-lrn1-P52.cjs.map +1 -0
  31. package/dist/{validate-Cvb5aOEb.cjs → validate-BgYhe_55.cjs} +3 -4
  32. package/dist/{validate-Cvb5aOEb.cjs.map → validate-BgYhe_55.cjs.map} +1 -1
  33. package/dist/{validate-YI4YkVTl.js → validate-DOeZKiGx.js} +3 -4
  34. package/dist/{validate-YI4YkVTl.js.map → validate-DOeZKiGx.js.map} +1 -1
  35. package/package.json +12 -21
  36. package/src/commands/agent/start.ts +17 -16
  37. package/src/commands/generate.ts +11 -7
  38. package/src/commands/init.ts +14 -14
  39. package/src/commands/mcp.ts +2 -2
  40. package/src/loggers/clackLogger.ts +49 -40
  41. package/src/loggers/githubActionsLogger.ts +36 -32
  42. package/src/loggers/plainLogger.ts +9 -9
  43. package/src/runners/generate.ts +9 -7
  44. package/src/utils/Writables.ts +2 -2
  45. package/src/utils/executeHooks.ts +4 -5
  46. package/src/utils/formatMsWithColor.ts +4 -4
  47. package/src/utils/getIntro.ts +52 -6
  48. package/src/utils/getSummary.ts +6 -6
  49. package/src/utils/packageManager.ts +3 -9
  50. package/src/utils/randomColor.ts +5 -6
  51. package/src/utils/watcher.ts +3 -3
  52. package/dist/generate-CQYwekvF.cjs.map +0 -1
  53. package/dist/generate-Cj8cU5XB.js.map +0 -1
  54. package/dist/index.d.cts +0 -6
  55. package/dist/init-BVwhJb7n.js.map +0 -1
  56. package/dist/init-Ckt9G-Yf.cjs.map +0 -1
  57. package/dist/mcp-C-e4yatL.js.map +0 -1
  58. package/dist/mcp-DGyip5BX.cjs.map +0 -1
  59. package/dist/package-CeCGexzv.cjs.map +0 -1
  60. package/dist/package-DNxKRFpk.js +0 -6
  61. package/dist/package-DNxKRFpk.js.map +0 -1
  62. package/dist/start-DqKYNdp1.cjs.map +0 -1
  63. package/dist/start-jaV7_Xss.js.map +0 -1
  64. package/src/utils/ansiColors.ts +0 -23
@@ -1,7 +1,7 @@
1
+ import { styleText } from 'node:util'
1
2
  import { type Config, defineLogger, LogLevel } from '@kubb/core'
2
3
  import { formatHrtime, formatMs } from '@kubb/core/utils'
3
- import { type ExecaError, execa } from 'execa'
4
- import pc from 'picocolors'
4
+ import { type NonZeroExitError, x } from 'tinyexec'
5
5
  import { formatMsWithColor } from '../utils/formatMsWithColor.ts'
6
6
 
7
7
  /**
@@ -42,18 +42,18 @@ export const githubActionsLogger = defineLogger({
42
42
  if (state.totalPlugins > 0) {
43
43
  const pluginStr =
44
44
  state.failedPlugins > 0
45
- ? `Plugins ${pc.green(state.completedPlugins.toString())}/${state.totalPlugins} ${pc.red(`(${state.failedPlugins} failed)`)}`
46
- : `Plugins ${pc.green(state.completedPlugins.toString())}/${state.totalPlugins}`
45
+ ? `Plugins ${styleText('green', state.completedPlugins.toString())}/${state.totalPlugins} ${styleText('red', `(${state.failedPlugins} failed)`)}`
46
+ : `Plugins ${styleText('green', state.completedPlugins.toString())}/${state.totalPlugins}`
47
47
  parts.push(pluginStr)
48
48
  }
49
49
 
50
50
  if (state.totalFiles > 0) {
51
- parts.push(`Files ${pc.green(state.processedFiles.toString())}/${state.totalFiles}`)
51
+ parts.push(`Files ${styleText('green', state.processedFiles.toString())}/${state.totalFiles}`)
52
52
  }
53
53
 
54
54
  if (parts.length > 0) {
55
- parts.push(`${pc.green(duration)} elapsed`)
56
- console.log(getMessage(parts.join(pc.dim(' | '))))
55
+ parts.push(`${styleText('green', duration)} elapsed`)
56
+ console.log(getMessage(parts.join(styleText('dim', ' | '))))
57
57
  }
58
58
  }
59
59
 
@@ -66,7 +66,7 @@ export const githubActionsLogger = defineLogger({
66
66
  second: '2-digit',
67
67
  })
68
68
 
69
- return [pc.dim(`[${timestamp}]`), message].join(' ')
69
+ return [styleText('dim', `[${timestamp}]`), message].join(' ')
70
70
  }
71
71
 
72
72
  return message
@@ -85,7 +85,7 @@ export const githubActionsLogger = defineLogger({
85
85
  return
86
86
  }
87
87
 
88
- const text = getMessage([pc.blue('ℹ'), message, pc.dim(info)].join(' '))
88
+ const text = getMessage([styleText('blue', 'ℹ'), message, styleText('dim', info)].join(' '))
89
89
 
90
90
  console.log(text)
91
91
  })
@@ -95,7 +95,7 @@ export const githubActionsLogger = defineLogger({
95
95
  return
96
96
  }
97
97
 
98
- const text = getMessage([pc.blue('✓'), message, logLevel >= LogLevel.info ? pc.dim(info) : undefined].filter(Boolean).join(' '))
98
+ const text = getMessage([styleText('blue', '✓'), message, logLevel >= LogLevel.info ? styleText('dim', info) : undefined].filter(Boolean).join(' '))
99
99
 
100
100
  console.log(text)
101
101
  })
@@ -105,7 +105,7 @@ export const githubActionsLogger = defineLogger({
105
105
  return
106
106
  }
107
107
 
108
- const text = getMessage([pc.yellow('⚠'), message, logLevel >= LogLevel.info ? pc.dim(info) : undefined].filter(Boolean).join(' '))
108
+ const text = getMessage([styleText('yellow', '⚠'), message, logLevel >= LogLevel.info ? styleText('dim', info) : undefined].filter(Boolean).join(' '))
109
109
 
110
110
  console.warn(`::warning::${text}`)
111
111
  })
@@ -123,22 +123,22 @@ export const githubActionsLogger = defineLogger({
123
123
  if (logLevel >= LogLevel.debug && error.stack) {
124
124
  const frames = error.stack.split('\n').slice(1, 4)
125
125
  for (const frame of frames) {
126
- console.log(getMessage(pc.dim(frame.trim())))
126
+ console.log(getMessage(styleText('dim', frame.trim())))
127
127
  }
128
128
 
129
129
  if (caused?.stack) {
130
- console.log(pc.dim(`└─ caused by ${caused.message}`))
130
+ console.log(styleText('dim', `└─ caused by ${caused.message}`))
131
131
 
132
132
  const frames = caused.stack.split('\n').slice(1, 4)
133
133
  for (const frame of frames) {
134
- console.log(getMessage(` ${pc.dim(frame.trim())}`))
134
+ console.log(getMessage(` ${styleText('dim', frame.trim())}`))
135
135
  }
136
136
  }
137
137
  }
138
138
  })
139
139
 
140
140
  context.on('lifecycle:start', (version) => {
141
- console.log(pc.yellow(`Kubb ${version} 🧩`))
141
+ console.log(styleText('yellow', `Kubb ${version} 🧩`))
142
142
  reset()
143
143
  })
144
144
 
@@ -172,7 +172,7 @@ export const githubActionsLogger = defineLogger({
172
172
  // Initialize progress tracking
173
173
  state.totalPlugins = config.plugins?.length || 0
174
174
 
175
- const text = config.name ? `Generation for ${pc.bold(config.name)}` : 'Generation'
175
+ const text = config.name ? `Generation for ${styleText('bold', config.name)}` : 'Generation'
176
176
 
177
177
  if (state.currentConfigs.length > 1) {
178
178
  openGroup(text)
@@ -189,7 +189,7 @@ export const githubActionsLogger = defineLogger({
189
189
  if (logLevel <= LogLevel.silent) {
190
190
  return
191
191
  }
192
- const text = getMessage(`Generating ${pc.bold(plugin.name)}`)
192
+ const text = getMessage(`Generating ${styleText('bold', plugin.name)}`)
193
193
 
194
194
  if (state.currentConfigs.length === 1) {
195
195
  openGroup(`Plugin: ${plugin.name}`)
@@ -211,7 +211,9 @@ export const githubActionsLogger = defineLogger({
211
211
 
212
212
  const durationStr = formatMsWithColor(duration)
213
213
  const text = getMessage(
214
- success ? `${pc.bold(plugin.name)} completed in ${durationStr}` : `${pc.bold(plugin.name)} failed in ${pc.red(formatMs(duration))}`,
214
+ success
215
+ ? `${styleText('bold', plugin.name)} completed in ${durationStr}`
216
+ : `${styleText('bold', plugin.name)} failed in ${styleText('red', formatMs(duration))}`,
215
217
  )
216
218
 
217
219
  console.log(text)
@@ -274,7 +276,9 @@ export const githubActionsLogger = defineLogger({
274
276
  })
275
277
 
276
278
  context.on('generation:end', (config) => {
277
- const text = getMessage(config.name ? `${pc.blue('✓')} Generation completed for ${pc.dim(config.name)}` : `${pc.blue('✓')} Generation completed`)
279
+ const text = getMessage(
280
+ config.name ? `${styleText('blue', '✓')} Generation completed for ${styleText('dim', config.name)}` : `${styleText('blue', '✓')} Generation completed`,
281
+ )
278
282
 
279
283
  console.log(text)
280
284
  })
@@ -337,7 +341,7 @@ export const githubActionsLogger = defineLogger({
337
341
 
338
342
  context.on('hook:start', async ({ id, command, args }) => {
339
343
  const commandWithArgs = args?.length ? `${command} ${args.join(' ')}` : command
340
- const text = getMessage(`Hook ${pc.dim(commandWithArgs)} started`)
344
+ const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} started`)
341
345
 
342
346
  if (logLevel > LogLevel.silent) {
343
347
  if (state.currentConfigs.length === 1) {
@@ -353,18 +357,18 @@ export const githubActionsLogger = defineLogger({
353
357
  }
354
358
 
355
359
  try {
356
- const result = await execa(command, args, {
357
- detached: true,
358
- stripFinalNewline: true,
360
+ const result = await x(command, [...(args ?? [])], {
361
+ nodeOptions: { detached: true },
362
+ throwOnError: true,
359
363
  })
360
364
 
361
365
  await context.emit('debug', {
362
366
  date: new Date(),
363
- logs: [result.stdout],
367
+ logs: [result.stdout.trimEnd()],
364
368
  })
365
369
 
366
370
  if (logLevel > LogLevel.silent) {
367
- console.log(result.stdout)
371
+ console.log(result.stdout.trimEnd())
368
372
  }
369
373
 
370
374
  await context.emit('hook:end', {
@@ -375,9 +379,9 @@ export const githubActionsLogger = defineLogger({
375
379
  error: null,
376
380
  })
377
381
  } catch (err) {
378
- const error = err as ExecaError
379
- const stderr = typeof error.stderr === 'string' ? error.stderr : String(error.stderr)
380
- const stdout = typeof error.stdout === 'string' ? error.stdout : String(error.stdout)
382
+ const error = err as NonZeroExitError
383
+ const stderr = error.output?.stderr ?? ''
384
+ const stdout = error.output?.stdout ?? ''
381
385
 
382
386
  await context.emit('debug', {
383
387
  date: new Date(),
@@ -411,7 +415,7 @@ export const githubActionsLogger = defineLogger({
411
415
  }
412
416
 
413
417
  const commandWithArgs = args?.length ? `${command} ${args.join(' ')}` : command
414
- const text = getMessage(`Hook ${pc.dim(commandWithArgs)} completed`)
418
+ const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} completed`)
415
419
 
416
420
  console.log(text)
417
421
 
@@ -431,12 +435,12 @@ export const githubActionsLogger = defineLogger({
431
435
 
432
436
  console.log(
433
437
  status === 'success'
434
- ? `Kubb Summary: ${pc.blue('✓')} ${`${successCount} successful`}, ${pluginsCount} total, ${pc.green(duration)}`
435
- : `Kubb Summary: ${pc.blue('✓')} ${`${successCount} successful`}, ✗ ${`${failedPlugins.size} failed`}, ${pluginsCount} total, ${pc.green(duration)}`,
438
+ ? `Kubb Summary: ${styleText('blue', '✓')} ${`${successCount} successful`}, ${pluginsCount} total, ${styleText('green', duration)}`
439
+ : `Kubb Summary: ${styleText('blue', '✓')} ${`${successCount} successful`}, ✗ ${`${failedPlugins.size} failed`}, ${pluginsCount} total, ${styleText('green', duration)}`,
436
440
  )
437
441
 
438
442
  if (state.currentConfigs.length > 1) {
439
- closeGroup(config.name ? `Generation for ${pc.bold(config.name)}` : 'Generation')
443
+ closeGroup(config.name ? `Generation for ${styleText('bold', config.name)}` : 'Generation')
440
444
  }
441
445
  })
442
446
  },
@@ -1,7 +1,7 @@
1
1
  import { relative } from 'node:path'
2
2
  import { defineLogger, LogLevel } from '@kubb/core'
3
3
  import { formatMs } from '@kubb/core/utils'
4
- import { type ExecaError, execa } from 'execa'
4
+ import { type NonZeroExitError, x } from 'tinyexec'
5
5
  import { getSummary } from '../utils/getSummary.ts'
6
6
 
7
7
  /**
@@ -223,18 +223,18 @@ export const plainLogger = defineLogger({
223
223
  }
224
224
 
225
225
  try {
226
- const result = await execa(command, args, {
227
- detached: true,
228
- stripFinalNewline: true,
226
+ const result = await x(command, [...(args ?? [])], {
227
+ nodeOptions: { detached: true },
228
+ throwOnError: true,
229
229
  })
230
230
 
231
231
  await context.emit('debug', {
232
232
  date: new Date(),
233
- logs: [result.stdout],
233
+ logs: [result.stdout.trimEnd()],
234
234
  })
235
235
 
236
236
  if (logLevel > LogLevel.silent) {
237
- console.log(result.stdout)
237
+ console.log(result.stdout.trimEnd())
238
238
  }
239
239
 
240
240
  await context.emit('hook:end', {
@@ -245,9 +245,9 @@ export const plainLogger = defineLogger({
245
245
  error: null,
246
246
  })
247
247
  } catch (err) {
248
- const error = err as ExecaError
249
- const stderr = typeof error.stderr === 'string' ? error.stderr : String(error.stderr)
250
- const stdout = typeof error.stdout === 'string' ? error.stdout : String(error.stdout)
248
+ const error = err as NonZeroExitError
249
+ const stderr = error.output?.stderr ?? ''
250
+ const stdout = error.output?.stdout ?? ''
251
251
 
252
252
  await context.emit('debug', {
253
253
  date: new Date(),
@@ -1,10 +1,10 @@
1
1
  import { createHash } from 'node:crypto'
2
2
  import path from 'node:path'
3
3
  import process from 'node:process'
4
+ import { styleText } from 'node:util'
4
5
  import { type Config, type KubbEvents, LogLevel, safeBuild, setup } from '@kubb/core'
5
6
  import type { AsyncEventEmitter } from '@kubb/core/utils'
6
7
  import { detectFormatter, detectLinter, formatters, linters } from '@kubb/core/utils'
7
- import pc from 'picocolors'
8
8
  import { executeHooks } from '../utils/executeHooks.ts'
9
9
 
10
10
  type GenerateProps = {
@@ -40,14 +40,14 @@ export async function generate({ input, config: userConfig, events, logLevel }:
40
40
 
41
41
  await events.emit('generation:start', config)
42
42
 
43
- await events.emit('info', config.name ? `Setup generation ${pc.bold(config.name)}` : 'Setup generation', inputPath)
43
+ await events.emit('info', config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', inputPath)
44
44
 
45
45
  const { sources, fabric, pluginManager } = await setup({
46
46
  config,
47
47
  events,
48
48
  })
49
49
 
50
- await events.emit('info', config.name ? `Build generation ${pc.bold(config.name)}` : 'Build generation', inputPath)
50
+ await events.emit('info', config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', inputPath)
51
51
 
52
52
  const { files, failedPlugins, pluginTimings, error } = await safeBuild(
53
53
  {
@@ -102,7 +102,7 @@ export async function generate({ input, config: userConfig, events, logLevel }:
102
102
  await events.emit('warn', 'No formatter found (biome, prettier, or oxfmt). Skipping formatting.')
103
103
  } else {
104
104
  formatter = detectedFormatter
105
- await events.emit('info', `Auto-detected formatter: ${pc.dim(formatter)}`)
105
+ await events.emit('info', `Auto-detected formatter: ${styleText('dim', formatter)}`)
106
106
  }
107
107
  }
108
108
 
@@ -123,7 +123,7 @@ export async function generate({ input, config: userConfig, events, logLevel }:
123
123
 
124
124
  await events.emit(
125
125
  'success',
126
- [`Formatting with ${pc.dim(formatter)}`, logLevel >= LogLevel.info ? `on ${pc.dim(outputPath)}` : undefined, 'successfully']
126
+ [`Formatting with ${styleText('dim', formatter)}`, logLevel >= LogLevel.info ? `on ${styleText('dim', outputPath)}` : undefined, 'successfully']
127
127
  .filter(Boolean)
128
128
  .join(' '),
129
129
  )
@@ -150,7 +150,7 @@ export async function generate({ input, config: userConfig, events, logLevel }:
150
150
  await events.emit('warn', 'No linter found (biome, oxlint, or eslint). Skipping linting.')
151
151
  } else {
152
152
  linter = detectedLinter
153
- await events.emit('info', `Auto-detected linter: ${pc.dim(linter)}`)
153
+ await events.emit('info', `Auto-detected linter: ${styleText('dim', linter)}`)
154
154
  }
155
155
  }
156
156
 
@@ -172,7 +172,9 @@ export async function generate({ input, config: userConfig, events, logLevel }:
172
172
 
173
173
  await events.emit(
174
174
  'success',
175
- [`Linting with ${pc.dim(linter)}`, logLevel >= LogLevel.info ? `on ${pc.dim(outputPath)}` : undefined, 'successfully'].filter(Boolean).join(' '),
175
+ [`Linting with ${styleText('dim', linter)}`, logLevel >= LogLevel.info ? `on ${styleText('dim', outputPath)}` : undefined, 'successfully']
176
+ .filter(Boolean)
177
+ .join(' '),
176
178
  )
177
179
  })
178
180
  } catch (caughtError) {
@@ -1,7 +1,7 @@
1
1
  import type { WritableOptions } from 'node:stream'
2
2
  import { Writable } from 'node:stream'
3
+ import { styleText } from 'node:util'
3
4
  import type * as clack from '@clack/prompts'
4
- import pc from 'picocolors'
5
5
 
6
6
  export class ClackWritable extends Writable {
7
7
  taskLog: ReturnType<typeof clack.taskLog>
@@ -11,7 +11,7 @@ export class ClackWritable extends Writable {
11
11
  this.taskLog = taskLog
12
12
  }
13
13
  _write(chunk: any, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
14
- this.taskLog.message(`${pc.dim(chunk?.toString())}`)
14
+ this.taskLog.message(`${styleText('dim', chunk?.toString())}`)
15
15
  callback()
16
16
  }
17
17
  }
@@ -1,9 +1,8 @@
1
1
  import { createHash } from 'node:crypto'
2
+ import { styleText } from 'node:util'
2
3
  import type { Config, KubbEvents } from '@kubb/core'
3
4
  import type { AsyncEventEmitter } from '@kubb/core/utils'
4
-
5
- import pc from 'picocolors'
6
- import { parseArgsStringToArgv } from 'string-argv'
5
+ import { tokenize } from '@kubb/core/utils'
7
6
 
8
7
  type ExecutingHooksProps = {
9
8
  hooks: NonNullable<Config['hooks']>
@@ -14,7 +13,7 @@ export async function executeHooks({ hooks, events }: ExecutingHooksProps): Prom
14
13
  const commands = Array.isArray(hooks.done) ? hooks.done : [hooks.done].filter(Boolean)
15
14
 
16
15
  for (const command of commands) {
17
- const [cmd, ...args] = [...parseArgsStringToArgv(command)]
16
+ const [cmd, ...args] = tokenize(command)
18
17
 
19
18
  if (!cmd) {
20
19
  continue
@@ -28,7 +27,7 @@ export async function executeHooks({ hooks, events }: ExecutingHooksProps): Prom
28
27
  throw error
29
28
  }
30
29
 
31
- await events.emit('success', `${pc.dim(command)} successfully executed`)
30
+ await events.emit('success', `${styleText('dim', command)} successfully executed`)
32
31
  })
33
32
  }
34
33
  }
@@ -1,5 +1,5 @@
1
+ import { styleText } from 'node:util'
1
2
  import { formatMs } from '@kubb/core/utils'
2
- import pc from 'picocolors'
3
3
 
4
4
  /**
5
5
  * Formats milliseconds with color based on duration thresholds:
@@ -11,12 +11,12 @@ export function formatMsWithColor(ms: number): string {
11
11
  const formatted = formatMs(ms)
12
12
 
13
13
  if (ms <= 500) {
14
- return pc.green(formatted)
14
+ return styleText('green', formatted)
15
15
  }
16
16
 
17
17
  if (ms <= 1000) {
18
- return pc.yellow(formatted)
18
+ return styleText('yellow', formatted)
19
19
  }
20
20
 
21
- return pc.red(formatted)
21
+ return styleText('red', formatted)
22
22
  }
@@ -1,6 +1,52 @@
1
- import { default as gradientString } from 'gradient-string'
2
- import pc from 'picocolors'
3
- import { hex } from './ansiColors.ts'
1
+ import { styleText } from 'node:util'
2
+
3
+ /**
4
+ * ANSI True Color (24-bit) utilities for terminal output
5
+ * Supports hex color codes without external dependencies like chalk
6
+ */
7
+
8
+ /**
9
+ * Convert hex color to ANSI 24-bit true color escape sequence
10
+ * @param color - Hex color code (with or without #), e.g., '#FF5500' or 'FF5500'
11
+ * @returns Function that wraps text with the color
12
+ */
13
+ function hex(color: string): (text: string) => string {
14
+ const cleanHex = color.replace('#', '')
15
+ const r = Number.parseInt(cleanHex.slice(0, 2), 16)
16
+ const g = Number.parseInt(cleanHex.slice(2, 4), 16)
17
+ const b = Number.parseInt(cleanHex.slice(4, 6), 16)
18
+
19
+ // Default to white (255) if parsing fails (NaN)
20
+ const safeR = Number.isNaN(r) ? 255 : r
21
+ const safeG = Number.isNaN(g) ? 255 : g
22
+ const safeB = Number.isNaN(b) ? 255 : b
23
+
24
+ return (text: string) => `\x1b[38;2;${safeR};${safeG};${safeB}m${text}\x1b[0m`
25
+ }
26
+
27
+ function hexToRgb(color: string) {
28
+ const c = color.replace('#', '')
29
+ return { r: Number.parseInt(c.slice(0, 2), 16), g: Number.parseInt(c.slice(2, 4), 16), b: Number.parseInt(c.slice(4, 6), 16) }
30
+ }
31
+
32
+ function gradient(colors: string[]) {
33
+ return (text: string) => {
34
+ const chars = [...text]
35
+ return chars
36
+ .map((char, i) => {
37
+ const t = chars.length <= 1 ? 0 : i / (chars.length - 1)
38
+ const seg = Math.min(Math.floor(t * (colors.length - 1)), colors.length - 2)
39
+ const lt = t * (colors.length - 1) - seg
40
+ const from = hexToRgb(colors[seg]!)
41
+ const to = hexToRgb(colors[seg + 1]!)
42
+ const r = Math.round(from.r + (to.r - from.r) * lt)
43
+ const g = Math.round(from.g + (to.g - from.g) * lt)
44
+ const b = Math.round(from.b + (to.b - from.b) * lt)
45
+ return `\x1b[38;2;${r};${g};${b}m${char}\x1b[0m`
46
+ })
47
+ .join('')
48
+ }
49
+ }
4
50
 
5
51
  // Custom Color Palette for "Wooden" Depth
6
52
  const colors = {
@@ -20,7 +66,7 @@ const colors = {
20
66
  */
21
67
  export function getIntro({ title, description, version, areEyesOpen }: { title: string; description: string; version: string; areEyesOpen: boolean }): string {
22
68
  // Use gradient-string for the KUBB version text
23
- const kubbVersion = gradientString(['#F58517', '#F5A217', '#F55A17'])(`KUBB v${version}`)
69
+ const kubbVersion = gradient(['#F58517', '#F5A217', '#F55A17'])(`KUBB v${version}`)
24
70
 
25
71
  const eyeTop = areEyesOpen ? colors.eye('█▀█') : colors.eye('───')
26
72
  const eyeBottom = areEyesOpen ? colors.eye('▀▀▀') : colors.eye('───')
@@ -28,8 +74,8 @@ export function getIntro({ title, description, version, areEyesOpen }: { title:
28
74
  return `
29
75
  ${colors.lid('▄▄▄▄▄▄▄▄▄▄▄▄▄')}
30
76
  ${colors.woodTop('█ ')}${colors.highlight('▄▄')}${colors.woodTop(' ')}${colors.highlight('▄▄')}${colors.woodTop(' █')} ${kubbVersion}
31
- ${colors.woodMid('█ ')}${eyeTop}${colors.woodMid(' ')}${eyeTop}${colors.woodMid(' █')} ${pc.gray(title)}
32
- ${colors.woodMid('█ ')}${eyeBottom}${colors.woodMid(' ')}${colors.blush('◡')}${colors.woodMid(' ')}${eyeBottom}${colors.woodMid(' █')} ${pc.yellow('➜')} ${pc.white(description)}
77
+ ${colors.woodMid('█ ')}${eyeTop}${colors.woodMid(' ')}${eyeTop}${colors.woodMid(' █')} ${styleText('gray', title)}
78
+ ${colors.woodMid('█ ')}${eyeBottom}${colors.woodMid(' ')}${colors.blush('◡')}${colors.woodMid(' ')}${eyeBottom}${colors.woodMid(' █')} ${styleText('yellow', '➜')} ${styleText('white', description)}
33
79
  ${colors.woodBase('▀▀▀▀▀▀▀▀▀▀▀▀▀')}
34
80
  `
35
81
  }
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path'
2
+ import { styleText } from 'node:util'
2
3
  import type { Config, Plugin } from '@kubb/core'
3
4
  import { formatHrtime } from '@kubb/core/utils'
4
- import pc from 'picocolors'
5
5
  import { randomCliColor } from './randomColor.ts'
6
6
 
7
7
  type SummaryProps = {
@@ -22,11 +22,11 @@ export function getSummary({ failedPlugins, filesCreated, status, hrStart, confi
22
22
  const meta = {
23
23
  plugins:
24
24
  status === 'success'
25
- ? `${pc.green(`${successCount} successful`)}, ${pluginsCount} total`
26
- : `${pc.green(`${successCount} successful`)}, ${pc.red(`${failedPlugins.size} failed`)}, ${pluginsCount} total`,
25
+ ? `${styleText('green', `${successCount} successful`)}, ${pluginsCount} total`
26
+ : `${styleText('green', `${successCount} successful`)}, ${styleText('red', `${failedPlugins.size} failed`)}, ${pluginsCount} total`,
27
27
  pluginsFailed: status === 'failed' ? [...failedPlugins]?.map(({ plugin }) => randomCliColor(plugin.name))?.join(', ') : undefined,
28
28
  filesCreated: filesCreated,
29
- time: pc.green(duration),
29
+ time: styleText('green', duration),
30
30
  output: path.isAbsolute(config.root) ? path.resolve(config.root, config.output.path) : config.root,
31
31
  } as const
32
32
 
@@ -60,9 +60,9 @@ export function getSummary({ failedPlugins, filesCreated, status, hrStart, confi
60
60
  sortedTimings.forEach(([name, time]) => {
61
61
  const timeStr = time >= 1000 ? `${(time / 1000).toFixed(2)}s` : `${Math.round(time)}ms`
62
62
  const barLength = Math.min(Math.ceil(time / TIME_SCALE_DIVISOR), MAX_BAR_LENGTH)
63
- const bar = pc.dim('█'.repeat(barLength))
63
+ const bar = styleText('dim', '█'.repeat(barLength))
64
64
 
65
- summaryLines.push(`${pc.dim('•')} ${name.padEnd(maxLength + 1)}${bar} ${timeStr}`)
65
+ summaryLines.push(`${styleText('dim', '•')} ${name.padEnd(maxLength + 1)}${bar} ${timeStr}`)
66
66
  })
67
67
  }
68
68
  }
@@ -1,6 +1,6 @@
1
+ import { spawn } from 'node:child_process'
1
2
  import fs from 'node:fs'
2
3
  import path from 'node:path'
3
- import { execa } from 'execa'
4
4
 
5
5
  export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'
6
6
 
@@ -73,15 +73,9 @@ export async function initPackageJson(cwd: string, packageManager: PackageManage
73
73
  bun: ['init', '-y'],
74
74
  }
75
75
 
76
- await execa(packageManager.name, commands[packageManager.name], {
77
- cwd,
78
- stdio: 'inherit',
79
- })
76
+ spawn(packageManager.name, commands[packageManager.name], { stdio: 'inherit', cwd })
80
77
  }
81
78
 
82
79
  export async function installPackages(packages: string[], packageManager: PackageManagerInfo, cwd: string = process.cwd()): Promise<void> {
83
- await execa(packageManager.name, [...packageManager.installCommand, ...packages], {
84
- cwd,
85
- stdio: 'inherit',
86
- })
80
+ spawn(packageManager.name, [...packageManager.installCommand, ...packages], { stdio: 'inherit', cwd })
87
81
  }
@@ -1,5 +1,5 @@
1
- import pc from 'picocolors'
2
- import seedrandom from 'seedrandom'
1
+ import { createHash } from 'node:crypto'
2
+ import { styleText } from 'node:util'
3
3
 
4
4
  export function randomColor(text?: string): 'black' | 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white' | 'gray' {
5
5
  if (!text) {
@@ -7,9 +7,9 @@ export function randomColor(text?: string): 'black' | 'red' | 'green' | 'yellow'
7
7
  }
8
8
 
9
9
  const defaultColors = ['black', 'red', 'green', 'yellow', 'blue', 'red', 'green', 'magenta', 'cyan', 'gray'] as const
10
+ const index = createHash('sha256').update(text).digest().readUInt32BE(0) % defaultColors.length
10
11
 
11
- const random = seedrandom(text)
12
- return defaultColors.at(Math.floor(random() * defaultColors.length)) || 'white'
12
+ return defaultColors[index] ?? 'white'
13
13
  }
14
14
 
15
15
  export function randomCliColor(text?: string): string {
@@ -19,6 +19,5 @@ export function randomCliColor(text?: string): string {
19
19
 
20
20
  const color = randomColor(text)
21
21
 
22
- const fn = pc[color]
23
- return fn ? fn(text) : text
22
+ return styleText(color, text)
24
23
  }
@@ -1,4 +1,4 @@
1
- import pc from 'picocolors'
1
+ import { styleText } from 'node:util'
2
2
 
3
3
  export async function startWatcher(path: string[], cb: (path: string[]) => Promise<void>): Promise<void> {
4
4
  const { watch } = await import('chokidar')
@@ -10,12 +10,12 @@ export async function startWatcher(path: string[], cb: (path: string[]) => Promi
10
10
  ignored,
11
11
  })
12
12
  watcher.on('all', async (type, file) => {
13
- console.log(pc.yellow(pc.bold(`Change detected: ${type} ${file}`)))
13
+ console.log(styleText('yellow', styleText('bold', `Change detected: ${type} ${file}`)))
14
14
 
15
15
  try {
16
16
  await cb(path)
17
17
  } catch (_e) {
18
- console.log(pc.red('Watcher failed'))
18
+ console.log(styleText('red', 'Watcher failed'))
19
19
  }
20
20
  })
21
21
  }