Skip to content

Commit 714dc08

Browse files
committed
chore: Refactor HtmlBeautifier class and improve error handling
1 parent 216a63c commit 714dc08

File tree

1 file changed

+131
-100
lines changed

1 file changed

+131
-100
lines changed

src/formatter/htmlbeautifier.ts

+131-100
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,39 @@ import * as cp from "child_process";
33
const isWsl = require("is-wsl");
44

55
export default class HtmlBeautifier {
6+
private logChannel: vscode.LogOutputChannel;
7+
8+
constructor() {
9+
this.logChannel = vscode.window.createOutputChannel("ERB Beautifier", {
10+
log: true,
11+
});
12+
}
13+
614
/**
7-
* Formats the given input using HTML Beautifier
8-
* @param {string} input - The input to be formatted
9-
* @returns {Promise<string>} The formatted input
15+
* Formats the input string using HTML Beautifier.
16+
* @param input The input string to be formatted.
17+
* @returns A promise that resolves to the formatted string.
1018
*/
1119
public async format(input: string): Promise<string> {
1220
try {
13-
const cmd = `${this.exe} ${this.cliOptions.join(
14-
" "
15-
)} with custom env ${JSON.stringify(this.customEnvVars)}`;
16-
console.log(`Formatting ERB with command: ${cmd}`);
17-
console.time(cmd);
18-
21+
const startTime = Date.now();
1922
const result = await this.executeCommand(input);
20-
21-
console.timeEnd(cmd);
23+
const duration = Date.now() - startTime;
24+
this.logChannel.info(
25+
`Formatting completed successfully in ${duration}ms.`
26+
);
2227
return result;
2328
} catch (error) {
24-
console.error(error);
25-
const errorMessage =
26-
error instanceof Error ? error.message : "Unknown error occurred";
27-
vscode.window.showErrorMessage(
28-
`Error occurred while formatting: ${errorMessage}`
29-
);
29+
this.handleError(error, "Error occurred while formatting");
3030
throw error;
3131
}
3232
}
3333

34+
/**
35+
* Executes the formatting command with the provided input.
36+
* @param input The input to format.
37+
* @returns A promise that resolves to the formatted output.
38+
*/
3439
private executeCommand(input: string): Promise<string> {
3540
return new Promise((resolve, reject) => {
3641
// Handle spawn EINVAL error on Windows. See https://github.com/nodejs/node/issues/52554
@@ -44,53 +49,37 @@ export default class HtmlBeautifier {
4449
...shellOptions,
4550
});
4651

47-
if (htmlbeautifier.stdin === null || htmlbeautifier.stdout === null) {
48-
const msg = "Couldn't initialize STDIN or STDOUT";
49-
console.warn(msg);
50-
vscode.window.showErrorMessage(msg);
51-
reject(new Error(msg));
52-
return;
53-
}
54-
55-
let formattedResult = "";
56-
let errorMessage = "";
57-
let stdoutChunks: Buffer[] = [];
58-
let stderrChunks: Buffer[] = [];
52+
const fullCommand = `${this.exe} ${this.cliOptions.join(" ")} (cwd: ${
53+
vscode.workspace.rootPath || __dirname
54+
}) with custom env: ${JSON.stringify(this.customEnvVars)}`;
55+
this.logChannel.info(`Formatting ERB with command: ${fullCommand}`);
5956

60-
htmlbeautifier.on("error", (err) => {
61-
console.warn(err);
62-
vscode.window.showErrorMessage(
63-
`Couldn't run ${this.exe} '${err.message}'`
57+
if (!htmlbeautifier.stdin || !htmlbeautifier.stdout) {
58+
return this.handleSpawnError(
59+
reject,
60+
"Couldn't initialize STDIN or STDOUT"
6461
);
65-
reject(err);
66-
});
67-
68-
htmlbeautifier.stdout.on("data", (chunk) => {
69-
stdoutChunks.push(chunk);
70-
});
62+
}
7163

72-
htmlbeautifier.stdout.on("end", () => {
73-
let result = Buffer.concat(stdoutChunks).toString();
74-
formattedResult = this.handleFinalNewline(input, result);
75-
});
64+
const stdoutChunks: Buffer[] = [];
65+
const stderrChunks: Buffer[] = [];
7666

77-
htmlbeautifier.stderr.on("data", (chunk) => {
78-
stderrChunks.push(chunk);
79-
});
67+
htmlbeautifier.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
68+
htmlbeautifier.stderr.on("data", (chunk) => stderrChunks.push(chunk));
8069

81-
htmlbeautifier.stderr.on("end", () => {
82-
errorMessage = Buffer.concat(stderrChunks).toString();
83-
});
70+
htmlbeautifier.on("error", (err) =>
71+
this.handleSpawnError(
72+
reject,
73+
`Couldn't run ${this.exe}: ${err.message}`,
74+
err
75+
)
76+
);
8477

8578
htmlbeautifier.on("exit", (code) => {
86-
if (code) {
87-
vscode.window.showErrorMessage(
88-
`Failed with exit code: ${code}. '${errorMessage}'`
89-
);
90-
reject(new Error(`Command failed with exit code ${code}`));
91-
} else {
92-
resolve(formattedResult);
93-
}
79+
const formattedResult = Buffer.concat(stdoutChunks).toString();
80+
const finalResult = this.handleFinalNewline(input, formattedResult);
81+
const errorMessage = Buffer.concat(stderrChunks).toString();
82+
this.handleExit(code, finalResult, errorMessage, resolve, reject);
9483
});
9584

9685
htmlbeautifier.stdin.write(input);
@@ -99,8 +88,64 @@ export default class HtmlBeautifier {
9988
}
10089

10190
/**
102-
* Returns the executable path for HTML Beautifier
103-
* @returns {string} The executable path
91+
* Handles errors during process spawning.
92+
* @param reject The promise reject function.
93+
* @param message The error message to log and show to the user.
94+
* @param err Optional error object.
95+
*/
96+
private handleSpawnError(
97+
reject: (reason?: any) => void,
98+
message: string,
99+
err?: Error
100+
): void {
101+
this.logChannel.warn(message);
102+
vscode.window.showErrorMessage(message);
103+
if (err) {
104+
this.logChannel.warn(err.message);
105+
}
106+
reject(err || new Error(message));
107+
}
108+
109+
/**
110+
* Handles the process exit event and resolves or rejects the promise.
111+
* @param code The process exit code.
112+
* @param result The formatted result.
113+
* @param errorMessage The error message, if any.
114+
* @param resolve The promise resolve function.
115+
* @param reject The promise reject function.
116+
*/
117+
private handleExit(
118+
code: number | null,
119+
result: string,
120+
errorMessage: string,
121+
resolve: (value: string | PromiseLike<string>) => void,
122+
reject: (reason?: any) => void
123+
): void {
124+
if (code && code !== 0) {
125+
const error = `Failed with exit code: ${code}. ${errorMessage}`;
126+
this.logChannel.error(error);
127+
vscode.window.showErrorMessage(error);
128+
reject(new Error(error));
129+
} else {
130+
resolve(result);
131+
}
132+
}
133+
134+
/**
135+
* Handles errors by logging and displaying a message to the user.
136+
* @param error The error object or message.
137+
* @param userMessage The message to display to the user.
138+
*/
139+
private handleError(error: any, userMessage: string): void {
140+
const errorMessage =
141+
error instanceof Error ? error.message : "Unknown error occurred";
142+
this.logChannel.error(errorMessage);
143+
vscode.window.showErrorMessage(`${userMessage}: ${errorMessage}`);
144+
}
145+
146+
/**
147+
* Gets the executable path for HTML Beautifier based on the configuration.
148+
* @returns The path to the executable.
104149
*/
105150
private get exe(): string {
106151
const config = vscode.workspace.getConfiguration("vscode-erb-beautify");
@@ -112,26 +157,26 @@ export default class HtmlBeautifier {
112157
}
113158

114159
/**
115-
* Determines if the current platform is Windows (excluding WSL)
116-
* @returns {boolean} True if the platform is Windows, false otherwise
160+
* Checks if the current platform is Windows (excluding WSL).
161+
* @returns True if the platform is Windows; false otherwise.
117162
*/
118163
private isWindows(): boolean {
119164
return process.platform === "win32" && !isWsl;
120165
}
121166

122167
/**
123-
* Returns the command-line options for HTML Beautifier
124-
* @returns {string[]} The command-line options
168+
* Retrieves the command-line options for HTML Beautifier from the configuration.
169+
* @returns An array of command-line options.
125170
*/
126171
private get cliOptions(): string[] {
127172
const config = vscode.workspace.getConfiguration("vscode-erb-beautify");
128-
const acc: string[] = [];
173+
const options: string[] = [];
129174

130175
if (config.get("useBundler")) {
131-
acc.push("exec", "htmlbeautifier");
176+
options.push("exec", "htmlbeautifier");
132177
}
133178

134-
return Object.keys(config).reduce(function (acc, key) {
179+
return Object.keys(config).reduce((acc, key) => {
135180
switch (key) {
136181
case "indentBy":
137182
acc.push("--indent-by", config[key]);
@@ -154,35 +199,31 @@ export default class HtmlBeautifier {
154199
break;
155200
}
156201
return acc;
157-
}, acc);
202+
}, options);
158203
}
159204

160205
/**
161-
* Retrieves the custom environment variables from the configuration
162-
* @returns {Record<string, string>} The custom environment variables
206+
* Retrieves custom environment variables from the configuration.
207+
* @returns A record of custom environment variables.
163208
*/
164209
private get customEnvVars(): Record<string, string> {
165210
const config = vscode.workspace.getConfiguration("vscode-erb-beautify");
166-
const customEnvVar = config.get("customEnvVar", {}) as Record<
167-
string,
168-
string
169-
>;
170-
return customEnvVar;
211+
return config.get("customEnvVar", {}) as Record<string, string>;
171212
}
172213

173214
/**
174-
* Adjusts the final newline of the result string based on the VS Code configuration and the input string.
175-
* @param {string} input - The original input string.
176-
* @param {string} result - The result string to be processed.
177-
* @returns {string} The processed result string.
215+
* Adjusts the final newline of the result string based on VS Code configuration.
216+
* @param input The original input string.
217+
* @param result The formatted result string.
218+
* @returns The adjusted result string.
178219
*/
179220
private handleFinalNewline(input: string, result: string): string {
180221
// Get the 'insertFinalNewline' setting from VS Code configuration
181222
const insertFinalNewline = vscode.workspace
182223
.getConfiguration()
183224
.get("files.insertFinalNewline");
184225

185-
// Check if the result string ends with a newline
226+
// Determine if the result ends with a newline
186227
const resultEndsWithNewline =
187228
result.endsWith("\n") || result.endsWith("\r\n");
188229

@@ -191,11 +232,8 @@ export default class HtmlBeautifier {
191232
// Get the 'files.eol' setting from VS Code configuration
192233
const eol = vscode.workspace.getConfiguration().get("files.eol");
193234

194-
// Set the newline character(s) based on the 'files.eol' setting and the platform
195-
let newline = eol;
196-
if (eol === "auto") {
197-
newline = this.isWindows() ? "\r\n" : "\n";
198-
}
235+
// Determine newline character(s) based on the 'files.eol' setting and the platform
236+
let newline = eol === "auto" ? (this.isWindows() ? "\r\n" : "\n") : eol;
199237

200238
// Append the newline to the result
201239
result += newline;
@@ -208,11 +246,8 @@ export default class HtmlBeautifier {
208246

209247
// If the input and result use different newline character(s)
210248
if (inputNewline !== resultNewline) {
211-
// Remove the newline from the end of the result
212-
result = result.slice(0, -resultNewline.length);
213-
214-
// Append the newline from the input to the result
215-
result += inputNewline;
249+
// Remove the newline from the end of the result and append the input's newline
250+
result = result.slice(0, -resultNewline.length) + inputNewline;
216251
}
217252
}
218253

@@ -221,20 +256,16 @@ export default class HtmlBeautifier {
221256
}
222257

223258
/**
224-
* Determines the type of newline used in the input string.
225-
* @param {string} input - The input string.
226-
* @returns {string} The newline character(s) used in the input string, or an empty string if the input does not end with a newline.
259+
* Determines the newline character(s) used in the input string.
260+
* @param input The input string.
261+
* @returns The newline character(s) used, or an empty string if none.
227262
*/
228263
private getNewline(input: string): string {
229-
// If the input ends with a Windows-style newline, return '\r\n'
230264
if (input.endsWith("\r\n")) {
231-
return "\r\n";
232-
}
233-
// If the input ends with a Unix-style newline, return '\n'
234-
else if (input.endsWith("\n")) {
235-
return "\n";
265+
return "\r\n"; // Return Windows-style newline
266+
} else if (input.endsWith("\n")) {
267+
return "\n"; // Return Unix-style newline
236268
}
237-
// If the input does not end with a newline, return an empty string
238-
return "";
269+
return ""; // Return empty if no newline found
239270
}
240271
}

0 commit comments

Comments
 (0)