Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
cfngc4594 opened this issue Feb 19, 2025 · 8 comments

Comments

@cfngc4594
Copy link

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."

@cfngc4594
Copy link
Author

Hi everyone,

I've managed to resolve the "Error: Missing service editorService" issue I reported in this thread (#870). It turns out the solution was quite simple.

Solution:

The issue can be fixed by adding await import("vscode") to the dynamic import of the Editor component.

Here's the corrected code snippet:

const DynamicEditor = dynamic(
  async () => {
    await import("vscode"); // Add this line

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

    loader.config({ monaco });

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

By adding await import("vscode"), the necessary editorService is properly initialized, and the error is resolved.

Important Note: With this fix, you can now successfully use @monaco-editor/react with LSP (Language Server Protocol) features in your Next.js project without any SSR (Server-Side Rendering) issues.

I hope this helps anyone else encountering the same issue.

@cfngc4594
Copy link
Author

Dear @Typefox/monaco-language Developers and Contributors,

I'm writing to share a solution I've developed to overcome the challenges of integrating Language Server Protocol (LSP) functionality within Next.js, specifically using @monaco-editor/react and @Typefox/monaco-language, and addressing the complexities of Server-Side Rendering (SSR).

I've created a demonstration repository, available here: https://github.com/cfngc4594/monaco-editor-lsp-next, which showcases a working implementation.

This repository leverages Docker Compose, enabling straightforward deployment with a single command. I've observed that many developers struggle with SSR-related issues when attempting to integrate LSP into Next.js using these libraries, and I believe this demo could serve as a valuable resource for those facing similar hurdles.

I would be immensely grateful if you would consider including this repository as a community-contributed example in the README. I'm confident it could significantly aid others in navigating these integration challenges.

Would you be receptive to a pull request to incorporate this into the README?

Thank you for your time and consideration.

Sincerely,

cfngc4594

@kaisalmen
Copy link
Collaborator

@cfngc4594 thank you. I was on vacation and will have a look this week.

@cfngc4594
Copy link
Author

Hi @kaisalmen ,

Thank you for taking the time to review my PR. The previous submission was an initial integration of the LSP, but the implementation wasn’t as elegant as it could be. After over a week of real-world usage and refinements, I’ve identified a better approach and plan to update the PR soon.

I’d appreciate it if you could hold off on merging for now—I’ll submit the improved version shortly. Thanks for your patience and support!

Best regards,
cfngc4594

@kaisalmen
Copy link
Collaborator

Hi @cfngc4594 you are talking about #880 right? Can you convert it to a Draft, then it is clear it is not ready. You can also add "WIP:" in the title.
Btw, are you aware of our next verification example?

@cfngc4594
Copy link
Author

Hi @kaisalmen ,
Yes, I was referring to #880. I've now converted it to a Draft.

Additionally, I am aware of the current next example, but the current example uses @typefox/monaco-editor-react, while I am developing based on @monaco-editor/react. Therefore, I would like to provide an example of integrating LSP functionality with @monaco-editor/react in Next.js while avoiding SSR issues. I just finished refactoring a lot of the code.

I would like to ask if, in addition to adding documentation, I can also provide an example in the verify folder?

@kaisalmen
Copy link
Collaborator

@cfngc4594 I think it is best if the documentation refers mostly to executable code and yes, it makes sense to have an independent verification example. And please use current versions. Let's continue any further discussion in the PR. 👍

@cfngc4594
Copy link
Author

@kaisalmen Okay, I will close the current issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants