@inquirer/core
Advanced tools
+11
-16
| { | ||
| "name": "@inquirer/core", | ||
| "version": "11.0.0", | ||
| "version": "11.0.1", | ||
| "description": "Core Inquirer prompt API", | ||
@@ -55,3 +55,6 @@ "keywords": [ | ||
| "./package.json": "./package.json", | ||
| ".": "./src/index.ts" | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "default": "./dist/index.js" | ||
| } | ||
| }, | ||
@@ -65,5 +68,5 @@ "files": [ | ||
| "dependencies": { | ||
| "@inquirer/ansi": "^2.0.0", | ||
| "@inquirer/figures": "^2.0.0", | ||
| "@inquirer/type": "^4.0.0", | ||
| "@inquirer/ansi": "^2.0.1", | ||
| "@inquirer/figures": "^2.0.1", | ||
| "@inquirer/type": "^4.0.1", | ||
| "cli-width": "^4.1.0", | ||
@@ -75,4 +78,3 @@ "mute-stream": "^3.0.0", | ||
| "devDependencies": { | ||
| "@inquirer/testing": "^3.0.0", | ||
| "@repo/tsconfig": "0.0.0", | ||
| "@inquirer/testing": "^3.0.1", | ||
| "@types/mute-stream": "^0.0.4", | ||
@@ -94,12 +96,5 @@ "@types/node": "^24.10.1", | ||
| "publishConfig": { | ||
| "access": "public", | ||
| "exports": { | ||
| "./package.json": "./package.json", | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "default": "./dist/index.js" | ||
| } | ||
| } | ||
| "access": "public" | ||
| }, | ||
| "gitHead": "676685d33374a30340c1b9f0831c7eae2b2357dd" | ||
| "gitHead": "cce79ce3b9bbdfb4dbb798078cf3b94b9adc7d1b" | ||
| } |
| export { isUpKey, isDownKey, isSpaceKey, isBackspaceKey, isTabKey, isNumberKey, isEnterKey, type KeypressEvent, type Keybinding, } from './lib/key.ts'; | ||
| export * from './lib/errors.ts'; | ||
| export { usePrefix } from './lib/use-prefix.ts'; | ||
| export { useState } from './lib/use-state.ts'; | ||
| export { useEffect } from './lib/use-effect.ts'; | ||
| export { useMemo } from './lib/use-memo.ts'; | ||
| export { useRef } from './lib/use-ref.ts'; | ||
| export { useKeypress } from './lib/use-keypress.ts'; | ||
| export { makeTheme } from './lib/make-theme.ts'; | ||
| export type { Theme, Status } from './lib/theme.ts'; | ||
| export { usePagination } from './lib/pagination/use-pagination.ts'; | ||
| export { createPrompt } from './lib/create-prompt.ts'; | ||
| export { Separator } from './lib/Separator.ts'; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
| for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Separator = exports.createPrompt = exports.usePagination = exports.makeTheme = exports.useKeypress = exports.useRef = exports.useMemo = exports.useEffect = exports.useState = exports.usePrefix = exports.isEnterKey = exports.isNumberKey = exports.isTabKey = exports.isBackspaceKey = exports.isSpaceKey = exports.isDownKey = exports.isUpKey = void 0; | ||
| var key_ts_1 = require("./lib/key.js"); | ||
| Object.defineProperty(exports, "isUpKey", { enumerable: true, get: function () { return key_ts_1.isUpKey; } }); | ||
| Object.defineProperty(exports, "isDownKey", { enumerable: true, get: function () { return key_ts_1.isDownKey; } }); | ||
| Object.defineProperty(exports, "isSpaceKey", { enumerable: true, get: function () { return key_ts_1.isSpaceKey; } }); | ||
| Object.defineProperty(exports, "isBackspaceKey", { enumerable: true, get: function () { return key_ts_1.isBackspaceKey; } }); | ||
| Object.defineProperty(exports, "isTabKey", { enumerable: true, get: function () { return key_ts_1.isTabKey; } }); | ||
| Object.defineProperty(exports, "isNumberKey", { enumerable: true, get: function () { return key_ts_1.isNumberKey; } }); | ||
| Object.defineProperty(exports, "isEnterKey", { enumerable: true, get: function () { return key_ts_1.isEnterKey; } }); | ||
| __exportStar(require("./lib/errors.js"), exports); | ||
| var use_prefix_ts_1 = require("./lib/use-prefix.js"); | ||
| Object.defineProperty(exports, "usePrefix", { enumerable: true, get: function () { return use_prefix_ts_1.usePrefix; } }); | ||
| var use_state_ts_1 = require("./lib/use-state.js"); | ||
| Object.defineProperty(exports, "useState", { enumerable: true, get: function () { return use_state_ts_1.useState; } }); | ||
| var use_effect_ts_1 = require("./lib/use-effect.js"); | ||
| Object.defineProperty(exports, "useEffect", { enumerable: true, get: function () { return use_effect_ts_1.useEffect; } }); | ||
| var use_memo_ts_1 = require("./lib/use-memo.js"); | ||
| Object.defineProperty(exports, "useMemo", { enumerable: true, get: function () { return use_memo_ts_1.useMemo; } }); | ||
| var use_ref_ts_1 = require("./lib/use-ref.js"); | ||
| Object.defineProperty(exports, "useRef", { enumerable: true, get: function () { return use_ref_ts_1.useRef; } }); | ||
| var use_keypress_ts_1 = require("./lib/use-keypress.js"); | ||
| Object.defineProperty(exports, "useKeypress", { enumerable: true, get: function () { return use_keypress_ts_1.useKeypress; } }); | ||
| var make_theme_ts_1 = require("./lib/make-theme.js"); | ||
| Object.defineProperty(exports, "makeTheme", { enumerable: true, get: function () { return make_theme_ts_1.makeTheme; } }); | ||
| var use_pagination_ts_1 = require("./lib/pagination/use-pagination.js"); | ||
| Object.defineProperty(exports, "usePagination", { enumerable: true, get: function () { return use_pagination_ts_1.usePagination; } }); | ||
| var create_prompt_ts_1 = require("./lib/create-prompt.js"); | ||
| Object.defineProperty(exports, "createPrompt", { enumerable: true, get: function () { return create_prompt_ts_1.createPrompt; } }); | ||
| var Separator_ts_1 = require("./lib/Separator.js"); | ||
| Object.defineProperty(exports, "Separator", { enumerable: true, get: function () { return Separator_ts_1.Separator; } }); |
| import { type Prompt, type Prettify } from '@inquirer/type'; | ||
| type ViewFunction<Value, Config> = (config: Prettify<Config>, done: (value: Value) => void) => string | [string, string | undefined]; | ||
| export declare function createPrompt<Value, Config>(view: ViewFunction<Value, Config>): Prompt<Value, Config>; | ||
| export {}; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.createPrompt = createPrompt; | ||
| const readline = __importStar(require("node:readline")); | ||
| const node_async_hooks_1 = require("node:async_hooks"); | ||
| const mute_stream_1 = __importDefault(require("mute-stream")); | ||
| const signal_exit_1 = require("signal-exit"); | ||
| const screen_manager_ts_1 = __importDefault(require("./screen-manager.js")); | ||
| const promise_polyfill_ts_1 = require("./promise-polyfill.js"); | ||
| const hook_engine_ts_1 = require("./hook-engine.js"); | ||
| const errors_ts_1 = require("./errors.js"); | ||
| function getCallSites() { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| const _prepareStackTrace = Error.prepareStackTrace; | ||
| let result = []; | ||
| try { | ||
| Error.prepareStackTrace = (_, callSites) => { | ||
| const callSitesWithoutCurrent = callSites.slice(1); | ||
| result = callSitesWithoutCurrent; | ||
| return callSitesWithoutCurrent; | ||
| }; | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-expressions | ||
| new Error().stack; | ||
| } | ||
| catch { | ||
| // An error will occur if the Node flag --frozen-intrinsics is used. | ||
| // https://nodejs.org/api/cli.html#--frozen-intrinsics | ||
| return result; | ||
| } | ||
| Error.prepareStackTrace = _prepareStackTrace; | ||
| return result; | ||
| } | ||
| function createPrompt(view) { | ||
| const callSites = getCallSites(); | ||
| const prompt = (config, context = {}) => { | ||
| // Default `input` to stdin | ||
| const { input = process.stdin, signal } = context; | ||
| const cleanups = new Set(); | ||
| // Add mute capabilities to the output | ||
| const output = new mute_stream_1.default(); | ||
| output.pipe(context.output ?? process.stdout); | ||
| const rl = readline.createInterface({ | ||
| terminal: true, | ||
| input, | ||
| output, | ||
| }); | ||
| const screen = new screen_manager_ts_1.default(rl); | ||
| const { promise, resolve, reject } = promise_polyfill_ts_1.PromisePolyfill.withResolver(); | ||
| const cancel = () => reject(new errors_ts_1.CancelPromptError()); | ||
| if (signal) { | ||
| const abort = () => reject(new errors_ts_1.AbortPromptError({ cause: signal.reason })); | ||
| if (signal.aborted) { | ||
| abort(); | ||
| return Object.assign(promise, { cancel }); | ||
| } | ||
| signal.addEventListener('abort', abort); | ||
| cleanups.add(() => signal.removeEventListener('abort', abort)); | ||
| } | ||
| cleanups.add((0, signal_exit_1.onExit)((code, signal) => { | ||
| reject(new errors_ts_1.ExitPromptError(`User force closed the prompt with ${code} ${signal}`)); | ||
| })); | ||
| // SIGINT must be explicitly handled by the prompt so the ExitPromptError can be handled. | ||
| // Otherwise, the prompt will stop and in some scenarios never resolve. | ||
| // Ref issue #1741 | ||
| const sigint = () => reject(new errors_ts_1.ExitPromptError(`User force closed the prompt with SIGINT`)); | ||
| rl.on('SIGINT', sigint); | ||
| cleanups.add(() => rl.removeListener('SIGINT', sigint)); | ||
| // Re-renders only happen when the state change; but the readline cursor could change position | ||
| // and that also requires a re-render (and a manual one because we mute the streams). | ||
| // We set the listener after the initial workLoop to avoid a double render if render triggered | ||
| // by a state change sets the cursor to the right position. | ||
| const checkCursorPos = () => screen.checkCursorPos(); | ||
| rl.input.on('keypress', checkCursorPos); | ||
| cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos)); | ||
| return (0, hook_engine_ts_1.withHooks)(rl, (cycle) => { | ||
| // The close event triggers immediately when the user press ctrl+c. SignalExit on the other hand | ||
| // triggers after the process is done (which happens after timeouts are done triggering.) | ||
| // We triggers the hooks cleanup phase on rl `close` so active timeouts can be cleared. | ||
| const hooksCleanup = node_async_hooks_1.AsyncResource.bind(() => hook_engine_ts_1.effectScheduler.clearAll()); | ||
| rl.on('close', hooksCleanup); | ||
| cleanups.add(() => rl.removeListener('close', hooksCleanup)); | ||
| cycle(() => { | ||
| try { | ||
| const nextView = view(config, (value) => { | ||
| setImmediate(() => resolve(value)); | ||
| }); | ||
| // Typescript won't allow this, but not all users rely on typescript. | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| if (nextView === undefined) { | ||
| const callerFilename = callSites[1]?.getFileName(); | ||
| throw new Error(`Prompt functions must return a string.\n at ${callerFilename}`); | ||
| } | ||
| const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView; | ||
| screen.render(content, bottomContent); | ||
| hook_engine_ts_1.effectScheduler.run(); | ||
| } | ||
| catch (error) { | ||
| reject(error); | ||
| } | ||
| }); | ||
| return Object.assign(promise | ||
| .then((answer) => { | ||
| hook_engine_ts_1.effectScheduler.clearAll(); | ||
| return answer; | ||
| }, (error) => { | ||
| hook_engine_ts_1.effectScheduler.clearAll(); | ||
| throw error; | ||
| }) | ||
| // Wait for the promise to settle, then cleanup. | ||
| .finally(() => { | ||
| cleanups.forEach((cleanup) => cleanup()); | ||
| screen.done({ clearContent: Boolean(context.clearPromptOnDone) }); | ||
| output.end(); | ||
| }) | ||
| // Once cleanup is done, let the expose promise resolve/reject to the internal one. | ||
| .then(() => promise), { cancel }); | ||
| }); | ||
| }; | ||
| return prompt; | ||
| } |
| export declare class AbortPromptError extends Error { | ||
| name: string; | ||
| message: string; | ||
| constructor(options?: { | ||
| cause?: unknown; | ||
| }); | ||
| } | ||
| export declare class CancelPromptError extends Error { | ||
| name: string; | ||
| message: string; | ||
| } | ||
| export declare class ExitPromptError extends Error { | ||
| name: string; | ||
| } | ||
| export declare class HookError extends Error { | ||
| name: string; | ||
| } | ||
| export declare class ValidationError extends Error { | ||
| name: string; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.ValidationError = exports.HookError = exports.ExitPromptError = exports.CancelPromptError = exports.AbortPromptError = void 0; | ||
| class AbortPromptError extends Error { | ||
| name = 'AbortPromptError'; | ||
| message = 'Prompt was aborted'; | ||
| constructor(options) { | ||
| super(); | ||
| this.cause = options?.cause; | ||
| } | ||
| } | ||
| exports.AbortPromptError = AbortPromptError; | ||
| class CancelPromptError extends Error { | ||
| name = 'CancelPromptError'; | ||
| message = 'Prompt was canceled'; | ||
| } | ||
| exports.CancelPromptError = CancelPromptError; | ||
| class ExitPromptError extends Error { | ||
| name = 'ExitPromptError'; | ||
| } | ||
| exports.ExitPromptError = ExitPromptError; | ||
| class HookError extends Error { | ||
| name = 'HookError'; | ||
| } | ||
| exports.HookError = HookError; | ||
| class ValidationError extends Error { | ||
| name = 'ValidationError'; | ||
| } | ||
| exports.ValidationError = ValidationError; |
| import type { InquirerReadline } from '@inquirer/type'; | ||
| export declare function withHooks<T>(rl: InquirerReadline, cb: (cycle: (render: () => void) => void) => T): T; | ||
| export declare function readline(): InquirerReadline; | ||
| export declare function withUpdates<Args extends unknown[], R>(fn: (...args: Args) => R): (...args: Args) => R; | ||
| type SetPointer<Value> = { | ||
| get(): Value; | ||
| set(value: Value): void; | ||
| initialized: true; | ||
| }; | ||
| type UnsetPointer<Value> = { | ||
| get(): void; | ||
| set(value: Value): void; | ||
| initialized: false; | ||
| }; | ||
| type Pointer<Value> = SetPointer<Value> | UnsetPointer<Value>; | ||
| export declare function withPointer<Value, ReturnValue>(cb: (pointer: Pointer<Value>) => ReturnValue): ReturnValue; | ||
| export declare function handleChange(): void; | ||
| export declare const effectScheduler: { | ||
| queue(cb: (readline: InquirerReadline) => void | (() => void)): void; | ||
| run(): void; | ||
| clearAll(): void; | ||
| }; | ||
| export {}; |
| "use strict"; | ||
| /* eslint @typescript-eslint/no-explicit-any: ["off"] */ | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.effectScheduler = void 0; | ||
| exports.withHooks = withHooks; | ||
| exports.readline = readline; | ||
| exports.withUpdates = withUpdates; | ||
| exports.withPointer = withPointer; | ||
| exports.handleChange = handleChange; | ||
| const node_async_hooks_1 = require("node:async_hooks"); | ||
| const errors_ts_1 = require("./errors.js"); | ||
| const hookStorage = new node_async_hooks_1.AsyncLocalStorage(); | ||
| function createStore(rl) { | ||
| const store = { | ||
| rl, | ||
| hooks: [], | ||
| hooksCleanup: [], | ||
| hooksEffect: [], | ||
| index: 0, | ||
| handleChange() { }, | ||
| }; | ||
| return store; | ||
| } | ||
| // Run callback in with the hook engine setup. | ||
| function withHooks(rl, cb) { | ||
| const store = createStore(rl); | ||
| return hookStorage.run(store, () => { | ||
| function cycle(render) { | ||
| store.handleChange = () => { | ||
| store.index = 0; | ||
| render(); | ||
| }; | ||
| store.handleChange(); | ||
| } | ||
| return cb(cycle); | ||
| }); | ||
| } | ||
| // Safe getStore utility that'll return the store or throw if undefined. | ||
| function getStore() { | ||
| const store = hookStorage.getStore(); | ||
| if (!store) { | ||
| throw new errors_ts_1.HookError('[Inquirer] Hook functions can only be called from within a prompt'); | ||
| } | ||
| return store; | ||
| } | ||
| function readline() { | ||
| return getStore().rl; | ||
| } | ||
| // Merge state updates happening within the callback function to avoid multiple renders. | ||
| function withUpdates(fn) { | ||
| const wrapped = (...args) => { | ||
| const store = getStore(); | ||
| let shouldUpdate = false; | ||
| const oldHandleChange = store.handleChange; | ||
| store.handleChange = () => { | ||
| shouldUpdate = true; | ||
| }; | ||
| const returnValue = fn(...args); | ||
| if (shouldUpdate) { | ||
| oldHandleChange(); | ||
| } | ||
| store.handleChange = oldHandleChange; | ||
| return returnValue; | ||
| }; | ||
| return node_async_hooks_1.AsyncResource.bind(wrapped); | ||
| } | ||
| function withPointer(cb) { | ||
| const store = getStore(); | ||
| const { index } = store; | ||
| const pointer = { | ||
| get() { | ||
| return store.hooks[index]; | ||
| }, | ||
| set(value) { | ||
| store.hooks[index] = value; | ||
| }, | ||
| initialized: index in store.hooks, | ||
| }; | ||
| const returnValue = cb(pointer); | ||
| store.index++; | ||
| return returnValue; | ||
| } | ||
| function handleChange() { | ||
| getStore().handleChange(); | ||
| } | ||
| exports.effectScheduler = { | ||
| queue(cb) { | ||
| const store = getStore(); | ||
| const { index } = store; | ||
| store.hooksEffect.push(() => { | ||
| store.hooksCleanup[index]?.(); | ||
| const cleanFn = cb(readline()); | ||
| if (cleanFn != null && typeof cleanFn !== 'function') { | ||
| throw new errors_ts_1.ValidationError('useEffect return value must be a cleanup function or nothing.'); | ||
| } | ||
| store.hooksCleanup[index] = cleanFn; | ||
| }); | ||
| }, | ||
| run() { | ||
| const store = getStore(); | ||
| withUpdates(() => { | ||
| store.hooksEffect.forEach((effect) => { | ||
| effect(); | ||
| }); | ||
| // Warning: Clean the hooks before exiting the `withUpdates` block. | ||
| // Failure to do so means an updates would hit the same effects again. | ||
| store.hooksEffect.length = 0; | ||
| })(); | ||
| }, | ||
| clearAll() { | ||
| const store = getStore(); | ||
| store.hooksCleanup.forEach((cleanFn) => { | ||
| cleanFn?.(); | ||
| }); | ||
| store.hooksEffect.length = 0; | ||
| store.hooksCleanup.length = 0; | ||
| }, | ||
| }; |
| export type KeypressEvent = { | ||
| name: string; | ||
| ctrl: boolean; | ||
| }; | ||
| export type Keybinding = 'emacs' | 'vim'; | ||
| export declare const isUpKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean; | ||
| export declare const isDownKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean; | ||
| export declare const isSpaceKey: (key: KeypressEvent) => boolean; | ||
| export declare const isBackspaceKey: (key: KeypressEvent) => boolean; | ||
| export declare const isTabKey: (key: KeypressEvent) => boolean; | ||
| export declare const isNumberKey: (key: KeypressEvent) => boolean; | ||
| export declare const isEnterKey: (key: KeypressEvent) => boolean; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.isEnterKey = exports.isNumberKey = exports.isTabKey = exports.isBackspaceKey = exports.isSpaceKey = exports.isDownKey = exports.isUpKey = void 0; | ||
| const isUpKey = (key, keybindings = []) => | ||
| // The up key | ||
| key.name === 'up' || | ||
| // Vim keybinding: hjkl keys map to left/down/up/right | ||
| (keybindings.includes('vim') && key.name === 'k') || | ||
| // Emacs keybinding: Ctrl+P means "previous" in Emacs navigation conventions | ||
| (keybindings.includes('emacs') && key.ctrl && key.name === 'p'); | ||
| exports.isUpKey = isUpKey; | ||
| const isDownKey = (key, keybindings = []) => | ||
| // The down key | ||
| key.name === 'down' || | ||
| // Vim keybinding: hjkl keys map to left/down/up/right | ||
| (keybindings.includes('vim') && key.name === 'j') || | ||
| // Emacs keybinding: Ctrl+N means "next" in Emacs navigation conventions | ||
| (keybindings.includes('emacs') && key.ctrl && key.name === 'n'); | ||
| exports.isDownKey = isDownKey; | ||
| const isSpaceKey = (key) => key.name === 'space'; | ||
| exports.isSpaceKey = isSpaceKey; | ||
| const isBackspaceKey = (key) => key.name === 'backspace'; | ||
| exports.isBackspaceKey = isBackspaceKey; | ||
| const isTabKey = (key) => key.name === 'tab'; | ||
| exports.isTabKey = isTabKey; | ||
| const isNumberKey = (key) => '1234567890'.includes(key.name); | ||
| exports.isNumberKey = isNumberKey; | ||
| const isEnterKey = (key) => key.name === 'enter' || key.name === 'return'; | ||
| exports.isEnterKey = isEnterKey; |
| import type { Prettify, PartialDeep } from '@inquirer/type'; | ||
| import { type Theme } from './theme.ts'; | ||
| export declare function makeTheme<SpecificTheme extends object>(...themes: ReadonlyArray<undefined | PartialDeep<Theme<SpecificTheme>>>): Prettify<Theme<SpecificTheme>>; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.makeTheme = makeTheme; | ||
| const theme_ts_1 = require("./theme.js"); | ||
| function isPlainObject(value) { | ||
| if (typeof value !== 'object' || value === null) | ||
| return false; | ||
| let proto = value; | ||
| while (Object.getPrototypeOf(proto) !== null) { | ||
| proto = Object.getPrototypeOf(proto); | ||
| } | ||
| return Object.getPrototypeOf(value) === proto; | ||
| } | ||
| function deepMerge(...objects) { | ||
| const output = {}; | ||
| for (const obj of objects) { | ||
| for (const [key, value] of Object.entries(obj)) { | ||
| const prevValue = output[key]; | ||
| output[key] = | ||
| isPlainObject(prevValue) && isPlainObject(value) | ||
| ? deepMerge(prevValue, value) | ||
| : value; | ||
| } | ||
| } | ||
| return output; | ||
| } | ||
| function makeTheme(...themes) { | ||
| const themesToMerge = [ | ||
| theme_ts_1.defaultTheme, | ||
| ...themes.filter((theme) => theme != null), | ||
| ]; | ||
| return deepMerge(...themesToMerge); | ||
| } |
| import type { Prettify } from '@inquirer/type'; | ||
| export declare function usePagination<T>({ items, active, renderItem, pageSize, loop, }: { | ||
| items: ReadonlyArray<T>; | ||
| /** The index of the active item. */ | ||
| active: number; | ||
| /** Renders an item as part of a page. */ | ||
| renderItem: (layout: Prettify<{ | ||
| item: T; | ||
| index: number; | ||
| isActive: boolean; | ||
| }>) => string; | ||
| /** The size of the page. */ | ||
| pageSize: number; | ||
| /** Allows creating an infinitely looping list. `true` if unspecified. */ | ||
| loop?: boolean; | ||
| }): string; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.usePagination = usePagination; | ||
| const use_ref_ts_1 = require("../use-ref.js"); | ||
| const utils_ts_1 = require("../utils.js"); | ||
| function usePointerPosition({ active, renderedItems, pageSize, loop, }) { | ||
| const state = (0, use_ref_ts_1.useRef)({ | ||
| lastPointer: active, | ||
| lastActive: undefined, | ||
| }); | ||
| const { lastPointer, lastActive } = state.current; | ||
| const middle = Math.floor(pageSize / 2); | ||
| const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0); | ||
| const defaultPointerPosition = renderedItems | ||
| .slice(0, active) | ||
| .reduce((acc, item) => acc + item.length, 0); | ||
| let pointer = defaultPointerPosition; | ||
| if (renderedLength > pageSize) { | ||
| if (loop) { | ||
| /** | ||
| * Creates the next position for the pointer considering an infinitely | ||
| * looping list of items to be rendered on the page. | ||
| * | ||
| * The goal is to progressively move the cursor to the middle position as the user move down, and then keep | ||
| * the cursor there. When the user move up, maintain the cursor position. | ||
| */ | ||
| // By default, keep the cursor position as-is. | ||
| pointer = lastPointer; | ||
| if ( | ||
| // First render, skip this logic. | ||
| lastActive != null && | ||
| // Only move the pointer down when the user moves down. | ||
| lastActive < active && | ||
| // Check user didn't move up across page boundary. | ||
| active - lastActive < pageSize) { | ||
| pointer = Math.min( | ||
| // Furthest allowed position for the pointer is the middle of the list | ||
| middle, Math.abs(active - lastActive) === 1 | ||
| ? Math.min( | ||
| // Move the pointer at most the height of the last active item. | ||
| lastPointer + (renderedItems[lastActive]?.length ?? 0), | ||
| // If the user moved by one item, move the pointer to the natural position of the active item as | ||
| // long as it doesn't move the cursor up. | ||
| Math.max(defaultPointerPosition, lastPointer)) | ||
| : // Otherwise, move the pointer down by the difference between the active and last active item. | ||
| lastPointer + active - lastActive); | ||
| } | ||
| } | ||
| else { | ||
| /** | ||
| * Creates the next position for the pointer considering a finite list of | ||
| * items to be rendered on a page. | ||
| * | ||
| * The goal is to keep the pointer in the middle of the page whenever possible, until | ||
| * we reach the bounds of the list (top or bottom). In which case, the cursor moves progressively | ||
| * to the bottom or top of the list. | ||
| */ | ||
| const spaceUnderActive = renderedItems | ||
| .slice(active) | ||
| .reduce((acc, item) => acc + item.length, 0); | ||
| pointer = | ||
| spaceUnderActive < pageSize - middle | ||
| ? // If the active item is near the end of the list, progressively move the cursor towards the end. | ||
| pageSize - spaceUnderActive | ||
| : // Otherwise, progressively move the pointer to the middle of the list. | ||
| Math.min(defaultPointerPosition, middle); | ||
| } | ||
| } | ||
| // Save state for the next render | ||
| state.current.lastPointer = pointer; | ||
| state.current.lastActive = active; | ||
| return pointer; | ||
| } | ||
| function usePagination({ items, active, renderItem, pageSize, loop = true, }) { | ||
| const width = (0, utils_ts_1.readlineWidth)(); | ||
| const bound = (num) => ((num % items.length) + items.length) % items.length; | ||
| const renderedItems = items.map((item, index) => { | ||
| if (item == null) | ||
| return []; | ||
| return (0, utils_ts_1.breakLines)(renderItem({ item, index, isActive: index === active }), width).split('\n'); | ||
| }); | ||
| const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0); | ||
| const renderItemAtIndex = (index) => renderedItems[index] ?? []; | ||
| const pointer = usePointerPosition({ active, renderedItems, pageSize, loop }); | ||
| // Render the active item to decide the position. | ||
| // If the active item fits under the pointer, we render it there. | ||
| // Otherwise, we need to render it to fit at the bottom of the page; moving the pointer up. | ||
| const activeItem = renderItemAtIndex(active).slice(0, pageSize); | ||
| const activeItemPosition = pointer + activeItem.length <= pageSize ? pointer : pageSize - activeItem.length; | ||
| // Create an array of lines for the page, and add the lines of the active item into the page | ||
| const pageBuffer = Array.from({ length: pageSize }); | ||
| pageBuffer.splice(activeItemPosition, activeItem.length, ...activeItem); | ||
| // Store to prevent rendering the same item twice | ||
| const itemVisited = new Set([active]); | ||
| // Fill the page under the active item | ||
| let bufferPointer = activeItemPosition + activeItem.length; | ||
| let itemPointer = bound(active + 1); | ||
| while (bufferPointer < pageSize && | ||
| !itemVisited.has(itemPointer) && | ||
| (loop && renderedLength > pageSize ? itemPointer !== active : itemPointer > active)) { | ||
| const lines = renderItemAtIndex(itemPointer); | ||
| const linesToAdd = lines.slice(0, pageSize - bufferPointer); | ||
| pageBuffer.splice(bufferPointer, linesToAdd.length, ...linesToAdd); | ||
| // Move pointers for next iteration | ||
| itemVisited.add(itemPointer); | ||
| bufferPointer += linesToAdd.length; | ||
| itemPointer = bound(itemPointer + 1); | ||
| } | ||
| // Fill the page over the active item | ||
| bufferPointer = activeItemPosition - 1; | ||
| itemPointer = bound(active - 1); | ||
| while (bufferPointer >= 0 && | ||
| !itemVisited.has(itemPointer) && | ||
| (loop && renderedLength > pageSize ? itemPointer !== active : itemPointer < active)) { | ||
| const lines = renderItemAtIndex(itemPointer); | ||
| const linesToAdd = lines.slice(Math.max(0, lines.length - bufferPointer - 1)); | ||
| pageBuffer.splice(bufferPointer - linesToAdd.length + 1, linesToAdd.length, ...linesToAdd); | ||
| // Move pointers for next iteration | ||
| itemVisited.add(itemPointer); | ||
| bufferPointer -= linesToAdd.length; | ||
| itemPointer = bound(itemPointer - 1); | ||
| } | ||
| return pageBuffer.filter((line) => typeof line === 'string').join('\n'); | ||
| } |
| export declare class PromisePolyfill<T> extends Promise<T> { | ||
| static withResolver<T>(): { | ||
| promise: Promise<T>; | ||
| resolve: (value: T) => void; | ||
| reject: (error: unknown) => void; | ||
| }; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.PromisePolyfill = void 0; | ||
| // TODO: Remove this class once Node 22 becomes the minimum supported version. | ||
| class PromisePolyfill extends Promise { | ||
| // Available starting from Node 22 | ||
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers | ||
| static withResolver() { | ||
| let resolve; | ||
| let reject; | ||
| const promise = new Promise((res, rej) => { | ||
| resolve = res; | ||
| reject = rej; | ||
| }); | ||
| return { promise, resolve: resolve, reject: reject }; | ||
| } | ||
| } | ||
| exports.PromisePolyfill = PromisePolyfill; |
| import type { InquirerReadline } from '@inquirer/type'; | ||
| export default class ScreenManager { | ||
| private height; | ||
| private extraLinesUnderPrompt; | ||
| private cursorPos; | ||
| private readonly rl; | ||
| constructor(rl: InquirerReadline); | ||
| write(content: string): void; | ||
| render(content: string, bottomContent?: string): void; | ||
| checkCursorPos(): void; | ||
| done({ clearContent }: { | ||
| clearContent: boolean; | ||
| }): void; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| const node_util_1 = require("node:util"); | ||
| const utils_ts_1 = require("./utils.js"); | ||
| const ansi_1 = require("@inquirer/ansi"); | ||
| const height = (content) => content.split('\n').length; | ||
| const lastLine = (content) => content.split('\n').pop() ?? ''; | ||
| class ScreenManager { | ||
| // These variables are keeping information to allow correct prompt re-rendering | ||
| height = 0; | ||
| extraLinesUnderPrompt = 0; | ||
| cursorPos; | ||
| rl; | ||
| constructor(rl) { | ||
| this.rl = rl; | ||
| this.cursorPos = rl.getCursorPos(); | ||
| } | ||
| write(content) { | ||
| this.rl.output.unmute(); | ||
| this.rl.output.write(content); | ||
| this.rl.output.mute(); | ||
| } | ||
| render(content, bottomContent = '') { | ||
| // Write message to screen and setPrompt to control backspace | ||
| const promptLine = lastLine(content); | ||
| const rawPromptLine = (0, node_util_1.stripVTControlCharacters)(promptLine); | ||
| // Remove the rl.line from our prompt. We can't rely on the content of | ||
| // rl.line (mainly because of the password prompt), so just rely on it's | ||
| // length. | ||
| let prompt = rawPromptLine; | ||
| if (this.rl.line.length > 0) { | ||
| prompt = prompt.slice(0, -this.rl.line.length); | ||
| } | ||
| this.rl.setPrompt(prompt); | ||
| // SetPrompt will change cursor position, now we can get correct value | ||
| this.cursorPos = this.rl.getCursorPos(); | ||
| const width = (0, utils_ts_1.readlineWidth)(); | ||
| content = (0, utils_ts_1.breakLines)(content, width); | ||
| bottomContent = (0, utils_ts_1.breakLines)(bottomContent, width); | ||
| // Manually insert an extra line if we're at the end of the line. | ||
| // This prevent the cursor from appearing at the beginning of the | ||
| // current line. | ||
| if (rawPromptLine.length % width === 0) { | ||
| content += '\n'; | ||
| } | ||
| let output = content + (bottomContent ? '\n' + bottomContent : ''); | ||
| /** | ||
| * Re-adjust the cursor at the correct position. | ||
| */ | ||
| // We need to consider parts of the prompt under the cursor as part of the bottom | ||
| // content in order to correctly cleanup and re-render. | ||
| const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - this.cursorPos.rows; | ||
| const bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0); | ||
| // Return cursor to the input position (on top of the bottomContent) | ||
| if (bottomContentHeight > 0) | ||
| output += (0, ansi_1.cursorUp)(bottomContentHeight); | ||
| // Return cursor to the initial left offset. | ||
| output += (0, ansi_1.cursorTo)(this.cursorPos.cols); | ||
| /** | ||
| * Render and store state for future re-rendering | ||
| */ | ||
| this.write((0, ansi_1.cursorDown)(this.extraLinesUnderPrompt) + (0, ansi_1.eraseLines)(this.height) + output); | ||
| this.extraLinesUnderPrompt = bottomContentHeight; | ||
| this.height = height(output); | ||
| } | ||
| checkCursorPos() { | ||
| const cursorPos = this.rl.getCursorPos(); | ||
| if (cursorPos.cols !== this.cursorPos.cols) { | ||
| this.write((0, ansi_1.cursorTo)(cursorPos.cols)); | ||
| this.cursorPos = cursorPos; | ||
| } | ||
| } | ||
| done({ clearContent }) { | ||
| this.rl.setPrompt(''); | ||
| let output = (0, ansi_1.cursorDown)(this.extraLinesUnderPrompt); | ||
| output += clearContent ? (0, ansi_1.eraseLines)(this.height) : '\n'; | ||
| output += ansi_1.cursorShow; | ||
| this.write(output); | ||
| this.rl.close(); | ||
| } | ||
| } | ||
| exports.default = ScreenManager; |
| /** | ||
| * Separator object | ||
| * Used to space/separate choices group | ||
| */ | ||
| export declare class Separator { | ||
| readonly separator: string; | ||
| readonly type: string; | ||
| constructor(separator?: string); | ||
| static isSeparator(choice: unknown): choice is Separator; | ||
| } |
| "use strict"; | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Separator = void 0; | ||
| const yoctocolors_cjs_1 = __importDefault(require("yoctocolors-cjs")); | ||
| const figures_1 = __importDefault(require("@inquirer/figures")); | ||
| /** | ||
| * Separator object | ||
| * Used to space/separate choices group | ||
| */ | ||
| class Separator { | ||
| separator = yoctocolors_cjs_1.default.dim(Array.from({ length: 15 }).join(figures_1.default.line)); | ||
| type = 'separator'; | ||
| constructor(separator) { | ||
| if (separator) { | ||
| this.separator = separator; | ||
| } | ||
| } | ||
| static isSeparator(choice) { | ||
| return Boolean(choice && | ||
| typeof choice === 'object' && | ||
| 'type' in choice && | ||
| choice.type === 'separator'); | ||
| } | ||
| } | ||
| exports.Separator = Separator; |
| import type { Prettify } from '@inquirer/type'; | ||
| /** | ||
| * Union type representing the possible statuses of a prompt. | ||
| * | ||
| * - `'loading'`: The prompt is currently loading. | ||
| * - `'idle'`: The prompt is loaded and currently waiting for the user to | ||
| * submit an answer. | ||
| * - `'done'`: The user has submitted an answer and the prompt is finished. | ||
| * - `string`: Any other string: The prompt is in a custom state. | ||
| */ | ||
| export type Status = 'loading' | 'idle' | 'done' | (string & {}); | ||
| type DefaultTheme = { | ||
| /** | ||
| * Prefix to prepend to the message. If a function is provided, it will be | ||
| * called with the current status of the prompt, and the return value will be | ||
| * used as the prefix. | ||
| * | ||
| * @remarks | ||
| * If `status === 'loading'`, this property is ignored and the spinner (styled | ||
| * by the `spinner` property) will be displayed instead. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (status) => status === 'done' ? colors.green('✔') : colors.blue('?') | ||
| * ``` | ||
| */ | ||
| prefix: string | Prettify<Omit<Record<Status, string>, 'loading'>>; | ||
| /** | ||
| * Configuration for the spinner that is displayed when the prompt is in the | ||
| * `'loading'` state. | ||
| * | ||
| * We recommend the use of {@link https://github.com/sindresorhus/cli-spinners|cli-spinners} for a list of available spinners. | ||
| */ | ||
| spinner: { | ||
| /** | ||
| * The time interval between frames, in milliseconds. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * 80 | ||
| * ``` | ||
| */ | ||
| interval: number; | ||
| /** | ||
| * A list of frames to show for the spinner. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] | ||
| * ``` | ||
| */ | ||
| frames: string[]; | ||
| }; | ||
| /** | ||
| * Object containing functions to style different parts of the prompt. | ||
| */ | ||
| style: { | ||
| /** | ||
| * Style to apply to the user's answer once it has been submitted. | ||
| * | ||
| * @param text - The user's answer. | ||
| * @returns The styled answer. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.cyan(text) | ||
| * ``` | ||
| */ | ||
| answer: (text: string) => string; | ||
| /** | ||
| * Style to apply to the message displayed to the user. | ||
| * | ||
| * @param text - The message to style. | ||
| * @param status - The current status of the prompt. | ||
| * @returns The styled message. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text, status) => colors.bold(text) | ||
| * ``` | ||
| */ | ||
| message: (text: string, status: Status) => string; | ||
| /** | ||
| * Style to apply to error messages. | ||
| * | ||
| * @param text - The error message. | ||
| * @returns The styled error message. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.red(`> ${text}`) | ||
| * ``` | ||
| */ | ||
| error: (text: string) => string; | ||
| /** | ||
| * Style to apply to the default answer when one is provided. | ||
| * | ||
| * @param text - The default answer. | ||
| * @returns The styled default answer. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.dim(`(${text})`) | ||
| * ``` | ||
| */ | ||
| defaultAnswer: (text: string) => string; | ||
| /** | ||
| * Style to apply to help text. | ||
| * | ||
| * @param text - The help text. | ||
| * @returns The styled help text. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.dim(text) | ||
| * ``` | ||
| */ | ||
| help: (text: string) => string; | ||
| /** | ||
| * Style to apply to highlighted text. | ||
| * | ||
| * @param text - The text to highlight. | ||
| * @returns The highlighted text. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.cyan(text) | ||
| * ``` | ||
| */ | ||
| highlight: (text: string) => string; | ||
| /** | ||
| * Style to apply to keyboard keys referred to in help texts. | ||
| * | ||
| * @param text - The key to style. | ||
| * @returns The styled key. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.cyan(colors.bold(`<${text}>`)) | ||
| * ``` | ||
| */ | ||
| key: (text: string) => string; | ||
| }; | ||
| }; | ||
| export type Theme<Extension extends object = object> = Prettify<Extension & DefaultTheme>; | ||
| export declare const defaultTheme: DefaultTheme; | ||
| export {}; |
| "use strict"; | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.defaultTheme = void 0; | ||
| const yoctocolors_cjs_1 = __importDefault(require("yoctocolors-cjs")); | ||
| const figures_1 = __importDefault(require("@inquirer/figures")); | ||
| exports.defaultTheme = { | ||
| prefix: { | ||
| idle: yoctocolors_cjs_1.default.blue('?'), | ||
| done: yoctocolors_cjs_1.default.green(figures_1.default.tick), | ||
| }, | ||
| spinner: { | ||
| interval: 80, | ||
| frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'].map((frame) => yoctocolors_cjs_1.default.yellow(frame)), | ||
| }, | ||
| style: { | ||
| answer: yoctocolors_cjs_1.default.cyan, | ||
| message: yoctocolors_cjs_1.default.bold, | ||
| error: (text) => yoctocolors_cjs_1.default.red(`> ${text}`), | ||
| defaultAnswer: (text) => yoctocolors_cjs_1.default.dim(`(${text})`), | ||
| help: yoctocolors_cjs_1.default.dim, | ||
| highlight: yoctocolors_cjs_1.default.cyan, | ||
| key: (text) => yoctocolors_cjs_1.default.cyan(yoctocolors_cjs_1.default.bold(`<${text}>`)), | ||
| }, | ||
| }; |
| import type { InquirerReadline } from '@inquirer/type'; | ||
| export declare function useEffect(cb: (rl: InquirerReadline) => void | (() => void), depArray: ReadonlyArray<unknown>): void; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.useEffect = useEffect; | ||
| const hook_engine_ts_1 = require("./hook-engine.js"); | ||
| function useEffect(cb, depArray) { | ||
| (0, hook_engine_ts_1.withPointer)((pointer) => { | ||
| const oldDeps = pointer.get(); | ||
| const hasChanged = !Array.isArray(oldDeps) || depArray.some((dep, i) => !Object.is(dep, oldDeps[i])); | ||
| if (hasChanged) { | ||
| hook_engine_ts_1.effectScheduler.queue(cb); | ||
| } | ||
| pointer.set(depArray); | ||
| }); | ||
| } |
| import { type InquirerReadline } from '@inquirer/type'; | ||
| import { type KeypressEvent } from './key.ts'; | ||
| export declare function useKeypress(userHandler: (event: KeypressEvent, rl: InquirerReadline) => void | Promise<void>): void; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.useKeypress = useKeypress; | ||
| const use_ref_ts_1 = require("./use-ref.js"); | ||
| const use_effect_ts_1 = require("./use-effect.js"); | ||
| const hook_engine_ts_1 = require("./hook-engine.js"); | ||
| function useKeypress(userHandler) { | ||
| const signal = (0, use_ref_ts_1.useRef)(userHandler); | ||
| signal.current = userHandler; | ||
| (0, use_effect_ts_1.useEffect)((rl) => { | ||
| let ignore = false; | ||
| const handler = (0, hook_engine_ts_1.withUpdates)((_input, event) => { | ||
| if (ignore) | ||
| return; | ||
| void signal.current(event, rl); | ||
| }); | ||
| rl.input.on('keypress', handler); | ||
| return () => { | ||
| ignore = true; | ||
| rl.input.removeListener('keypress', handler); | ||
| }; | ||
| }, []); | ||
| } |
| export declare function useMemo<Value>(fn: () => Value, dependencies: ReadonlyArray<unknown>): Value; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.useMemo = useMemo; | ||
| const hook_engine_ts_1 = require("./hook-engine.js"); | ||
| function useMemo(fn, dependencies) { | ||
| return (0, hook_engine_ts_1.withPointer)((pointer) => { | ||
| const prev = pointer.get(); | ||
| if (!prev || | ||
| prev.dependencies.length !== dependencies.length || | ||
| prev.dependencies.some((dep, i) => dep !== dependencies[i])) { | ||
| const value = fn(); | ||
| pointer.set({ value, dependencies }); | ||
| return value; | ||
| } | ||
| return prev.value; | ||
| }); | ||
| } |
| import type { Theme, Status } from './theme.ts'; | ||
| export declare function usePrefix({ status, theme, }: { | ||
| status?: Status; | ||
| theme?: Theme; | ||
| }): string; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.usePrefix = usePrefix; | ||
| const use_state_ts_1 = require("./use-state.js"); | ||
| const use_effect_ts_1 = require("./use-effect.js"); | ||
| const make_theme_ts_1 = require("./make-theme.js"); | ||
| function usePrefix({ status = 'idle', theme, }) { | ||
| const [showLoader, setShowLoader] = (0, use_state_ts_1.useState)(false); | ||
| const [tick, setTick] = (0, use_state_ts_1.useState)(0); | ||
| const { prefix, spinner } = (0, make_theme_ts_1.makeTheme)(theme); | ||
| (0, use_effect_ts_1.useEffect)(() => { | ||
| if (status === 'loading') { | ||
| let tickInterval; | ||
| let inc = -1; | ||
| // Delay displaying spinner by 300ms, to avoid flickering | ||
| const delayTimeout = setTimeout(() => { | ||
| setShowLoader(true); | ||
| tickInterval = setInterval(() => { | ||
| inc = inc + 1; | ||
| setTick(inc % spinner.frames.length); | ||
| }, spinner.interval); | ||
| }, 300); | ||
| return () => { | ||
| clearTimeout(delayTimeout); | ||
| clearInterval(tickInterval); | ||
| }; | ||
| } | ||
| else { | ||
| setShowLoader(false); | ||
| } | ||
| }, [status]); | ||
| if (showLoader) { | ||
| return spinner.frames[tick]; | ||
| } | ||
| // There's a delay before we show the loader. So we want to ignore `loading` here, and pass idle instead. | ||
| const iconName = status === 'loading' ? 'idle' : status; | ||
| return typeof prefix === 'string' ? prefix : (prefix[iconName] ?? prefix['idle']); | ||
| } |
| export declare function useRef<Value>(val: Value): { | ||
| current: Value; | ||
| }; | ||
| export declare function useRef<Value>(val?: Value): { | ||
| current: Value | undefined; | ||
| }; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.useRef = useRef; | ||
| const use_state_ts_1 = require("./use-state.js"); | ||
| function useRef(val) { | ||
| return (0, use_state_ts_1.useState)({ current: val })[0]; | ||
| } |
| type NotFunction<T> = T extends (...args: never) => unknown ? never : T; | ||
| export declare function useState<Value>(defaultValue: NotFunction<Value> | (() => Value)): [Value, (newValue: Value) => void]; | ||
| export declare function useState<Value>(defaultValue?: NotFunction<Value> | (() => Value)): [Value | undefined, (newValue?: Value) => void]; | ||
| export {}; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.useState = useState; | ||
| const node_async_hooks_1 = require("node:async_hooks"); | ||
| const hook_engine_ts_1 = require("./hook-engine.js"); | ||
| function useState(defaultValue) { | ||
| return (0, hook_engine_ts_1.withPointer)((pointer) => { | ||
| const setState = node_async_hooks_1.AsyncResource.bind(function setState(newValue) { | ||
| // Noop if the value is still the same. | ||
| if (pointer.get() !== newValue) { | ||
| pointer.set(newValue); | ||
| // Trigger re-render | ||
| (0, hook_engine_ts_1.handleChange)(); | ||
| } | ||
| }); | ||
| if (pointer.initialized) { | ||
| return [pointer.get(), setState]; | ||
| } | ||
| const value = typeof defaultValue === 'function' ? defaultValue() : defaultValue; | ||
| pointer.set(value); | ||
| return [value, setState]; | ||
| }); | ||
| } |
| /** | ||
| * Force line returns at specific width. This function is ANSI code friendly and it'll | ||
| * ignore invisible codes during width calculation. | ||
| * @param {string} content | ||
| * @param {number} width | ||
| * @return {string} | ||
| */ | ||
| export declare function breakLines(content: string, width: number): string; | ||
| /** | ||
| * Returns the width of the active readline, or 80 as default value. | ||
| * @returns {number} | ||
| */ | ||
| export declare function readlineWidth(): number; |
| "use strict"; | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.breakLines = breakLines; | ||
| exports.readlineWidth = readlineWidth; | ||
| const cli_width_1 = __importDefault(require("cli-width")); | ||
| const wrap_ansi_1 = __importDefault(require("wrap-ansi")); | ||
| const hook_engine_ts_1 = require("./hook-engine.js"); | ||
| /** | ||
| * Force line returns at specific width. This function is ANSI code friendly and it'll | ||
| * ignore invisible codes during width calculation. | ||
| * @param {string} content | ||
| * @param {number} width | ||
| * @return {string} | ||
| */ | ||
| function breakLines(content, width) { | ||
| return content | ||
| .split('\n') | ||
| .flatMap((line) => (0, wrap_ansi_1.default)(line, width, { trim: false, hard: true }) | ||
| .split('\n') | ||
| .map((str) => str.trimEnd())) | ||
| .join('\n'); | ||
| } | ||
| /** | ||
| * Returns the width of the active readline, or 80 as default value. | ||
| * @returns {number} | ||
| */ | ||
| function readlineWidth() { | ||
| return (0, cli_width_1.default)({ defaultWidth: 80, output: (0, hook_engine_ts_1.readline)().output }); | ||
| } |
| { | ||
| "type": "commonjs" | ||
| } |
| export { isUpKey, isDownKey, isSpaceKey, isBackspaceKey, isTabKey, isNumberKey, isEnterKey, type KeypressEvent, type Keybinding, } from './lib/key.ts'; | ||
| export * from './lib/errors.ts'; | ||
| export { usePrefix } from './lib/use-prefix.ts'; | ||
| export { useState } from './lib/use-state.ts'; | ||
| export { useEffect } from './lib/use-effect.ts'; | ||
| export { useMemo } from './lib/use-memo.ts'; | ||
| export { useRef } from './lib/use-ref.ts'; | ||
| export { useKeypress } from './lib/use-keypress.ts'; | ||
| export { makeTheme } from './lib/make-theme.ts'; | ||
| export type { Theme, Status } from './lib/theme.ts'; | ||
| export { usePagination } from './lib/pagination/use-pagination.ts'; | ||
| export { createPrompt } from './lib/create-prompt.ts'; | ||
| export { Separator } from './lib/Separator.ts'; |
| export { isUpKey, isDownKey, isSpaceKey, isBackspaceKey, isTabKey, isNumberKey, isEnterKey, } from "./lib/key.js"; | ||
| export * from "./lib/errors.js"; | ||
| export { usePrefix } from "./lib/use-prefix.js"; | ||
| export { useState } from "./lib/use-state.js"; | ||
| export { useEffect } from "./lib/use-effect.js"; | ||
| export { useMemo } from "./lib/use-memo.js"; | ||
| export { useRef } from "./lib/use-ref.js"; | ||
| export { useKeypress } from "./lib/use-keypress.js"; | ||
| export { makeTheme } from "./lib/make-theme.js"; | ||
| export { usePagination } from "./lib/pagination/use-pagination.js"; | ||
| export { createPrompt } from "./lib/create-prompt.js"; | ||
| export { Separator } from "./lib/Separator.js"; |
| import { type Prompt, type Prettify } from '@inquirer/type'; | ||
| type ViewFunction<Value, Config> = (config: Prettify<Config>, done: (value: Value) => void) => string | [string, string | undefined]; | ||
| export declare function createPrompt<Value, Config>(view: ViewFunction<Value, Config>): Prompt<Value, Config>; | ||
| export {}; |
| import * as readline from 'node:readline'; | ||
| import { AsyncResource } from 'node:async_hooks'; | ||
| import MuteStream from 'mute-stream'; | ||
| import { onExit as onSignalExit } from 'signal-exit'; | ||
| import ScreenManager from "./screen-manager.js"; | ||
| import { PromisePolyfill } from "./promise-polyfill.js"; | ||
| import { withHooks, effectScheduler } from "./hook-engine.js"; | ||
| import { AbortPromptError, CancelPromptError, ExitPromptError } from "./errors.js"; | ||
| function getCallSites() { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| const _prepareStackTrace = Error.prepareStackTrace; | ||
| let result = []; | ||
| try { | ||
| Error.prepareStackTrace = (_, callSites) => { | ||
| const callSitesWithoutCurrent = callSites.slice(1); | ||
| result = callSitesWithoutCurrent; | ||
| return callSitesWithoutCurrent; | ||
| }; | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-expressions | ||
| new Error().stack; | ||
| } | ||
| catch { | ||
| // An error will occur if the Node flag --frozen-intrinsics is used. | ||
| // https://nodejs.org/api/cli.html#--frozen-intrinsics | ||
| return result; | ||
| } | ||
| Error.prepareStackTrace = _prepareStackTrace; | ||
| return result; | ||
| } | ||
| export function createPrompt(view) { | ||
| const callSites = getCallSites(); | ||
| const prompt = (config, context = {}) => { | ||
| // Default `input` to stdin | ||
| const { input = process.stdin, signal } = context; | ||
| const cleanups = new Set(); | ||
| // Add mute capabilities to the output | ||
| const output = new MuteStream(); | ||
| output.pipe(context.output ?? process.stdout); | ||
| const rl = readline.createInterface({ | ||
| terminal: true, | ||
| input, | ||
| output, | ||
| }); | ||
| const screen = new ScreenManager(rl); | ||
| const { promise, resolve, reject } = PromisePolyfill.withResolver(); | ||
| const cancel = () => reject(new CancelPromptError()); | ||
| if (signal) { | ||
| const abort = () => reject(new AbortPromptError({ cause: signal.reason })); | ||
| if (signal.aborted) { | ||
| abort(); | ||
| return Object.assign(promise, { cancel }); | ||
| } | ||
| signal.addEventListener('abort', abort); | ||
| cleanups.add(() => signal.removeEventListener('abort', abort)); | ||
| } | ||
| cleanups.add(onSignalExit((code, signal) => { | ||
| reject(new ExitPromptError(`User force closed the prompt with ${code} ${signal}`)); | ||
| })); | ||
| // SIGINT must be explicitly handled by the prompt so the ExitPromptError can be handled. | ||
| // Otherwise, the prompt will stop and in some scenarios never resolve. | ||
| // Ref issue #1741 | ||
| const sigint = () => reject(new ExitPromptError(`User force closed the prompt with SIGINT`)); | ||
| rl.on('SIGINT', sigint); | ||
| cleanups.add(() => rl.removeListener('SIGINT', sigint)); | ||
| // Re-renders only happen when the state change; but the readline cursor could change position | ||
| // and that also requires a re-render (and a manual one because we mute the streams). | ||
| // We set the listener after the initial workLoop to avoid a double render if render triggered | ||
| // by a state change sets the cursor to the right position. | ||
| const checkCursorPos = () => screen.checkCursorPos(); | ||
| rl.input.on('keypress', checkCursorPos); | ||
| cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos)); | ||
| return withHooks(rl, (cycle) => { | ||
| // The close event triggers immediately when the user press ctrl+c. SignalExit on the other hand | ||
| // triggers after the process is done (which happens after timeouts are done triggering.) | ||
| // We triggers the hooks cleanup phase on rl `close` so active timeouts can be cleared. | ||
| const hooksCleanup = AsyncResource.bind(() => effectScheduler.clearAll()); | ||
| rl.on('close', hooksCleanup); | ||
| cleanups.add(() => rl.removeListener('close', hooksCleanup)); | ||
| cycle(() => { | ||
| try { | ||
| const nextView = view(config, (value) => { | ||
| setImmediate(() => resolve(value)); | ||
| }); | ||
| // Typescript won't allow this, but not all users rely on typescript. | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| if (nextView === undefined) { | ||
| const callerFilename = callSites[1]?.getFileName(); | ||
| throw new Error(`Prompt functions must return a string.\n at ${callerFilename}`); | ||
| } | ||
| const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView; | ||
| screen.render(content, bottomContent); | ||
| effectScheduler.run(); | ||
| } | ||
| catch (error) { | ||
| reject(error); | ||
| } | ||
| }); | ||
| return Object.assign(promise | ||
| .then((answer) => { | ||
| effectScheduler.clearAll(); | ||
| return answer; | ||
| }, (error) => { | ||
| effectScheduler.clearAll(); | ||
| throw error; | ||
| }) | ||
| // Wait for the promise to settle, then cleanup. | ||
| .finally(() => { | ||
| cleanups.forEach((cleanup) => cleanup()); | ||
| screen.done({ clearContent: Boolean(context.clearPromptOnDone) }); | ||
| output.end(); | ||
| }) | ||
| // Once cleanup is done, let the expose promise resolve/reject to the internal one. | ||
| .then(() => promise), { cancel }); | ||
| }); | ||
| }; | ||
| return prompt; | ||
| } |
| export declare class AbortPromptError extends Error { | ||
| name: string; | ||
| message: string; | ||
| constructor(options?: { | ||
| cause?: unknown; | ||
| }); | ||
| } | ||
| export declare class CancelPromptError extends Error { | ||
| name: string; | ||
| message: string; | ||
| } | ||
| export declare class ExitPromptError extends Error { | ||
| name: string; | ||
| } | ||
| export declare class HookError extends Error { | ||
| name: string; | ||
| } | ||
| export declare class ValidationError extends Error { | ||
| name: string; | ||
| } |
| export class AbortPromptError extends Error { | ||
| name = 'AbortPromptError'; | ||
| message = 'Prompt was aborted'; | ||
| constructor(options) { | ||
| super(); | ||
| this.cause = options?.cause; | ||
| } | ||
| } | ||
| export class CancelPromptError extends Error { | ||
| name = 'CancelPromptError'; | ||
| message = 'Prompt was canceled'; | ||
| } | ||
| export class ExitPromptError extends Error { | ||
| name = 'ExitPromptError'; | ||
| } | ||
| export class HookError extends Error { | ||
| name = 'HookError'; | ||
| } | ||
| export class ValidationError extends Error { | ||
| name = 'ValidationError'; | ||
| } |
| import type { InquirerReadline } from '@inquirer/type'; | ||
| export declare function withHooks<T>(rl: InquirerReadline, cb: (cycle: (render: () => void) => void) => T): T; | ||
| export declare function readline(): InquirerReadline; | ||
| export declare function withUpdates<Args extends unknown[], R>(fn: (...args: Args) => R): (...args: Args) => R; | ||
| type SetPointer<Value> = { | ||
| get(): Value; | ||
| set(value: Value): void; | ||
| initialized: true; | ||
| }; | ||
| type UnsetPointer<Value> = { | ||
| get(): void; | ||
| set(value: Value): void; | ||
| initialized: false; | ||
| }; | ||
| type Pointer<Value> = SetPointer<Value> | UnsetPointer<Value>; | ||
| export declare function withPointer<Value, ReturnValue>(cb: (pointer: Pointer<Value>) => ReturnValue): ReturnValue; | ||
| export declare function handleChange(): void; | ||
| export declare const effectScheduler: { | ||
| queue(cb: (readline: InquirerReadline) => void | (() => void)): void; | ||
| run(): void; | ||
| clearAll(): void; | ||
| }; | ||
| export {}; |
| /* eslint @typescript-eslint/no-explicit-any: ["off"] */ | ||
| import { AsyncLocalStorage, AsyncResource } from 'node:async_hooks'; | ||
| import { HookError, ValidationError } from "./errors.js"; | ||
| const hookStorage = new AsyncLocalStorage(); | ||
| function createStore(rl) { | ||
| const store = { | ||
| rl, | ||
| hooks: [], | ||
| hooksCleanup: [], | ||
| hooksEffect: [], | ||
| index: 0, | ||
| handleChange() { }, | ||
| }; | ||
| return store; | ||
| } | ||
| // Run callback in with the hook engine setup. | ||
| export function withHooks(rl, cb) { | ||
| const store = createStore(rl); | ||
| return hookStorage.run(store, () => { | ||
| function cycle(render) { | ||
| store.handleChange = () => { | ||
| store.index = 0; | ||
| render(); | ||
| }; | ||
| store.handleChange(); | ||
| } | ||
| return cb(cycle); | ||
| }); | ||
| } | ||
| // Safe getStore utility that'll return the store or throw if undefined. | ||
| function getStore() { | ||
| const store = hookStorage.getStore(); | ||
| if (!store) { | ||
| throw new HookError('[Inquirer] Hook functions can only be called from within a prompt'); | ||
| } | ||
| return store; | ||
| } | ||
| export function readline() { | ||
| return getStore().rl; | ||
| } | ||
| // Merge state updates happening within the callback function to avoid multiple renders. | ||
| export function withUpdates(fn) { | ||
| const wrapped = (...args) => { | ||
| const store = getStore(); | ||
| let shouldUpdate = false; | ||
| const oldHandleChange = store.handleChange; | ||
| store.handleChange = () => { | ||
| shouldUpdate = true; | ||
| }; | ||
| const returnValue = fn(...args); | ||
| if (shouldUpdate) { | ||
| oldHandleChange(); | ||
| } | ||
| store.handleChange = oldHandleChange; | ||
| return returnValue; | ||
| }; | ||
| return AsyncResource.bind(wrapped); | ||
| } | ||
| export function withPointer(cb) { | ||
| const store = getStore(); | ||
| const { index } = store; | ||
| const pointer = { | ||
| get() { | ||
| return store.hooks[index]; | ||
| }, | ||
| set(value) { | ||
| store.hooks[index] = value; | ||
| }, | ||
| initialized: index in store.hooks, | ||
| }; | ||
| const returnValue = cb(pointer); | ||
| store.index++; | ||
| return returnValue; | ||
| } | ||
| export function handleChange() { | ||
| getStore().handleChange(); | ||
| } | ||
| export const effectScheduler = { | ||
| queue(cb) { | ||
| const store = getStore(); | ||
| const { index } = store; | ||
| store.hooksEffect.push(() => { | ||
| store.hooksCleanup[index]?.(); | ||
| const cleanFn = cb(readline()); | ||
| if (cleanFn != null && typeof cleanFn !== 'function') { | ||
| throw new ValidationError('useEffect return value must be a cleanup function or nothing.'); | ||
| } | ||
| store.hooksCleanup[index] = cleanFn; | ||
| }); | ||
| }, | ||
| run() { | ||
| const store = getStore(); | ||
| withUpdates(() => { | ||
| store.hooksEffect.forEach((effect) => { | ||
| effect(); | ||
| }); | ||
| // Warning: Clean the hooks before exiting the `withUpdates` block. | ||
| // Failure to do so means an updates would hit the same effects again. | ||
| store.hooksEffect.length = 0; | ||
| })(); | ||
| }, | ||
| clearAll() { | ||
| const store = getStore(); | ||
| store.hooksCleanup.forEach((cleanFn) => { | ||
| cleanFn?.(); | ||
| }); | ||
| store.hooksEffect.length = 0; | ||
| store.hooksCleanup.length = 0; | ||
| }, | ||
| }; |
| export type KeypressEvent = { | ||
| name: string; | ||
| ctrl: boolean; | ||
| }; | ||
| export type Keybinding = 'emacs' | 'vim'; | ||
| export declare const isUpKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean; | ||
| export declare const isDownKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean; | ||
| export declare const isSpaceKey: (key: KeypressEvent) => boolean; | ||
| export declare const isBackspaceKey: (key: KeypressEvent) => boolean; | ||
| export declare const isTabKey: (key: KeypressEvent) => boolean; | ||
| export declare const isNumberKey: (key: KeypressEvent) => boolean; | ||
| export declare const isEnterKey: (key: KeypressEvent) => boolean; |
| export const isUpKey = (key, keybindings = []) => | ||
| // The up key | ||
| key.name === 'up' || | ||
| // Vim keybinding: hjkl keys map to left/down/up/right | ||
| (keybindings.includes('vim') && key.name === 'k') || | ||
| // Emacs keybinding: Ctrl+P means "previous" in Emacs navigation conventions | ||
| (keybindings.includes('emacs') && key.ctrl && key.name === 'p'); | ||
| export const isDownKey = (key, keybindings = []) => | ||
| // The down key | ||
| key.name === 'down' || | ||
| // Vim keybinding: hjkl keys map to left/down/up/right | ||
| (keybindings.includes('vim') && key.name === 'j') || | ||
| // Emacs keybinding: Ctrl+N means "next" in Emacs navigation conventions | ||
| (keybindings.includes('emacs') && key.ctrl && key.name === 'n'); | ||
| export const isSpaceKey = (key) => key.name === 'space'; | ||
| export const isBackspaceKey = (key) => key.name === 'backspace'; | ||
| export const isTabKey = (key) => key.name === 'tab'; | ||
| export const isNumberKey = (key) => '1234567890'.includes(key.name); | ||
| export const isEnterKey = (key) => key.name === 'enter' || key.name === 'return'; |
| import type { Prettify, PartialDeep } from '@inquirer/type'; | ||
| import { type Theme } from './theme.ts'; | ||
| export declare function makeTheme<SpecificTheme extends object>(...themes: ReadonlyArray<undefined | PartialDeep<Theme<SpecificTheme>>>): Prettify<Theme<SpecificTheme>>; |
| import { defaultTheme } from "./theme.js"; | ||
| function isPlainObject(value) { | ||
| if (typeof value !== 'object' || value === null) | ||
| return false; | ||
| let proto = value; | ||
| while (Object.getPrototypeOf(proto) !== null) { | ||
| proto = Object.getPrototypeOf(proto); | ||
| } | ||
| return Object.getPrototypeOf(value) === proto; | ||
| } | ||
| function deepMerge(...objects) { | ||
| const output = {}; | ||
| for (const obj of objects) { | ||
| for (const [key, value] of Object.entries(obj)) { | ||
| const prevValue = output[key]; | ||
| output[key] = | ||
| isPlainObject(prevValue) && isPlainObject(value) | ||
| ? deepMerge(prevValue, value) | ||
| : value; | ||
| } | ||
| } | ||
| return output; | ||
| } | ||
| export function makeTheme(...themes) { | ||
| const themesToMerge = [ | ||
| defaultTheme, | ||
| ...themes.filter((theme) => theme != null), | ||
| ]; | ||
| return deepMerge(...themesToMerge); | ||
| } |
| import type { Prettify } from '@inquirer/type'; | ||
| export declare function usePagination<T>({ items, active, renderItem, pageSize, loop, }: { | ||
| items: ReadonlyArray<T>; | ||
| /** The index of the active item. */ | ||
| active: number; | ||
| /** Renders an item as part of a page. */ | ||
| renderItem: (layout: Prettify<{ | ||
| item: T; | ||
| index: number; | ||
| isActive: boolean; | ||
| }>) => string; | ||
| /** The size of the page. */ | ||
| pageSize: number; | ||
| /** Allows creating an infinitely looping list. `true` if unspecified. */ | ||
| loop?: boolean; | ||
| }): string; |
| import { useRef } from "../use-ref.js"; | ||
| import { readlineWidth, breakLines } from "../utils.js"; | ||
| function usePointerPosition({ active, renderedItems, pageSize, loop, }) { | ||
| const state = useRef({ | ||
| lastPointer: active, | ||
| lastActive: undefined, | ||
| }); | ||
| const { lastPointer, lastActive } = state.current; | ||
| const middle = Math.floor(pageSize / 2); | ||
| const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0); | ||
| const defaultPointerPosition = renderedItems | ||
| .slice(0, active) | ||
| .reduce((acc, item) => acc + item.length, 0); | ||
| let pointer = defaultPointerPosition; | ||
| if (renderedLength > pageSize) { | ||
| if (loop) { | ||
| /** | ||
| * Creates the next position for the pointer considering an infinitely | ||
| * looping list of items to be rendered on the page. | ||
| * | ||
| * The goal is to progressively move the cursor to the middle position as the user move down, and then keep | ||
| * the cursor there. When the user move up, maintain the cursor position. | ||
| */ | ||
| // By default, keep the cursor position as-is. | ||
| pointer = lastPointer; | ||
| if ( | ||
| // First render, skip this logic. | ||
| lastActive != null && | ||
| // Only move the pointer down when the user moves down. | ||
| lastActive < active && | ||
| // Check user didn't move up across page boundary. | ||
| active - lastActive < pageSize) { | ||
| pointer = Math.min( | ||
| // Furthest allowed position for the pointer is the middle of the list | ||
| middle, Math.abs(active - lastActive) === 1 | ||
| ? Math.min( | ||
| // Move the pointer at most the height of the last active item. | ||
| lastPointer + (renderedItems[lastActive]?.length ?? 0), | ||
| // If the user moved by one item, move the pointer to the natural position of the active item as | ||
| // long as it doesn't move the cursor up. | ||
| Math.max(defaultPointerPosition, lastPointer)) | ||
| : // Otherwise, move the pointer down by the difference between the active and last active item. | ||
| lastPointer + active - lastActive); | ||
| } | ||
| } | ||
| else { | ||
| /** | ||
| * Creates the next position for the pointer considering a finite list of | ||
| * items to be rendered on a page. | ||
| * | ||
| * The goal is to keep the pointer in the middle of the page whenever possible, until | ||
| * we reach the bounds of the list (top or bottom). In which case, the cursor moves progressively | ||
| * to the bottom or top of the list. | ||
| */ | ||
| const spaceUnderActive = renderedItems | ||
| .slice(active) | ||
| .reduce((acc, item) => acc + item.length, 0); | ||
| pointer = | ||
| spaceUnderActive < pageSize - middle | ||
| ? // If the active item is near the end of the list, progressively move the cursor towards the end. | ||
| pageSize - spaceUnderActive | ||
| : // Otherwise, progressively move the pointer to the middle of the list. | ||
| Math.min(defaultPointerPosition, middle); | ||
| } | ||
| } | ||
| // Save state for the next render | ||
| state.current.lastPointer = pointer; | ||
| state.current.lastActive = active; | ||
| return pointer; | ||
| } | ||
| export function usePagination({ items, active, renderItem, pageSize, loop = true, }) { | ||
| const width = readlineWidth(); | ||
| const bound = (num) => ((num % items.length) + items.length) % items.length; | ||
| const renderedItems = items.map((item, index) => { | ||
| if (item == null) | ||
| return []; | ||
| return breakLines(renderItem({ item, index, isActive: index === active }), width).split('\n'); | ||
| }); | ||
| const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0); | ||
| const renderItemAtIndex = (index) => renderedItems[index] ?? []; | ||
| const pointer = usePointerPosition({ active, renderedItems, pageSize, loop }); | ||
| // Render the active item to decide the position. | ||
| // If the active item fits under the pointer, we render it there. | ||
| // Otherwise, we need to render it to fit at the bottom of the page; moving the pointer up. | ||
| const activeItem = renderItemAtIndex(active).slice(0, pageSize); | ||
| const activeItemPosition = pointer + activeItem.length <= pageSize ? pointer : pageSize - activeItem.length; | ||
| // Create an array of lines for the page, and add the lines of the active item into the page | ||
| const pageBuffer = Array.from({ length: pageSize }); | ||
| pageBuffer.splice(activeItemPosition, activeItem.length, ...activeItem); | ||
| // Store to prevent rendering the same item twice | ||
| const itemVisited = new Set([active]); | ||
| // Fill the page under the active item | ||
| let bufferPointer = activeItemPosition + activeItem.length; | ||
| let itemPointer = bound(active + 1); | ||
| while (bufferPointer < pageSize && | ||
| !itemVisited.has(itemPointer) && | ||
| (loop && renderedLength > pageSize ? itemPointer !== active : itemPointer > active)) { | ||
| const lines = renderItemAtIndex(itemPointer); | ||
| const linesToAdd = lines.slice(0, pageSize - bufferPointer); | ||
| pageBuffer.splice(bufferPointer, linesToAdd.length, ...linesToAdd); | ||
| // Move pointers for next iteration | ||
| itemVisited.add(itemPointer); | ||
| bufferPointer += linesToAdd.length; | ||
| itemPointer = bound(itemPointer + 1); | ||
| } | ||
| // Fill the page over the active item | ||
| bufferPointer = activeItemPosition - 1; | ||
| itemPointer = bound(active - 1); | ||
| while (bufferPointer >= 0 && | ||
| !itemVisited.has(itemPointer) && | ||
| (loop && renderedLength > pageSize ? itemPointer !== active : itemPointer < active)) { | ||
| const lines = renderItemAtIndex(itemPointer); | ||
| const linesToAdd = lines.slice(Math.max(0, lines.length - bufferPointer - 1)); | ||
| pageBuffer.splice(bufferPointer - linesToAdd.length + 1, linesToAdd.length, ...linesToAdd); | ||
| // Move pointers for next iteration | ||
| itemVisited.add(itemPointer); | ||
| bufferPointer -= linesToAdd.length; | ||
| itemPointer = bound(itemPointer - 1); | ||
| } | ||
| return pageBuffer.filter((line) => typeof line === 'string').join('\n'); | ||
| } |
| export declare class PromisePolyfill<T> extends Promise<T> { | ||
| static withResolver<T>(): { | ||
| promise: Promise<T>; | ||
| resolve: (value: T) => void; | ||
| reject: (error: unknown) => void; | ||
| }; | ||
| } |
| // TODO: Remove this class once Node 22 becomes the minimum supported version. | ||
| export class PromisePolyfill extends Promise { | ||
| // Available starting from Node 22 | ||
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers | ||
| static withResolver() { | ||
| let resolve; | ||
| let reject; | ||
| const promise = new Promise((res, rej) => { | ||
| resolve = res; | ||
| reject = rej; | ||
| }); | ||
| return { promise, resolve: resolve, reject: reject }; | ||
| } | ||
| } |
| import type { InquirerReadline } from '@inquirer/type'; | ||
| export default class ScreenManager { | ||
| private height; | ||
| private extraLinesUnderPrompt; | ||
| private cursorPos; | ||
| private readonly rl; | ||
| constructor(rl: InquirerReadline); | ||
| write(content: string): void; | ||
| render(content: string, bottomContent?: string): void; | ||
| checkCursorPos(): void; | ||
| done({ clearContent }: { | ||
| clearContent: boolean; | ||
| }): void; | ||
| } |
| import { stripVTControlCharacters } from 'node:util'; | ||
| import { breakLines, readlineWidth } from "./utils.js"; | ||
| import { cursorDown, cursorUp, cursorTo, cursorShow, eraseLines } from '@inquirer/ansi'; | ||
| const height = (content) => content.split('\n').length; | ||
| const lastLine = (content) => content.split('\n').pop() ?? ''; | ||
| export default class ScreenManager { | ||
| // These variables are keeping information to allow correct prompt re-rendering | ||
| height = 0; | ||
| extraLinesUnderPrompt = 0; | ||
| cursorPos; | ||
| rl; | ||
| constructor(rl) { | ||
| this.rl = rl; | ||
| this.cursorPos = rl.getCursorPos(); | ||
| } | ||
| write(content) { | ||
| this.rl.output.unmute(); | ||
| this.rl.output.write(content); | ||
| this.rl.output.mute(); | ||
| } | ||
| render(content, bottomContent = '') { | ||
| // Write message to screen and setPrompt to control backspace | ||
| const promptLine = lastLine(content); | ||
| const rawPromptLine = stripVTControlCharacters(promptLine); | ||
| // Remove the rl.line from our prompt. We can't rely on the content of | ||
| // rl.line (mainly because of the password prompt), so just rely on it's | ||
| // length. | ||
| let prompt = rawPromptLine; | ||
| if (this.rl.line.length > 0) { | ||
| prompt = prompt.slice(0, -this.rl.line.length); | ||
| } | ||
| this.rl.setPrompt(prompt); | ||
| // SetPrompt will change cursor position, now we can get correct value | ||
| this.cursorPos = this.rl.getCursorPos(); | ||
| const width = readlineWidth(); | ||
| content = breakLines(content, width); | ||
| bottomContent = breakLines(bottomContent, width); | ||
| // Manually insert an extra line if we're at the end of the line. | ||
| // This prevent the cursor from appearing at the beginning of the | ||
| // current line. | ||
| if (rawPromptLine.length % width === 0) { | ||
| content += '\n'; | ||
| } | ||
| let output = content + (bottomContent ? '\n' + bottomContent : ''); | ||
| /** | ||
| * Re-adjust the cursor at the correct position. | ||
| */ | ||
| // We need to consider parts of the prompt under the cursor as part of the bottom | ||
| // content in order to correctly cleanup and re-render. | ||
| const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - this.cursorPos.rows; | ||
| const bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0); | ||
| // Return cursor to the input position (on top of the bottomContent) | ||
| if (bottomContentHeight > 0) | ||
| output += cursorUp(bottomContentHeight); | ||
| // Return cursor to the initial left offset. | ||
| output += cursorTo(this.cursorPos.cols); | ||
| /** | ||
| * Render and store state for future re-rendering | ||
| */ | ||
| this.write(cursorDown(this.extraLinesUnderPrompt) + eraseLines(this.height) + output); | ||
| this.extraLinesUnderPrompt = bottomContentHeight; | ||
| this.height = height(output); | ||
| } | ||
| checkCursorPos() { | ||
| const cursorPos = this.rl.getCursorPos(); | ||
| if (cursorPos.cols !== this.cursorPos.cols) { | ||
| this.write(cursorTo(cursorPos.cols)); | ||
| this.cursorPos = cursorPos; | ||
| } | ||
| } | ||
| done({ clearContent }) { | ||
| this.rl.setPrompt(''); | ||
| let output = cursorDown(this.extraLinesUnderPrompt); | ||
| output += clearContent ? eraseLines(this.height) : '\n'; | ||
| output += cursorShow; | ||
| this.write(output); | ||
| this.rl.close(); | ||
| } | ||
| } |
| /** | ||
| * Separator object | ||
| * Used to space/separate choices group | ||
| */ | ||
| export declare class Separator { | ||
| readonly separator: string; | ||
| readonly type: string; | ||
| constructor(separator?: string); | ||
| static isSeparator(choice: unknown): choice is Separator; | ||
| } |
| import colors from 'yoctocolors-cjs'; | ||
| import figures from '@inquirer/figures'; | ||
| /** | ||
| * Separator object | ||
| * Used to space/separate choices group | ||
| */ | ||
| export class Separator { | ||
| separator = colors.dim(Array.from({ length: 15 }).join(figures.line)); | ||
| type = 'separator'; | ||
| constructor(separator) { | ||
| if (separator) { | ||
| this.separator = separator; | ||
| } | ||
| } | ||
| static isSeparator(choice) { | ||
| return Boolean(choice && | ||
| typeof choice === 'object' && | ||
| 'type' in choice && | ||
| choice.type === 'separator'); | ||
| } | ||
| } |
| import type { Prettify } from '@inquirer/type'; | ||
| /** | ||
| * Union type representing the possible statuses of a prompt. | ||
| * | ||
| * - `'loading'`: The prompt is currently loading. | ||
| * - `'idle'`: The prompt is loaded and currently waiting for the user to | ||
| * submit an answer. | ||
| * - `'done'`: The user has submitted an answer and the prompt is finished. | ||
| * - `string`: Any other string: The prompt is in a custom state. | ||
| */ | ||
| export type Status = 'loading' | 'idle' | 'done' | (string & {}); | ||
| type DefaultTheme = { | ||
| /** | ||
| * Prefix to prepend to the message. If a function is provided, it will be | ||
| * called with the current status of the prompt, and the return value will be | ||
| * used as the prefix. | ||
| * | ||
| * @remarks | ||
| * If `status === 'loading'`, this property is ignored and the spinner (styled | ||
| * by the `spinner` property) will be displayed instead. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (status) => status === 'done' ? colors.green('✔') : colors.blue('?') | ||
| * ``` | ||
| */ | ||
| prefix: string | Prettify<Omit<Record<Status, string>, 'loading'>>; | ||
| /** | ||
| * Configuration for the spinner that is displayed when the prompt is in the | ||
| * `'loading'` state. | ||
| * | ||
| * We recommend the use of {@link https://github.com/sindresorhus/cli-spinners|cli-spinners} for a list of available spinners. | ||
| */ | ||
| spinner: { | ||
| /** | ||
| * The time interval between frames, in milliseconds. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * 80 | ||
| * ``` | ||
| */ | ||
| interval: number; | ||
| /** | ||
| * A list of frames to show for the spinner. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] | ||
| * ``` | ||
| */ | ||
| frames: string[]; | ||
| }; | ||
| /** | ||
| * Object containing functions to style different parts of the prompt. | ||
| */ | ||
| style: { | ||
| /** | ||
| * Style to apply to the user's answer once it has been submitted. | ||
| * | ||
| * @param text - The user's answer. | ||
| * @returns The styled answer. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.cyan(text) | ||
| * ``` | ||
| */ | ||
| answer: (text: string) => string; | ||
| /** | ||
| * Style to apply to the message displayed to the user. | ||
| * | ||
| * @param text - The message to style. | ||
| * @param status - The current status of the prompt. | ||
| * @returns The styled message. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text, status) => colors.bold(text) | ||
| * ``` | ||
| */ | ||
| message: (text: string, status: Status) => string; | ||
| /** | ||
| * Style to apply to error messages. | ||
| * | ||
| * @param text - The error message. | ||
| * @returns The styled error message. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.red(`> ${text}`) | ||
| * ``` | ||
| */ | ||
| error: (text: string) => string; | ||
| /** | ||
| * Style to apply to the default answer when one is provided. | ||
| * | ||
| * @param text - The default answer. | ||
| * @returns The styled default answer. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.dim(`(${text})`) | ||
| * ``` | ||
| */ | ||
| defaultAnswer: (text: string) => string; | ||
| /** | ||
| * Style to apply to help text. | ||
| * | ||
| * @param text - The help text. | ||
| * @returns The styled help text. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.dim(text) | ||
| * ``` | ||
| */ | ||
| help: (text: string) => string; | ||
| /** | ||
| * Style to apply to highlighted text. | ||
| * | ||
| * @param text - The text to highlight. | ||
| * @returns The highlighted text. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.cyan(text) | ||
| * ``` | ||
| */ | ||
| highlight: (text: string) => string; | ||
| /** | ||
| * Style to apply to keyboard keys referred to in help texts. | ||
| * | ||
| * @param text - The key to style. | ||
| * @returns The styled key. | ||
| * | ||
| * @defaultValue | ||
| * ```ts | ||
| * // import colors from 'yoctocolors-cjs'; | ||
| * (text) => colors.cyan(colors.bold(`<${text}>`)) | ||
| * ``` | ||
| */ | ||
| key: (text: string) => string; | ||
| }; | ||
| }; | ||
| export type Theme<Extension extends object = object> = Prettify<Extension & DefaultTheme>; | ||
| export declare const defaultTheme: DefaultTheme; | ||
| export {}; |
| import colors from 'yoctocolors-cjs'; | ||
| import figures from '@inquirer/figures'; | ||
| export const defaultTheme = { | ||
| prefix: { | ||
| idle: colors.blue('?'), | ||
| done: colors.green(figures.tick), | ||
| }, | ||
| spinner: { | ||
| interval: 80, | ||
| frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'].map((frame) => colors.yellow(frame)), | ||
| }, | ||
| style: { | ||
| answer: colors.cyan, | ||
| message: colors.bold, | ||
| error: (text) => colors.red(`> ${text}`), | ||
| defaultAnswer: (text) => colors.dim(`(${text})`), | ||
| help: colors.dim, | ||
| highlight: colors.cyan, | ||
| key: (text) => colors.cyan(colors.bold(`<${text}>`)), | ||
| }, | ||
| }; |
| import type { InquirerReadline } from '@inquirer/type'; | ||
| export declare function useEffect(cb: (rl: InquirerReadline) => void | (() => void), depArray: ReadonlyArray<unknown>): void; |
| import { withPointer, effectScheduler } from "./hook-engine.js"; | ||
| export function useEffect(cb, depArray) { | ||
| withPointer((pointer) => { | ||
| const oldDeps = pointer.get(); | ||
| const hasChanged = !Array.isArray(oldDeps) || depArray.some((dep, i) => !Object.is(dep, oldDeps[i])); | ||
| if (hasChanged) { | ||
| effectScheduler.queue(cb); | ||
| } | ||
| pointer.set(depArray); | ||
| }); | ||
| } |
| import { type InquirerReadline } from '@inquirer/type'; | ||
| import { type KeypressEvent } from './key.ts'; | ||
| export declare function useKeypress(userHandler: (event: KeypressEvent, rl: InquirerReadline) => void | Promise<void>): void; |
| import { useRef } from "./use-ref.js"; | ||
| import { useEffect } from "./use-effect.js"; | ||
| import { withUpdates } from "./hook-engine.js"; | ||
| export function useKeypress(userHandler) { | ||
| const signal = useRef(userHandler); | ||
| signal.current = userHandler; | ||
| useEffect((rl) => { | ||
| let ignore = false; | ||
| const handler = withUpdates((_input, event) => { | ||
| if (ignore) | ||
| return; | ||
| void signal.current(event, rl); | ||
| }); | ||
| rl.input.on('keypress', handler); | ||
| return () => { | ||
| ignore = true; | ||
| rl.input.removeListener('keypress', handler); | ||
| }; | ||
| }, []); | ||
| } |
| export declare function useMemo<Value>(fn: () => Value, dependencies: ReadonlyArray<unknown>): Value; |
| import { withPointer } from "./hook-engine.js"; | ||
| export function useMemo(fn, dependencies) { | ||
| return withPointer((pointer) => { | ||
| const prev = pointer.get(); | ||
| if (!prev || | ||
| prev.dependencies.length !== dependencies.length || | ||
| prev.dependencies.some((dep, i) => dep !== dependencies[i])) { | ||
| const value = fn(); | ||
| pointer.set({ value, dependencies }); | ||
| return value; | ||
| } | ||
| return prev.value; | ||
| }); | ||
| } |
| import type { Theme, Status } from './theme.ts'; | ||
| export declare function usePrefix({ status, theme, }: { | ||
| status?: Status; | ||
| theme?: Theme; | ||
| }): string; |
| import { useState } from "./use-state.js"; | ||
| import { useEffect } from "./use-effect.js"; | ||
| import { makeTheme } from "./make-theme.js"; | ||
| export function usePrefix({ status = 'idle', theme, }) { | ||
| const [showLoader, setShowLoader] = useState(false); | ||
| const [tick, setTick] = useState(0); | ||
| const { prefix, spinner } = makeTheme(theme); | ||
| useEffect(() => { | ||
| if (status === 'loading') { | ||
| let tickInterval; | ||
| let inc = -1; | ||
| // Delay displaying spinner by 300ms, to avoid flickering | ||
| const delayTimeout = setTimeout(() => { | ||
| setShowLoader(true); | ||
| tickInterval = setInterval(() => { | ||
| inc = inc + 1; | ||
| setTick(inc % spinner.frames.length); | ||
| }, spinner.interval); | ||
| }, 300); | ||
| return () => { | ||
| clearTimeout(delayTimeout); | ||
| clearInterval(tickInterval); | ||
| }; | ||
| } | ||
| else { | ||
| setShowLoader(false); | ||
| } | ||
| }, [status]); | ||
| if (showLoader) { | ||
| return spinner.frames[tick]; | ||
| } | ||
| // There's a delay before we show the loader. So we want to ignore `loading` here, and pass idle instead. | ||
| const iconName = status === 'loading' ? 'idle' : status; | ||
| return typeof prefix === 'string' ? prefix : (prefix[iconName] ?? prefix['idle']); | ||
| } |
| export declare function useRef<Value>(val: Value): { | ||
| current: Value; | ||
| }; | ||
| export declare function useRef<Value>(val?: Value): { | ||
| current: Value | undefined; | ||
| }; |
| import { useState } from "./use-state.js"; | ||
| export function useRef(val) { | ||
| return useState({ current: val })[0]; | ||
| } |
| type NotFunction<T> = T extends (...args: never) => unknown ? never : T; | ||
| export declare function useState<Value>(defaultValue: NotFunction<Value> | (() => Value)): [Value, (newValue: Value) => void]; | ||
| export declare function useState<Value>(defaultValue?: NotFunction<Value> | (() => Value)): [Value | undefined, (newValue?: Value) => void]; | ||
| export {}; |
| import { AsyncResource } from 'node:async_hooks'; | ||
| import { withPointer, handleChange } from "./hook-engine.js"; | ||
| export function useState(defaultValue) { | ||
| return withPointer((pointer) => { | ||
| const setState = AsyncResource.bind(function setState(newValue) { | ||
| // Noop if the value is still the same. | ||
| if (pointer.get() !== newValue) { | ||
| pointer.set(newValue); | ||
| // Trigger re-render | ||
| handleChange(); | ||
| } | ||
| }); | ||
| if (pointer.initialized) { | ||
| return [pointer.get(), setState]; | ||
| } | ||
| const value = typeof defaultValue === 'function' ? defaultValue() : defaultValue; | ||
| pointer.set(value); | ||
| return [value, setState]; | ||
| }); | ||
| } |
| /** | ||
| * Force line returns at specific width. This function is ANSI code friendly and it'll | ||
| * ignore invisible codes during width calculation. | ||
| * @param {string} content | ||
| * @param {number} width | ||
| * @return {string} | ||
| */ | ||
| export declare function breakLines(content: string, width: number): string; | ||
| /** | ||
| * Returns the width of the active readline, or 80 as default value. | ||
| * @returns {number} | ||
| */ | ||
| export declare function readlineWidth(): number; |
| import cliWidth from 'cli-width'; | ||
| import wrapAnsi from 'wrap-ansi'; | ||
| import { readline } from "./hook-engine.js"; | ||
| /** | ||
| * Force line returns at specific width. This function is ANSI code friendly and it'll | ||
| * ignore invisible codes during width calculation. | ||
| * @param {string} content | ||
| * @param {number} width | ||
| * @return {string} | ||
| */ | ||
| export function breakLines(content, width) { | ||
| return content | ||
| .split('\n') | ||
| .flatMap((line) => wrapAnsi(line, width, { trim: false, hard: true }) | ||
| .split('\n') | ||
| .map((str) => str.trimEnd())) | ||
| .join('\n'); | ||
| } | ||
| /** | ||
| * Returns the width of the active readline, or 80 as default value. | ||
| * @returns {number} | ||
| */ | ||
| export function readlineWidth() { | ||
| return cliWidth({ defaultWidth: 80, output: readline().output }); | ||
| } |
| { | ||
| "type": "module" | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
4
-20%2
-66.67%54170
-61.96%39
-65.49%1005
-68.26%1
Infinity%Updated
Updated
Updated