@peteanderson/ansi 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +124 -28
  2. package/dist/ansi.d.ts +52 -0
  3. package/dist/ansi.js +55 -0
  4. package/dist/caret.d.ts +28 -0
  5. package/dist/caret.js +70 -0
  6. package/dist/erase.d.ts +13 -0
  7. package/dist/erase.js +30 -0
  8. package/dist/features/build.d.ts +11 -0
  9. package/dist/features/build.js +50 -0
  10. package/dist/features/detect.d.ts +4 -0
  11. package/dist/features/detect.js +33 -0
  12. package/dist/features/features.d.ts +5 -0
  13. package/dist/features/features.js +39 -0
  14. package/dist/features/index.d.ts +2 -0
  15. package/dist/features/index.js +2 -0
  16. package/dist/index.d.ts +59 -0
  17. package/dist/index.js +2 -0
  18. package/dist/pad.d.ts +12 -0
  19. package/dist/pad.js +15 -0
  20. package/dist/patterns.d.ts +3 -0
  21. package/dist/patterns.js +43 -0
  22. package/dist/scroll.d.ts +6 -0
  23. package/dist/scroll.js +14 -0
  24. package/dist/sgr/color/color.d.ts +9 -0
  25. package/dist/sgr/color/color.js +76 -0
  26. package/dist/sgr/color/index.d.ts +2 -0
  27. package/dist/sgr/color/index.js +2 -0
  28. package/dist/sgr/color/utils.d.ts +27 -0
  29. package/dist/sgr/color/utils.js +67 -0
  30. package/dist/sgr/index.d.ts +9 -0
  31. package/dist/sgr/index.js +6 -0
  32. package/dist/sgr/reset.d.ts +1 -0
  33. package/dist/sgr/reset.js +3 -0
  34. package/dist/sgr/sgr.d.ts +14 -0
  35. package/dist/sgr/sgr.js +57 -0
  36. package/dist/sgr/style.d.ts +13 -0
  37. package/dist/sgr/style.js +19 -0
  38. package/dist/sgr/utils.d.ts +2 -0
  39. package/dist/sgr/utils.js +16 -0
  40. package/dist/simplify/atom.d.ts +3 -0
  41. package/dist/simplify/atom.js +28 -0
  42. package/dist/simplify/index.d.ts +2 -0
  43. package/dist/simplify/index.js +2 -0
  44. package/dist/simplify/lookup.d.ts +5 -0
  45. package/dist/simplify/lookup.js +77 -0
  46. package/dist/simplify/simplify.d.ts +1 -0
  47. package/dist/simplify/simplify.js +25 -0
  48. package/dist/simplify/state.d.ts +10 -0
  49. package/dist/simplify/state.js +57 -0
  50. package/dist/simplify/utils.d.ts +2 -0
  51. package/dist/simplify/utils.js +31 -0
  52. package/dist/slice.d.ts +1 -0
  53. package/dist/slice.js +83 -0
  54. package/dist/strip.d.ts +15 -0
  55. package/dist/strip.js +18 -0
  56. package/package.json +13 -20
  57. package/src/ansi.js +65 -0
  58. package/src/caret.js +86 -0
  59. package/src/erase.js +34 -0
  60. package/src/features/build.js +60 -0
  61. package/src/features/detect.js +39 -0
  62. package/src/features/features.js +53 -0
  63. package/src/features/index.js +1 -0
  64. package/src/features/types.d.ts +9 -0
  65. package/src/index.js +1 -0
  66. package/src/pad.js +21 -0
  67. package/src/patterns.js +44 -0
  68. package/src/scroll.js +18 -0
  69. package/src/sgr/color/color.js +104 -0
  70. package/src/sgr/color/index.js +1 -0
  71. package/src/sgr/color/types.d.ts +28 -0
  72. package/src/sgr/color/utils.js +81 -0
  73. package/src/sgr/index.js +5 -0
  74. package/src/sgr/reset.js +3 -0
  75. package/src/sgr/sgr.js +77 -0
  76. package/src/sgr/style.js +23 -0
  77. package/src/sgr/utils.js +20 -0
  78. package/src/simplify/atom.js +34 -0
  79. package/src/simplify/index.js +1 -0
  80. package/src/simplify/lookup.js +86 -0
  81. package/src/simplify/simplify.js +33 -0
  82. package/src/simplify/state.js +73 -0
  83. package/src/simplify/types.d.ts +28 -0
  84. package/src/simplify/utils.js +41 -0
  85. package/src/slice.js +99 -0
  86. package/src/strip.js +23 -0
  87. package/ansi.js +0 -28
