/* * Copyright (c) 2022 R3BL LLC. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import EventEmitter from "events" import { render } from "ink" import { TimerRegistry } from "../timer-utils" import { TextColor } from "../tui-colors" /** * Launches a CLI app. This is the "bootloader" equivalent for a CLI app. * * If you have any event listeners attached (eg, using `useKeyboard()`, or `useNodeKeypress()`) then * your app won't exit if you don't call `LifecycleHelper.fireExit()`. * * Usage example: * ```tsx * const App:FC = ()=> { * const createShortcutsFn = (): ShortcutToActionMap => * _also( * createNewShortcutToActionMap(), * map => map.set("q", LifecycleHelper.fireExit) * ) * useKeyboardWithMapCached(createShortcutsFn) * return( "Hello" ) * } * * inkCLIAppMainFn( ()=>{ * const args = processCommandLineArgs() * return createInkApp(args) * } ).catch(console.error) * ``` * * @param {() => ReturnType} runFn Do what you need to create an Ink instance * @param okExitMsg If non empty string (truthy) display this on exit w/out errors * @param errExitMsg If non empty string (truthy) display this on exit w/ errors * @return {Promise} Optionally attach a catch block to this promise */ export const inkCLIAppMainFn = async ( runFn: () => ReturnType, okExitMsg = "", errExitMsg = "" ): Promise => { const instance = runFn() LifecycleHelper.addExitListener(() => { TimerRegistry.killAll() instance.unmount() }) try { await instance.waitUntilExit() if (okExitMsg) console.log(TextColor.builder.bgYellow.black.build()(okExitMsg)) } catch (err) { if (errExitMsg) console.error(TextColor.builder.bgYellow.black.build()(errExitMsg)) } } // LifecycleHelper. type EventName = "exit" | "start" type EventListener = (name: EventName) => void export class LifecycleHelper extends EventEmitter { static instance = new LifecycleHelper() static addStartListener = (listener: EventListener) => this.instance.on("start", listener) static addExitListener = (listener: EventListener) => this.instance.on("exit", listener) static fireExit = () => this.instance.emit("exit") static fireStart = () => this.instance.emit("start") static removeAll = () => this.instance.removeAllListeners() }