@ayonli/jsext
Version:
A JavaScript extension package for building strong and modern applications.
241 lines (240 loc) β’ 7.72 kB
TypeScript
import { ByteArray } from "../bytes.ts";
import { ControlKeys, ControlSequences, FunctionKeys, NavigationKeys } from "./constants.ts";
export { ControlKeys, ControlSequences, FunctionKeys, NavigationKeys, };
/**
* The command-line arguments passed to the program.
*
* This variable is the same as `Deno.args` in Deno and `process.argv.slice(2)`
* in Node.js or Bun.
*
* @example
* ```ts
* // main.ts
* // launch with `deno run main.ts --name=Bob --age=30`
* // or `node main.js --name=Bob --age=30`
* // or `bun run main.ts --name=Bob --age=30`
* import { args } from "@ayonli/jsext/cli";
*
* console.log(args);
* // [
* // "--name=Bob",
* // "--age=30"
* // ]
* ```
*/
export declare const args: string[];
/**
* Whether the standard IO is a text terminal.
*/
export declare const isTTY: boolean;
/**
* Returns the width of a single character.
*
* @example
* ```ts
* import { charWidth } from "@ayonli/jsext/cli";
*
* console.log(charWidth("a")); // 1
* console.log(charWidth("δ½ ")); // 2
* console.log(charWidth("π")); // 2
* console.log(charWidth("β₯")); // 1
* ```
*/
export declare function charWidth(char: string): 1 | 2;
/**
* Returns the width of a string.
*
* @example
* ```ts
* import { stringWidth } from "@ayonli/jsext/cli";
*
* console.log(stringWidth("Hello, World!")); // 13
* console.log(stringWidth("δ½ ε₯½οΌδΈηοΌ")); // 12
* console.log(stringWidth("πππβ₯οΈβ£")); // 8
* ```
*/
export declare function stringWidth(str: string): number;
/**
* Requests the standard input to be used only by the given task until it is
* completed.
*
* This function sets the `stdin` in raw mode, and excludes other parts of the
* program from reading from it at the same time . This is important so that our
* program won't be affected by other tasks, especially in a REPL environment.
*
* @example
* ```ts
* // A simple program that reads a line of text from user input.
* import process from "node:process";
* import bytes, { equals } from "@ayonli/jsext/bytes";
* import { chars } from "@ayonli/jsext/string";
* import {
* ControlKeys,
* ControlSequences,
* lockStdin,
* readStdin,
* writeStdout,
* isTypingInput,
* moveLeftBy,
* } from "@ayonli/jsext/cli";
*
* const input = await lockStdin(async () => {
* await writeStdout(bytes("Type something: "));
*
* const buf: string[] = []
*
* while (true) {
* const input = await readStdin();
*
* if (equals(input, ControlKeys.CTRL_C) || equals(input, ControlKeys.ESC)) {
* console.error("\nUser cancelled");
* process.exit(1);
* } else if (equals(input, ControlKeys.CR) || equals(input, ControlKeys.LF)) {
* await writeStdout(ControlKeys.LF);
* break;
* } else if (equals(input, ControlKeys.BS) || equals(input, ControlKeys.DEL)) {
* if (buf.length > 0) {
* const char = buf.pop()!;
* await moveLeftBy(char);
* await writeStdout(ControlSequences.CLR_RIGHT);
* }
* } else if (isTypingInput(input)) {
* buf.push(...chars(String(input)));
* await writeStdout(input);
* }
* }
*
* return buf.join("")
* });
*
* console.log(`You typed: ${input}`);
* ```
*/
export declare function lockStdin<T>(task: () => Promise<T>): Promise<T | null>;
/**
* Reads a chunk of data from the standard input. This could be a single key
* stroke, or a multi-byte sequence for input from an IME.
*
* NOTE: This function should be used within the task function of {@link lockStdin}.
*/
export declare function readStdin(): Promise<ByteArray>;
/**
* Writes a chunk of data to the standard output.
*/
export declare function writeStdout(data: ByteArray): Promise<void>;
/**
* Writes a chunk of data to the standard output synchronously.
*
* NOTE: Despite the function name, the synchronous behavior is only guaranteed
* in Deno, in Node.js, it may still be asynchronous.
*
* Since the behavior is not guaranteed, it is recommended to use the asynchronous
* `writeStdout` function instead. This synchronous function is only provided for
* special cases where the asynchronous behavior is not acceptable.
*/
export declare function writeStdoutSync(data: ByteArray): void;
/**
* Moves the cursor to the left base on the width of the given string.
*/
export declare function moveLeftBy(str: string): Promise<void>;
/**
* Moves the cursor to the right base on the width of the given string.
*/
export declare function moveRightBy(str: string): Promise<void>;
/**
* Returns `true` if the given data is a typing input. That is, it is not a
* control key, navigation key, or function key.
*/
export declare function isTypingInput(data: ByteArray): boolean;
/**
* Returns the current size of the application window.
*
* In the terminal, this is the size of the terminal window, where `width` and
* `height` are the corresponding columns and rows.
*
* In the browser, this is the size of the viewport, where `width` and `height`
* are measured in pixels.
*/
export declare function getWindowSize(): {
width: number;
height: number;
};
/** Checks if the program is running in Windows Subsystem for Linux. */
export declare function isWSL(): boolean;
/**
* Options for parsing CLI arguments, used by the {@link parseArgs} function.
*/
export interface ParseOptions {
/**
* A map of alias characters to their full names. Once set, we can use the
* alias characters in the arguments, and they will be converted to their
* full names in the result object after parsing.
*/
alias?: {
[char: string]: string;
};
/**
* Argument names that should be treated as lists. When an argument is in
* this list, the result object will store its values in an array.
*/
lists?: string[];
/**
* By default, the {@link parseArgs} function will automatically convert the
* argument value to a number or boolean if it looks like one. If we don't
* want this behavior for some arguments, we can set this option to `true`,
* or an array of argument names that should not be coerced.
*/
noCoercion?: boolean | string[];
}
/**
* Parses the given CLI arguments into an object.
*
* @example
* ```ts
* import { parseArgs } from "@ayonli/jsext/cli";
*
* const args = parseArgs([
* "Bob",
* "--age", "30",
* "--married",
* "--wife=Alice",
* "--children", "Mia",
* "--children", "Ava",
* "-p"
* ], {
* alias: { "p": "has-parents" },
* lists: ["children"],
* });
*
* console.log(args);
* // {
* // "0": "Bob",
* // age: 30,
* // married: true,
* // wife: "Alice",
* // children: ["Mia", "Ava"],
* // "has-parents": true
* // }
* ```
*/
export declare function parseArgs(args: string[], options?: ParseOptions): {
[key: string]: string | number | boolean | (string | number | boolean)[];
[x: number]: string | number | boolean;
"--"?: string[];
};
/**
* Quotes a string to be used as a single argument to a shell command.
*
* @example
* ```ts
* import { quote } from "@ayonli/jsext/cli";
*
* console.log(quote("Hello, World!")); // "Hello, World!"
* console.log(quote("Hello, 'World'!")); // "Hello, 'World'!"
* console.log(quote("Hello, \"World\"!")); // "Hello, \"World\"!"
* console.log(quote("Hello, $World!")); // "Hello, \$World!"
* console.log(quote("Hello, `World`!")); // "Hello, \`World\`!"
* console.log(quote("Hello, \\World!")); // "Hello, \\World!"
* ```
*/
export declare function quote(arg: string): string;