diff --git a/src/node/util.ts b/src/node/util.ts index 1bf43cd89..a13076fe6 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -17,39 +17,7 @@ export interface Paths { runtime: string } -// From https://github.com/chalk/ansi-regex -const pattern = [ - "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", - "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", -].join("|") -const re = new RegExp(pattern, "g") - export type OnLineCallback = (strippedLine: string, originalLine: string) => void -/** - * Split stdout on newlines and strip ANSI codes. - */ -export const onLine = (proc: cp.ChildProcess, callback: OnLineCallback): void => { - let buffer = "" - if (!proc.stdout) { - throw new Error("no stdout") - } - proc.stdout.setEncoding("utf8") - proc.stdout.on("data", (d) => { - const data = buffer + d - const split = data.split("\n") - const last = split.length - 1 - - for (let i = 0; i < last; ++i) { - callback(split[i].replace(re, ""), split[i]) - } - - // The last item will either be an empty string (the data ended with a - // newline) or a partial line (did not end with a newline) and we must - // wait to parse it until we get a full line. - buffer = split[last] - }) -} - export const paths = getEnvPaths() /** diff --git a/test/e2e/models/CodeServer.ts b/test/e2e/models/CodeServer.ts index 8b3c4a4af..23847fe57 100644 --- a/test/e2e/models/CodeServer.ts +++ b/test/e2e/models/CodeServer.ts @@ -5,9 +5,8 @@ import * as path from "path" import { Page } from "playwright" import * as util from "util" import { logError, plural } from "../../../src/common/util" -import { onLine } from "../../../src/node/util" import { PASSWORD, workspaceDir } from "../../utils/constants" -import { idleTimer, tmpdir } from "../../utils/helpers" +import { idleTimer, onLine, tmpdir } from "../../utils/helpers" interface CodeServerProcess { process: cp.ChildProcess @@ -147,7 +146,7 @@ export class CodeServer { let resolved = false proc.stdout.setEncoding("utf8") - onLine(proc, (line) => { + onLine(proc, (line: string) => { // As long as we are actively getting input reset the timer. If we stop // getting input and still have not found the address the timer will // reject. diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index 1455a7e07..0c1376d1a 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -4,6 +4,7 @@ import * as path from "path" import { generateUuid } from "../../../src/common/util" import { tmpdir } from "../../../src/node/constants" import * as util from "../../../src/node/util" +import { onLine } from "../../utils/helpers" describe("getEnvPaths", () => { describe("on darwin", () => { @@ -379,7 +380,7 @@ describe("onLine", () => { const size = 100 const received = new Promise((resolve) => { const lines: string[] = [] - util.onLine(proc!, (line) => { + onLine(proc!, (line: string) => { lines.push(line) if (lines.length === size) { resolve(lines) @@ -412,7 +413,7 @@ describe("onLine", () => { }) const mockCallback = jest.fn() - expect(() => util.onLine(proc, mockCallback)).toThrowError(/stdout/) + expect(() => onLine(proc, mockCallback)).toThrowError(/stdout/) // Cleanup proc?.kill() diff --git a/test/utils/helpers.ts b/test/utils/helpers.ts index 3f9399d4b..761bb9c06 100644 --- a/test/utils/helpers.ts +++ b/test/utils/helpers.ts @@ -1,5 +1,6 @@ import { logger } from "@coder/logger" import { promises as fs } from "fs" +import * as cp from "child_process" import * as net from "net" import * as os from "os" import * as path from "path" @@ -119,3 +120,36 @@ export function isAddressInfo(address: unknown): address is net.AddressInfo { (address as net.AddressInfo).address !== undefined ) } + +// From https://github.com/chalk/ansi-regex +const pattern = [ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", +].join("|") +const re = new RegExp(pattern, "g") + +type OnLineCallback = (strippedLine: string, originalLine: string) => void +/** + * Split stdout on newlines and strip ANSI codes. + */ +export const onLine = (proc: cp.ChildProcess, callback: OnLineCallback): void => { + let buffer = "" + if (!proc.stdout) { + throw new Error("no stdout") + } + proc.stdout.setEncoding("utf8") + proc.stdout.on("data", (d) => { + const data = buffer + d + const split = data.split("\n") + const last = split.length - 1 + + for (let i = 0; i < last; ++i) { + callback(split[i].replace(re, ""), split[i]) + } + + // The last item will either be an empty string (the data ended with a + // newline) or a partial line (did not end with a newline) and we must + // wait to parse it until we get a full line. + buffer = split[last] + }) +}