Electron+Monaco集成clangd报错:StreamMessageReader不是构造函数
Electron + Monaco + clangd LSP集成报错:
TypeError: node.exports.StreamMessageReader is not a constructor 报错信息
TypeError: node.exports.StreamMessageReader is not a constructor at createStreamConnection (/Volumes/Data/Develop/cpcode/dist/electron/main.js:3100:18) at createProcessStreamConnection (/Volumes/Data/Develop/cpcode/dist/electron/main.js:3094:12) at createServerProcess (/Volumes/Data/Develop/cpcode/dist/electron/main.js:3090:10) at launch (/Volumes/Data/Develop/cpcode/dist/electron/main.js:3114:28) at /Volumes/Data/Develop/cpcode/dist/electron/main.js:3160:13 at WebSocketServer.completeUpgrade (/Volumes/Data/Develop/cpcode/node_modules/ws/lib/websocket-server.js:431:5) at WebSocketServer.handleUpgrade (/Volumes/Data/Develop/cpcode/node_modules/ws/lib/websocket-server.js:339:10) at Server.<anonymous> (/Volumes/Data/Develop/cpcode/dist/electron/main.js:3147:13) at Server.emit (node:events:526:28) at onParserExecuteCommon (node:_http_server:727:14)
主进程代码
process.env.DIST = join(__dirname, '..') process.env.PUBLIC = app.isPackaged ? process.env.DIST : join(process.env.DIST, '../public') import { join } from 'path' import { app, BrowserWindow } from 'electron' import ws from "ws"; import http from "http"; import url from "url"; import net from "net"; import express from "express"; import * as rpc from "vscode-ws-jsonrpc"; import * as server from "vscode-ws-jsonrpc/server"; import * as lsp from "vscode-languageserver"; import { Message } from "vscode-languageserver"; export function launch(socket: rpc.IWebSocket) { const reader = new rpc.WebSocketMessageReader(socket); const writer = new rpc.WebSocketMessageWriter(socket); // start the language server as an external process const socketConnection = server.createConnection(reader, writer, () => socket.dispose() ); const serverConnection = server.createServerProcess("CPP", "clangd"); if (serverConnection) { server.forward(socketConnection, serverConnection, (message) => { if (Message.isRequest(message)) { console.log("server request!!!"); if (message.method === lsp.InitializeRequest.type.method) { const initializeParams = message.params as lsp.InitializeParams; initializeParams.processId = process.pid; } } return message; }); } } process.on("uncaughtException", function (err: any) { console.error("Uncaught Exception: ", err.toString()); if (err.stack) { console.error(err.stack); } }); export const startLSP = () => { // create the express application const app = express(); // server the static content, i.e. index.html app.use(express.static(__dirname)); // start the server const server = app.listen(3000); // create the web socket const wss = new ws.Server({ noServer: true, perMessageDeflate: false }); server.on( "upgrade", (request: http.IncomingMessage, socket: net.Socket, head: Buffer) => { // eslint-disable-next-line n/no-deprecated-api const pathname = request.url ? url.parse(request.url).pathname : undefined; if (pathname === "/lsp") { wss.handleUpgrade(request, socket, head, (webSocket) => { const socket: rpc.IWebSocket = { send: (content) => webSocket.send(content, (error) => { if (error) { throw error; } }), onMessage: (cb) => webSocket.on("message", cb), onError: (cb) => webSocket.on("error", cb), onClose: (cb) => webSocket.on("close", cb), dispose: () => webSocket.close() }; // launch the server when the web socket is opened if (webSocket.readyState === webSocket.OPEN) { launch(socket); } else { webSocket.on("open", () => launch(socket)); } }); } } ); }; let win: BrowserWindow | null // Here, you can also use other preload const preload = join(__dirname, './preload.js') // 🚧 Use ['ENV_NAME'] avoid vite:define plugin - Vite@2.x const serverURL = process.env['VITE_DEV_SERVER_URL'] function createWindow() { win = new BrowserWindow({ icon: join(process.env.PUBLIC, 'logo.svg'), webPreferences: { contextIsolation: false, nodeIntegration: true, preload, }, titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'hidden', }) startLSP(); // Test active push message to Renderer-process. win.webContents.on('did-finish-load', () => { win?.webContents.send('main-process-message', (new Date).toLocaleString()) }) if (app.isPackaged) { win.loadFile(join(process.env.DIST, 'index.html')) } else { win.loadURL(serverURL) } } app.on('window-all-closed', () => { win = null }) app.whenReady().then(createWindow)
Vite配置文件
import fs from 'fs' import path from 'path' import { defineConfig } from 'vite' import electron from 'vite-plugin-electron' import renderer from 'vite-plugin-electron-renderer'; import { nodePolyfills } from 'vite-plugin-node-polyfills' fs.rmSync('dist', { recursive: true, force: true }) // v14.14.0 export default defineConfig({ plugins: [ electron({ main: { entry: 'electron/main.ts', }, preload: { input: { // Must be use absolute path, this is the restrict of Rollup preload: path.join(__dirname, 'electron/preload.ts'), }, }, // Enables use of Node.js API in the Renderer-process renderer: {}, }), nodePolyfills({ // Whether to polyfill `node:` protocol imports. protocolImports: true, }), ], })
已尝试操作
- 安装
vscode-languageserver和vscode-languageserver-protocol依赖,问题未解决
解决方案
1. 锁定依赖版本确保兼容性
vscode-ws-jsonrpc与vscode-languageserver版本不匹配会导致导出结构异常,执行以下命令安装兼容版本:
npm install vscode-ws-jsonrpc@0.1.0 vscode-languageserver@7.0.0 --save-exact
2. 修改Vite配置,排除LSP相关依赖的打包
Vite对CommonJS格式的LSP依赖打包时会破坏其导出结构,需要在Electron主进程配置中添加external字段,让这些依赖直接使用Node_modules中的原始文件:
// vite.config.ts export default defineConfig({ plugins: [ electron({ main: { entry: 'electron/main.ts', // 添加external配置 external: ['vscode-ws-jsonrpc', 'vscode-languageserver', 'ws'], }, preload: { input: { preload: path.join(__dirname, 'electron/preload.ts'), }, }, renderer: {}, }), nodePolyfills({ protocolImports: true, }), ], })
3. 验证clangd路径正确性
确保clangd已正确安装并加入系统环境变量,或在createServerProcess中指定完整路径:
// 替换原createServerProcess调用 const serverConnection = server.createServerProcess("CPP", "/usr/bin/clangd"); // 根据你的clangd实际路径修改
内容的提问来源于stack exchange,提问作者Sam Zhang
相关产品推荐
相关产品推荐

