You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 00:55:54