Skip to content

Commit cb4671c

Browse files
committed
chore: refactor ConsoleFormatter construction
1 parent 8228fbf commit cb4671c

1 file changed

Lines changed: 74 additions & 92 deletions

File tree

src/formatters/ConsoleFormatter.ts

Lines changed: 74 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -15,61 +15,68 @@ import type {ConsoleMessage} from '../third_party/index.js';
1515

1616
export interface ConsoleFormatterOptions {
1717
fetchDetailedData?: boolean;
18-
id?: number;
18+
id: number;
1919
devTools?: TargetUniverse;
20+
resolvedArgsForTesting?: unknown[];
2021
resolvedStackTraceForTesting?: DevTools.DevTools.StackTrace.StackTrace.StackTrace;
2122
}
2223

2324
export class ConsoleFormatter {
24-
#msg: ConsoleMessage | SymbolizedError;
25-
#resolvedArgs: unknown[] = [];
26-
#resolvedStackTrace?: DevTools.DevTools.StackTrace.StackTrace.StackTrace;
27-
#id?: number;
28-
29-
private constructor(
30-
msg: ConsoleMessage | SymbolizedError,
31-
options?: ConsoleFormatterOptions,
32-
) {
33-
this.#msg = msg;
34-
this.#id = options?.id;
35-
this.#resolvedStackTrace = options?.resolvedStackTraceForTesting;
25+
readonly #id: number;
26+
readonly #type: string;
27+
readonly #text: string;
28+
29+
readonly #argCount: number;
30+
readonly #resolvedArgs: unknown[];
31+
32+
readonly #stack?: DevTools.DevTools.StackTrace.StackTrace.StackTrace;
33+
readonly #cause?: SymbolizedError; // eslint-disable-line no-unused-private-class-members
34+
35+
private constructor(params: {
36+
id: number;
37+
type: string;
38+
text: string;
39+
argCount?: number;
40+
resolvedArgs?: unknown[];
41+
stack?: DevTools.DevTools.StackTrace.StackTrace.StackTrace;
42+
cause?: SymbolizedError;
43+
}) {
44+
this.#id = params.id;
45+
this.#type = params.type;
46+
this.#text = params.text;
47+
this.#argCount = params.argCount ?? 0;
48+
this.#resolvedArgs = params.resolvedArgs ?? [];
49+
this.#stack = params.stack;
50+
this.#cause = params.cause;
3651
}
3752

3853
static async from(
3954
msg: ConsoleMessage | UncaughtError,
40-
options?: ConsoleFormatterOptions,
55+
options: ConsoleFormatterOptions,
4156
): Promise<ConsoleFormatter> {
4257
if (msg instanceof UncaughtError) {
43-
return new ConsoleFormatter(
44-
await SymbolizedError.fromDetails({
45-
devTools: options?.devTools,
46-
details: msg.details,
47-
targetId: msg.targetId,
48-
includeStackAndCause: options?.fetchDetailedData,
49-
resolvedStackTraceForTesting: options?.resolvedStackTraceForTesting,
50-
}),
51-
options,
52-
);
58+
const error = await SymbolizedError.fromDetails({
59+
devTools: options?.devTools,
60+
details: msg.details,
61+
targetId: msg.targetId,
62+
includeStackAndCause: options?.fetchDetailedData,
63+
resolvedStackTraceForTesting: options?.resolvedStackTraceForTesting,
64+
});
65+
return new ConsoleFormatter({
66+
id: options.id,
67+
type: 'error',
68+
text: error.message,
69+
stack: error.stackTrace,
70+
cause: error.cause,
71+
});
5372
}
5473

55-
const formatter = new ConsoleFormatter(msg, options);
56-
if (options?.fetchDetailedData) {
57-
await formatter.#loadDetailedData(options?.devTools);
58-
}
59-
return formatter;
60-
}
61-
62-
#isConsoleMessage(
63-
msg: ConsoleMessage | SymbolizedError,
64-
): msg is ConsoleMessage {
65-
// No `instanceof` as tests mock `ConsoleMessage`.
66-
return 'args' in msg && typeof msg.args === 'function';
67-
}
68-
69-
async #loadDetailedData(devTools?: TargetUniverse): Promise<void> {
70-
if (this.#isConsoleMessage(this.#msg)) {
71-
this.#resolvedArgs = await Promise.all(
72-
this.#msg.args().map(async (arg, i) => {
74+
let resolvedArgs: unknown[] = [];
75+
if (options.resolvedArgsForTesting) {
76+
resolvedArgs = options.resolvedArgsForTesting;
77+
} else if (options.fetchDetailedData) {
78+
resolvedArgs = await Promise.all(
79+
msg.args().map(async (arg, i) => {
7380
try {
7481
return await arg.jsonValue();
7582
} catch {
@@ -79,80 +86,55 @@ export class ConsoleFormatter {
7986
);
8087
}
8188

82-
if (devTools) {
89+
let stack: DevTools.DevTools.StackTrace.StackTrace.StackTrace | undefined;
90+
if (options.resolvedStackTraceForTesting) {
91+
stack = options.resolvedStackTraceForTesting;
92+
} else if (options.fetchDetailedData && options.devTools) {
8393
try {
84-
if (this.#isConsoleMessage(this.#msg)) {
85-
this.#resolvedStackTrace = await createStackTraceForConsoleMessage(
86-
devTools,
87-
this.#msg,
88-
);
89-
}
94+
stack = await createStackTraceForConsoleMessage(options.devTools, msg);
9095
} catch {
9196
// ignore
9297
}
9398
}
99+
100+
return new ConsoleFormatter({
101+
id: options.id,
102+
type: msg.type(),
103+
text: msg.text(),
104+
argCount: resolvedArgs.length || msg.args().length,
105+
resolvedArgs,
106+
stack,
107+
});
94108
}
95109

96110
// The short format for a console message.
97111
toString(): string {
98-
const type = this.#getType();
99-
const text = this.#getText();
100-
const argsCount = this.#getArgsCount();
101-
const idPart = this.#id !== undefined ? `msgid=${this.#id} ` : '';
102-
return `${idPart}[${type}] ${text} (${argsCount} args)`;
112+
return `msgid=${this.#id} [${this.#type}] ${this.#text} (${this.#argCount} args)`;
103113
}
104114

105115
// The verbose format for a console message, including all details.
106116
toStringDetailed(): string {
107117
const result = [
108-
this.#id !== undefined ? `ID: ${this.#id}` : '',
109-
`Message: ${this.#getType()}> ${this.#getText()}`,
118+
`ID: ${this.#id}`,
119+
`Message: ${this.#type}> ${this.#text}`,
110120
this.#formatArgs(),
111-
this.#formatStackTrace(
112-
this.#msg instanceof SymbolizedError
113-
? this.#msg.stackTrace
114-
: this.#resolvedStackTrace,
115-
),
121+
this.#formatStackTrace(this.#stack),
116122
].filter(line => !!line);
117123
return result.join('\n');
118124
}
119125

120-
#getType(): string {
121-
if (!this.#isConsoleMessage(this.#msg)) {
122-
return 'error';
123-
}
124-
return this.#msg.type();
125-
}
126-
127-
#getText(): string {
128-
if (!this.#isConsoleMessage(this.#msg)) {
129-
return this.#msg.message;
130-
}
131-
return this.#msg.text();
132-
}
133-
134126
#getArgs(): unknown[] {
135-
if (!this.#isConsoleMessage(this.#msg)) {
136-
return [];
137-
}
138127
if (this.#resolvedArgs.length > 0) {
139128
const args = [...this.#resolvedArgs];
140129
// If there is no text, the first argument serves as text (see formatMessage).
141-
if (!this.#msg.text()) {
130+
if (!this.#text) {
142131
args.shift();
143132
}
144133
return args;
145134
}
146135
return [];
147136
}
148137

149-
#getArgsCount(): number {
150-
if (!this.#isConsoleMessage(this.#msg)) {
151-
return 0;
152-
}
153-
return this.#resolvedArgs.length || this.#msg.args().length;
154-
}
155-
156138
#formatArg(arg: unknown) {
157139
return typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
158140
}
@@ -215,22 +197,22 @@ export class ConsoleFormatter {
215197
}
216198
toJSON(): object {
217199
return {
218-
type: this.#getType(),
219-
text: this.#getText(),
220-
argsCount: this.#getArgsCount(),
200+
type: this.#type,
201+
text: this.#text,
202+
argsCount: this.#argCount,
221203
id: this.#id,
222204
};
223205
}
224206

225207
toJSONDetailed(): object {
226208
return {
227209
id: this.#id,
228-
type: this.#getType(),
229-
text: this.#getText(),
210+
type: this.#type,
211+
text: this.#text,
230212
args: this.#getArgs().map(arg =>
231213
typeof arg === 'object' ? arg : String(arg),
232214
),
233-
stackTrace: this.#resolvedStackTrace,
215+
stackTrace: this.#stack,
234216
};
235217
}
236218
}

0 commit comments

Comments
 (0)