Skip to content

Error: Missing service editorService" when using monaco-languageclient in production Stage #870

Closed
@cfngc4594

Description

@cfngc4594

I'm integrating a C language LSP service into my Next.js project using @monaco-editor/react and monaco-languageclient. Everything works fine in bun run dev development mode, with no SSR errors and the LSP service functioning correctly.

However, when I build and run the project in production using bun run build and bun run start, I encounter the error: Error: Missing service editorService in the browser console. This prevents the LSP service from working.

Reproduction Steps:

  1. Clone my GitHub repository: https://github.com/cfngc4594/monaco-editor-lsp-next
  2. Navigate to the docker directory: cd docker
  3. Run docker compose up -d to start the LSP container. This will build and run the clangd language server.
  4. Navigate back to the project root directory: cd ..
  5. Run bun install to install dependencies.
  6. Run bun run build to build the project.
  7. Run bun run start to start the production server.
  8. Open the project in a browser and check the console for the error.

Expected Behavior:

  • The LSP service should function correctly in production mode, just like in development mode, without the Error: Missing service editorService error.

Actual Behavior:

  • The console displays the Error: Missing service editorService error, and the LSP service does not work.

Environment Information:

Versions of monaco-editor and monaco-languageclient were chosen after multiple attempts to find a stable and working configuration

Versions of vscode-languageclient and vscode-ws-jsonrpc are latest, but they work well in development stage

  • monaco-editor version: 0.36.1
  • monaco-languageclient version: 5.0.1
  • vscode-languageclient version: ^9.0.1
  • vscode-ws-jsonrpc version: ^3.4.0

Code Snippet:

// src/app/page.tsx
"use client";

import {
  toSocket,
  WebSocketMessageReader,
  WebSocketMessageWriter,
} from "vscode-ws-jsonrpc";
import { useEffect } from "react";
import dynamic from "next/dynamic";
import normalizeUrl from "normalize-url";
import { Skeleton } from "@/components/ui/skeleton";

const DynamicEditor = dynamic(
  async () => {
    const monaco = await import("monaco-editor");
    const { loader, Editor } = await import("@monaco-editor/react");

    loader.config({ monaco });

    return Editor;
  },
  { ssr: false }
);

export default function Home() {
  useEffect(() => {
    const url = normalizeUrl("ws://localhost:4594/clangd");
    const webSocket = new WebSocket(url);

    webSocket.onopen = async () => {
      const socket = toSocket(webSocket);
      const reader = new WebSocketMessageReader(socket);
      const writer = new WebSocketMessageWriter(socket);

      const { MonacoLanguageClient } = await import("monaco-languageclient");
      const { ErrorAction, CloseAction } = await import(
        "vscode-languageclient"
      );

      const languageClient = new MonacoLanguageClient({
        name: "C Language Client",
        clientOptions: {
          documentSelector: ["c"],
          errorHandler: {
            error: () => ({ action: ErrorAction.Continue }),
            closed: () => ({ action: CloseAction.DoNotRestart }),
          },
        },
        connectionProvider: {
          get: () => Promise.resolve({ reader, writer }),
        },
      });

      languageClient.start();
      reader.onClose(() => languageClient.stop());
    };

    webSocket.onerror = (event) => {
      console.error("WebSocket error observed:", event);
    };

    webSocket.onclose = (event) => {
      console.log("WebSocket closed:", event);
    };

    return () => {
      webSocket.close();
    };
  }, []);

  return (
    <div className="h-screen flex flex-col">
      <DynamicEditor
        theme="vs-dark"
        defaultLanguage="c"
        defaultValue="# include<stdio.h>"
        path="file:///main.c"
        onValidate={(markers) => {
          markers.forEach((marker) =>
            console.log("onValidate:", marker.message)
          );
        }}
        options={{ automaticLayout: true }}
        loading={<Skeleton className="h-full w-full p-4" />}
      />
    </div>
  );
}

Additional Context:

"I understand that many developers wish to use Monaco Editor with LSP functionality in Next.js. @monaco-editor/react has already optimized for SSR, making it a popular choice for Next.js users. However, the SSR issues introduced by integrating LSP often lead to developers abandoning the integration.

Currently, I have not found any successful examples of integrating LSP with @monaco-editor/react in Next.js across the entire web. We are now only one step away from success. I plan to develop an open-source online judge for my school (as the school's original online judge is as basic as a notepad, lacking syntax highlighting and other essential features). I've also noticed that most open-source OJs do not prioritize student programming experience and LSP integration. LeetCode seems to offer LSP functionality, but it requires a premium membership. Therefore, I intend to create an open-source code editor OJ with LSP integration. I believe this will be incredibly helpful for college students who are new to programming, especially during exams.

I already have a repository that integrates an LSP code editor using React, which also utilizes @monaco-editor/react. You can find it here: https://github.com/cfngc4594/monaco-editor-lsp-react. I hope this can be beneficial to others."

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions