Skip to content

Commit fbe4393

Browse files
feat: added colors helpers for CLI
1 parent 3e5edf0 commit fbe4393

6 files changed

Lines changed: 593 additions & 190 deletions

File tree

lib/cli.js

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"use strict";
77

88
const path = require("path");
9+
const tty = require("tty");
910
const webpackSchema = require("../schemas/WebpackOptions.json");
1011

1112
/** @typedef {import("json-schema").JSONSchema4} JSONSchema4 */
@@ -712,5 +713,181 @@ const processArguments = (args, config, values) => {
712713
return problems;
713714
};
714715

716+
/**
717+
* @returns {boolean} true when colors supported, otherwise false
718+
*/
719+
const isColorSupported = () => {
720+
const { env = {}, argv = [], platform = "" } = process;
721+
722+
const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
723+
const isForced = "FORCE_COLOR" in env || argv.includes("--color");
724+
const isWindows = platform === "win32";
725+
const isDumbTerminal = env.TERM === "dumb";
726+
727+
const isCompatibleTerminal = tty.isatty(1) && env.TERM && !isDumbTerminal;
728+
729+
const isCI =
730+
"CI" in env &&
731+
("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
732+
733+
return (
734+
!isDisabled &&
735+
(isForced || (isWindows && !isDumbTerminal) || isCompatibleTerminal || isCI)
736+
);
737+
};
738+
739+
/**
740+
* @param {number} index index
741+
* @param {string} string string
742+
* @param {string} close close
743+
* @param {string=} replace replace
744+
* @param {string=} head head
745+
* @param {string=} tail tail
746+
* @param {number=} next next
747+
* @returns {string} result
748+
*/
749+
const replaceClose = (
750+
index,
751+
string,
752+
close,
753+
replace,
754+
head = string.slice(0, Math.max(0, index)) + replace,
755+
tail = string.slice(Math.max(0, index + close.length)),
756+
next = tail.indexOf(close)
757+
) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
758+
759+
/**
760+
* @param {number} index index to replace
761+
* @param {string} string string
762+
* @param {string} open open string
763+
* @param {string} close close string
764+
* @param {string=} replace extra replace
765+
* @returns {string} result
766+
*/
767+
const clearBleed = (index, string, open, close, replace) =>
768+
index < 0
769+
? open + string + close
770+
: open + replaceClose(index, string, close, replace) + close;
771+
772+
/** @typedef {(value: EXPECTED_ANY) => string} PrintFunction */
773+
774+
/**
775+
* @param {string} open open string
776+
* @param {string} close close string
777+
* @param {string=} replace extra replace
778+
* @param {number=} at at
779+
* @returns {PrintFunction} function to create color
780+
*/
781+
const filterEmpty =
782+
(open, close, replace = open, at = open.length + 1) =>
783+
(string) =>
784+
string || !(string === "" || string === undefined)
785+
? clearBleed(`${string}`.indexOf(close, at), string, open, close, replace)
786+
: "";
787+
788+
/**
789+
* @param {number} open open code
790+
* @param {number} close close code
791+
* @param {string=} replace extra replace
792+
* @returns {PrintFunction} result
793+
*/
794+
const init = (open, close, replace) =>
795+
filterEmpty(`\u001B[${open}m`, `\u001B[${close}m`, replace);
796+
797+
/**
798+
* @typedef {{
799+
* reset: PrintFunction
800+
* bold: PrintFunction
801+
* dim: PrintFunction
802+
* italic: PrintFunction
803+
* underline: PrintFunction
804+
* inverse: PrintFunction
805+
* hidden: PrintFunction
806+
* strikethrough: PrintFunction
807+
* black: PrintFunction
808+
* red: PrintFunction
809+
* green: PrintFunction
810+
* yellow: PrintFunction
811+
* blue: PrintFunction
812+
* magenta: PrintFunction
813+
* cyan: PrintFunction
814+
* white: PrintFunction
815+
* gray: PrintFunction
816+
* bgBlack: PrintFunction
817+
* bgRed: PrintFunction
818+
* bgGreen: PrintFunction
819+
* bgYellow: PrintFunction
820+
* bgBlue: PrintFunction
821+
* bgMagenta: PrintFunction
822+
* bgCyan: PrintFunction
823+
* bgWhite: PrintFunction
824+
* blackBright: PrintFunction
825+
* redBright: PrintFunction
826+
* greenBright: PrintFunction
827+
* yellowBright: PrintFunction
828+
* blueBright: PrintFunction
829+
* magentaBright: PrintFunction
830+
* cyanBright: PrintFunction
831+
* whiteBright: PrintFunction
832+
* bgBlackBright: PrintFunction
833+
* bgRedBright: PrintFunction
834+
* bgGreenBright: PrintFunction
835+
* bgYellowBright: PrintFunction
836+
* bgBlueBright: PrintFunction
837+
* bgMagentaBright: PrintFunction
838+
* bgCyanBright: PrintFunction
839+
* bgWhiteBright: PrintFunction
840+
}} Colors */
841+
842+
/**
843+
* @param {{ useColor?: boolean }=} options options
844+
* @returns {Colors} colors
845+
*/
846+
const createColors = ({ useColor = isColorSupported() } = {}) => ({
847+
reset: useColor ? init(0, 0) : String,
848+
bold: useColor ? init(1, 22, "\u001B[22m\u001B[1m") : String,
849+
dim: useColor ? init(2, 22, "\u001B[22m\u001B[2m") : String,
850+
italic: useColor ? init(3, 23) : String,
851+
underline: useColor ? init(4, 24) : String,
852+
inverse: useColor ? init(7, 27) : String,
853+
hidden: useColor ? init(8, 28) : String,
854+
strikethrough: useColor ? init(9, 29) : String,
855+
black: useColor ? init(30, 39) : String,
856+
red: useColor ? init(31, 39) : String,
857+
green: useColor ? init(32, 39) : String,
858+
yellow: useColor ? init(33, 39) : String,
859+
blue: useColor ? init(34, 39) : String,
860+
magenta: useColor ? init(35, 39) : String,
861+
cyan: useColor ? init(36, 39) : String,
862+
white: useColor ? init(37, 39) : String,
863+
gray: useColor ? init(90, 39) : String,
864+
bgBlack: useColor ? init(40, 49) : String,
865+
bgRed: useColor ? init(41, 49) : String,
866+
bgGreen: useColor ? init(42, 49) : String,
867+
bgYellow: useColor ? init(43, 49) : String,
868+
bgBlue: useColor ? init(44, 49) : String,
869+
bgMagenta: useColor ? init(45, 49) : String,
870+
bgCyan: useColor ? init(46, 49) : String,
871+
bgWhite: useColor ? init(47, 49) : String,
872+
blackBright: useColor ? init(90, 39) : String,
873+
redBright: useColor ? init(91, 39) : String,
874+
greenBright: useColor ? init(92, 39) : String,
875+
yellowBright: useColor ? init(93, 39) : String,
876+
blueBright: useColor ? init(94, 39) : String,
877+
magentaBright: useColor ? init(95, 39) : String,
878+
cyanBright: useColor ? init(96, 39) : String,
879+
whiteBright: useColor ? init(97, 39) : String,
880+
bgBlackBright: useColor ? init(100, 49) : String,
881+
bgRedBright: useColor ? init(101, 49) : String,
882+
bgGreenBright: useColor ? init(102, 49) : String,
883+
bgYellowBright: useColor ? init(103, 49) : String,
884+
bgBlueBright: useColor ? init(104, 49) : String,
885+
bgMagentaBright: useColor ? init(105, 49) : String,
886+
bgCyanBright: useColor ? init(106, 49) : String,
887+
bgWhiteBright: useColor ? init(107, 49) : String
888+
});
889+
890+
module.exports.createColors = createColors;
715891
module.exports.getArguments = getArguments;
892+
module.exports.isColorSupported = isColorSupported;
716893
module.exports.processArguments = processArguments;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
"es6-promise-polyfill": "^1.2.0",
134134
"eslint": "^9.29.0",
135135
"eslint-config-prettier": "^10.1.1",
136-
"eslint-config-webpack": "^4.3.0",
136+
"eslint-config-webpack": "^4.4.1",
137137
"eslint-plugin-import": "^2.32.0",
138138
"eslint-plugin-jest": "^29.0.1",
139139
"eslint-plugin-jsdoc": "^51.2.3",

0 commit comments

Comments
 (0)