diff --git a/build.optim.ts b/build.optim.ts index e827fd9..fec9926 100644 --- a/build.optim.ts +++ b/build.optim.ts @@ -1,11 +1,10 @@ import fs from "fs-extra"; import { globby } from "globby"; import path from "pathe"; -import strip from "strip-comments"; import { fileURLToPath } from "url"; // Verbose logging -const debug = false; +export const verbose = false; // Parse command-line arguments to check for '--jsr' flag const args: string[] = process.argv.slice(2); @@ -14,7 +13,7 @@ const isJSR: boolean = args.includes("--jsr"); // Get current directory using import.meta.url const currentDir = path.dirname(fileURLToPath(import.meta.url)); -// Define directories based on the presence of '--jsr' flag +// Directories based on the presence of '--jsr' flag const sourceDir: string = path.resolve(currentDir, "src"); const outputDir: string = path.resolve( currentDir, @@ -22,30 +21,18 @@ const outputDir: string = path.resolve( ); // Separate patterns for files to delete in different modes -const npmFilesToDelete: string[] = [ +const arrayFilesToDelete: string[] = [ "**/*.test.js", + "**/*.test.ts", "**/*.test.d.ts", - "**/*.spec.js", - "**/*.spec.d.ts", "types/internal.js", "types/internal.d.ts", "**/*.temp.js", "**/*.temp.d.ts", - "__snapshots__", - "testing", -]; - -const jsrFilesToDelete: string[] = [ - "**/*.test.ts", - "**/*.spec.ts", - "**/*.temp.ts", - "__snapshots__", ]; /** * Deletes files matching the provided patterns within the base directory. - * @param patterns - Array of glob patterns to match files for deletion. - * @param baseDir - The base directory to search for files. */ async function deleteFiles(patterns: string[], baseDir: string): Promise { try { @@ -62,25 +49,27 @@ async function deleteFiles(patterns: string[], baseDir: string): Promise { for (const filePath of files) { try { await fs.remove(filePath); - if (debug) { + if (verbose) { console.log(`Deleted: ${filePath}`); } } catch (error) { - console.error(`Error deleting file ${filePath}:`, error); + console.error( + `Error deleting file ${filePath}:`, + error instanceof Error ? error.message : JSON.stringify(error), + ); } } } catch (error) { - console.error("Error processing deletion patterns:", error); + console.error( + "Error processing deletion patterns:", + error instanceof Error ? error.message : JSON.stringify(error), + ); } } /** * Replaces import paths that use '~/' with relative paths. * If `isJSR` is true, also replaces '.js' extensions with '.ts'. - * @param content - The file content. - * @param fileDir - The directory of the current file. - * @param rootDir - The root directory to resolve relative paths. - * @param isJSR - Flag indicating whether to apply JSR-specific transformations. * @returns The updated file content with modified import paths. */ function replaceImportPaths( @@ -98,7 +87,7 @@ function replaceImportPaths( importPath: string, suffix: string, ): string => { - const relativePathToRoot: string = path.relative(fileDir, rootDir) || "."; + const relativePathToRoot: string = path.relative(fileDir, rootDir); // Remove leading '~/' or '~' from importPath importPath = importPath.replace(/^~\/?/, ""); let newPath: string = path.join(relativePathToRoot, importPath); @@ -117,7 +106,7 @@ function replaceImportPaths( // @see https://jsr.io/docs/publishing-packages#relative-imports updatedContent = updatedContent.replace(/(\.js)(?=['";])/g, ".ts"); - if (debug) { + if (verbose) { console.log("Replaced '.js' with '.ts' in import paths."); } } @@ -128,31 +117,29 @@ function replaceImportPaths( /** * Removes comments from the given content string. * - Strips block comments using `strip-comments`. - * @param content - The file content. - * @param filePath - The path of the file being processed. * @returns The content without unwanted comments. */ -function removeComments(content: string, filePath: string): string { +// function removeComments(content: string, filePath: string): string { +function removeComments(content: string): string { // When not in JSR mode, strip all comments using strip-comments - const stripped = strip(content, { - line: true, - block: true, - keepProtected: true, - preserveNewlines: false, - }); + // const stripped = strip(content, { + // line: true, + // block: true, + // keepProtected: true, + // preserveNewlines: false, + // }); - if (debug) { - console.log(`\nProcessing file: ${filePath}`); - console.log("Stripped all comments."); - } + // if (debug) { + // console.log(`\nProcessing file: ${filePath}`); + // console.log("Stripped all comments."); + // } - return stripped; + return content; // return stripped; } /** * Processes all relevant files in the given directory * by replacing import paths and removing comments. - * @param dir - The directory to process. */ async function processFiles(dir: string): Promise { const files: string[] = await fs.readdir(dir); @@ -174,9 +161,10 @@ async function processFiles(dir: string): Promise { filePath.endsWith(".mts") || filePath.endsWith(".cts") ) { - if (debug) { + if (verbose) { console.log(`\nProcessing file: ${filePath}`); } + try { const content: string = await fs.readFile(filePath, "utf8"); @@ -188,17 +176,21 @@ async function processFiles(dir: string): Promise { ); if (!isJSR) { - updatedContent = removeComments(updatedContent, filePath); + // updatedContent = removeComments(updatedContent, filePath); + updatedContent = removeComments(updatedContent); } if (content !== updatedContent) { await fs.writeFile(filePath, updatedContent, "utf8"); - if (debug) { + if (verbose) { console.log(`Updated file: ${filePath}`); } } } catch (error) { - console.error(`Error processing file ${filePath}:`, error); + console.error( + `Error processing file ${filePath}:`, + error instanceof Error ? error.message : JSON.stringify(error), + ); } } } @@ -212,12 +204,15 @@ async function removeOutputDirectory(): Promise { const exists: boolean = await fs.pathExists(outputDir); if (exists) { await fs.remove(outputDir); - if (debug) { + if (verbose) { console.log(`Removed existing '${outputDir}' directory.`); } } } catch (error) { - console.error(`Error removing '${outputDir}' directory:`, error); + console.error( + `Error removing '${outputDir}' directory:`, + error instanceof Error ? error.message : JSON.stringify(error), + ); throw error; } } @@ -231,18 +226,44 @@ async function copySrcToOutput(): Promise { overwrite: true, errorOnExist: false, }); - if (debug) { + if (verbose) { console.log(`Copied 'src' to '${outputDir}'`); } } catch (error) { - console.error(`Error copying 'src' to '${outputDir}':`, error); + console.error( + `Error copying 'src' to '${outputDir}':`, + error instanceof Error ? error.message : JSON.stringify(error), + ); throw error; } } +/** + * Renames all .tsx files to -tsx.txt in the specified directory and its subdirectories. + */ +async function renameTsxFiles(dir: string): Promise { + try { + const files = await globby("**/*.tsx", { + cwd: dir, + absolute: true, + }); + + for (const filePath of files) { + const newPath = filePath.replace(/\.tsx$/, "-tsx.txt"); + await fs.rename(filePath, newPath); + if (verbose) { + console.log(`Renamed: ${filePath} -> ${newPath}`); + } + } + } catch (error) { + console.error( + `Error renaming .tsx files: ${error instanceof Error ? error.message : JSON.stringify(error)}`, + ); + } +} + /** * Optimizes the build for production by processing files and deleting unnecessary ones. - * @param dir - The directory to optimize. */ async function optimizeBuildForProduction(dir: string): Promise { if (isJSR) { @@ -252,11 +273,13 @@ async function optimizeBuildForProduction(dir: string): Promise { await copySrcToOutput(); console.log("Processing copied files to replace import paths..."); await processFiles(outputDir); // Process files after copying + console.log("Renaming .tsx files to -tsx.txt for JSR compatibility..."); + await renameTsxFiles(outputDir); } else { console.log("Creating an optimized production build..."); await processFiles(dir); console.log("Cleaning up unnecessary files..."); - const filesToDelete: string[] = isJSR ? jsrFilesToDelete : npmFilesToDelete; + const filesToDelete: string[] = arrayFilesToDelete; await deleteFiles(filesToDelete, dir); } } @@ -283,14 +306,14 @@ await optimizeBuildForProduction(outputDir) .then(() => { getDirectorySize(outputDir) .then((size) => { - console.log(`Total size of ${outputDir}: ${size} bytes`); + console.log(`Total size of ${outputDir}: ${String(size)} bytes`); }) - .catch((error) => { + .catch((error: unknown) => { console.error( `Error calculating directory size for ${outputDir}: ${error instanceof Error ? error.message : "Unknown error"}`, ); }); }) - .catch((error: Error) => { - console.error(error.message); + .catch((error: unknown) => { + console.log(error instanceof Error ? error.message : JSON.stringify(error)); }); diff --git a/build.publish.ts b/build.publish.ts index 5f24bfa..8583ba3 100644 --- a/build.publish.ts +++ b/build.publish.ts @@ -1,13 +1,18 @@ // 👉 usage example: `bun pub --bump=1.2.3` +import { parseJSONC, parseJSON5 } from "confbox"; +import { destr } from "destr"; import { execaCommand } from "execa"; import fs from "fs-extra"; import { globby } from "globby"; import mri from "mri"; -import path from "path"; +import path from "pathe"; + +// TODO: implement @reliverse/bump npm library function showHelp() { - console.log(`Usage: bun tsx build.publish.ts [newVersion] [options] + console.log( + `Usage: bun tsx build.publish.ts [newVersion] [options] Arguments: newVersion The new version to set (e.g. 1.2.3) @@ -16,7 +21,8 @@ Options: --jsr Publish to JSR registry --dry-run Perform a dry run of the publish process -h, --help Show help -`); +`, + ); } const argv = mri(process.argv.slice(2), { @@ -57,9 +63,12 @@ async function publishNpm(dryRun: boolean) { await execaCommand("bun build:npm", { stdio: "inherit" }); await execaCommand("npm publish", { stdio: "inherit" }); } - console.log("Published to npm successfully."); + console.log("success", "Published to npm successfully."); } catch (error) { - console.error("❌ Failed to publish to npm:", error); + console.error( + "❌ Failed to publish to npm:", + error instanceof Error ? error.message : String(error), + ); process.exit(1); } } @@ -67,81 +76,171 @@ async function publishNpm(dryRun: boolean) { async function publishJsr(dryRun: boolean) { try { if (dryRun) { - await execaCommand( - "bunx jsr publish --allow-slow-types --allow-dirty --dry-run", - { stdio: "inherit" }, - ); + await execaCommand("bunx jsr publish --dry-run", { + stdio: "inherit", + }); } else { await execaCommand("bun build:jsr", { stdio: "inherit" }); await execaCommand("bunx jsr publish --allow-slow-types --allow-dirty", { stdio: "inherit", }); } - console.log("Published to JSR successfully."); + console.log("success", "Published to JSR successfully."); } catch (error) { - console.error("❌ Failed to publish to JSR:", error); + console.error( + "❌ Failed to publish to JSR:", + error instanceof Error ? error.message : String(error), + ); process.exit(1); } } async function bumpVersions(oldVersion: string, newVersion: string) { - // Update package.json - const pkgPath = path.resolve("package.json"); - const pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8")) as { - version: string; - }; - pkg.version = newVersion; - await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); - - // Update jsr.jsonc - const jsrPath = path.resolve("jsr.jsonc"); - if (await fs.pathExists(jsrPath)) { - const jsrConfig = JSON.parse(await fs.readFile(jsrPath, "utf-8")) as { - version: string; - }; - jsrConfig.version = newVersion; - await fs.writeFile(jsrPath, JSON.stringify(jsrConfig, null, 2)); - } + try { + // Find all relevant files + const codebase = await globby("**/*.{reliverse,json,jsonc,json5,ts}", { + ignore: [ + "**/node_modules/**", + "**/.git/**", + "**/dist/**", + "**/build/**", + "**/.next/**", + "**/coverage/**", + "**/.cache/**", + "**/tmp/**", + "**/.temp/**", + "**/package-lock.json", + "**/pnpm-lock.yaml", + "**/yarn.lock", + "**/bun.lockb", + ], + }); + + // Track which files were updated + const updatedFiles: string[] = []; + + // Process each file + for (const file of codebase) { + try { + const content = await fs.readFile(file, "utf-8"); + + // Handle different file types + if (file.endsWith(".json") || file.endsWith(".reliverse")) { + const parsed = destr(content); + if (parsed && typeof parsed === "object" && "version" in parsed) { + parsed.version = newVersion; + await fs.writeFile(file, `${JSON.stringify(parsed, null, 2)}\n`); + updatedFiles.push(file); + continue; + } + } else if (file.endsWith(".jsonc")) { + const parsed = parseJSONC(content); + if (parsed && typeof parsed === "object" && "version" in parsed) { + parsed.version = newVersion; + await fs.writeFile(file, `${JSON.stringify(parsed, null, 2)}\n`); + updatedFiles.push(file); + continue; + } + } else if (file.endsWith(".json5")) { + const parsed = parseJSON5(content); + if (parsed && typeof parsed === "object" && "version" in parsed) { + parsed.version = newVersion; + await fs.writeFile(file, `${JSON.stringify(parsed, null, 2)}\n`); + updatedFiles.push(file); + continue; + } + } + + // For other files (including .ts), do string replacement if version is found + if (content.includes(oldVersion)) { + const updated = content.replaceAll(oldVersion, newVersion); + await fs.writeFile(file, updated); + updatedFiles.push(file); + } + } catch (error) { + console.warn( + `Failed to process ${file}:`, + error instanceof Error ? error.message : String(error), + ); + } + } - // Replace version in src/**/*.ts and examples/**/*.ts - const tsFiles = await globby(["src/**/*.ts", "examples/**/*.ts"]); - for (const file of tsFiles) { - const content = await fs.readFile(file, "utf-8"); - if (content.includes(oldVersion)) { - const updated = content.replaceAll(oldVersion, newVersion); - await fs.writeFile(file, updated); + if (updatedFiles.length > 0) { + console.log( + `Version updated from ${oldVersion} to ${newVersion}`, + `Updated ${String(updatedFiles.length)} files:`, + updatedFiles.join("\n"), + ); + } else { + console.warn("No files were updated with the new version"); } + } catch (error) { + console.error( + "Failed to bump versions:", + error instanceof Error ? error.message : String(error), + ); + throw error; } - - console.log(`Version updated from ${oldVersion} to ${newVersion}`); } async function main() { - const { jsr, "dry-run": dryRun } = argv; - const newVersion = argv._[0]; // The new version provided by the user (if any) - - if (newVersion) { - // Perform version bump - const pkg = JSON.parse(await fs.readFile("package.json", "utf-8")) as { - version: string; + try { + const { jsr, "dry-run": dryRun } = argv as unknown as { + jsr: boolean; + "dry-run": boolean; }; - const oldVersion = pkg.version; - if (oldVersion !== newVersion) { - await bumpVersions(oldVersion, newVersion); + const newVersion = argv._[0]; + + if (!newVersion) { + console.log("No version specified, skipping version bump"); } else { - console.log(`No version change required: already at ${oldVersion}`); + // Validate version format + if (!/^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(newVersion)) { + throw new Error( + "Invalid version format. Must be a valid semver (e.g., 1.2.3, 1.2.3-beta.1)", + ); + } + + // Read current version + const pkgPath = path.resolve("package.json"); + if (!(await fs.pathExists(pkgPath))) { + throw new Error("package.json not found"); + } + + const pkg = destr<{ version: string }>( + await fs.readFile(pkgPath, "utf-8"), + ); + if (!pkg.version) { + throw new Error("No version field found in package.json"); + } + + const oldVersion = pkg.version; + if (oldVersion === newVersion) { + console.log(`No version change required: already at ${oldVersion}`); + } else { + await bumpVersions(oldVersion, newVersion); + } } - } - // After potential bump, proceed with publishing - if (jsr) { - await publishJsr(dryRun); - } else { - await publishNpm(dryRun); + // Proceed with publishing + if (jsr) { + await publishJsr(dryRun); + } else { + await publishNpm(dryRun); + } + } catch (error) { + console.error( + "❌ An unexpected error occurred:", + error instanceof Error ? error.message : String(error), + ); + process.exit(1); } } -main().catch((error) => { - console.error("❌ An unexpected error occurred:", error); +main().catch((error: unknown) => { + console.error( + "❌ An unexpected error occurred:", + error instanceof Error ? error.message : String(error), + ); process.exit(1); }); diff --git a/bun.lockb b/bun.lockb index c98e7f7..a6fb7f1 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/cspell.json b/cspell.json index 70b316e..56ba033 100644 --- a/cspell.json +++ b/cspell.json @@ -1,5 +1,5 @@ { - "version": "0.2", + "version": "1.4.9", "language": "en", "import": ["@cspell/dict-npm/cspell-ext.json"], "ignorePaths": ["examples/deprecated", "dist-jsr", "dist-npm"], @@ -100,7 +100,6 @@ "outro", "pagedown", "pageup", - "picocolors", "polski", "printj", "rawlist", @@ -140,7 +139,6 @@ "unpub", "unstub", "valign", - "valtio", "venv", "Vous", "vsprintf", diff --git a/examples/launcher.ts b/examples/launcher.ts index 2659ed1..bcb72ed 100644 --- a/examples/launcher.ts +++ b/examples/launcher.ts @@ -1,4 +1,4 @@ -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import { errorHandler, selectPrompt } from "~/main.js"; @@ -22,29 +22,29 @@ async function examplesRunner() { hint: "experimental", }, { - label: pc.dim("Task Example"), + label: re.dim("Task Example"), value: "task", - hint: pc.dim("not finished"), + hint: re.dim("not finished"), }, { - label: pc.dim("Progressbar Example"), + label: re.dim("Progressbar Example"), value: "progressbar", - hint: pc.dim("not finished"), + hint: re.dim("not finished"), }, { - label: pc.dim("Simple Example"), + label: re.dim("Simple Example"), value: "simple", - hint: pc.dim("not finished"), + hint: re.dim("not finished"), }, { - label: pc.dim("with flags 1 Example"), + label: re.dim("with flags 1 Example"), value: "cmd-a", - hint: pc.dim("not finished"), + hint: re.dim("not finished"), }, { - label: pc.dim("with flags 2 Example"), + label: re.dim("with flags 2 Example"), value: "cmd-b", - hint: pc.dim("not finished"), + hint: re.dim("not finished"), }, { label: "🗝️ Exit", value: "exit" }, ] as const, diff --git a/examples/other/simple.ts b/examples/other/simple.ts index 2b334bc..b192104 100644 --- a/examples/other/simple.ts +++ b/examples/other/simple.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import figures from "~/figures/index.js"; import { select } from "~/prompts/index.js"; @@ -66,7 +66,7 @@ async function askNextDemo() { ], theme: { prefix: { - done: pc.magenta(figures.play), + done: re.magenta(figures.play), }, }, }); diff --git a/examples/src/prompts.ts b/examples/src/prompts.ts index b3165ea..366fcda 100644 --- a/examples/src/prompts.ts +++ b/examples/src/prompts.ts @@ -1,7 +1,7 @@ import { msg } from "@reliverse/relinka"; import { detect } from "detect-package-manager"; import { emojify } from "node-emoji"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import { anykeyPrompt, spinnerTaskPrompt } from "~/main.js"; import { multiselectPrompt } from "~/main.js"; @@ -36,7 +36,7 @@ const pkg = packageJson; // const pkg = { // name: "@reliverse/prompts", -// version: "1.4.7", +// version: "1.4.9", // description: // "@reliverse/prompts is a powerful library that enables seamless, typesafe, and resilient prompts for command-line applications. Crafted with simplicity and elegance, it provides developers with an intuitive and robust way to build interactive CLIs.", // }; @@ -76,7 +76,7 @@ export async function showAnykeyPrompt( username?: string, ) { const pm = await detect(); - let notification = pc.bold("[anykeyPrompt] Press any key to continue..."); + let notification = re.bold("[anykeyPrompt] Press any key to continue..."); if (kind === "privacy") { notification = `Before you continue, please note that you are only testing an example CLI app. None of your responses will be sent anywhere. No actions, such as installing dependencies, will actually take place; this is simply a simulation with a sleep timer and spinner. You can always review the source code to learn more.\n============================\n${notification}`; } diff --git a/examples/src/simple/input.ts b/examples/src/simple/input.ts index 5dcba4f..a5aa35b 100644 --- a/examples/src/simple/input.ts +++ b/examples/src/simple/input.ts @@ -1,5 +1,5 @@ import * as url from "node:url"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import { input } from "~/prompts/index.js"; @@ -21,7 +21,7 @@ const demo = async () => { message: "Enter an hex color?", // biome-ignore lint/style/useDefaultParameterLast: transformer(value = "", { isFinal }) { - return isFinal ? pc.underline(value) : value; + return isFinal ? re.underline(value) : value; }, validate: (value = "") => isHex(value) || "Pass a valid hex value", }); diff --git a/jsr.jsonc b/jsr.jsonc index 9bd30e2..61aa9ec 100644 --- a/jsr.jsonc +++ b/jsr.jsonc @@ -1,6 +1,6 @@ { "name": "@reliverse/prompts", - "version": "1.4.7", + "version": "1.4.9", "author": "blefnk", "license": "MIT", "exports": "./dist-jsr/main.ts", diff --git a/package.json b/package.json index 0826dde..561b039 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@reliverse/prompts", - "version": "1.4.7", + "version": "1.4.9", "author": "blefnk", "type": "module", "description": "@reliverse/prompts is a powerful library that enables seamless, typesafe, and resilient prompts for command-line applications. Crafted with simplicity and elegance, it provides developers with an intuitive and robust way to build interactive CLIs.", @@ -42,15 +42,24 @@ "url": "https://github.com/reliverse/prompts/issues", "email": "blefnk@gmail.com" }, - "files": ["package.json", "README.md", "LICENSE.md", "dist-npm"], + "files": [ + "package.json", + "README.md", + "LICENSE.md", + "dist-npm" + ], "homepage": "https://github.com/reliverse/prompts", - "keywords": ["cli", "reliverse"], + "keywords": [ + "cli", + "reliverse" + ], "license": "MIT", "dependencies": { "@alcalzone/ansi-tokenize": "^0.1.3", "@figliolia/chalk-animation": "^1.0.4", + "@reliverse/relico": "^1.0.0", "@reliverse/relinka": "^1.2.8", - "@sinclair/typebox": "^0.34.13", + "@sinclair/typebox": "^0.34.14", "ansi-diff-stream": "^1.2.1", "ansi-escapes": "^7.0.0", "auto-bind": "^5.0.1", @@ -69,7 +78,7 @@ "es-toolkit": "^1.31.0", "external-editor": "^3.1.0", "figlet": "^1.8.0", - "fs-extra": "^11.2.0", + "fs-extra": "^11.3.0", "get-pixels": "^3.3.3", "globby": "^14.0.2", "gradient-string": "^3.0.0", @@ -80,12 +89,11 @@ "mri": "^1.2.0", "mute-stream": "^2.0.0", "node-emoji": "^2.2.0", - "nypm": "^0.4.1", + "nypm": "^0.5.0", "ora": "^8.1.1", "p-map": "^7.0.3", "patch-console": "^2.0.0", - "pathe": "^2.0.1", - "picocolors": "^1.1.1", + "pathe": "^2.0.2", "plur": "^5.1.0", "precision": "^1.0.1", "read-package-up": "^11.0.0", @@ -99,7 +107,6 @@ "strip-ansi": "^7.1.0", "terminal-size": "^4.0.0", "ts-regex-builder": "^1.8.2", - "valtio": "^2.1.2", "widest-line": "^5.0.0", "window-size": "^1.1.1", "wrap-ansi": "^9.0.0", @@ -109,20 +116,20 @@ "devDependencies": { "@arethetypeswrong/cli": "^0.17.3", "@biomejs/biome": "1.9.4", - "@cspell/dict-npm": "^5.1.22", + "@cspell/dict-npm": "^5.1.23", "@eslint/js": "^9.18.0", - "@eslint/json": "^0.9.0", + "@eslint/json": "^0.9.1", "@eslint/markdown": "^6.2.1", - "@faker-js/faker": "^9.3.0", + "@faker-js/faker": "^9.4.0", "@stylistic/eslint-plugin": "^2.13.0", "@types/ansi-diff-stream": "^1.2.3", - "@types/bun": "^1.1.16", + "@types/bun": "^1.1.18", "@types/chalk-animation": "^1.6.3", "@types/eslint__js": "^8.42.3", "@types/figlet": "^1.7.0", "@types/fs-extra": "^11.0.4", "@types/mute-stream": "^0.0.4", - "@types/node": "^22.10.6", + "@types/node": "^22.10.7", "@types/sentencer": "^0.2.3", "@types/signal-exit": "^3.0.4", "@types/strip-comments": "^2.0.4", @@ -130,10 +137,10 @@ "@types/wrap-ansi": "^8.1.0", "delay": "^6.0.0", "eslint": "^9.18.0", - "eslint-plugin-perfectionist": "^4.6.0", + "eslint-plugin-perfectionist": "^4.7.0", "execa": "^9.5.2", "jiti": "^2.4.2", - "knip": "^5.42.0", + "knip": "^5.42.3", "mock-stdin": "^1.0.0", "p-queue": "^8.0.1", "printj": "^1.3.1", @@ -141,8 +148,8 @@ "strip-comments": "^2.0.1", "tsx": "^4.19.2", "typescript": "^5.7.3", - "typescript-eslint": "^8.20.0", + "typescript-eslint": "^8.21.0", "unbuild": "^3.3.1", - "vitest": "^2.1.8" + "vitest": "^3.0.3" } } diff --git a/publish.ts b/publish.ts index a94b0a6..35ee885 100644 --- a/publish.ts +++ b/publish.ts @@ -1,10 +1,8 @@ // 👉 usage example: `bun pub --bump=1.2.3` -import { relinka } from "@reliverse/relinka"; +import { defineCommand, runMain } from "citty"; import { execa } from "execa"; -import { defineCommand, errorHandler, runMain } from "~/main.js"; - const main = defineCommand({ meta: { name: "pub", @@ -30,31 +28,36 @@ const main = defineCommand({ }, run: async ({ args }) => { if (args.jsr) { - relinka("info", "Publishing the JSR version"); + console.log("Publishing the JSR version"); await execa("bun", ["build.publish.ts", args.bump, "--jsr"], { stdio: "inherit", }); } else if (args.npm) { - relinka("info", "Publishing the NPM version"); + console.log("Publishing the NPM version"); await execa("bun", ["build.publish.ts", args.bump], { stdio: "inherit" }); } else if (args.dryRun) { - relinka("info", "Dry run the publish process"); + console.log("Dry run the publish process"); await execa("bun", ["pub:jsr", "--dry-run"], { stdio: "inherit" }); await execa("bun", ["pub:npm", "--dry-run"], { stdio: "inherit" }); } else { - relinka("info", "Publishing the JSR version"); + console.log("Publishing the JSR version"); await execa("bun", ["build.publish.ts", args.bump, "--jsr"], { stdio: "inherit", }); - relinka("info", "Publishing the NPM version"); + console.log("Publishing the NPM version"); await execa("bun", ["pub:npm", args.bump], { stdio: "inherit" }); } }, }); -await runMain(main).catch((error: unknown) => +function errorHandler(error: unknown, message: string) { + console.error(message); + console.error(error instanceof Error ? error.message : String(error)); +} + +await runMain(main).catch((error: unknown) => { errorHandler( error instanceof Error ? error : new Error(String(error)), "If this issue is related to @reliverse/cli itself, please\n│ report the details at https://github.com/reliverse/cli", - ), -); + ); +}); diff --git a/src/checkbox/index.ts b/src/checkbox/index.ts index f027066..51b91bb 100644 --- a/src/checkbox/index.ts +++ b/src/checkbox/index.ts @@ -1,5 +1,5 @@ import ansiEscapes from "ansi-escapes"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { PartialDeep } from "~/types/utils.js"; @@ -43,15 +43,15 @@ type CheckboxTheme = { const checkboxTheme: CheckboxTheme = { icon: { - checked: pc.green(figures.circleFilled), + checked: re.green(figures.circleFilled), unchecked: figures.circle, cursor: figures.pointer, }, style: { - disabledChoice: (text: string) => pc.dim(`- ${text}`), + disabledChoice: (text: string) => re.dim(`- ${text}`), renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "), - description: (text: string) => pc.cyan(text), + description: (text: string) => re.cyan(text), }, helpMode: "auto", }; diff --git a/src/confirm/confirm-main.ts b/src/confirm/confirm-main.ts index 4f5c7d5..b8ed39f 100644 --- a/src/confirm/confirm-main.ts +++ b/src/confirm/confirm-main.ts @@ -9,7 +9,7 @@ import { bar, msg } from "@reliverse/relinka"; import { deleteLastLine } from "@reliverse/relinka"; import { stdin as input, stdout as output } from "node:process"; import readline from "node:readline/promises"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import { colorize } from "~/main.js"; import { completePrompt } from "~/utils/prompt-end.js"; @@ -112,7 +112,7 @@ function renderPrompt(params: { if (displayInstructions && !isRerender) { msg({ type: "M_NULL", - title: pc.blue(instructions), + title: re.blue(instructions), }); uiLineCount++; } @@ -121,7 +121,7 @@ function renderPrompt(params: { if (errorMessage) { msg({ type: "M_NULL", - title: pc.redBright(errorMessage), + title: re.redBright(errorMessage), }); uiLineCount++; } @@ -164,7 +164,7 @@ export async function confirmPrompt( // Only prepend the default hint to the title if instructions are not displayed const adjustedTitle = displayInstructions ? title - : `${pc.blue(defaultHint)} ${title}`; + : `${re.blue(defaultHint)} ${title}`; const instructions = `Use to confirm or deny, for default (${effectiveDefault ? "Y" : "N"}), to exit`; @@ -236,7 +236,7 @@ export async function confirmPrompt( deleteLastLine(); msg({ type: "M_NULL", - title: `${colorize(pc.reset(formattedBar), borderColor)} ${pc.reset(effectiveDefault ? "y" : "n")}`, + title: `${colorize(re.reset(formattedBar), borderColor)} ${re.reset(effectiveDefault ? "y" : "n")}`, }); result = effectiveDefault; } else if (answer === "y" || answer === "yes") { diff --git a/src/core/Separator.ts b/src/core/Separator.ts index 60fd016..2d507f0 100644 --- a/src/core/Separator.ts +++ b/src/core/Separator.ts @@ -1,4 +1,4 @@ -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import figures from "~/figures/index.js"; @@ -8,7 +8,7 @@ import figures from "~/figures/index.js"; */ export class Separator { - readonly separator = pc.dim(Array.from({ length: 15 }).join(figures.line)); + readonly separator = re.dim(Array.from({ length: 15 }).join(figures.line)); readonly type = "separator"; constructor(separator?: string) { diff --git a/src/core/theme.ts b/src/core/theme.ts index 9f91378..1db1143 100644 --- a/src/core/theme.ts +++ b/src/core/theme.ts @@ -1,4 +1,4 @@ -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { Prettify } from "~/types/index.js"; @@ -28,7 +28,7 @@ type DefaultTheme = { * @defaultValue * ```ts * // import colors from 'yoctocolors-cjs'; - * (status) => status === 'done' ? pc.green('✔') : pc.blue('?') + * (status) => status === 'done' ? re.green('✔') : re.blue('?') * ``` */ prefix: string | Prettify, "loading">>; @@ -73,7 +73,7 @@ type DefaultTheme = { * @defaultValue * ```ts * // import colors from 'yoctocolors-cjs'; - * (text) => pc.cyan(text) + * (text) => re.cyan(text) * ``` */ answer: (text: string) => string; @@ -88,7 +88,7 @@ type DefaultTheme = { * @defaultValue * ```ts * // import colors from 'yoctocolors-cjs'; - * (text, status) => pc.bold(text) + * (text, status) => re.bold(text) * ``` */ message: (text: string, status: Status) => string; @@ -102,7 +102,7 @@ type DefaultTheme = { * @defaultValue * ```ts * // import colors from 'yoctocolors-cjs'; - * (text) => pc.red(`> ${text}`) + * (text) => re.red(`> ${text}`) * ``` */ error: (text: string) => string; @@ -116,7 +116,7 @@ type DefaultTheme = { * @defaultValue * ```ts * // import colors from 'yoctocolors-cjs'; - * (text) => pc.dim(`(${text})`) + * (text) => re.dim(`(${text})`) * ``` */ defaultAnswer: (text: string) => string; @@ -130,7 +130,7 @@ type DefaultTheme = { * @defaultValue * ```ts * // import colors from 'yoctocolors-cjs'; - * (text) => pc.dim(text) + * (text) => re.dim(text) * ``` */ help: (text: string) => string; @@ -144,7 +144,7 @@ type DefaultTheme = { * @defaultValue * ```ts * // import colors from 'yoctocolors-cjs'; - * (text) => pc.cyan(text) + * (text) => re.cyan(text) * ``` */ highlight: (text: string) => string; @@ -158,7 +158,7 @@ type DefaultTheme = { * @defaultValue * ```ts * // import colors from 'yoctocolors-cjs'; - * (text) => pc.cyan(pc.bold(`<${text}>`)) + * (text) => re.cyan(re.bold(`<${text}>`)) * ``` */ key: (text: string) => string; @@ -171,23 +171,23 @@ export type Theme = Prettify< export const defaultTheme: DefaultTheme = { prefix: { - idle: pc.blue("?"), + idle: re.blue("?"), // TODO: use figure - done: pc.green(figures.tick), + done: re.green(figures.tick), }, spinner: { interval: 80, frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"].map((frame) => - pc.yellow(frame), + re.yellow(frame), ), }, style: { - answer: pc.cyan, - message: pc.bold, - error: (text) => pc.red(`> ${text}`), - defaultAnswer: (text) => pc.dim(`(${text})`), - help: pc.dim, - highlight: pc.cyan, - key: (text: string) => pc.cyan(pc.bold(`<${text}>`)), + answer: re.cyan, + message: re.bold, + error: (text) => re.red(`> ${text}`), + defaultAnswer: (text) => re.dim(`(${text})`), + help: re.dim, + highlight: re.cyan, + key: (text: string) => re.cyan(re.bold(`<${text}>`)), }, }; diff --git a/src/date/date.ts b/src/date/date.ts index b4a4be6..2b92773 100644 --- a/src/date/date.ts +++ b/src/date/date.ts @@ -12,7 +12,7 @@ import { Type, type TSchema } from "@sinclair/typebox"; import { Value } from "@sinclair/typebox/value"; import { stdin as input, stdout as output } from "node:process"; import readline from "node:readline/promises"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import { buildRegExp, digit, @@ -170,7 +170,7 @@ export async function datePrompt( } // Prompt the user for input - const answerInput = await rl.question(`${pc.dim(symbols.middle)} `); + const answerInput = await rl.question(`${re.dim(symbols.middle)} `); // Check if user pressed Ctrl+C or input stream closed: if (answerInput === null) { diff --git a/src/expand/index.ts b/src/expand/index.ts index 36e7f21..22ace8d 100644 --- a/src/expand/index.ts +++ b/src/expand/index.ts @@ -1,4 +1,4 @@ -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { PartialDeep } from "~/types/utils.js"; @@ -139,7 +139,7 @@ const expand = createPrompt( } else if (value === "") { setError("Please input a value"); } else { - setError(`"${pc.red(value)}" isn't an available option`); + setError(`"${re.red(value)}" isn't an available option`); } } } else { @@ -203,7 +203,7 @@ const expand = createPrompt( !Separator.isSeparator(choice) && choice.key === value.toLowerCase(), ); if (currentOption) { - helpTip = `${pc.cyan(">>")} ${currentOption.name}`; + helpTip = `${re.cyan(">>")} ${currentOption.name}`; } let error = ""; diff --git a/src/flags/usage.ts b/src/flags/usage.ts index 3431ed3..600da32 100644 --- a/src/flags/usage.ts +++ b/src/flags/usage.ts @@ -1,4 +1,4 @@ -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { ArgsDef, CommandDef } from "./types.js"; @@ -98,7 +98,7 @@ export async function renderUsage( const version = cmdMeta.version || parentMeta.version; usageLines.push( - pc.gray( + re.gray( `${cmdMeta.description} (${ commandName + (version ? ` v${version}` : "") })`, @@ -108,26 +108,26 @@ export async function renderUsage( const hasOptions = argLines.length > 0 || posLines.length > 0; usageLines.push( - `${pc.underline(pc.bold("USAGE"))} \`${commandName}${ + `${re.underline(re.bold("USAGE"))} \`${commandName}${ hasOptions ? " [OPTIONS]" : "" } ${usageLine.join(" ")}\``, "", ); if (posLines.length > 0) { - usageLines.push(pc.underline(pc.bold("ARGUMENTS")), ""); + usageLines.push(re.underline(re.bold("ARGUMENTS")), ""); usageLines.push(formatLineColumns(posLines, " ")); usageLines.push(""); } if (argLines.length > 0) { - usageLines.push(pc.underline(pc.bold("OPTIONS")), ""); + usageLines.push(re.underline(re.bold("OPTIONS")), ""); usageLines.push(formatLineColumns(argLines, " ")); usageLines.push(""); } if (commandsLines.length > 0) { - usageLines.push(pc.underline(pc.bold("COMMANDS")), ""); + usageLines.push(re.underline(re.bold("COMMANDS")), ""); usageLines.push(formatLineColumns(commandsLines, " ")); usageLines.push( "", diff --git a/src/input/input-main.ts b/src/input/input-main.ts index 35870ab..081fa86 100644 --- a/src/input/input-main.ts +++ b/src/input/input-main.ts @@ -18,7 +18,7 @@ import { } from "@reliverse/relinka"; import { Value } from "@sinclair/typebox/value"; import readline from "node:readline/promises"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { PromptOptions } from "~/main.js"; @@ -512,7 +512,7 @@ export async function inputPrompt( }); } else { deleteLastLine(); - msg({ type: "M_MIDDLE", title: ` ${pc.reset(defaultValue)}` }); + msg({ type: "M_MIDDLE", title: ` ${re.reset(defaultValue)}` }); } } if (errorMessage) { diff --git a/src/input/input.ts b/src/input/input.ts index a623606..e8ca898 100644 --- a/src/input/input.ts +++ b/src/input/input.ts @@ -1,4 +1,4 @@ -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { PromptOptions } from "../prompts/prompt.js"; @@ -25,11 +25,11 @@ export default class InputPrompt extends Prompt { }); this.on("value", () => { if (this.cursor >= this.value.length) { - this.valueWithCursor = `${this.value}${pc.inverse(pc.hidden("_"))}`; + this.valueWithCursor = `${this.value}${re.inverse(re.hidden("_"))}`; } else { const s1 = this.value.slice(0, this.cursor); const s2 = this.value.slice(this.cursor); - this.valueWithCursor = `${s1}${pc.inverse(s2[0])}${s2.slice(1)}`; + this.valueWithCursor = `${s1}${re.inverse(s2[0])}${s2.slice(1)}`; } }); } diff --git a/src/multiselect/multiselect-main.ts b/src/multiselect/multiselect-main.ts index 058ac4c..26bd43c 100644 --- a/src/multiselect/multiselect-main.ts +++ b/src/multiselect/multiselect-main.ts @@ -9,7 +9,7 @@ import { } from "@reliverse/relinka"; import { stdin as input, stdout as output } from "node:process"; import readline from "node:readline"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import terminalSize from "terminal-size"; import { completePrompt } from "~/utils/prompt-end.js"; @@ -130,19 +130,19 @@ function renderPromptUI(params: { if (errorMessage) { msg({ type: "M_NULL", - title: `${pc.redBright(symbols.step_error)} ${pc.redBright(errorMessage)}`, + title: `${re.redBright(symbols.step_error)} ${re.redBright(errorMessage)}`, }); uiLineCount++; } else if (allDisabled) { msg({ type: "M_NULL", - title: pc.redBright("All options are disabled."), + title: re.redBright("All options are disabled."), }); uiLineCount++; } else if (displayInstructions && !isRerender) { msg({ type: "M_NULL", - title: pc.blue(instructions), + title: re.blue(instructions), }); uiLineCount++; } @@ -177,7 +177,7 @@ function renderPromptUI(params: { ); if (shouldRenderTopEllipsis) { - msg({ type: "M_NULL", title: pc.dim("...") }); + msg({ type: "M_NULL", title: re.dim("...") }); uiLineCount++; } @@ -196,7 +196,7 @@ function renderPromptUI(params: { const lineSymbol = symbolKey in symbols ? symbols[symbolKey] : "─"; msg({ type: "M_NULL", - title: pc.dim(lineSymbol.repeat(width)), + title: re.dim(lineSymbol.repeat(width)), }); uiLineCount++; continue; @@ -206,20 +206,20 @@ function renderPromptUI(params: { const isHighlighted = index === pointer; const isDisabled = option.disabled; const checkbox = isSelected ? "[x]" : "[ ]"; - const prefix = isHighlighted ? pc.yellow(pc.reset("> ")) : " "; + const prefix = isHighlighted ? re.yellow(re.reset("> ")) : " "; const labelColor = isDisabled - ? pc.dim(pc.reset(option.label)) + ? re.dim(re.reset(option.label)) : isHighlighted - ? pc.reset(pc.yellow(option.label)) - : pc.reset(option.label); + ? re.reset(re.yellow(option.label)) + : re.reset(option.label); const hint = option.hint - ? pc.reset( - ` (${isDisabled ? pc.dim(option.hint) : pc.gray(option.hint)})`, + ? re.reset( + ` (${isDisabled ? re.dim(option.hint) : re.gray(option.hint)})`, ) : ""; - const formattedCheckbox = isHighlighted ? pc.yellow(checkbox) : checkbox; + const formattedCheckbox = isHighlighted ? re.yellow(checkbox) : checkbox; msg({ type: "M_NULL", @@ -229,7 +229,7 @@ function renderPromptUI(params: { } if (shouldRenderBottomEllipsis) { - msg({ type: "M_NULL", title: pc.dim("...") }); + msg({ type: "M_NULL", title: re.dim("...") }); uiLineCount++; } diff --git a/src/multiselect/num-multi-select.ts b/src/multiselect/num-multi-select.ts index e0bf8db..e176c65 100644 --- a/src/multiselect/num-multi-select.ts +++ b/src/multiselect/num-multi-select.ts @@ -9,7 +9,7 @@ import { import { Value } from "@sinclair/typebox/value"; import { stdin as input, stdout as output } from "node:process"; import readline from "node:readline/promises"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { PromptOptions } from "~/types/general.js"; @@ -73,7 +73,7 @@ export async function numMultiSelectPrompt(opts: NumMultiSelectPromptOptions) { // Generate choices text with formatted bar const choicesText = choices .map((choice, index) => - pc.dim( + re.dim( `${formattedBar} ${index + 1}) ${choice.title}${ choice.description ? ` - ${choice.description}` : "" }`, @@ -81,7 +81,7 @@ export async function numMultiSelectPrompt(opts: NumMultiSelectPromptOptions) { ) .join("\n"); - const fullPrompt = `${question}\n${choicesText}\n${formattedBar} ${pc.bold(pc.blue(`Enter your choices (comma-separated numbers between 1-${choices.length})`))}:\n${formattedBar} `; + const fullPrompt = `${question}\n${choicesText}\n${formattedBar} ${re.bold(re.blue(`Enter your choices (comma-separated numbers between 1-${choices.length})`))}:\n${formattedBar} `; const { text: formattedPrompt } = fmt({ hintPlaceholderColor, diff --git a/src/range/range.ts b/src/range/range.ts index d5e5b96..cc048af 100644 --- a/src/range/range.ts +++ b/src/range/range.ts @@ -4,7 +4,7 @@ import Differ from "ansi-diff-stream"; import esc from "ansi-escapes"; import ui from "cli-styles"; import { EventEmitter } from "events"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import precision from "precision"; import stringWidth from "string-width"; import windowSize from "window-size"; @@ -67,7 +67,7 @@ export class RangePrompt extends EventEmitter { // Default options const defaults: Required = { hint: "– Use arrow keys or type a value.", - marker: pc.cyan("●"), + marker: re.cyan("●"), bar: "–", values: [], value: null, @@ -346,10 +346,10 @@ export class RangePrompt extends EventEmitter { out += [ ui.symbol(this.done, this.aborted), - pc.bold(this.msg), + re.bold(this.msg), ui.delimiter(this.done), this.value !== null ? this.value : "", - pc.gray(this.unit), + re.gray(this.unit), ].join(" ") + "\n"; const size = @@ -372,7 +372,7 @@ export class RangePrompt extends EventEmitter { [ "", this.min, - pc.gray(leftBar) + this.marker + pc.gray(rightBar), + re.gray(leftBar) + this.marker + re.gray(rightBar), this.max, "", ].join(" ") + "\n"; diff --git a/src/rawlist/index.ts b/src/rawlist/index.ts index a65bb9e..5141508 100644 --- a/src/rawlist/index.ts +++ b/src/rawlist/index.ts @@ -1,4 +1,4 @@ -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { PartialDeep } from "~/types/utils.js"; @@ -110,7 +110,7 @@ const rawlist = createPrompt( } else if (value === "") { setError("Please input a value"); } else { - setError(`"${pc.red(value)}" isn't an available option`); + setError(`"${re.red(value)}" isn't an available option`); } } else { setValue(rl.line); diff --git a/src/search/index.ts b/src/search/index.ts index 3447dfa..061cbbe 100644 --- a/src/search/index.ts +++ b/src/search/index.ts @@ -1,4 +1,4 @@ -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { PartialDeep } from "~/types/utils.js"; @@ -32,9 +32,9 @@ type SearchTheme = { const searchTheme: SearchTheme = { icon: { cursor: figures.pointer }, style: { - disabled: (text: string) => pc.dim(`- ${text}`), - searchTerm: (text: string) => pc.cyan(text), - description: (text: string) => pc.cyan(text), + disabled: (text: string) => re.dim(`- ${text}`), + searchTerm: (text: string) => re.cyan(text), + description: (text: string) => re.cyan(text), }, helpMode: "auto", }; diff --git a/src/select/index.ts b/src/select/index.ts index f46f651..8181bb7 100644 --- a/src/select/index.ts +++ b/src/select/index.ts @@ -1,5 +1,5 @@ import ansiEscapes from "ansi-escapes"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { PartialDeep } from "~/types/utils.js"; @@ -37,8 +37,8 @@ type SelectTheme = { const selectTheme: SelectTheme = { icon: { cursor: figures.pointer }, style: { - disabled: (text: string) => pc.dim(`- ${text}`), - description: (text: string) => pc.cyan(text), + disabled: (text: string) => re.dim(`- ${text}`), + description: (text: string) => re.cyan(text), }, helpMode: "auto", }; diff --git a/src/select/select-main.ts b/src/select/select-main.ts index 2f08355..f7fc938 100644 --- a/src/select/select-main.ts +++ b/src/select/select-main.ts @@ -4,7 +4,7 @@ import { deleteLastLine, symbols } from "@reliverse/relinka"; import { msg, type ColorName, type TypographyName } from "@reliverse/relinka"; import { stdin as input, stdout as output } from "node:process"; import readline from "node:readline"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import terminalSize from "terminal-size"; import { completePrompt } from "~/utils/prompt-end.js"; @@ -119,19 +119,19 @@ function renderPromptUI(params: { if (errorMessage) { msg({ type: "M_ERROR", - title: `${pc.redBright(symbols.step_error)} ${pc.redBright(errorMessage)}`, + title: `${re.redBright(symbols.step_error)} ${re.redBright(errorMessage)}`, }); uiLineCount++; } else if (allDisabled) { msg({ type: "M_ERROR", - title: pc.redBright("All options are disabled."), + title: re.redBright("All options are disabled."), }); uiLineCount++; } else if (displayInstructions && !isRerender) { msg({ type: "M_NULL", - title: pc.blue(instructions), + title: re.blue(instructions), }); uiLineCount++; } @@ -169,7 +169,7 @@ function renderPromptUI(params: { ); if (shouldRenderTopEllipsis) { - msg({ type: "M_NULL", title: pc.dim("...") }); + msg({ type: "M_NULL", title: re.dim("...") }); uiLineCount++; } @@ -189,7 +189,7 @@ function renderPromptUI(params: { const lineSymbol = symbolKey in symbols ? symbols[symbolKey] : "─"; msg({ type: "M_NULL", - title: pc.dim(lineSymbol.repeat(width)), + title: re.dim(lineSymbol.repeat(width)), }); uiLineCount++; continue; @@ -197,16 +197,16 @@ function renderPromptUI(params: { const isSelected = index === selectedIndex; const isDisabled = option.disabled; - const prefix = isSelected ? pc.dim(pc.reset("> ")) : " "; + const prefix = isSelected ? re.dim(re.reset("> ")) : " "; const labelColor = isDisabled - ? pc.dim(pc.reset(option.label)) + ? re.dim(re.reset(option.label)) : isSelected - ? pc.reset(pc.yellow(option.label)) - : pc.reset(option.label); + ? re.reset(re.yellow(option.label)) + : re.reset(option.label); const hint = option.hint - ? pc.reset( - ` (${isDisabled ? pc.dim(option.hint) : pc.italic(pc.dim(option.hint))})`, + ? re.reset( + ` (${isDisabled ? re.dim(option.hint) : re.italic(re.dim(option.hint))})`, ) : ""; @@ -218,7 +218,7 @@ function renderPromptUI(params: { } if (shouldRenderBottomEllipsis) { - msg({ type: "M_NULL", title: pc.dim("...") }); + msg({ type: "M_NULL", title: re.dim("...") }); uiLineCount++; } diff --git a/src/select/select-two.ts b/src/select/select-two.ts index f890ce3..154ae13 100644 --- a/src/select/select-two.ts +++ b/src/select/select-two.ts @@ -2,7 +2,7 @@ import type { Key } from "node:readline"; import { Value } from "@sinclair/typebox/value"; import { stdout } from "node:process"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import type { PromptOptions } from "~/types/general.js"; @@ -54,12 +54,12 @@ export async function selectPrompt( throw new Error("Choices are required for select prompt."); } resetCursorAndClear(stdout, 0, 0); - console.log(pc.cyanBright(pc.bold(coloredTitle))); + console.log(re.cyanBright(re.bold(coloredTitle))); choices.forEach((choice, index) => { const isSelected = index === selectedIndex; - const prefix = isSelected ? pc.green(">") : " "; + const prefix = isSelected ? re.green(">") : " "; const choiceText = isSelected - ? pc.bgGreen(pc.black(choice.title)) + ? re.bgGreen(re.black(choice.title)) : choice.title; console.log(`${prefix} ${choiceText}`); }); diff --git a/src/task/progress.ts b/src/task/progress.ts index 422dac9..2ecd333 100644 --- a/src/task/progress.ts +++ b/src/task/progress.ts @@ -1,4 +1,4 @@ -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import { cursor, erase } from "sisteransi"; import type { ProgressBar, ProgressBarOptions } from "./types.js"; @@ -38,7 +38,7 @@ export async function progressTaskPrompt( const filled = state.completeChar.repeat(filledLength); const empty = state.incompleteChar.repeat(emptyLength); - return state.colorize ? pc.green(filled) + pc.red(empty) : filled + empty; + return state.colorize ? re.green(filled) + re.red(empty) : filled + empty; }; const render = async () => { @@ -52,7 +52,7 @@ export async function progressTaskPrompt( .replace(":elapsed", elapsed); process.stdout.write(cursor.move(-999, 0) + erase.line); - process.stdout.write(pc.green("◆") + " " + output); + process.stdout.write(re.green("◆") + " " + output); if (state.current >= state.total) { process.stdout.write("\n"); diff --git a/src/task/spinner.ts b/src/task/spinner.ts index 79a9b71..42f1d4b 100644 --- a/src/task/spinner.ts +++ b/src/task/spinner.ts @@ -2,7 +2,7 @@ import { msg } from "@reliverse/relinka"; import { type SpinnerName } from "cli-spinners"; import process from "node:process"; import ora from "ora"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import { cursor, erase } from "sisteransi"; type SimpleSpinnerType = "default" | "dottedCircle" | "boxSpinner"; @@ -59,7 +59,7 @@ export async function spinnerTaskPrompt( }); } catch (error) { oraSpinner.stopAndPersist({ - symbol: pc.red("✖"), + symbol: re.red("✖"), text: errorMessage, }); @@ -102,9 +102,9 @@ export async function spinnerTaskPrompt( } interval = setInterval(() => { - const frame = pc.magenta(frames[frameIndex]); + const frame = re.magenta(frames[frameIndex]); process.stdout.write( - `${cursor.move(-999, 0)}${erase.line}${frame} ${pc.cyan(message)}`, + `${cursor.move(-999, 0)}${erase.line}${frame} ${re.cyan(message)}`, ); frameIndex = (frameIndex + 1) % frames.length; }, delay); @@ -117,7 +117,7 @@ export async function spinnerTaskPrompt( interval = null; process.stdout.write( - `\r${erase.line}${pc.green("✔")} ${successMessage}\n`, + `\r${erase.line}${re.green("✔")} ${successMessage}\n`, ); msg({ @@ -131,7 +131,7 @@ export async function spinnerTaskPrompt( } process.stdout.write( - `\r${erase.line}${pc.red("✖")} ${ + `\r${erase.line}${re.red("✖")} ${ error instanceof Error ? errorMessage : "An unknown error occurred." }\n`, ); diff --git a/src/task/task.ts b/src/task/task.ts index 24f29b4..41dcec7 100644 --- a/src/task/task.ts +++ b/src/task/task.ts @@ -1,12 +1,12 @@ import type { Options } from "p-map"; import { msg } from "@reliverse/relinka"; +import { EventEmitter } from "node:events"; import process from "node:process"; import ora from "ora"; import pMap from "p-map"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import { cursor, erase } from "sisteransi"; -import { proxy, subscribe } from "valtio"; import type { ProgressBar } from "~/task/types.js"; @@ -157,6 +157,9 @@ function arrayRemove(array: T[], element: T) { } } +// Create a simple event emitter for state changes +const stateEmitter = new EventEmitter(); + function registerTask( taskList: TaskList, taskTitle: string, @@ -185,8 +188,8 @@ function registerTask( task.cancelToken = cancelToken; - // Subscribe to state changes for timing information - subscribe(task, () => { + // Replace valtio subscribe with event emitter + const handleStateChange = () => { if (task.state === "loading" && !task.startTime) { task.startTime = Date.now(); } else if ( @@ -197,7 +200,9 @@ function registerTask( task.endTime = Date.now(); task.duration = task.endTime - task.startTime; } - }); + }; + + stateEmitter.on(`stateChange:${task.id}`, handleStateChange); // Set up spinner const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; @@ -230,8 +235,8 @@ function registerTask( const frames = spinnerType ? simpleSpinners[spinnerType] : simpleSpinners.default; - const currentFrame = pc.magenta(frames[frameIndex]); - process.stdout.write(`${currentFrame} ${pc.cyan(task.title)}`); + const currentFrame = re.magenta(frames[frameIndex]); + process.stdout.write(`${currentFrame} ${re.cyan(task.title)}`); if (task.status) { process.stdout.write(` - ${task.status}`); } @@ -275,25 +280,25 @@ function registerTask( // Write status with proper styling if (task.state === "success") { process.stdout.write( - `${statusIcon} ${pc.cyan(task.title)}${task.status ? ` - ${pc.greenBright(task.status)}` : ""}\n`, + `${statusIcon} ${re.cyan(task.title)}${task.status ? ` - ${re.greenBright(task.status)}` : ""}\n`, ); } else if (task.state === "error" || task.state === "cancelled") { process.stdout.write( - `${statusIcon} ${pc.cyan(task.title)}${task.status ? ` - ${pc.red(task.status)}` : ""}\n`, + `${statusIcon} ${re.cyan(task.title)}${task.status ? ` - ${re.red(task.status)}` : ""}\n`, ); if (task.error?.message) { - process.stdout.write(` ${pc.red(task.error.message)}\n`); + process.stdout.write(` ${re.red(task.error.message)}\n`); } } else { process.stdout.write( - `${statusIcon} ${pc.cyan(task.title)}${task.status ? ` - ${task.status}` : ""}\n`, + `${statusIcon} ${re.cyan(task.title)}${task.status ? ` - ${task.status}` : ""}\n`, ); } } }; - // Start spinner when task starts - subscribe(task, () => { + // Start spinner when task state changes + stateEmitter.on(`stateChange:${task.id}`, () => { if (task.state === "loading" && !interval) { interval = setInterval(() => { frameIndex = (frameIndex + 1) % frames.length; @@ -309,6 +314,7 @@ function registerTask( async [runSymbol]() { const api = createTaskInnerApi(task); task.state = "loading"; + stateEmitter.emit(`stateChange:${task.id}`); try { if (cancelToken.signal.aborted) { @@ -342,6 +348,7 @@ function registerTask( } catch (error) { if (cancelToken.signal.aborted) { task.state = "cancelled"; + stateEmitter.emit(`stateChange:${task.id}`); } else if (error instanceof Error) { api.setError(error); } else { @@ -351,22 +358,26 @@ function registerTask( process.exit(1); } throw error; + } finally { + stateEmitter.removeAllListeners(`stateChange:${task.id}`); } }, clear() { clearLine(); arrayRemove(taskList, task); + stateEmitter.removeAllListeners(`stateChange:${task.id}`); }, cancel() { clearLine(); cancelToken.abort(); task.state = "cancelled"; + stateEmitter.emit(`stateChange:${task.id}`); }, }; } -// Create and export the root task list and default task function -const rootTaskList = proxy([]); +// Create and export the root task list without valtio proxy +const rootTaskList: TaskList = []; // Add a task state manager const taskStateManager = new Map(); @@ -522,9 +533,11 @@ const createTaskInnerApi = (taskState: TaskObject): TaskInnerAPI => { task: advancedTaskPrompt(taskState.children), setTitle(title) { taskState.title = title; + stateEmitter.emit(`stateChange:${taskState.id}`); }, setStatus(status) { taskState.status = status; + stateEmitter.emit(`stateChange:${taskState.id}`); }, setOutput(output) { taskState.output = @@ -533,6 +546,7 @@ const createTaskInnerApi = (taskState: TaskObject): TaskInnerAPI => { : "message" in output ? output.message : ""; + stateEmitter.emit(`stateChange:${taskState.id}`); }, setWarning(warning) { taskState.state = "warning"; @@ -542,6 +556,7 @@ const createTaskInnerApi = (taskState: TaskObject): TaskInnerAPI => { taskState.error = warning; } } + stateEmitter.emit(`stateChange:${taskState.id}`); }, setError(error) { taskState.state = "error"; @@ -551,9 +566,11 @@ const createTaskInnerApi = (taskState: TaskObject): TaskInnerAPI => { taskState.error = error; } } + stateEmitter.emit(`stateChange:${taskState.id}`); }, setProgress(progress) { taskState.progress = progress; + stateEmitter.emit(`stateChange:${taskState.id}`); }, updateProgress(current: number, total?: number, message?: string) { const currentProgress = taskState.progress || { current: 0, total: 100 }; @@ -562,11 +579,13 @@ const createTaskInnerApi = (taskState: TaskObject): TaskInnerAPI => { total: total ?? currentProgress.total, message: message ?? currentProgress.message, }; + stateEmitter.emit(`stateChange:${taskState.id}`); }, cancel() { if (taskState.cancelToken) { taskState.cancelToken.abort(); taskState.state = "cancelled"; + stateEmitter.emit(`stateChange:${taskState.id}`); } }, isCancelled() { @@ -612,7 +631,7 @@ export async function spinnerTask(options: SpinnerTaskOptions): Promise { oraSpinner.stop(); } catch (error) { oraSpinner.stopAndPersist({ - symbol: pc.red("✖"), + symbol: re.red("✖"), text: errorMessage, }); @@ -656,9 +675,9 @@ export async function spinnerTask(options: SpinnerTaskOptions): Promise { } interval = setInterval(() => { - const frame = pc.magenta(frames[frameIndex]); + const frame = re.magenta(frames[frameIndex]); process.stdout.write( - `${cursor.move(-999, 0)}${erase.line}${frame} ${pc.cyan(message)}`, + `${cursor.move(-999, 0)}${erase.line}${frame} ${re.cyan(message)}`, ); frameIndex = (frameIndex + 1) % frames.length; }, delay); @@ -675,7 +694,7 @@ export async function spinnerTask(options: SpinnerTaskOptions): Promise { } process.stdout.write( - `\r${erase.line}${pc.red("✖")} ${ + `\r${erase.line}${re.red("✖")} ${ error instanceof Error ? errorMessage : "An unknown error occurred." }\n`, ); diff --git a/src/toggle/index.ts b/src/toggle/index.ts index cb1a503..51ca037 100644 --- a/src/toggle/index.ts +++ b/src/toggle/index.ts @@ -8,7 +8,7 @@ import type { VariantName } from "@reliverse/relinka"; import { deleteLastLine, msg } from "@reliverse/relinka"; import { stdin as input, stdout as output } from "node:process"; import readline from "node:readline"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import { completePrompt } from "~/utils/prompt-end.js"; @@ -99,22 +99,22 @@ function renderTogglePrompt(params: { if (errorMessage) { msg({ type: "M_NULL", - title: pc.redBright(errorMessage), + title: re.redBright(errorMessage), }); uiLineCount++; } else if (displayInstructions && !isRerender) { msg({ type: "M_NULL", - title: pc.yellow(instructions), + title: re.yellow(instructions), }); uiLineCount++; } const displayString = options .map((option, index) => - index === selectedIndex ? pc.yellow(option) : pc.reset(option), + index === selectedIndex ? re.yellow(option) : re.reset(option), ) - .join(pc.dim(pc.reset(" / "))); + .join(re.dim(re.reset(" / "))); msg({ type: "M_NULL", title: displayString }); uiLineCount++; diff --git a/src/utils/color.ts b/src/utils/color.ts index 3b97a52..e2d5bf3 100644 --- a/src/utils/color.ts +++ b/src/utils/color.ts @@ -127,7 +127,7 @@ export type ColorFunction = (text: string | number) => string; * Creates an object that maps color names to their respective color functions, * based on whether or not color support is enabled. * @param {boolean} [useColor=isColorSupported] - Specifies whether to use color functions or fallback to plain strings. - * @returns {Record} An object where keys are color names and values are functions to apply those pc. See {@link ColorFunction}. + * @returns {Record} An object where keys are color names and values are functions to apply those re. See {@link ColorFunction}. */ function createColors(useColor = isColorSupported) { return useColor @@ -136,7 +136,7 @@ function createColors(useColor = isColorSupported) { } /** - * An object containing functions for coloring text. Each function corresponds to a terminal color. See {@link ColorName} for available pc. + * An object containing functions for coloring text. Each function corresponds to a terminal color. See {@link ColorName} for available re. */ export const colors = createColors() as Record< DeprecatedColorName, diff --git a/src/utils/colorize.ts b/src/utils/colorize.ts index 98a0be1..a4266e7 100644 --- a/src/utils/colorize.ts +++ b/src/utils/colorize.ts @@ -1,5 +1,6 @@ import type { ColorName, TypographyName } from "@reliverse/relinka"; +import { re } from "@reliverse/relico"; import gradient, { cristal, mind, @@ -7,11 +8,10 @@ import gradient, { rainbow, vice, } from "gradient-string"; -import pc from "picocolors"; -// Strip ANSI color codes using picocolors +// Strip ANSI color codes using @reliverse/relico function stripAnsi(text: string): string { - return pc.reset(text); + return re.reset(text); } export function colorize( @@ -65,61 +65,61 @@ export function colorize( // Handle regular colors switch (colorName) { case "inverse": - result = pc.inverse(` ${result} `); + result = re.inverse(` ${result} `); break; case "dim": - result = pc.dim(result); + result = re.dim(result); break; case "black": - result = pc.black(result); + result = re.black(result); break; case "red": - result = pc.red(result); + result = re.red(result); break; case "redBright": - result = pc.redBright(result); + result = re.redBright(result); break; case "green": - result = pc.green(result); + result = re.green(result); break; case "greenBright": - result = pc.greenBright(result); + result = re.greenBright(result); break; case "yellow": - result = pc.yellow(result); + result = re.yellow(result); break; case "yellowBright": - result = pc.yellowBright(result); + result = re.yellowBright(result); break; case "blue": - result = pc.blue(result); + result = re.blue(result); break; case "blueBright": - result = pc.blueBright(result); + result = re.blueBright(result); break; case "magenta": - result = pc.magenta(result); + result = re.magenta(result); break; case "magentaBright": - result = pc.magentaBright(result); + result = re.magentaBright(result); break; case "cyan": - result = pc.cyan(result); + result = re.cyan(result); break; case "cyanBright": - result = pc.cyanBright(result); + result = re.cyanBright(result); break; case "bgCyan": - result = pc.bgCyan(` ${result} `); + result = re.bgCyan(` ${result} `); break; case "bgCyanBright": - result = pc.bgCyanBright(` ${result} `); + result = re.bgCyanBright(` ${result} `); break; case "white": - result = pc.white(result); + result = re.white(result); break; case "gray": - result = pc.gray(result); + result = re.gray(result); break; case "none": break; @@ -132,16 +132,16 @@ export function colorize( if (typography) { switch (typography) { case "bold": - result = pc.bold(result); + result = re.bold(result); break; case "strikethrough": - result = pc.strikethrough(result); + result = re.strikethrough(result); break; case "underline": - result = pc.underline(result); + result = re.underline(result); break; case "italic": - result = pc.italic(result); + result = re.italic(result); break; default: break; diff --git a/src/utils/component.ts b/src/utils/component.ts index 7ccee18..1d8b9a3 100644 --- a/src/utils/component.ts +++ b/src/utils/component.ts @@ -1,5 +1,5 @@ +import { re } from "@reliverse/relico"; import { isUnicodeSupported } from "@reliverse/relinka"; -import pc from "picocolors"; import { cursor, erase } from "sisteransi"; import type { State } from "~/types/general.js"; @@ -48,13 +48,13 @@ const symbol = (state: State) => { switch (state) { case "initial": case "active": - return pc.cyan(S_STEP_ACTIVE); + return re.cyan(S_STEP_ACTIVE); case "cancel": - return pc.red(S_STEP_CANCEL); + return re.red(S_STEP_CANCEL); case "error": - return pc.yellow(S_STEP_ERROR); + return re.yellow(S_STEP_ERROR); case "submit": - return pc.green(S_STEP_SUBMIT); + return re.green(S_STEP_SUBMIT); } }; @@ -97,7 +97,7 @@ const limitOptions = ( const isTopLimit = i === 0 && shouldRenderTopEllipsis; const isBottomLimit = i === arr.length - 1 && shouldRenderBottomEllipsis; return isTopLimit || isBottomLimit - ? pc.dim("...") + ? re.dim("...") : style(option, i + slidingWindowLocation === cursor); }); }; @@ -116,25 +116,25 @@ export const text = (opts: TextOptions) => { defaultValue: opts.defaultValue, initialValue: opts.initialValue, render() { - const title = `${pc.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${re.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; const placeholder = opts.placeholder - ? pc.inverse(opts.placeholder[0]) + pc.dim(opts.placeholder.slice(1)) - : pc.inverse(pc.hidden("_")); + ? re.inverse(opts.placeholder[0]) + re.dim(opts.placeholder.slice(1)) + : re.inverse(re.hidden("_")); const value = !this.value ? placeholder : this.valueWithCursor; switch (this.state) { case "error": - return `${title.trim()}\n${pc.yellow(S_BAR)} ${value}\n${pc.yellow( + return `${title.trim()}\n${re.yellow(S_BAR)} ${value}\n${re.yellow( S_BAR_END, - )} ${pc.yellow(this.error)}\n`; + )} ${re.yellow(this.error)}\n`; case "submit": - return `${title}${pc.gray(S_BAR)} ${pc.dim(this.value || opts.placeholder)}`; + return `${title}${re.gray(S_BAR)} ${re.dim(this.value || opts.placeholder)}`; case "cancel": - return `${title}${pc.gray(S_BAR)} ${pc.strikethrough( - pc.dim(this.value ?? ""), - )}${this.value?.trim() ? `\n${pc.gray(S_BAR)}` : ""}`; + return `${title}${re.gray(S_BAR)} ${re.strikethrough( + re.dim(this.value ?? ""), + )}${this.value?.trim() ? `\n${re.gray(S_BAR)}` : ""}`; default: - return `${title}${pc.cyan(S_BAR)} ${value}\n${pc.cyan(S_BAR_END)}\n`; + return `${title}${re.cyan(S_BAR)} ${value}\n${re.cyan(S_BAR_END)}\n`; } }, }).prompt(); @@ -154,26 +154,26 @@ export const confirm = (opts: ConfirmOptions) => { inactive, initialValue: opts.initialValue ?? true, render() { - const title = `${pc.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${re.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; const value = this.value ? active : inactive; switch (this.state) { case "submit": - return `${title}${pc.gray(S_BAR)} ${pc.dim(value)}`; + return `${title}${re.gray(S_BAR)} ${re.dim(value)}`; case "cancel": - return `${title}${pc.gray(S_BAR)} ${pc.strikethrough( - pc.dim(value), - )}\n${pc.gray(S_BAR)}`; + return `${title}${re.gray(S_BAR)} ${re.strikethrough( + re.dim(value), + )}\n${re.gray(S_BAR)}`; default: { - return `${title}${pc.cyan(S_BAR)} ${ + return `${title}${re.cyan(S_BAR)} ${ this.value - ? `${pc.green(S_RADIO_ACTIVE)} ${active}` - : `${pc.dim(S_RADIO_INACTIVE)} ${pc.dim(active)}` - } ${pc.dim("/")} ${ + ? `${re.green(S_RADIO_ACTIVE)} ${active}` + : `${re.dim(S_RADIO_INACTIVE)} ${re.dim(active)}` + } ${re.dim("/")} ${ !this.value - ? `${pc.green(S_RADIO_ACTIVE)} ${inactive}` - : `${pc.dim(S_RADIO_INACTIVE)} ${pc.dim(inactive)}` - }\n${pc.cyan(S_BAR_END)}\n`; + ? `${re.green(S_RADIO_ACTIVE)} ${inactive}` + : `${re.dim(S_RADIO_INACTIVE)} ${re.dim(inactive)}` + }\n${re.cyan(S_BAR_END)}\n`; } } }, @@ -201,15 +201,15 @@ export const select = (opts: SelectOptions) => { const label = option.label ?? String(option.value); switch (state) { case "selected": - return pc.dim(label); + return re.dim(label); case "active": - return `${pc.green(S_RADIO_ACTIVE)} ${label} ${ - option.hint ? pc.dim(`(${option.hint})`) : "" + return `${re.green(S_RADIO_ACTIVE)} ${label} ${ + option.hint ? re.dim(`(${option.hint})`) : "" }`; case "cancelled": - return pc.strikethrough(pc.dim(label)); + return re.strikethrough(re.dim(label)); default: - return `${pc.dim(S_RADIO_INACTIVE)} ${pc.dim(label)}`; + return `${re.dim(S_RADIO_INACTIVE)} ${re.dim(label)}`; } }; @@ -217,23 +217,23 @@ export const select = (opts: SelectOptions) => { options: opts.options, initialValue: opts.initialValue, render() { - const title = `${pc.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${re.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; switch (this.state) { case "submit": - return `${title}${pc.gray(S_BAR)} ${opt(this.options[this.cursor], "selected")}`; + return `${title}${re.gray(S_BAR)} ${opt(this.options[this.cursor], "selected")}`; case "cancel": - return `${title}${pc.gray(S_BAR)} ${opt( + return `${title}${re.gray(S_BAR)} ${opt( this.options[this.cursor], "cancelled", - )}\n${pc.gray(S_BAR)}`; + )}\n${re.gray(S_BAR)}`; default: { - return `${title}${pc.cyan(S_BAR)} ${limitOptions({ + return `${title}${re.cyan(S_BAR)} ${limitOptions({ cursor: this.cursor, options: this.options, maxItems: opts.maxItems, style: (item, active) => opt(item, active ? "active" : "inactive"), - }).join(`\n${pc.cyan(S_BAR)} `)}\n${pc.cyan(S_BAR_END)}\n`; + }).join(`\n${re.cyan(S_BAR)} `)}\n${re.cyan(S_BAR_END)}\n`; } } }, @@ -247,16 +247,16 @@ export const selectKey = (opts: SelectOptions) => { ) => { const label = option.label ?? String(option.value); if (state === "selected") { - return pc.dim(label); + return re.dim(label); } else if (state === "cancelled") { - return pc.strikethrough(pc.dim(label)); + return re.strikethrough(re.dim(label)); } else if (state === "active") { - return `${pc.bgCyan(pc.gray(` ${option.value} `))} ${label} ${ - option.hint ? pc.dim(`(${option.hint})`) : "" + return `${re.bgCyan(re.gray(` ${option.value} `))} ${label} ${ + option.hint ? re.dim(`(${option.hint})`) : "" }`; } - return `${pc.gray(pc.bgWhite(pc.inverse(` ${option.value} `)))} ${label} ${ - option.hint ? pc.dim(`(${option.hint})`) : "" + return `${re.gray(re.bgWhite(re.inverse(` ${option.value} `)))} ${label} ${ + option.hint ? re.dim(`(${option.hint})`) : "" }`; }; @@ -264,24 +264,24 @@ export const selectKey = (opts: SelectOptions) => { options: opts.options, initialValue: opts.initialValue, render() { - const title = `${pc.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${re.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; switch (this.state) { case "submit": - return `${title}${pc.gray(S_BAR)} ${opt( + return `${title}${re.gray(S_BAR)} ${opt( this.options.find((opt) => opt.value === this.value), "selected", )}`; case "cancel": - return `${title}${pc.gray(S_BAR)} ${opt(this.options[0], "cancelled")}\n${pc.gray( + return `${title}${re.gray(S_BAR)} ${opt(this.options[0], "cancelled")}\n${re.gray( S_BAR, )}`; default: { - return `${title}${pc.cyan(S_BAR)} ${this.options + return `${title}${re.cyan(S_BAR)} ${this.options .map((option, i) => opt(option, i === this.cursor ? "active" : "inactive"), ) - .join(`\n${pc.cyan(S_BAR)} `)}\n${pc.cyan(S_BAR_END)}\n`; + .join(`\n${re.cyan(S_BAR)} `)}\n${re.cyan(S_BAR_END)}\n`; } } }, @@ -309,21 +309,21 @@ export const multiselect = (opts: MultiSelectOptions) => { ) => { const label = option.label ?? String(option.value); if (state === "active") { - return `${pc.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ - option.hint ? pc.dim(`(${option.hint})`) : "" + return `${re.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ + option.hint ? re.dim(`(${option.hint})`) : "" }`; } else if (state === "selected") { - return `${pc.green(S_CHECKBOX_SELECTED)} ${pc.dim(label)}`; + return `${re.green(S_CHECKBOX_SELECTED)} ${re.dim(label)}`; } else if (state === "cancelled") { - return pc.strikethrough(pc.dim(label)); + return re.strikethrough(re.dim(label)); } else if (state === "active-selected") { - return `${pc.green(S_CHECKBOX_SELECTED)} ${label} ${ - option.hint ? pc.dim(`(${option.hint})`) : "" + return `${re.green(S_CHECKBOX_SELECTED)} ${label} ${ + option.hint ? re.dim(`(${option.hint})`) : "" }`; } else if (state === "submitted") { - return pc.dim(label); + return re.dim(label); } - return `${pc.dim(S_CHECKBOX_INACTIVE)} ${pc.dim(label)}`; + return `${re.dim(S_CHECKBOX_INACTIVE)} ${re.dim(label)}`; }; return new MultiSelectPrompt({ @@ -332,7 +332,7 @@ export const multiselect = (opts: MultiSelectOptions) => { required: opts.required ?? true, cursorAt: opts.cursorAt, render() { - const title = `${pc.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${re.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; const styleOption = (option: Option, active: boolean) => { const selected = this.value.includes(option.value); @@ -347,20 +347,20 @@ export const multiselect = (opts: MultiSelectOptions) => { switch (this.state) { case "submit": { - return `${title}${pc.gray(S_BAR)} ${ + return `${title}${re.gray(S_BAR)} ${ this.options .filter(({ value }) => this.value.includes(value)) .map((option) => opt(option, "submitted")) - .join(pc.dim(", ")) || pc.dim("none") + .join(re.dim(", ")) || re.dim("none") }`; } case "cancel": { const label = this.options .filter(({ value }) => this.value.includes(value)) .map((option) => opt(option, "cancelled")) - .join(pc.dim(", ")); - return `${title}${pc.gray(S_BAR)} ${ - label.trim() ? `${label}\n${pc.gray(S_BAR)}` : "" + .join(re.dim(", ")); + return `${title}${re.gray(S_BAR)} ${ + label.trim() ? `${label}\n${re.gray(S_BAR)}` : "" }`; } case "error": { @@ -368,24 +368,24 @@ export const multiselect = (opts: MultiSelectOptions) => { .split("\n") .map((ln, i) => i === 0 - ? `${pc.yellow(S_BAR_END)} ${pc.yellow(ln)}` + ? `${re.yellow(S_BAR_END)} ${re.yellow(ln)}` : ` ${ln}`, ) .join("\n"); - return `${title + pc.yellow(S_BAR)} ${limitOptions({ + return `${title + re.yellow(S_BAR)} ${limitOptions({ options: this.options, cursor: this.cursor, maxItems: opts.maxItems, style: styleOption, - }).join(`\n${pc.yellow(S_BAR)} `)}\n${footer}\n`; + }).join(`\n${re.yellow(S_BAR)} `)}\n${footer}\n`; } default: { - return `${title}${pc.cyan(S_BAR)} ${limitOptions({ + return `${title}${re.cyan(S_BAR)} ${limitOptions({ options: this.options, cursor: this.cursor, maxItems: opts.maxItems, style: styleOption, - }).join(`\n${pc.cyan(S_BAR)} `)}\n${pc.cyan(S_BAR_END)}\n`; + }).join(`\n${re.cyan(S_BAR)} `)}\n${re.cyan(S_BAR_END)}\n`; } } }, @@ -423,25 +423,25 @@ export const groupMultiselect = ( const prefix = isItem ? `${isLast ? S_BAR_END : S_BAR} ` : ""; if (state === "active") { - return `${pc.dim(prefix)}${pc.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ - option.hint ? pc.dim(`(${option.hint})`) : "" + return `${re.dim(prefix)}${re.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ + option.hint ? re.dim(`(${option.hint})`) : "" }`; } else if (state === "group-active") { - return `${prefix}${pc.cyan(S_CHECKBOX_ACTIVE)} ${pc.dim(label)}`; + return `${prefix}${re.cyan(S_CHECKBOX_ACTIVE)} ${re.dim(label)}`; } else if (state === "group-active-selected") { - return `${prefix}${pc.green(S_CHECKBOX_SELECTED)} ${pc.dim(label)}`; + return `${prefix}${re.green(S_CHECKBOX_SELECTED)} ${re.dim(label)}`; } else if (state === "selected") { - return `${pc.dim(prefix)}${pc.green(S_CHECKBOX_SELECTED)} ${pc.dim(label)}`; + return `${re.dim(prefix)}${re.green(S_CHECKBOX_SELECTED)} ${re.dim(label)}`; } else if (state === "cancelled") { - return pc.strikethrough(pc.dim(label)); + return re.strikethrough(re.dim(label)); } else if (state === "active-selected") { - return `${pc.dim(prefix)}${pc.green(S_CHECKBOX_SELECTED)} ${label} ${ - option.hint ? pc.dim(`(${option.hint})`) : "" + return `${re.dim(prefix)}${re.green(S_CHECKBOX_SELECTED)} ${label} ${ + option.hint ? re.dim(`(${option.hint})`) : "" }`; } else if (state === "submitted") { - return pc.dim(label); + return re.dim(label); } - return `${pc.dim(prefix)}${pc.dim(S_CHECKBOX_INACTIVE)} ${pc.dim(label)}`; + return `${re.dim(prefix)}${re.dim(S_CHECKBOX_INACTIVE)} ${re.dim(label)}`; }; return new GroupMultiSelectPrompt({ @@ -450,22 +450,22 @@ export const groupMultiselect = ( required: opts.required ?? true, cursorAt: opts.cursorAt, render() { - const title = `${pc.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${re.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; switch (this.state) { case "submit": { - return `${title}${pc.gray(S_BAR)} ${this.options + return `${title}${re.gray(S_BAR)} ${this.options .filter(({ value }) => this.value.includes(value)) .map((option) => opt(option, "submitted")) - .join(pc.dim(", "))}`; + .join(re.dim(", "))}`; } case "cancel": { const label = this.options .filter(({ value }) => this.value.includes(value)) .map((option) => opt(option, "cancelled")) - .join(pc.dim(", ")); - return `${title}${pc.gray(S_BAR)} ${ - label.trim() ? `${label}\n${pc.gray(S_BAR)}` : "" + .join(re.dim(", ")); + return `${title}${re.gray(S_BAR)} ${ + label.trim() ? `${label}\n${re.gray(S_BAR)}` : "" }`; } case "error": { @@ -473,11 +473,11 @@ export const groupMultiselect = ( .split("\n") .map((ln, i) => i === 0 - ? `${pc.yellow(S_BAR_END)} ${pc.yellow(ln)}` + ? `${re.yellow(S_BAR_END)} ${re.yellow(ln)}` : ` ${ln}`, ) .join("\n"); - return `${title}${pc.yellow(S_BAR)} ${this.options + return `${title}${re.yellow(S_BAR)} ${this.options .map((option, i, options) => { const selected = this.value.includes(option.value) || @@ -503,10 +503,10 @@ export const groupMultiselect = ( } return opt(option, active ? "active" : "inactive", options); }) - .join(`\n${pc.yellow(S_BAR)} `)}\n${footer}\n`; + .join(`\n${re.yellow(S_BAR)} `)}\n${footer}\n`; } default: { - return `${title}${pc.cyan(S_BAR)} ${this.options + return `${title}${re.cyan(S_BAR)} ${this.options .map((option, i, options) => { const selected = this.value.includes(option.value) || @@ -532,7 +532,7 @@ export const groupMultiselect = ( } return opt(option, active ? "active" : "inactive", options); }) - .join(`\n${pc.cyan(S_BAR)} `)}\n${pc.cyan(S_BAR_END)}\n`; + .join(`\n${re.cyan(S_BAR)} `)}\n${re.cyan(S_BAR_END)}\n`; } } }, @@ -554,29 +554,29 @@ export const note = (message = "", title = "") => { const msg = lines .map( (ln) => - `${pc.gray(S_BAR)} ${pc.dim(ln)}${" ".repeat(len - strip(ln).length)}${pc.gray( + `${re.gray(S_BAR)} ${re.dim(ln)}${" ".repeat(len - strip(ln).length)}${re.gray( S_BAR, )}`, ) .join("\n"); process.stdout.write( - `${pc.gray(S_BAR)}\n${pc.green(S_STEP_SUBMIT)} ${pc.reset(title)} ${pc.gray( + `${re.gray(S_BAR)}\n${re.green(S_STEP_SUBMIT)} ${re.reset(title)} ${re.gray( S_BAR_H.repeat(Math.max(len - titleLen - 1, 1)) + S_CORNER_TOP_RIGHT, - )}\n${msg}\n${pc.gray(S_CONNECT_LEFT + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}\n`, + )}\n${msg}\n${re.gray(S_CONNECT_LEFT + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}\n`, ); }; export const cancel = (message = "") => { - process.stdout.write(`${pc.gray(S_BAR_END)} ${pc.red(message)}\n\n`); + process.stdout.write(`${re.gray(S_BAR_END)} ${re.red(message)}\n\n`); }; export const intro = (title = "") => { - process.stdout.write(`${pc.gray(S_BAR_START)} ${title}\n`); + process.stdout.write(`${re.gray(S_BAR_START)} ${title}\n`); }; export const outro = (message = "") => { process.stdout.write( - `${pc.gray(S_BAR)}\n${pc.gray(S_BAR_END)} ${message}\n\n`, + `${re.gray(S_BAR)}\n${re.gray(S_BAR_END)} ${message}\n\n`, ); }; @@ -586,36 +586,36 @@ export type LogMessageOptions = { export const log = { message: ( message = "", - { symbol = pc.gray(S_BAR) }: LogMessageOptions = {}, + { symbol = re.gray(S_BAR) }: LogMessageOptions = {}, ) => { - const parts = [pc.gray(S_BAR)]; + const parts = [re.gray(S_BAR)]; if (message) { const [firstLine, ...lines] = message.split("\n"); parts.push( `${symbol} ${firstLine}`, - ...lines.map((ln) => `${pc.gray(S_BAR)} ${ln}`), + ...lines.map((ln) => `${re.gray(S_BAR)} ${ln}`), ); } process.stdout.write(`${parts.join("\n")}\n`); }, info: (message: string) => { - log.message(message, { symbol: pc.blue(S_INFO) }); + log.message(message, { symbol: re.blue(S_INFO) }); }, success: (message: string) => { - log.message(message, { symbol: pc.green(S_SUCCESS) }); + log.message(message, { symbol: re.green(S_SUCCESS) }); }, step: (message: string) => { - log.message(message, { symbol: pc.green(S_STEP_SUBMIT) }); + log.message(message, { symbol: re.green(S_STEP_SUBMIT) }); }, warn: (message: string) => { - log.message(message, { symbol: pc.yellow(S_WARN) }); + log.message(message, { symbol: re.yellow(S_WARN) }); }, /** alias for `log.warn()`. */ warning: (message: string) => { log.warn(message); }, error: (message: string) => { - log.message(message, { symbol: pc.red(S_ERROR) }); + log.message(message, { symbol: re.red(S_ERROR) }); }, }; @@ -665,14 +665,14 @@ export const spinner = () => { isSpinnerActive = true; unblock = block(); _message = msg.replace(/\.+$/, ""); - process.stdout.write(`${pc.gray(S_BAR)}\n`); + process.stdout.write(`${re.gray(S_BAR)}\n`); let frameIndex = 0; let dotsTimer = 0; registerHooks(); // @ts-expect-error TODO: fix ts loop = setInterval(() => { - const frame = pc.magenta(frames[frameIndex]); + const frame = re.magenta(frames[frameIndex]); const loadingDots = ".".repeat(Math.floor(dotsTimer)).slice(0, 3); process.stdout.write(cursor.move(-999, 0)); process.stdout.write(erase.down(1)); @@ -688,10 +688,10 @@ export const spinner = () => { clearInterval(loop); const step = code === 0 - ? pc.green(S_STEP_SUBMIT) + ? re.green(S_STEP_SUBMIT) : code === 1 - ? pc.red(S_STEP_CANCEL) - : pc.red(S_STEP_ERROR); + ? re.red(S_STEP_CANCEL) + : re.red(S_STEP_ERROR); process.stdout.write(cursor.move(-999, 0)); process.stdout.write(erase.down(1)); process.stdout.write(`${step} ${_message}\n`); diff --git a/src/utils/prevent.ts b/src/utils/prevent.ts index bf44dcd..7fc10c3 100644 --- a/src/utils/prevent.ts +++ b/src/utils/prevent.ts @@ -3,7 +3,7 @@ import { getTerminalWidth, msg, } from "@reliverse/relinka"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; import terminalSize from "terminal-size"; export type PreventWrongTerminalSizeOptions = { @@ -43,7 +43,7 @@ export async function preventWrongTerminalSize({ if (errors.length > 0) { msg({ type: "M_ERROR", - title: pc.redBright(errors.join("\n││ ")), + title: re.redBright(errors.join("\n││ ")), content: size.rows >= 7 && terminalWidth >= 70 ? sizeErrorDescription : "", contentColor: "redBright", diff --git a/src/utils/prompt-end.ts b/src/utils/prompt-end.ts index 83c897a..e84b266 100644 --- a/src/utils/prompt-end.ts +++ b/src/utils/prompt-end.ts @@ -7,7 +7,7 @@ import { type ColorName, type TypographyName, } from "@reliverse/relinka"; -import pc from "picocolors"; +import { re } from "@reliverse/relico"; /** * Ends the prompt by optionally displaying an end message and running the action if confirmed. @@ -66,14 +66,14 @@ export async function completePrompt( export function renderEndLine() { const lineLength = getExactTerminalWidth() - 2; - console.log(pc.dim(symbols.middle)); - console.log(pc.dim(`${symbols.end}${symbols.line.repeat(lineLength)}⊱`)); + console.log(re.dim(symbols.middle)); + console.log(re.dim(`${symbols.end}${symbols.line.repeat(lineLength)}⊱`)); console.log(); } export function renderEndLineInput() { const lineLength = getExactTerminalWidth() - 2; console.log(); - console.log(pc.dim(`${symbols.end}${symbols.line.repeat(lineLength)}⊱`)); + console.log(re.dim(`${symbols.end}${symbols.line.repeat(lineLength)}⊱`)); console.log(); } diff --git a/src/utils/system.ts b/src/utils/system.ts index b833a00..78ed749 100644 --- a/src/utils/system.ts +++ b/src/utils/system.ts @@ -4,7 +4,7 @@ export const pm = await detect(); export const pmv = await getNpmVersion(pm); export const pkg = { name: "@reliverse/prompts", - version: "1.4.7", + version: "1.4.9", description: "@reliverse/prompts is a powerful library that enables seamless, typesafe, and resilient prompts for command-line applications. Crafted with simplicity and elegance, it provides developers with an intuitive and robust way to build interactive CLIs.", };