import { spawn, SpawnOptions } from 'child_process' import { prompt, PromptModule } from 'inquirer' import yargs, { Argv } from 'yargs' import chalk from 'chalk' import { loadModule } from './utils' export { Argv, yargs } const { yellow, red, blue } = chalk export interface Logger { warn: (msg: string) => void error: (msg: string) => void log: (msg: string) => void notice: (msg: string) => void } export class BasicLogger implements Logger { logger: typeof console = console previousNotice: string = '' warn (msg: string) { this.logger.log(yellow(` ${msg}`)) } error (msg: string) { this.logger.log(red(msg)) } log (msg: string) { this.logger.log(msg) } notice (msg: string) { if (this.previousNotice !== msg) { this.logger.log(blue(` ${msg}`)) this.previousNotice = msg } } } export type PinionTrace = { name: string timestamp: number info: unknown } export type Configuration = { cwd: string logger: Logger force: boolean prompt: PromptModule trace: PinionTrace[] exec: (command: string, args: string[], options?: SpawnOptions) => Promise } export type PinionContext = { cwd: string _?: (number | string)[] pinion: Configuration } export type ContextCallable = (ctx: C) => T | Promise export type Callable = T | ContextCallable export type Promisable = T | Promise export const getCallable = async (callable: Callable, context: C) => typeof callable === 'function' ? (callable as ContextCallable)(context) : callable export const mapCallables = (callables: Callable[], context: C) => Promise.all(callables.map((callable) => getCallable(callable, context))) export const getConfig = (initialConfig?: Partial): Configuration => ({ prompt, logger: new BasicLogger(), cwd: process.cwd(), force: false, trace: [], exec: (command: string, args: string[], options?: SpawnOptions) => { const child = spawn(command, args, { stdio: 'inherit', shell: true, ...options }) return new Promise((resolve, reject) => { child.once('exit', (code) => (code === 0 ? resolve(code) : reject(code))) }) }, ...initialConfig }) export const getContext = ( initialCtx: Partial, initialConfig: Partial = {} ) => { const pinion = getConfig(initialConfig) return { cwd: pinion.cwd, ...initialCtx, pinion } as T } export const generator = async (initialContext: T) => initialContext export const runModule = async (file: string, ctx: PinionContext) => { const { generate } = await loadModule(file) return generate(ctx) }