Can't get monaco-languageclient to work in ElectronForge/webpack
Hi everyone, I am working on a text-editor desktop app and I am using the monaco-editor library, I have been able to implement loading and switching between different files and saving their states, but the next thing I am trying to implement is a language server, and I have been on it since 3 days now, after searching online the only solution I have seen is using monaco-languageclient, vscode-ws-jsonrpc, websockets and language-specific LSP like pyright, I have tried the guide on the monaco-languageclient/vscode-ws-jsonrpc page and the guide on the main monaco-languageclient page, but I still could get it to work, the current issue I am getting when trying to initialize the MonacoLanguageClient() class is below
Default api is not ready yet, do not forget to import 'vscode/localExtensionHost' and wait for services initialization
this is my code
Before that this is the environment of my Electronforge application
- Webpack
- TypeScript
- React JS
This is my code in the Editor.tsx file
import React from "react";
import '@codingame/monaco-vscode-python-default-extension';
import "@codingame/monaco-vscode-theme-defaults-default-extension";
import * as monaco from 'monaco-editor';
import { useMainState, usePersistentMainState } from "@/shared/zustand-state";
import { TDirContent, TEditorState, WorkerLoader } from "@/shared/types";
import {listen} from 'vscode-ws-jsonrpc'
import { WebSocketMessageReader } from 'vscode-ws-jsonrpc';
import { CloseAction, ErrorAction, MessageTransports } from 'vscode-languageclient';
import { WebSocketMessageWriter } from 'vscode-ws-jsonrpc';
import { toSocket } from 'vscode-ws-jsonrpc';
import { MonacoLanguageClient } from 'monaco-languageclient';
import { initWebSocketAndStartClient } from "@/shared/lsp-client";
import * as vscode from "vscode";
import "vscode/localExtensionHost"
import { createUrl } from "monaco-languageclient/tools";
//...
function createLanguageClient(): Promise<MonacoLanguageClient> {
return new Promise((resolve) => {
const url = createUrl({
secured: false,
host: 'localhost',
port: 5045,
path: 'pyright',
extraParams: {
authorization: 'UserAuth'
}
});
const ws = new WebSocket(url);
ws.onopen = () => {
const socket = toSocket(ws);
const reader = new WebSocketMessageReader(socket);
const writer = new WebSocketMessageWriter(socket);
const languageClient = new MonacoLanguageClient({
name: 'Python Language Client',
clientOptions: {
documentSelector: [{ language: 'python' }], // change per language
errorHandler: {
error: () => ({ action: ErrorAction.Continue }),
closed: () => ({ action: CloseAction.Restart }),
},
},
// @ts-ignore
connectionProvider: {
get: async () => ({ reader, writer }),
},
});
languageClient.start();
resolve(languageClient);
};
});
}
export default React.memo((props: any) => {
//...
const initialize_editor = React.useCallback(async () => {
if (editor_container_ref.current != null) {
await createLanguageClient() // it dies here with the above error
editor_ref.current = monaco.editor.create(editor_container_ref.current, {
wordBasedSuggestions: 'currentDocument'
});
editor_ref.current.layout();
set_state('monaco_editor_instance', editor_ref.current)
handle_listen_for_cont_width_change()
}
}, [])
//...
The language server running in the main process, though I am still testing it because I am planning to make it run from a worker threads process so as not to block the main thread
import { Worker } from 'worker_threads';
import * as net from 'net'
import * as child_process from 'child_process';
import WebSocket from 'ws';
//...
function createLSPWebSocketBridge() {
const wss = new WebSocket.Server({ port: 5045 });
wss.on('connection', (ws: WebSocket) => {
const socket = net.connect(5040); // Connect to the TCP server in step 1
ws.on('message', msg => socket.write(msg as Buffer));
socket.on('data', data => ws.send(data));
ws.on('close', () => socket.end());
socket.on('close', () => ws.close());
});
console.log('WebSocket bridge listening on port 5007');
}
function launchLanguageServer(): void {
const server = net.createServer(socket => {
const ls = child_process.spawn('pyright-langserver', ['--stdio']); // Change for your language server
socket.pipe(ls.stdin!);
ls.stdout!.pipe(socket);
});
server.listen(5040, () => {
createLSPWebSocketBridge()
console.log('Language server listening on port 5007');
});
}
launchLanguageServer();
The monaco-editor is working well with language highlighting because I am using the monaco-editor-webpack-plugin and all the needed languages workers are running and working, this is my webpack configuration for the workers
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
//...
export const plugins = [
//...
new MonacoWebpackPlugin()
];
this is my package.json
//...
"devDependencies": {
"@electron-forge/cli": "^6.4.2",
"@electron-forge/maker-deb": "^6.4.2",
"@electron-forge/maker-rpm": "^6.4.2",
"@electron-forge/maker-squirrel": "^6.4.2",
"@electron-forge/maker-zip": "^6.4.2",
"@electron-forge/plugin-auto-unpack-natives": "^6.4.2",
"@electron-forge/plugin-webpack": "^6.4.2",
"@types/react": "^19.1.4",
"@types/react-dom": "^19.1.4",
"@types/vscode": "^1.100.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vercel/webpack-asset-relocator-loader": "^1.7.3",
"css-loader": "^6.11.0",
"electron": "36.2.0",
"eslint": "^8.57.1",
"eslint-plugin-import": "^2.31.0",
"fork-ts-checker-webpack-plugin": "^7.3.0",
"monaco-editor-webpack-plugin": "^7.1.0",
"node-loader": "^2.1.0",
"pyright": "^1.1.400",
"style-loader": "^3.3.4",
"ts-loader": "^9.5.2",
"ts-node": "^10.9.2",
"typescript": "~4.5.4"
},
"dependencies": {
"@codingame/monaco-editor-wrapper": "15.0.0",
"@codingame/monaco-vscode-api": "^17.1.0",
"@codingame/monaco-vscode-languages-service-override": "^17.1.0",
"@codingame/monaco-vscode-layout-service-override": "^4.2.0",
"@codingame/monaco-vscode-python-default-extension": "^17.1.0",
"@codingame/monaco-vscode-remote-agent-service-override": "^17.1.0",
"@codingame/monaco-vscode-secret-storage-service-override": "^17.1.0",
"@codingame/monaco-vscode-textmate-service-override": "^17.1.0",
"@codingame/monaco-vscode-theme-defaults-default-extension": "^17.1.0",
"@codingame/monaco-vscode-theme-service-override": "^17.1.0",
"@radix-ui/react-scroll-area": "^1.2.8",
"@radix-ui/react-slot": "^1.2.2",
"@tailwindcss/postcss": "^4.1.6",
"@timfish/webpack-asset-relocator-loader": "^0.1.0",
"@typefox/monaco-editor-react": "^6.7.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"electron-squirrel-startup": "^1.0.1",
"file-loader": "^6.2.0",
"lucide-react": "^0.510.0",
"monaco-editor": "^0.50.0",
"monaco-editor-wrapper": "~6.7.0",
"monaco-languageclient": "^9.6.0",
"net": "^1.0.2",
"postcss": "^8.5.3",
"postcss-loader": "^8.1.1",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-resizable-panels": "^3.0.1",
"tailwind-merge": "^3.3.0",
"tailwindcss": "^4.1.6",
"tw-animate-css": "^1.2.9",
"typescript-language-server": "^4.3.4",
"url-loader": "^4.1.1",
"vscode": "npm:@codingame/monaco-vscode-extension-api@~16.1.1",
"vscode-jsonrpc": "^8.2.1",
"vscode-languageclient": "^9.0.1",
"vscode-languageserver-protocol": "^3.17.5",
"vscode-ws-jsonrpc": "^3.4.0",
"ws": "^8.18.2",
"zustand": "^5.0.4"
}
Other things I have tried
- Using ChatGPT and Gemini
- followed the documentation of
monaco-vscode-apihttps://github.com/CodinGame/monaco-vscode-api?tab=readme-ov-file - followed the documentation in https://github.com/CodinGame/monaco-vscode-api/wiki/Getting-started-guide
Please what I'm I doing wrong or not doing, thank you
Hi @paulosabayomi can you please provide a link to public repository where the problem is reproducible. Thank you.
@kaisalmen thank you for your response, I have created a repo and pushed the codebase to it
This is the link: https://github.com/paulosabayomi/monaco-lc-bug-reproduce
2 major files are the Editor.tsx and index.ts
The Editor.tsx
path: project-folder/src/components/editor-comps/
Description: It contains the monaco-editor instantiation and handling all the editor's operations such as creating models, switching models, saving model states and trying to connect to the LSP server using MonacoLanguageClient
The index.ts
path: project-folder/src
Description: It contains Electron JS main process code and the code for the LSP server starts from line 21 to line 49
Additionally, I am using MonacoWebpackPlugin plugin to handle the monaco language workers using webpack, the code can be found in the project-folder/webpack.plugin.ts file on line 2 and line 11
To reproduce error
- After installing all dependencies and launching the app with
npm start(please it might take up to 3 mins to launch, I will address what is causing that later) - Click the "Open Directory" button in the sidebar to choose a project directory (please choose a directory that contains a python project), then it will list out the content of the directory recursively in the UI with directories listed first and followed by files alphabetically (you can click on any directory to list out its content)
- Then click on any file to open the file in the editor
- Then you will see the error
Default api is not ready yet, do not forget to import 'vscode/localExtensionHost' and wait for services initialization
Thank you for your help
Hi @paulosabayomi thank you for providing the project. I haven't yet used it, but there are already some finding. Regarding the version of monaco-editor-wrapper, monaco-languageclient and all @codingame/* packages, you have to follow this compatibility table. This is mentioned in our README here.
You can't use monaco-editor-webpack-plugin, see the explanation from monaco-vscode-api.
And please also see our Troubleshooting section which describes how to use codingame/monaco-vscode-editor-api instead of monaco-editor when using the monaco-editor api with this stack.
Hi @kaisalmen , thank you for your response, okay I will apply your suggestions and I will also try to change to Vite
Hi @kaisalmen , I decided to use vscode-ws-jsonrpc directly without the MonacoLanguageClient monaco-languageclient to connect to Pyright language server - which I am running from the cloned monaco-languageclient's Python example directory - and I was able to connect to Pyright language server and exchange data between the client and Pyright, but the issue I am currently having is I don't know the kind of methods to send, and when, to Pyright language server, because when I ran the monaco-languageclient example in the browser, together with docker, I noticed there are different methods sent to the language server on different actions for example the textDocument/diagnostic, textDocument/hover, textDocument/codeAction, textDocument/documentSymbol methods, and my current problem is I don't know when to send those methods and what other parameters to send along each of them, also because monaco-editor only emits onDidChangeContent event and not hover or codeAction or documentSymbol events, if I could know all the methods to send and when to send them maybe I can build my own languageclient to communicate with language server to match ElectronForge/webpack
This is the code I wrote to connect to the Pyright LS
function createLanguageClient(model: monaco.editor.ITextModel, folder_root: string): Promise<WebSocket> {
return new Promise((resolve) => {
const ws = new WebSocket('ws://localhost:30001/pyright?authorization=UserAuth');
ws.onmessage = (ev: any) => {
console.log("createLanguageClient got message", ev);
}
let requestId = 0;
ws.onopen = async (ev) => {
const socket = toSocket(ws);
const reader = new WebSocketMessageReader(socket);
const writer = new WebSocketMessageWriter(socket);
let syncKind: number = 2; // Default to Incremental
// Send initialize request
const initializeId = requestId;
writer.write({
jsonrpc: '2.0',
// @ts-ignore
id: initializeId,
method: 'initialize',
params: {
processId: null,
rootPath: folder_root,
rootUri: "file://"+folder_root,
capabilities: {
textDocument: {
synchronization: {
didSave: true,
willSave: false,
willSaveWaitUntil: false,
dynamicRegistration: false
}
}
}
}
});
reader.listen((data) => {
console.log("reader got data", data);
// @ts-ignore
if (data.id === initializeId) {
// Handle initialize response
// @ts-ignore
const serverCapabilities = data.result.capabilities;
console.log('Server capabilities:', serverCapabilities);
// Determine textDocumentSync kind
const textDocumentSync = serverCapabilities.textDocumentSync;
if (typeof textDocumentSync === 'number') {
syncKind = textDocumentSync;
} else if (textDocumentSync && textDocumentSync.change) {
syncKind = textDocumentSync.change;
}
// Send initialized notification
writer.write({
jsonrpc: '2.0',
// @ts-ignore
method: 'initialized',
params: {}
});
model.onDidChangeContent((event) => {
console.log("model content changed", event,);
requestId++
sendDidChange(model, event, syncKind, requestId);
});
// @ts-ignore
} else if (data.id) {
// Handle other responses
console.log('Received response:', data);
} else {
// Handle notifications
// @ts-ignore
if (data.method === 'textDocument/publishDiagnostics') {
// @ts-ignore
const uri = data.params.uri;
// @ts-ignore
const diagnostics = data.params.diagnostics;
const targetModel = monaco.editor.getModel(monaco.Uri.parse(uri));
console.log("targetModeltargetModel", targetModel, diagnostics);
if (targetModel) {
const markers = diagnostics.map((diag: any) => ({
severity: diag.severity === 1 ? monaco.MarkerSeverity.Error : monaco.MarkerSeverity.Warning,
startLineNumber: diag.range.start.line + 1,
startColumn: diag.range.start.character + 1,
endLineNumber: diag.range.end.line + 1,
endColumn: diag.range.end.character + 1,
message: diag.message
}));
monaco.editor.setModelMarkers(targetModel, 'lsp', markers);
}
}
}
});
resolve(ws);
function sendDidOpen(model: monaco.editor.ITextModel) {
console.log("will send didopen", model);
requestId++
const textDocument = {
uri: model.uri.toString(),
languageId: model.getLanguageId(),
version: model.getVersionId(),
text: model.getValue()
};
writer.write({
jsonrpc: '2.0',
// @ts-ignore
id: requestId,
// @ts-ignore
method: 'textDocument/didOpen',
params: { textDocument }
});
}
function sendDidChange(
model: monaco.editor.ITextModel,
event: monaco.editor.IModelContentChangedEvent,
syncKind: number,
requestId: number,
) {
if (syncKind === 1) {
// Full sync
writer.write({
jsonrpc: '2.0',
// @ts-ignore
id: requestId,
// @ts-ignore
method: 'textDocument/diagnostic',
params: {
textDocument: {
uri: model.uri.toString(),
version: model.getVersionId(),
text: model.getValue()
},
contentChanges: [{ text: model.getValue() }]
}
});
} else {
// Incremental sync
const changes = event.changes.map((change) => ({
range: {
start: {
line: change.range.startLineNumber - 1,
character: change.range.startColumn - 1
},
end: {
line: change.range.endLineNumber - 1,
character: change.range.endColumn - 1
}
},
text: change.text
}));
writer.write({
jsonrpc: '2.0',
// @ts-ignore
id: requestId,
// @ts-ignore
method: 'textDocument/diagnostic',
params: {
textDocument: {
uri: model.uri.toString(),
version: model.getVersionId(),
text: model.getValue()
},
contentChanges: changes
}
});
}
}
};
});
}
These are the logs in the Pyright terminal when I connected to it with vscode-ws-jsonrpc with the code above
PYRIGHT Server received: initialize
{
jsonrpc: '2.0',
id: 0,
method: 'initialize',
params: {
processId: 27375,
rootPath: '/Users/paulos_ab/Documents/personals/astronomybot-dev',
rootUri: 'file:///Users/paulos_ab/Documents/personals/astronomybot-dev',
capabilities: { textDocument: [Object] }
}
}
PYRIGHT Server sent:
{
jsonrpc: '2.0',
id: 0,
result: {
capabilities: {
textDocumentSync: 2,
definitionProvider: [Object],
declarationProvider: [Object],
typeDefinitionProvider: [Object],
referencesProvider: [Object],
documentSymbolProvider: [Object],
workspaceSymbolProvider: [Object],
hoverProvider: [Object],
documentHighlightProvider: [Object],
renameProvider: [Object],
completionProvider: [Object],
signatureHelpProvider: [Object],
codeActionProvider: [Object],
executeCommandProvider: [Object],
callHierarchyProvider: true,
workspace: [Object]
}
}
}
PYRIGHT Server received: textDocument/diagnostic
{
jsonrpc: '2.0',
id: 1,
method: 'textDocument/diagnostic',
params: {
textDocument: {
uri: 'file:///Users/paulos_ab/Documents/personals/astronomybot-dev/rag-test.py',
version: 2,
text: '# from llama_index.llms.gemini import Gemini\n' +
'# from llama_index.core import VectorStoreIndex, SimpleDirectoryReader\n' +
'import os, requests\n' +
'from dotenv import load_dotenv\n' +
'# from llama_index.embeddings.google_genai import GoogleGenAIEmbedding\n' +
'# from google.genai.types import EmbedContentConfig\n' +
'# from llama_index.core import Settings\n' +
'\n' +
'load_dotenv()\n' +
'\n' +
'\n' +
'# embed_model = GoogleGenAIEmbedding(\n' +
'# model_name="text-embedding-004",\n' +
'# embed_batch_size=100,\n' +
"# api_key=os.environ['google_palm_api_key'],\n" +
'# # can pass in the api key directly\n' +
'# # api_key="...",\n' +
'# # or pass in a vertexai_config\n' +
'# # vertexai_config={\n' +
'# # "project": "...",\n' +
'# # "location": "...",\n' +
'# # }\n' +
'# # can also pass in an embedding_config\n' +
'# # embedding_config=EmbedContentConfig(...)\n' +
'# )\n' +
'\n' +
'# Settings.embed_model = embed_model\n' +
'\n' +
'\n' +
'# def process_llamaindex(folder_path):\n' +
'# documents = SimpleDirectoryReader(folder_path).load_data()\n' +
'# print("folder_path", folder_path)\n' +
`# llm = Gemini(model="models/gemini-2.0-flash", api_key=os.environ['google_palm_api_key'])\n` +
'# print("llm", llm)\n' +
'# index = VectorStoreIndex.from_documents(documents, llm=llm, embed_model=embed_model)\n' +
'# print("index", index)\n' +
'# query_engine = index.as_query_engine(llm=llm)\n' +
`# response = query_engine.query("The folder contains a project's source code, write a 20 line usage developer documentation for the codebase, response should be in markdown")\n` +
'# print(response)\n' +
'\n' +
'# process_llamaindex("./gc-functions")\n' +
'\n' +
'excg_key = "+TctvJaY9CJLvP/sR2VVVm9X9TrGAqqy9nvaa3uY"\n' +
'\n' +
'fk_req = requests.post(\n' +
' "http://127.0.0.1:5001/paulosmapkey/us-central1/astronomy_bot", \n' +
' headers={\n' +
' "Exchange": excg_key,\n' +
' "X-Cloudscheduler-Jobname": "weather"\n' +
' },\n' +
' allow_redirects=False\n' +
')\n' +
'\n' +
'print("response::", fk_req.text)'
},
contentChanges: [ [Object] ]
}
}
PYRIGHT Server sent:
{
jsonrpc: '2.0',
id: 1,
result: { kind: 'full', resultId: '2', items: [] }
}
But these are the logs from the Python example I ran in the browser
PYRIGHT Server received: textDocument/diagnostic
{
jsonrpc: '2.0',
id: 83,
method: 'textDocument/diagnostic',
params: {
identifier: 'pyright',
textDocument: { uri: 'file:///home/mlc/workspace/hello2.py' },
previousResultId: '47'
}
}
PYRIGHT Server received: textDocument/documentSymbol
{
jsonrpc: '2.0',
id: 84,
method: 'textDocument/documentSymbol',
params: { textDocument: { uri: 'file:///home/mlc/workspace/hello2.py' } }
}
PYRIGHT Server received: window/workDoneProgress/create
{
jsonrpc: '2.0',
id: 19,
method: 'window/workDoneProgress/create',
params: { token: '8c5b5775-0224-4100-be57-5516e41c8c72' }
}
PYRIGHT Server sent:
{
jsonrpc: '2.0',
id: 83,
result: { kind: 'full', resultId: '50', items: [ [Object], [Object] ] }
}
PYRIGHT Server sent:
{ jsonrpc: '2.0', id: 19, result: null }
PYRIGHT Server sent:
{
jsonrpc: '2.0',
id: 84,
result: [
{
name: 'print_hello',
kind: 12,
range: [Object],
selectionRange: [Object],
children: [Array]
}
]
}
Thank you
, I decided to use vscode-ws-jsonrpc directly without the MonacoLanguageClient monaco-languageclient to connect to Pyright language server
Hi @paulosabayomi why did you do that? You have a tooling issue that can/must be solved. Now you use a far more complex approach.
Hi @kaisalmen , thank you for your response, I have decided to create a new ElectronForge project from ground up using vite this time, and I followed the Getting started guide on the monaco-vscode-api wiki, but I am still getting some errors, I am getting 1 error and 2 warnings.
This is my vite ElectronForge environment
- ElectronForge
- React JS
- TypeScript
Error.
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'classList')
at updateRootClasses (vscode_services.js?v=2dd68df4:86367:41)
at AccessibilityService2.initReducedMotionListeners (vscode_services.js?v=2dd68df4:86370:5)
at new AccessibilityService2 (vscode_services.js?v=2dd68df4:86355:10)
at _InstantiationService._createInstance (vscode_services.js?v=2dd68df4:87744:20)
at _InstantiationService._createServiceInstance (vscode_services.js?v=2dd68df4:87847:27)
at _InstantiationService._createServiceInstanceWithOwner (vscode_services.js?v=2dd68df4:87838:19)
at _InstantiationService._createAndCacheServiceInstance (vscode_services.js?v=2dd68df4:87828:33)
at _InstantiationService._safeCreateAndCacheServiceInstance (vscode_services.js?v=2dd68df4:87781:19)
at _InstantiationService._getOrCreateServiceInstance (vscode_services.js?v=2dd68df4:87769:19)
at Object.get (vscode_services.js?v=2dd68df4:87697:31)
I got this error when I called the initialize function imported from vscode/services and the error goes away when I comment it out
Warnings
Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes. Please see https://github.com/microsoft/monaco-editor#faq
requestIdleCallback
Second Warning just below above
Worker editorWorkerService not found
requestIdleCallback
Because of these warnings the language highlighting for python is not working
I have tried other things like switching to monaco-editor-react but still faced tons of errors and then I decided to switch back by uninstalling all monaco related dependencies and re-installed them freshly and restarted the Getting started guide again, and then I still got those warnings and error above, this is my code
Editor.tsx
import '@codingame/monaco-vscode-python-default-extension';
import "@codingame/monaco-vscode-theme-defaults-default-extension";
import React from "react";
import * as monaco from 'monaco-editor';
import { initialize } from 'vscode/services'
import getLanguagesServiceOverride from "@codingame/monaco-vscode-languages-service-override";
import getThemeServiceOverride from "@codingame/monaco-vscode-theme-service-override";
import getTextMateServiceOverride from "@codingame/monaco-vscode-textmate-service-override";
export type WorkerLoader = () => Worker;
const workerLoaders: Partial<Record<string, WorkerLoader>> = {
// @ts-ignore
TextEditorWorker: () => new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url), { type: 'module' }),
// @ts-ignore
TextMateWorker: () => new Worker(new URL('@codingame/monaco-vscode-textmate-service-override/worker', import.meta.url), { type: 'module' })
}
window.MonacoEnvironment = {
getWorker: function (_moduleId, label) {
console.log('getWorker', _moduleId, label);
const workerFactory = workerLoaders[label]
if (workerFactory != null) {
return workerFactory()
}
throw new Error(`Worker ${label} not found`)
}
}
export default React.memo((props: any) => {
const editor_ref = React.useRef<HTMLDivElement | null>(null);
const handleInitEditor = React.useCallback(async () => {
if (!editor_ref.current) return;
initialize({
...getTextMateServiceOverride(),
...getThemeServiceOverride(),
...getLanguagesServiceOverride(),
}, document.querySelector('.mnain'));
const editor = monaco.editor.create(editor_ref.current, {
value: "print('Hello world!')",
language: "python"
});
editor.layout();
editor.focus();
}, [])
React.useLayoutEffect(() => {
handleInitEditor()
}, [])
return (
<div ref={editor_ref} className="h-[100vh] mnain">
</div>
)
})
The other configurations I did in vite.renderer.config.mts
import { defineConfig } from 'vite';
// @ts-ignore
import tailwindcss from '@tailwindcss/vite'
import importMetaUrlPlugin from '@codingame/esbuild-import-meta-url-plugin'
// https://vitejs.dev/config
export default defineConfig({
optimizeDeps: {
esbuildOptions: {
plugins: [
importMetaUrlPlugin,
],
},
include: ['vscode-oniguruma', 'vscode-textmate', '@vscode/vscode-languagedetection']
},
plugins: [
tailwindcss(),
],
resolve: {
dedupe: ['vscode', '@codingame/monaco-vscode-api']
}
});
And this is the dependencies section in my package.json file
//...
"dependencies": {
"@codingame/esbuild-import-meta-url-plugin": "^1.0.3",
"@codingame/monaco-vscode-languages-service-override": "^17.1.1",
"@codingame/monaco-vscode-python-default-extension": "^17.1.1",
"@codingame/monaco-vscode-textmate-service-override": "^17.1.1",
"@codingame/monaco-vscode-theme-defaults-default-extension": "^17.1.1",
"@codingame/monaco-vscode-theme-service-override": "^17.1.1",
"@tailwindcss/vite": "^4.1.7",
"@types/react": "^19.1.4",
"@types/react-dom": "^19.1.5",
"@vitejs/plugin-react": "^4.4.1",
"@vscode/vscode-languagedetection": "^1.0.22",
"electron-squirrel-startup": "^1.0.1",
"monaco-editor": "npm:@codingame/monaco-vscode-editor-api@^17.1.1",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tailwindcss": "^4.1.7",
"vscode": "npm:@codingame/monaco-vscode-api@^17.1.1",
"vscode-oniguruma": "^2.0.1",
"vscode-textmate": "^9.2.0"
}
I have also tried to follow the version compatibility table but got tons of error like "import" was not exported from @codingame/monaco-vscode-random-ids-common/... and the errors are like 10 to 15 and point to some imports not exported from @codingame/monaco-vscode-random-ids-common/..., and I have deleted both package-lock.json and node_modules and reinstalled multiple times.
I also tried to add language server following the introducing language server guide but I got the following error
Uncaught TypeError: Class extends value undefined is not a constructor or null
at node_modules/vscode-languageclient/lib/common/features.js (chunk-T5B5MU7F.js?v=22768359:6215:55)
at __require (chunk-MJOZMRNF.js?v=22768359:15:50)
at node_modules/vscode-languageclient/lib/common/api.js (chunk-T5B5MU7F.js?v=22768359:16033:18)
at __require (chunk-MJOZMRNF.js?v=22768359:15:50)
at node_modules/vscode-languageclient/lib/browser/main.js (chunk-T5B5MU7F.js?v=22768359:16075:17)
at __require (chunk-MJOZMRNF.js?v=22768359:15:50)
at node_modules/vscode-languageclient/browser.js (chunk-T5B5MU7F.js?v=22768359:16097:22)
at __require (chunk-MJOZMRNF.js?v=22768359:15:50)
at vscode-languageclient_browser__js.js?v=22768359:15:16
this is the language server code as copied directly from the introducing language server section in the monaco-vscode-api getting started wiki page
import { WebSocketMessageReader } from 'vscode-ws-jsonrpc';
import { CloseAction, ErrorAction, MessageTransports } from 'vscode-languageclient/browser.js';
import { WebSocketMessageWriter } from 'vscode-ws-jsonrpc';
import { toSocket } from 'vscode-ws-jsonrpc';
import { MonacoLanguageClient } from 'monaco-languageclient';
export const initWebSocketAndStartClient = (url: string): WebSocket => {
const webSocket = new WebSocket(url);
webSocket.onopen = () => {
// creating messageTransport
const socket = toSocket(webSocket);
const reader = new WebSocketMessageReader(socket);
const writer = new WebSocketMessageWriter(socket);
// creating language client
const languageClient = createLanguageClient({
reader,
writer
});
languageClient.start();
reader.onClose(() => languageClient.stop());
};
return webSocket;
};
const createLanguageClient = (messageTransports: MessageTransports): MonacoLanguageClient => {
return new MonacoLanguageClient({
name: 'Sample Language Client',
clientOptions: {
// use a language id as a document selector
documentSelector: ['python'],
// disable the default error handler
errorHandler: {
error: () => ({ action: ErrorAction.Continue }),
closed: () => ({ action: CloseAction.DoNotRestart })
}
},
// create a language client connection from the JSON RPC connection on demand
// @ts-ignore
connectionProvider: {
get: async (_encoding: string) => messageTransports
}
});
};
Thank you
Hi @kaisalmen , I finally resolved it,
the main issue is actually the compatibility, I downgraded all the monaco and vscode related libraries to 16.1.1 and it worked and I am now able to connect to the Python language server, and then I did something tricky, instead of importing initialize from vscode/services which has been the main cause of the issue I imported it from @codingame/monaco-vscode-api/services because I had to install @codingame/monaco-vscode-api/ separately as standalone instead of as vscode as it is in the Getting started guide and then I installed @codingame/monaco-vscode-extension-api as vscode instead, now my imports are like this
import '@codingame/monaco-vscode-python-default-extension';
import "@codingame/monaco-vscode-theme-defaults-default-extension";
import React from "react";
import * as vscode from "vscode" // <----------- @codingame/monaco-vscode-extension-api instead of @codingame/monaco-vscode-api
import * as monaco from 'monaco-editor';
import { initialize } from '@codingame/monaco-vscode-api/services' // <------- @codingame/monaco-vscode-api/ instead if 'vscode'
import getLanguagesServiceOverride from "@codingame/monaco-vscode-languages-service-override";
import getThemeServiceOverride from "@codingame/monaco-vscode-theme-service-override";
import getTextMateServiceOverride from "@codingame/monaco-vscode-textmate-service-override";
import { WebSocketMessageReader } from 'vscode-ws-jsonrpc';
import { CloseAction, ErrorAction, MessageTransports } from 'vscode-languageclient/browser.js';
import { WebSocketMessageWriter } from 'vscode-ws-jsonrpc';
import { toSocket } from 'vscode-ws-jsonrpc';
import { MonacoLanguageClient } from 'monaco-languageclient';
and the dependency section in my package.json is like so now
"dependencies": {
"@codingame/esbuild-import-meta-url-plugin": "^1.0.3",
"@codingame/monaco-vscode-api": "^16.1.1", // < ------ installed separately and standalone
"@codingame/monaco-vscode-languages-service-override": "^16.1.1",
"@codingame/monaco-vscode-python-default-extension": "^16.1.1",
"@codingame/monaco-vscode-textmate-service-override": "^16.1.1",
"@codingame/monaco-vscode-theme-defaults-default-extension": "^16.1.1",
"@codingame/monaco-vscode-theme-service-override": "^16.1.1",
"@tailwindcss/vite": "^4.1.7",
"@types/react": "^19.1.4",
"@types/react-dom": "^19.1.5",
"@vitejs/plugin-react": "^4.4.1",
"@vscode/vscode-languagedetection": "^1.0.22",
"electron-squirrel-startup": "^1.0.1",
"monaco-editor": "npm:@codingame/monaco-vscode-editor-api@^16.1.1",
"monaco-languageclient": "^9.6.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tailwindcss": "^4.1.7",
"vscode": "npm:@codingame/monaco-vscode-extension-api@^16.1.1", // <---- instead of @codingame/monaco-vscode-api
"vscode-languageclient": "^9.0.1",
"vscode-oniguruma": "^2.0.1",
"vscode-textmate": "^9.2.0",
"vscode-ws-jsonrpc": "^3.4.0"
}
Thanks for the compatibility tip, it's the root of the solution
Please don't close the issue yet, I am trying to check if it will work in the former webpack project environment
Update
Nevermind I have moved the project environment permanently to vite, at least for now until an unforeseen future issue like when packaging the app
Issue review before new release. I will close this now.