We want to develop a custom Web Worker for our custom language with typescript. We already wrote a working Web Worker, but since the worker code imports monaco stuff from js files under esm
directory (e.g. monaco-editor/esm/vs/editor/common/languages
), we lose the typing information. We attempted to import from monaco-editor/esm/vs/editor/editor.api.js
, but then the worker couldn’t start (error message pasted below).
Relevant software:
- “monaco-editor”: “^0.46.0”
- “vite”: “^5.2.13”
Example of losing type (ILineTokens is identified as any
):
// @ts-ignore
import * as languages from 'monaco-editor/esm/vs/editor/common/languages';
// ...
function doSomething(tokens: languages.ILineTokens): string {
// ...
}
Relevant piece of code:
- JasmineWorker.ts
// @ts-ignore
import * as worker from 'monaco-editor/esm/vs/editor/editor.worker';
// @ts-ignore
import * as languages from 'monaco-editor/esm/vs/editor/common/languages';
// @ts-ignore
import { Position } from 'monaco-editor/esm/vs/editor/common/core/position';
import IWorkerContext = worker.IWorkerContext;
// ...
export class JasmineWorker {
private ctx: IWorkerContext;
constructor(ctx: IWorkerContext, _createData: ICreateData) {
this.ctx = ctx;
}
public async getCompletionItems(
options: AutoCompleteOptions,
word: IWordAtPosition,
position: Position,
range: languages.CompletionItemRanges
): Promise<languages.CompletionList> {
// ...
}
}
export function create(
ctx: IWorkerContext,
createData: ICreateData
): JasmineWorker {
return new JasmineWorker(ctx, createData);
}
self.onmessage = () => {
worker.initialize((ctx: IWorkerContext, createData: ICreateData) => {
return new JasmineWorker(ctx, createData);
});
};
export default JasmineWorker;
- JasmineSourceEditor.ts
import { JasmineWorker } from './JasmineWorker';
import WorkerUrl from './JasmineWorker?worker&url';
// ...
self.MonacoEnvironment = {
getWorker: function (_moduleId, label) {
if (label === 'jasmine') {
return new Worker(new URL('./JasmineWorker.ts', import.meta.url), {
type: 'module',
});
}
return new Worker(
new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url),
{ type: 'module' }
);
},
};
export class JasmineSourceEditor {
// ...
private autoCompleteDisposal: IDisposable | undefined;
private worker: editor.MonacoWebWorker<JasmineWorker>;
constructor(parent: HTMLElement, options: EditorOptions) {
this.worker = editor.createWebWorker<JasmineWorker>({
moduleId: WorkerUrl,
label: 'jasmine',
keepIdleModels: true,
createData: {},
});
}
registerCompletionItemProvider(options: AutoCompleteOptions): IDisposable {
return monaco.languages.registerCompletionItemProvider('jasmine', {
provideCompletionItems: async (model, position) => {
await this.worker.withSyncedResources([model.uri]);
const word = model.getWordUntilPosition(position);
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn,
};
const proxy = await this.worker.getProxy();
const suggestions = proxy.getCompletionItems(
options,
word,
position,
range
);
return suggestions;
},
});
}
configureAutoComplete(options: AutoCompleteOptions): void {
if (this.autoCompleteDisposal) {
this.autoCompleteDisposal.dispose();
}
if (options.mode !== AutoCompleteMode.Disabled) {
this.autoCompleteDisposal = this.registerCompletionItemProvider(options);
}
console.log(`auto complete configured`);
}
}
We tried to import from editor.api.js
because there’s editor.api.d.ts
like below:
import { editor } from 'monaco-editor/esm/vs/editor/editor.api';
import { worker } from 'monaco-editor/esm/vs/editor/editor.api';
import { languages } from 'monaco-editor/esm/vs/editor/editor.api';
import { IPosition, IRange } from 'monaco-editor/esm/vs/editor/editor.api';
However, the worker could not start with the following errors:
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
and
Uncaught Error: Unexpected usage
Error: Unexpected usage
at _EditorSimpleWorker.loadForeignModule (chunk-4C672B3V.js?v=b058a95f:9058:27)
at chunk-UV56DTU5.js?v=b058a95f:29446:22
at async Object.provideCompletionItems (JasmineSourceEditor.ts:90:9)
at async monaco-editor.js?v=b058a95f:28834:22
at async Promise.all (index 0)
at async provideSuggestionItems (monaco-editor.js?v=b058a95f:28822:5)
at async Promise.all (index 0)
at chunk-7ATFQVGH.js?v=b058a95f:3090:21