Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions client-src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/* global __resourceQuery, __webpack_public_path__ */

import stripAnsi from "strip-ansi";

import configureOverlay from "./overlay.js";
import applyUpdate from "./process-update.js";
import { log, setLogLevel } from "./utils/log.js";
import stripAnsi from "./utils/strip-ansi.js";

/** @typedef {import("./utils/log.js").LogLevel} LogLevel */

Expand Down
23 changes: 23 additions & 0 deletions client-src/utils/strip-ansi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Matches ANSI escape sequences. Ported from ansi-regex
// (https://github.com/chalk/ansi-regex), inlined so the browser client does
// not need the strip-ansi dependency.

// Valid string terminator sequences are BEL, ESC\, and 0x9c
const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";

// OSC sequences only: ESC ] ... ST (non-greedy until the first ST)
const OSC = `(?:\\u001B\\][\\s\\S]*?${ST})`;

// CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte
const CSI =
"[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";

const ANSI_REGEX = new RegExp(`${OSC}|${CSI}`, "g");

/**
* @param {string} string string possibly containing ANSI escape sequences
* @returns {string} the string without ANSI escape sequences
*/
export default function stripAnsi(string) {
return string.replace(ANSI_REGEX, "");
}
4 changes: 3 additions & 1 deletion cspell.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"eslintcache",
"esmodules",
"noopener",
"noreferrer"
"noreferrer",
"mred",
"mbold"
]
}
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@
"mime-types": "^3.0.2",
"on-finished": "^2.4.1",
"range-parser": "^1.2.1",
"schema-utils": "^4.3.3",
"strip-ansi": "^6.0.1"
"schema-utils": "^4.3.3"
},
"devDependencies": {
"@babel/cli": "^7.16.7",
Expand Down
40 changes: 40 additions & 0 deletions test/strip-ansi.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import stripAnsi from "../client-src/utils/strip-ansi";

const ESC = String.fromCharCode(27);
const BEL = String.fromCharCode(7);

describe("stripAnsi", () => {
it("strips color sequences", () => {
expect(stripAnsi(`${ESC}[31mred${ESC}[39m plain`)).toBe("red plain");
});

it("strips style and background sequences", () => {
expect(stripAnsi(`${ESC}[1m${ESC}[42mbold-bg${ESC}[0m`)).toBe("bold-bg");
});

it("strips 256-color sequences", () => {
expect(stripAnsi(`${ESC}[38;5;196mred${ESC}[0m`)).toBe("red");
});

it("strips cursor and erase sequences", () => {
expect(stripAnsi(`${ESC}[2K${ESC}[1A${ESC}[2K${ESC}[G`)).toBe("");
});

it("strips terminal hyperlinks", () => {
expect(
stripAnsi(`${ESC}]8;;https://example.com${BEL}link${ESC}]8;;${BEL}`),
).toBe("link");
});

it("keeps plain strings untouched", () => {
expect(stripAnsi("no ansi at all")).toBe("no ansi at all");
});

it("strips ansi from a webpack-style error line", () => {
expect(
stripAnsi(
`webpack ${ESC}[1m${ESC}[31mERROR${ESC}[39m${ESC}[22m in ./src/index.js 3:10`,
),
).toBe("webpack ERROR in ./src/index.js 3:10");
});
});
Loading