Closed JeanJPNM closed 1 year ago
Maybe you should find a solution for packing webworker in the vite docs, Editor
component loading monaco from a CDN, there are not support packing monaco from local floder, it is should be done by vite or rollup.
But the problem is not in packing the webworkers or importing them. The problem is that I can't tell @monaco-editor/loader
, and by consequence, your editor, that I'm loading my own monaco instance and that it should not load its own from the CDN.
Because of the way the library is written, calling loader.config({ monaco })
will not cause any sort of update in the component, because it already loaded its own monaco instance.
I haven't found a workaround that doesn't involve making my own version of this library and passing a monaco ref
via inject
.
I here is a demonstration of the issue
You're wanna to dynamically switch monaco-editor
versions via the @monaco-editor/loader
, but it doesn't work, right?
I can see what your saying, but we hadn't considered this senario.
As you can see from the following lines of code, the loader only loads once, repeating settings doesn't reload:
function init() {
const state = getState(({ monaco, isInitialized, resolve }) => ({ monaco, isInitialized, resolve }));
// look here, it is only executeds once.
if (!state.isInitialized) {
setState({ isInitialized: true });
// something...
}
return makeCancelable(wrapperPromise);
}
The principle of the @monaco-editor/loader
is very simple: dynamically create a script element to loading monaco-editor
.
The problem now is that @monaco-editor/loader
is only exectued once. Maybe you can create a pr to fix this problem or fork this repo and rewrite to your needs.
After experimenting I found a possible solution, and I would like to see what you think of it.
changing useMonaco.ts:
import type { Nullable, MonacoEditor } from '../types'
import { InjectionKey, ShallowRef, inject, onMounted, provide, shallowRef } from 'vue-demi'
import loader from '@monaco-editor/loader'
interface MonacoContext {
monacoRef?: ShallowRef<MonacoEditor | null>
}
const key: InjectionKey<MonacoContext> = Symbol()
// export this in index.ts
export function provideMonaco(context: MonacoContext) {
provide(key, context)
}
// maybe export this too
export function injectMonaco() {
return inject<MonacoContext>(key, () => ({}), true)
}
export function useMonaco() {
// this declaration has been scope to
// not modify the original monacoRef
// declaration type, since this one may be undefined
{
const { monacoRef } = injectMonaco()
if (monacoRef) return { monacoRef, unload() {} }
}
const monacoRef = shallowRef<Nullable<MonacoEditor>>(loader.__getMonacoInstance())
let promise: ReturnType<(typeof loader)['init']>
onMounted(() => {
// the instance has already been loaded
if (monacoRef.value) return
promise = loader.init()
promise
.then(monacoInstance => (monacoRef.value = monacoInstance))
.catch(error => {
if (error?.type !== 'cancelation') {
console.error('Monaco initialization error:', error)
}
})
})
// monaco mount
const unload = () => promise?.cancel()
return {
monacoRef,
unload,
}
}
And then it would be used like this:
<script setup>
import Editor, { provideMonaco } from '@guolao/vue-monaco-editor';
import { onMounted, shallowRef } from 'vue'
const monacoRef = shallowRef()
provideMonaco({ monacoRef })
onMounted(async () => {
monacoRef.value = await import("./my_monaco_module")
});
</script>
<template>
<Editor language="typescript" />
</template>
This change is backwards-compatible, but I'm not very familiar with the provide
/inject
API to know if I should just export the key or if it's okay to export provideMonaco
and injectMonaco
.
After experimenting I found a possible solution, and I would like to see what you think of it.
changing useMonaco.ts:
import type { Nullable, MonacoEditor } from '../types' import { InjectionKey, ShallowRef, inject, onMounted, provide, shallowRef } from 'vue-demi' import loader from '@monaco-editor/loader' interface MonacoContext { monacoRef?: ShallowRef<MonacoEditor | null> } const key: InjectionKey<MonacoContext> = Symbol() // export this in index.ts export function provideMonaco(context: MonacoContext) { provide(key, context) } // maybe export this too export function injectMonaco() { return inject<MonacoContext>(key, () => ({}), true) } export function useMonaco() { // this declaration has been scope to // not modify the original monacoRef // declaration type, since this one may be undefined { const { monacoRef } = injectMonaco() if (monacoRef) return { monacoRef, unload() {} } } const monacoRef = shallowRef<Nullable<MonacoEditor>>(loader.__getMonacoInstance()) let promise: ReturnType<(typeof loader)['init']> onMounted(() => { // the instance has already been loaded if (monacoRef.value) return promise = loader.init() promise .then(monacoInstance => (monacoRef.value = monacoInstance)) .catch(error => { if (error?.type !== 'cancelation') { console.error('Monaco initialization error:', error) } }) }) // monaco mount const unload = () => promise?.cancel() return { monacoRef, unload, } }
And then it would be used like this:
<script setup> import Editor, { provideMonaco } from '@guolao/vue-monaco-editor'; import { onMounted, shallowRef } from 'vue' const monacoRef = shallowRef() provideMonaco({ monacoRef }) onMounted(async () => { monacoRef.value = await import("./my_monaco_module") }); </script> <template> <Editor language="typescript" /> </template>
This change is backwards-compatible, but I'm not very familiar with the
provide
/inject
API to know if I should just export the key or if it's okay to exportprovideMonaco
andinjectMonaco
.
There are some problems with your suggestion.
props
to pass monacoRef
is better than using provide
& inject
api.@monaco-editor/loader
and await import
to danamically load two different monaco-editor
over http, no way to known which https request will return result first (Race Condition).monacoRef
variable correctly. And then, we still need to destroy the created editor
instance and create a new editor
instance. It will bring many breaking changes,causing me need a lot of time to test the change.This reverses my idea of making monaco-editor
easier to use.
I'm not sure I'm misunderstanding what you are saying. If you only wanna package the monaco-editor
file from your local node_modules
, just use vite can solve it.
If you are wanna load two different version of the monaco-editor
. I don't known why.
But any way, I have a suggestion that you can fork this repo and rewrite it to suit your needs, because this repo just simply wraps the monaco-editor
in Vue, very seay.
When using both @monaco-editor/loader and await import to danamically load two different monaco-editor over http, no way to known which https request will return result first (Race Condition).
Alright, I guess I should have explained better:
I decided to use provide
/inject
because the component that is responsible for loading the monaco instance may be much higher up in the component tree than the component that is instantiating the editor (like a nuxt layout).
As a consequence of using provide
/inject
, the useMonaco
hook can check if a parent component provided its own monaco instance ref, and if it does, then useMonaco
returns it without invoking loader.init()
and thus, without creating a race condition.
But any way, I have a suggestion that you can fork this repo and rewrite it to suit your needs, because this repo just simply wraps the monaco-editor in Vue, very seay.
Yeah, it's probably better if I just make a fork that fits my needs
I was trying to use the version of
monaco-editor
from my node_modules folder and bundle it using vite to only import the languages that the component uses, however, I couldn't find a way to use the dynamically imported monaco instance with theEditor
component. ``:monaco.ts