Async-ify prettier wrapper

Kirill Bulatov created

Change summary

crates/prettier/prettier_server/src/index.js | 98 +++++++++++++++------
1 file changed, 67 insertions(+), 31 deletions(-)

Detailed changes

crates/prettier/prettier_server/src/index.js 🔗

@@ -1,8 +1,9 @@
 const { Buffer } = require('buffer');
 const fs = require("fs");
 const path = require("path");
+const { once } = require('events');
 
-let prettierContainerPath = process.argv[2];
+const prettierContainerPath = process.argv[2];
 if (prettierContainerPath == null || prettierContainerPath.length == 0) {
     console.error(`Prettier path argument was not specified or empty.\nUsage: ${process.argv[0]} ${process.argv[1]} prettier/path`);
     process.exit(1);
@@ -18,48 +19,83 @@ fs.stat(prettierContainerPath, (err, stats) => {
         process.exit(1);
     }
 });
-let prettierPath = path.join(prettierContainerPath, 'node_modules/prettier');
+const prettierPath = path.join(prettierContainerPath, 'node_modules/prettier');
+
 
 (async () => {
     let prettier;
     try {
         prettier = await loadPrettier(prettierPath);
     } catch (error) {
-        console.error(error);
+        console.error("Failed to load prettier: ", error);
         process.exit(1);
     }
     console.log("Prettier loadded successfully.");
-    // TODO kb do the rest here
+    process.stdin.resume();
+    handleBuffer(prettier);
 })()
 
-let buffer = Buffer.alloc(0);
-process.stdin.resume();
-process.stdin.on('data', (data) => {
-    buffer = Buffer.concat([buffer, data]);
-    handleData();
-});
-process.stdin.on('end', () => {
-    handleData();
-});
-
-function handleData() {
-    if (buffer.length < 4) {
-        return;
+async function handleBuffer(prettier) {
+    for await (let messageText of readStdin()) {
+        handleData(messageText, prettier).catch(e => {
+            console.error("Failed to handle formatter request", e);
+        });
     }
+}
 
-    const length = buffer.readUInt32LE(0);
-    console.log(length);
-    console.log(buffer.toString());
-    if (buffer.length < 4 + length) {
-        return;
-    }
+async function* readStdin() {
+    const bufferLengthOffset = 4;
+    let buffer = Buffer.alloc(0);
+    let streamEnded = false;
+    process.stdin.on('end', () => {
+        streamEnded = true;
+    });
+    process.stdin.on('data', (data) => {
+        buffer = Buffer.concat([buffer, data]);
+    });
 
-    const bytes = buffer.subarray(4, 4 + length);
-    buffer = buffer.subarray(4 + length);
+    try {
+        main_loop: while (true) {
+            while (buffer.length < bufferLengthOffset) {
+                if (streamEnded) {
+                    sendResponse(makeError(`Unexpected end of stream: less than ${bufferLengthOffset} characters passed`));
+                    buffer = Buffer.alloc(0);
+                    streamEnded = false;
+                    await once(process.stdin, 'readable');
+                    continue main_loop;
+                }
+                await once(process.stdin, 'readable');
+            }
+
+            const length = buffer.readUInt32LE(0);
+
+            while (buffer.length < (bufferLengthOffset + length)) {
+                if (streamEnded) {
+                    sendResponse(makeError(
+                        `Unexpected end of stream: buffer length ${buffer.length} does not match expected length ${bufferLengthOffset} + ${length}`));
+                    buffer = Buffer.alloc(0);
+                    streamEnded = false;
+                    await once(process.stdin, 'readable');
+                    continue main_loop;
+                }
+                await once(process.stdin, 'readable');
+            }
+
+            const message = buffer.subarray(4, 4 + length);
+            buffer = buffer.subarray(4 + length);
+            yield message.toString('utf8');
+        }
+    } catch (e) {
+        console.error(`Error reading stdin: ${e}`);
+    } finally {
+        process.stdin.off('data');
+    }
+}
 
+async function handleData(messageText, prettier) {
     try {
-        const message = JSON.parse(bytes);
-        handleMessage(message);
+        const message = JSON.parse(messageText);
+        await handleMessage(prettier, message);
     } catch (e) {
         sendResponse(makeError(`Request JSON parse error: ${e}`));
         return;
@@ -72,8 +108,8 @@ function handleData() {
 // shutdown
 // error
 
-function handleMessage(message) {
-    console.log(message);
+async function handleMessage(prettier, message) {
+    console.log(`message: ${message}`);
     sendResponse({ method: "hi", result: null });
 }
 
@@ -82,8 +118,8 @@ function makeError(message) {
 }
 
 function sendResponse(response) {
-    let message = Buffer.from(JSON.stringify(response));
-    let length = Buffer.alloc(4);
+    const message = Buffer.from(JSON.stringify(response));
+    const length = Buffer.alloc(4);
     length.writeUInt32LE(message.length);
     process.stdout.write(length);
     process.stdout.write(message);