index.js

  1const { Buffer } = require('buffer');
  2const fs = require("fs");
  3const path = require("path");
  4const { once } = require('events');
  5
  6const prettierContainerPath = process.argv[2];
  7if (prettierContainerPath == null || prettierContainerPath.length == 0) {
  8    console.error(`Prettier path argument was not specified or empty.\nUsage: ${process.argv[0]} ${process.argv[1]} prettier/path`);
  9    process.exit(1);
 10}
 11fs.stat(prettierContainerPath, (err, stats) => {
 12    if (err) {
 13        console.error(`Path '${prettierContainerPath}' does not exist.`);
 14        process.exit(1);
 15    }
 16
 17    if (!stats.isDirectory()) {
 18        console.log(`Path '${prettierContainerPath}' exists but is not a directory.`);
 19        process.exit(1);
 20    }
 21});
 22const prettierPath = path.join(prettierContainerPath, 'node_modules/prettier');
 23
 24
 25(async () => {
 26    let prettier;
 27    try {
 28        prettier = await loadPrettier(prettierPath);
 29    } catch (error) {
 30        console.error("Failed to load prettier: ", error);
 31        process.exit(1);
 32    }
 33    console.log("Prettier loadded successfully.");
 34    process.stdin.resume();
 35    handleBuffer(prettier);
 36})()
 37
 38async function handleBuffer(prettier) {
 39    for await (let messageText of readStdin()) {
 40        handleData(messageText, prettier).catch(e => {
 41            console.error("Failed to handle formatter request", e);
 42        });
 43    }
 44}
 45
 46async function* readStdin() {
 47    const bufferLengthOffset = 4;
 48    let buffer = Buffer.alloc(0);
 49    let streamEnded = false;
 50    process.stdin.on('end', () => {
 51        streamEnded = true;
 52    });
 53    process.stdin.on('data', (data) => {
 54        buffer = Buffer.concat([buffer, data]);
 55    });
 56
 57    try {
 58        main_loop: while (true) {
 59            while (buffer.length < bufferLengthOffset) {
 60                if (streamEnded) {
 61                    sendResponse(makeError(`Unexpected end of stream: less than ${bufferLengthOffset} characters passed`));
 62                    buffer = Buffer.alloc(0);
 63                    streamEnded = false;
 64                    await once(process.stdin, 'readable');
 65                    continue main_loop;
 66                }
 67                await once(process.stdin, 'readable');
 68            }
 69
 70            const length = buffer.readUInt32LE(0);
 71
 72            while (buffer.length < (bufferLengthOffset + length)) {
 73                if (streamEnded) {
 74                    sendResponse(makeError(
 75                        `Unexpected end of stream: buffer length ${buffer.length} does not match expected length ${bufferLengthOffset} + ${length}`));
 76                    buffer = Buffer.alloc(0);
 77                    streamEnded = false;
 78                    await once(process.stdin, 'readable');
 79                    continue main_loop;
 80                }
 81                await once(process.stdin, 'readable');
 82            }
 83
 84            const message = buffer.subarray(4, 4 + length);
 85            buffer = buffer.subarray(4 + length);
 86            yield message.toString('utf8');
 87        }
 88    } catch (e) {
 89        console.error(`Error reading stdin: ${e}`);
 90    } finally {
 91        process.stdin.off('data');
 92    }
 93}
 94
 95async function handleData(messageText, prettier) {
 96    try {
 97        const message = JSON.parse(messageText);
 98        await handleMessage(prettier, message);
 99    } catch (e) {
100        sendResponse(makeError(`Request JSON parse error: ${e}`));
101        return;
102    }
103}
104
105// format
106// clear_cache
107//
108// shutdown
109// error
110
111async function handleMessage(prettier, message) {
112    console.log(`message: ${message}`);
113    sendResponse({ method: "hi", result: null });
114}
115
116function makeError(message) {
117    return { method: "error", message };
118}
119
120function sendResponse(response) {
121    const message = Buffer.from(JSON.stringify(response));
122    const length = Buffer.alloc(4);
123    length.writeUInt32LE(message.length);
124    process.stdout.write(length);
125    process.stdout.write(message);
126}
127
128function loadPrettier(prettierPath) {
129    return new Promise((resolve, reject) => {
130        fs.access(prettierPath, fs.constants.F_OK, (err) => {
131            if (err) {
132                reject(`Path '${prettierPath}' does not exist.Error: ${err}`);
133            } else {
134                try {
135                    resolve(require(prettierPath));
136                } catch (err) {
137                    reject(`Error requiring prettier module from path '${prettierPath}'.Error: ${err}`);
138                }
139            }
140        });
141    });
142}