package/dist/pad.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ const { visibleLength } = require("./strip");
3
+ /**
4
+ * Pad the given text at the end with the specified fill string to the target visible length without
5
+ * counting ANSI escape codes.
6
+ * @type {(text: string, targetLength: number, fillString?: string) => string}
7
+ */
8
+ const padEnd = (text, targetLength, fillString = " ") => text + fillString.repeat(Math.max(0, targetLength - visibleLength(text)) / fillString.length);
9
+ /**
10
+ * Pad the given text at the start with the specified fill string to the target visible length
11
+ * without counting ANSI escape codes.
12
+ * @type {(text: string, targetLength: number, fillString?: string) => string}
13
+ */
14
+ const padStart = (text, targetLength, fillString = " ") => fillString.repeat(Math.max(0, targetLength - visibleLength(text)) / fillString.length) + text;
15
+ module.exports = { padEnd, padStart };
@@ -0,0 +1,3 @@
1
+ export const rxSGR: Readonly<RegExp>;
2
+ export const rxCSI: Readonly<RegExp>;
3
+ export const rxUnsafe: Readonly<RegExp>;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ /*
3
+ I'm freezing these to prevent accidental mutation of the `lastIndex` property, which could keep
4
+ future calls from starting at the beginning of the string.
5
+
6
+ Code using these objects will have to clone them (with `new RegExp(rx)`) before using functions
7
+ that modify `lastIndex` (such as `exec`). But according to my benchmarks, cloning the regex takes
8
+ mere nanoseconds, and it wasn't even measurable when operating on longer strings. V8 clearly uses a
9
+ global cache of compiled regexes. I assume that SpiderMonkey does the same thing.
10
+
11
+ Here are my `timeit` benchmark results:
12
+
13
+ ```js
14
+ const {timeit} = require("$timeit");
15
+
16
+ const rxSGR = /\x1b\[[\d;]*m/g;
17
+ const short = "\x1b[31mred \x1b[32mgreen \x1b[34mblue \x1b[0m";
18
+ const long = short.repeat(1000);
19
+
20
+ timeit(() => short.replace(rxSGR, ""), {description: "short - no clone "});
21
+ timeit(() => short.replace(new RegExp(rxSGR), ""), {description: "short - clone object "});
22
+ timeit(() => short.replace(new RegExp(rxSGR.source, "g"), ""), {description: "short - clone source "});
23
+
24
+ console.log();
25
+
26
+ timeit(() => long .replace(rxSGR, ""), {description: "long - no clone "});
27
+ timeit(() => long .replace(new RegExp(rxSGR), ""), {description: "long - clone object "});
28
+ timeit(() => long .replace(new RegExp(rxSGR.source, "g"), ""), {description: "long - clone source "});
29
+
30
+ // Output:
31
+ // short - no clone : 82.73 ns (12,087,731 in 1.000016982 s)
32
+ // short - clone object : 142.132 ns (7,035,692 in 1.0000001770000002 s)
33
+ // short - clone source : 127.872 ns (7,820,293 in 1.00000017 s)
34
+ //
35
+ // long - no clone : 47.953 ųs (20,854 in 1.0000098149999999 s)
36
+ // long - clone object : 46.713 ųs (21,408 in 1.0000285520000003 s)
37
+ // long - clone source : 46.491 ųs (21,510 in 1.000026492 s)
38
+ ```
39
+ */
40
+ const rxSGR = Object.freeze(/\x1b\[[\d;]*m/g);
41
+ const rxCSI = Object.freeze(/\x1b\[[0-?]*[ -/]*[@-~]/g);
42
+ const rxUnsafe = Object.freeze(/\x1b\[[0-?]*[ -/]*[@-ln-~]/g); // Everything except SGR codes (m).
43
+ module.exports = { rxSGR, rxCSI, rxUnsafe };
@@ -0,0 +1,6 @@
1
+ export function makeScroll(enabled?: boolean): {
2
+ /** @type {(lines?: number) => string} */
3
+ up: (lines?: number) => string;
4
+ /** @type {(lines?: number) => string} */
5
+ down: (lines?: number) => string;
6
+ };
package/dist/scroll.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ const scroll = {
3
+ /** @type {(lines?: number) => string} */
4
+ up: lines => `\x1b[${lines ?? 1}S`,
5
+ /** @type {(lines?: number) => string} */
6
+ down: lines => `\x1b[${lines ?? 1}T`,
7
+ };
8
+ /** @type {typeof scroll} */
9
+ const disabled = {
10
+ up: () => "",
11
+ down: () => "",
12
+ };
13
+ const makeScroll = (enabled = true) => enabled ? scroll : disabled;
14
+ module.exports = { makeScroll };
@@ -0,0 +1,9 @@
1
+ export type Code = import("../sgr").Code;
2
+ export type ColorDepth = import("../../features/types").ColorDepth;
3
+ export type SGR = import("../sgr").SGR;
4
+ export type Channel = import("./types").Channel;
5
+ /** @type {(colorDepth: ColorDepth) => {fg: Channel, bg: Channel}} */
6
+ export const makeColor: (colorDepth: ColorDepth) => {
7
+ fg: Channel;
8
+ bg: Channel;
9
+ };
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ const { makeSGR } = require("../sgr");
3
+ const { makeStyle } = require("../style");
4
+ const { clip, rgbToX256, rgbToX16, rgbToX8, x256ToRgb } = require("./utils");
5
+ /** @typedef {import("../sgr").Code} Code */
6
+ /** @typedef {import("../../features/types").ColorDepth} ColorDepth */
7
+ /** @typedef {import("../sgr").SGR} SGR */
8
+ /** @typedef {import("./types").Channel} Channel */
9
+ const fgOpen = 30, fgClose = 39;
10
+ const bgOpen = 40, bgClose = 49;
11
+ /** @type {(colorDepth: ColorDepth, open: number, close: number) => Channel} */
12
+ function makeChannel(colorDepth, open, close) {
13
+ const sgrBase = makeSGR(colorDepth > 1);
14
+ const style = makeStyle(colorDepth > 1);
15
+ /** @type {(code: Code) => SGR} */
16
+ const sgr = code => sgrBase(code, close);
17
+ /** @type {(index: number) => SGR} */
18
+ const bright = colorDepth >= 4 ? index => sgr(open + 60 + index) :
19
+ colorDepth === 3 ?
20
+ open === fgOpen ?
21
+ index => style.bold.combine(sgr(open + index)) :
22
+ index => sgr(open + index) :
23
+ () => sgr(0);
24
+ /** @type {(code: number) => SGR} */
25
+ const x16ToX8 = open === fgOpen ?
26
+ code => code >= 60 ? style.bold.combine(sgr(open + code - 60)) : sgr(open + code) :
27
+ code => code >= 60 ? sgr(open + code - 60) : sgr(open + code);
28
+ return {
29
+ black: sgr(open + 0),
30
+ red: sgr(open + 1),
31
+ green: sgr(open + 2),
32
+ yellow: sgr(open + 3),
33
+ blue: sgr(open + 4),
34
+ magenta: sgr(open + 5),
35
+ cyan: sgr(open + 6),
36
+ white: sgr(open + 7),
37
+ brightBlack: bright(0),
38
+ brightRed: bright(1),
39
+ brightGreen: bright(2),
40
+ brightYellow: bright(3),
41
+ brightBlue: bright(4),
42
+ brightMagenta: bright(5),
43
+ brightCyan: bright(6),
44
+ brightWhite: bright(7),
45
+ rgb: colorDepth >= 24 ? (r, g, b) => sgr([open + 8, 2, ...clip(r, g, b)]) :
46
+ colorDepth >= 8 ? (r, g, b) => sgr([open + 8, 5, rgbToX256(...clip(r, g, b))]) :
47
+ colorDepth >= 4 ? (r, g, b) => sgr(open + rgbToX16(...clip(r, g, b))) :
48
+ colorDepth === 3 ?
49
+ open === fgOpen ?
50
+ (r, g, b) => x16ToX8(rgbToX16(...clip(r, g, b))) :
51
+ (r, g, b) => sgr(open + rgbToX8(...clip(r, g, b))) :
52
+ () => sgr(0),
53
+ /** @type {(...args: [number] | [number, number, number]) => SGR} */
54
+ x256: colorDepth >= 8 ?
55
+ (...args) => sgr([open + 8, 5, args.length === 3 ? rgbToX256(...clip(...args)) : clip(args[0])]) :
56
+ colorDepth >= 4 ?
57
+ (...args) => sgr(open +
58
+ rgbToX16(...(args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0]))))) :
59
+ colorDepth === 3 ?
60
+ open === fgOpen ?
61
+ (...args) => x16ToX8(args.length === 3 ?
62
+ rgbToX16(...clip(...args)) :
63
+ rgbToX16(...x256ToRgb(clip(args[0])))) :
64
+ (...args) => sgr(open + rgbToX8(...args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0])))) :
65
+ () => sgr(0),
66
+ default: colorDepth > 1 ? `\x1b[${close}m` : "",
67
+ };
68
+ }
69
+ /** @type {Partial<Record<ColorDepth, {fg: Channel, bg: Channel}>>} */
70
+ const cache = {};
71
+ /** @type {(colorDepth: ColorDepth) => {fg: Channel, bg: Channel}} */
72
+ const makeColor = (colorDepth = 4) => cache[colorDepth] ?? (cache[colorDepth] = {
73
+ fg: makeChannel(colorDepth, fgOpen, fgClose),
74
+ bg: makeChannel(colorDepth, bgOpen, bgClose),
75
+ });
76
+ module.exports = { makeColor };
@@ -0,0 +1,2 @@
1
+ declare const _exports: typeof import("./color");
2
+ export = _exports;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ module.exports = require("./color");
@@ -0,0 +1,27 @@
1
+ export type Code = import("../sgr").Code;
2
+ export function rgbToX256(r: number, g: number, b: number): number;
3
+ export function x256ToRgb(code: number): [number, number, number];
4
+ export function rgbToX16(r: number, g: number, b: number): number;
5
+ export function rgbToX8(r: number, g: number, b: number): number;
6
+ /**
7
+ * @overload
8
+ * @param {number} x
9
+ * @returns {number}
10
+ * @overload
11
+ * @param {number} r
12
+ * @param {number} g
13
+ * @param {number} b
14
+ * @returns {[number, number, number]}
15
+ */
16
+ export function clip(x: number): number;
17
+ /**
18
+ * @overload
19
+ * @param {number} x
20
+ * @returns {number}
21
+ * @overload
22
+ * @param {number} r
23
+ * @param {number} g
24
+ * @param {number} b
25
+ * @returns {[number, number, number]}
26
+ */
27
+ export function clip(r: number, g: number, b: number): [number, number, number];
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ /** @typedef {import("../sgr").Code} Code */
3
+ /** @type {(r: number, g: number, b: number) => number} */
4
+ function rgbToX256(r, g, b) {
5
+ if (r === g && g === b) {
6
+ // grayscale
7
+ return 232 + Math.floor(r * 24 / 256);
8
+ }
9
+ // 6x6x6 color cube
10
+ return (16 +
11
+ Math.floor(r * 6 / 256) * 36 +
12
+ Math.floor(g * 6 / 256) * 6 +
13
+ Math.floor(b * 6 / 256));
14
+ }
15
+ /** @type {(code: number) => [number, number, number]} */
16
+ function x256ToRgb(code) {
17
+ if (code >= 232) {
18
+ // grayscale
19
+ const gray = Math.round((code - 232) * 255 / 24);
20
+ return [gray, gray, gray];
21
+ }
22
+ code -= 16;
23
+ const r = Math.floor(code / 36) * 51;
24
+ const g = Math.floor((code % 36) / 6) * 51;
25
+ const b = (code % 6) * 51;
26
+ return [r, g, b];
27
+ }
28
+ /** @type {(r: number, g: number, b: number) => number} */
29
+ function rgbToX16(r, g, b) {
30
+ // First calculate it as a 4x4x4 cube.
31
+ r = Math.min(2, Math.floor(r / 85));
32
+ g = Math.min(2, Math.floor(g / 85));
33
+ b = Math.min(2, Math.floor(b / 85));
34
+ // Which channels are "on" determines the hue (ANSI color index 0–7).
35
+ const index = (r ? 1 : 0) | (g ? 2 : 0) | (b ? 4 : 0);
36
+ // The bright bit is derived from the max channel level across all three.
37
+ const twos = (r === 2 ? 1 : 0) + (g === 2 ? 1 : 0) + (b === 2 ? 1 : 0);
38
+ const ones = (r === 1 ? 1 : 0) + (g === 1 ? 1 : 0) + (b === 1 ? 1 : 0);
39
+ const bright = twos > 0 && twos >= ones;
40
+ // Return value uses the SGR offset scheme: 0–7 for normal, 60–67 for bright.
41
+ return index + (bright ? 60 : 0);
42
+ }
43
+ /** @type {(r: number, g: number, b: number) => number} */
44
+ function rgbToX8(r, g, b) {
45
+ r = r >= 128 ? 1 : 0;
46
+ g = g >= 128 ? 1 : 0;
47
+ b = b >= 128 ? 1 : 0;
48
+ return (r ? 1 : 0) | (g ? 2 : 0) | (b ? 4 : 0);
49
+ }
50
+ /**
51
+ * @overload
52
+ * @param {number} x
53
+ * @returns {number}
54
+ * @overload
55
+ * @param {number} r
56
+ * @param {number} g
57
+ * @param {number} b
58
+ * @returns {[number, number, number]}
59
+ */ /** @param {[number] | [number, number, number]} args */
60
+ const clip = (...args) => args.length === 1 ?
61
+ Math.max(0, Math.min(255, Math.floor(args[0]))) :
62
+ [
63
+ Math.max(0, Math.min(255, Math.floor(args[0]))),
64
+ Math.max(0, Math.min(255, Math.floor(args[1]))),
65
+ Math.max(0, Math.min(255, Math.floor(args[2]))),
66
+ ];
67
+ module.exports = { rgbToX256, x256ToRgb, rgbToX16, rgbToX8, clip };
@@ -0,0 +1,9 @@
1
+ declare const _exports: {
2
+ makeColor: (colorDepth: ColorDepth) => {
3
+ fg: Channel;
4
+ bg: Channel;
5
+ };
6
+ makeStyle: typeof import("./style").makeStyle;
7
+ makeReset: (enabled?: boolean) => "" | "\u001B[0m";
8
+ };
9
+ export = _exports;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ module.exports = {
3
+ ...require("./reset"),
4
+ ...require("./style"),
5
+ ...require("./color"),
6
+ };
@@ -0,0 +1 @@
1
+ export function makeReset(enabled?: boolean): "" | "\u001B[0m";
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ const makeReset = (enabled = true) => enabled ? "\x1b[0m" : "";
3
+ module.exports = { makeReset };
@@ -0,0 +1,14 @@
1
+ export type Code = number | number[];
2
+ export type SGR = {
3
+ (text: string): string;
4
+ open: string;
5
+ close: string;
6
+ codes: {
7
+ open: Code;
8
+ close: Code;
9
+ };
10
+ combine: (...styles: SGR[]) => SGR;
11
+ };
12
+ export function makeSGR(enabled?: boolean): typeof sgr;
13
+ declare function sgr(open: Code, close: Code, reset?: boolean): SGR;
14
+ export {};
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ @typedef {number | number[]} Code
4
+ @typedef {{
5
+ (text: string): string;
6
+ open : string;
7
+ close : string;
8
+ codes : {open: Code, close: Code};
9
+ combine : (...styles: SGR[]) => SGR;
10
+ }} SGR
11
+ */
12
+ /** @type {(code: Code, codes: Code[]) => Code} */
13
+ function combine(code, codes) {
14
+ if (!codes.length)
15
+ return code;
16
+ const rtn = Array.isArray(code) ? [...code] : [code];
17
+ for (const c of codes) {
18
+ if (Array.isArray(c))
19
+ rtn.push(...c);
20
+ else
21
+ rtn.push(c);
22
+ }
23
+ return rtn;
24
+ }
25
+ /** @type {(code: Code) => string | number} */
26
+ const codeSequence = code => Array.isArray(code) ? code.join(';') : code;
27
+ /** @type {(open: Code, close: Code, reset?: boolean) => SGR} */
28
+ function sgr(open, close, reset = false) {
29
+ const openCode = codeSequence(open);
30
+ const closeCode = codeSequence(close);
31
+ const openSequence = `\x1b[${openCode}m`;
32
+ const closeSequence = `\x1b[${closeCode}m`;
33
+ const reopenCode = reset ? `${closeCode};${openCode}` : openCode;
34
+ const rxClose = new RegExp(`(?<start>\\x1b\\[(?:\\d+;)*)${closeCode}(?<end>(?:;\\d+)*m)`, "g");
35
+ const replace = `$<start>${reopenCode}$<end>`;
36
+ /** @type {(text: string) => string} */
37
+ const func = s => openSequence + s.replace(rxClose, replace) + closeSequence;
38
+ return Object.assign(func, {
39
+ codes: { open, close },
40
+ open: openSequence,
41
+ close: closeSequence,
42
+ /** @type {(...styles: SGR[]) => SGR} */
43
+ combine: (...styles) => sgr(combine(open, styles.map(s => s.codes.open)), combine(close, styles.map(s => s.codes.close))),
44
+ });
45
+ }
46
+ ;
47
+ /** @type {SGR} */
48
+ const disabledSGR = Object.assign(/** @type {(text: string) => string} */ /** @type {(text: string) => string} */ s => s, {
49
+ codes: { open: 0, close: 0 },
50
+ open: "",
51
+ close: "",
52
+ combine: () => disabledSGR,
53
+ });
54
+ /** @type {typeof sgr} */
55
+ const disabled = () => disabledSGR;
56
+ const makeSGR = (enabled = true) => enabled ? sgr : disabled;
57
+ module.exports = { makeSGR };
@@ -0,0 +1,13 @@
1
+ export function makeStyle(enabled?: boolean): {
2
+ bold: import("./sgr").SGR;
3
+ dim: import("./sgr").SGR;
4
+ italic: import("./sgr").SGR;
5
+ underline: import("./sgr").SGR;
6
+ inverse: import("./sgr").SGR;
7
+ hidden: import("./sgr").SGR;
8
+ strikethrough: import("./sgr").SGR;
9
+ doubleUnderline: import("./sgr").SGR;
10
+ frame: import("./sgr").SGR;
11
+ encircle: import("./sgr").SGR;
12
+ overline: import("./sgr").SGR;
13
+ };
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ const { makeSGR } = require("./sgr");
3
+ function makeStyle(enabled = true) {
4
+ const sgr = makeSGR(enabled);
5
+ return {
6
+ bold: sgr(1, 22, true),
7
+ dim: sgr(2, 22, true),
8
+ italic: sgr(3, 23),
9
+ underline: sgr(4, 24),
10
+ inverse: sgr(7, 27),
11
+ hidden: sgr(8, 28),
12
+ strikethrough: sgr(9, 29),
13
+ doubleUnderline: sgr(21, 24),
14
+ frame: sgr(51, 54),
15
+ encircle: sgr(52, 54),
16
+ overline: sgr(53, 55),
17
+ };
18
+ }
19
+ module.exports = { makeStyle };
@@ -0,0 +1,2 @@
1
+ export type Code = number | number[];
2
+ export function combine(code: Code, codes: Code[]): Code;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /** @typedef {number | number[]} Code */
3
+ /** @type {(code: Code, codes: Code[]) => Code} */
4
+ function combine(code, codes) {
5
+ if (!codes.length)
6
+ return code;
7
+ const rtn = Array.isArray(code) ? [...code] : [code];
8
+ for (const c of codes) {
9
+ if (Array.isArray(c))
10
+ rtn.push(...c);
11
+ else
12
+ rtn.push(c);
13
+ }
14
+ return rtn;
15
+ }
16
+ module.exports = { combine };
@@ -0,0 +1,3 @@
1
+ export type BuildAtom = import("./types").BuildAtom;
2
+ export function buildExtendedColorAtom(attribute: import("./types").Attribute, codes: number[], index: number, state: import("./types").AttributeMap): import("./types").Atom;
3
+ export function buildIntensityAtom(attribute: import("./types").Attribute, codes: number[], index: number, state: import("./types").AttributeMap): import("./types").Atom;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ /** @typedef {import("./types").BuildAtom} BuildAtom */
3
+ /** @type {BuildAtom} */
4
+ function buildExtendedColorAtom(attribute, codes, index) {
5
+ switch (codes[index + 1]) {
6
+ case 2:
7
+ if (index + 5 >= codes.length)
8
+ return { attribute, code: codes.slice(index), skip: codes.length - index - 1 };
9
+ return { attribute, code: codes.slice(index, index + 5), skip: 4 };
10
+ case 5:
11
+ if (index + 3 >= codes.length)
12
+ return { attribute, code: codes.slice(index), skip: codes.length - index - 1 };
13
+ return { attribute, code: codes.slice(index, index + 3), skip: 2 };
14
+ default:
15
+ return { attribute, code: codes.slice(index, index + 2), skip: 1 };
16
+ }
17
+ }
18
+ /** @type {BuildAtom} */
19
+ function buildIntensityAtom(attribute, codes, index, state) {
20
+ const code = codes[index];
21
+ if (code === 22)
22
+ return { attribute, code: [code] };
23
+ const current = state.get("intensity");
24
+ if (!current || current.length == 1 && current[0] === code || current[0] === 22)
25
+ return { attribute, code: [code] };
26
+ return { attribute, code: [1, 2] };
27
+ }
28
+ module.exports = { buildExtendedColorAtom, buildIntensityAtom };
@@ -0,0 +1,2 @@
1
+ declare const _exports: typeof import("./simplify");
2
+ export = _exports;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ module.exports = require("./simplify");
@@ -0,0 +1,5 @@
1
+ export type Atom = import("./types").Atom;
2
+ export type Attribute = import("./types").Attribute;
3
+ export type AttributeMap = import("./types").AttributeMap;
4
+ export type BuildAtom = import("./types").BuildAtom;
5
+ export function lookUpAtom(codes: number[], index: number, state: AttributeMap): Atom;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ const { buildExtendedColorAtom, buildIntensityAtom } = require("./atom");
3
+ /** @typedef {import("./types").Atom} Atom */
4
+ /** @typedef {import("./types").Attribute} Attribute */
5
+ /** @typedef {import("./types").AttributeMap} AttributeMap */
6
+ /** @typedef {import("./types").BuildAtom} BuildAtom */
7
+ /** @type {Record<number, [attribute: Attribute, buildAtom?: BuildAtom]>} */
8
+ const codeMap = {
9
+ 0: ["reset"],
10
+ 1: ["intensity", buildIntensityAtom],
11
+ 2: ["intensity", buildIntensityAtom],
12
+ 3: ["italic"],
13
+ 4: ["underline"],
14
+ 7: ["inverse"],
15
+ 8: ["hidden"],
16
+ 9: ["strikethrough"],
17
+ 21: ["underline"],
18
+ 22: ["intensity", buildIntensityAtom],
19
+ 23: ["italic"],
20
+ 24: ["underline"],
21
+ 27: ["inverse"],
22
+ 28: ["hidden"],
23
+ 29: ["strikethrough"],
24
+ 30: ["fg"],
25
+ 31: ["fg"],
26
+ 32: ["fg"],
27
+ 33: ["fg"],
28
+ 34: ["fg"],
29
+ 35: ["fg"],
30
+ 36: ["fg"],
31
+ 37: ["fg"],
32
+ 38: ["fg", buildExtendedColorAtom],
33
+ 39: ["fg"],
34
+ 40: ["bg"],
35
+ 41: ["bg"],
36
+ 42: ["bg"],
37
+ 43: ["bg"],
38
+ 44: ["bg"],
39
+ 45: ["bg"],
40
+ 46: ["bg"],
41
+ 47: ["bg"],
42
+ 48: ["bg", buildExtendedColorAtom],
43
+ 49: ["bg"],
44
+ 51: ["frame"],
45
+ 52: ["frame"],
46
+ 53: ["overline"],
47
+ 54: ["frame"],
48
+ 55: ["overline"],
49
+ 90: ["fg"],
50
+ 91: ["fg"],
51
+ 92: ["fg"],
52
+ 93: ["fg"],
53
+ 94: ["fg"],
54
+ 95: ["fg"],
55
+ 96: ["fg"],
56
+ 97: ["fg"],
57
+ 100: ["bg"],
58
+ 101: ["bg"],
59
+ 102: ["bg"],
60
+ 103: ["bg"],
61
+ 104: ["bg"],
62
+ 105: ["bg"],
63
+ 106: ["bg"],
64
+ 107: ["bg"],
65
+ };
66
+ /** @type {(codes: number[], index: number, state: AttributeMap) => Atom} */
67
+ function lookUpAtom(codes, index, state) {
68
+ const code = codes[index];
69
+ const entry = codeMap[code];
70
+ if (!entry)
71
+ return { attribute: code, code: [code] };
72
+ const [attribute, buildAtom] = entry;
73
+ if (buildAtom)
74
+ return buildAtom(attribute, codes, index, state);
75
+ return { attribute, code: [code] };
76
+ }
77
+ module.exports = { lookUpAtom };
@@ -0,0 +1 @@
1
+ export function simplify(text: string): string;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ const { rxSGR } = require("../patterns");
3
+ const { State } = require("./state");
4
+ /** @type {(text: string) => string} */
5
+ function simplify(text) {
6
+ // `.split` doesn't modify `lastIndex`, so no clone is necessary here.
7
+ const parts = text.split(rxSGR);
8
+ const state = new State();
9
+ let result = parts[0];
10
+ for (let i = 1; i < parts.length;) {
11
+ let codes = parts[i++].split(";").map(Number);
12
+ let chunk = parts[i++];
13
+ while (!chunk && i < parts.length) {
14
+ // Merge consecutive SGR sequences.
15
+ codes.push(...parts[i++].split(";").map(Number));
16
+ chunk = parts[i++];
17
+ }
18
+ codes = state.update(codes);
19
+ if (codes.length)
20
+ result += `\x1b[${codes.join(";")}m`;
21
+ result += chunk;
22
+ }
23
+ return result;
24
+ }
25
+ module.exports = { simplify };
@@ -0,0 +1,10 @@
1
+ export type Attribute = import("./types").Attribute;
2
+ export type AttributeMap = import("./types").AttributeMap;
3
+ /** @type {AttributeMap} */
4
+ export class State extends Map<any, any> {
5
+ constructor();
6
+ constructor(entries?: readonly (readonly [any, any])[] | null | undefined);
7
+ constructor();
8
+ constructor(iterable?: Iterable<readonly [any, any]> | null | undefined);
9
+ update(codes: number[]): number[];
10
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ const { lookUpAtom } = require("./lookup");
3
+ const { arraysEqual, transitionIntensity } = require("./utils");
4
+ /** @typedef {import("./types").Attribute} Attribute */
5
+ /** @typedef {import("./types").AttributeMap} AttributeMap */
6
+ /** @type {Map<Attribute, number[]>} */
7
+ const defaultCodes = new Map([
8
+ ["intensity", [22]],
9
+ ["italic", [23]],
10
+ ["underline", [24]],
11
+ ["inverse", [27]],
12
+ ["hidden", [28]],
13
+ ["strikethrough", [29]],
14
+ ["fg", [39]],
15
+ ["bg", [49]],
16
+ ["frame", [54]],
17
+ ["overline", [55]],
18
+ ]);
19
+ /** @type {AttributeMap} */
20
+ class State extends Map {
21
+ /** @type {(codes: number[]) => number[]} */
22
+ update(codes) {
23
+ /** @type {AttributeMap} */
24
+ let snapshot = new Map(this);
25
+ /** @type {Set<Attribute>} */
26
+ const attributes = new Set();
27
+ for (let i = 0; i < codes.length; i++) {
28
+ const { attribute, code, skip } = lookUpAtom(codes, i, this);
29
+ if (attribute === "reset") {
30
+ attributes.clear();
31
+ this.clear();
32
+ attributes.add("reset");
33
+ for (const [attr, defaultCode] of defaultCodes)
34
+ this.set(attr, defaultCode);
35
+ snapshot = new Map(this);
36
+ continue;
37
+ }
38
+ attributes.add(attribute);
39
+ this.set(attribute, code);
40
+ if (skip)
41
+ i += skip;
42
+ }
43
+ return Array.from(attributes).flatMap(attribute => {
44
+ if (attribute === "reset")
45
+ return [0];
46
+ const code = this.get(attribute);
47
+ if (arraysEqual(code, snapshot.get(attribute)))
48
+ return [];
49
+ if (attribute === "intensity") {
50
+ const previous = snapshot.get(attribute);
51
+ return transitionIntensity(previous, code);
52
+ }
53
+ return code;
54
+ });
55
+ }
56
+ }
57
+ module.exports = { State };