remcohaszing/monaco-yaml

How to have multiple instance with differents schema validation ?

tchiotludo opened this issue · 5 comments

Looking at this :

import { setDiagnosticsOptions } from 'monaco-yaml';
setDiagnosticsOptions({
  // ...
});

It's a global method that, as I understand, will create a worker with the schema that need to be validated. In my app, I want to be able to insert the schema based on current instance.

On the same page, I use multiple editor with multiple different schema that I need to inject only to this instance.

Is this possible?

You appear to be referencing to the monaco-editor module as an instance. There is no such thing as multiple instances of this. If there are, you are probably doing something wrong.

It’s important to understand the difference between models and editors. Monaco provides a single model registry. Models represent a uri and its contents. Each model uri needs to be unique.

monaco.editor.createModel('', undefined, monaco.Uri.parse('file:///a.yaml'))
monaco.editor.createModel('', undefined, monaco.Uri.parse('file:///b.yaml'))

An editor is merely a graphical view on a model. I think multiple editors could even use the same model.

monaco-yaml doesn’t operate on editors. It just operates on models. However your editors are displaying the information provided by monaco-yaml are just Monaco internals.

If you want to display different contents and validation in different editors, you’ll have to use different models.

import * as monaco from 'monaco-editor'
import { setDiagnosticsOptions } from 'monaco-yaml'

setDiagnosticsOptions({
  schemas: [
    {
      fileMatch: ['a.yaml'],
      uri: 'https://example.com/a.schema.json'
    },
    {
      fileMatch: ['b.yaml'],
      uri: 'https://example.com/b.schema.json'
    }
  ]
})

const modelA = monaco.editor.createModel('', undefined, monaco.Uri.parse('file:///a.yaml'))
const modelB = monaco.editor.createModel('', undefined, monaco.Uri.parse('file:///b.yaml'))

monaco.editor.create(document.getElementById('editor-a'), { model: modelA })
monaco.editor.create(document.getElementById('editor-b'), { model: modelB })

Exactly how you want to differentiate between contexts is up to you. This example used two hardcoded file:// uris, but you could use globbing with a directory structure of different protocols.

@remcohaszing thanks for the precision, it's probably what I understood but I preferred to ask to be sure, since in my special case it will not be simple: the schemas for the whole app is really huge (probably few megabits that will be loaded in memory I assume) if I need to provide one unique.
Anyway thanks for taking time to confirm 👍

Those are some bold assumptions.

  1. A few megabits is only a few hundreds of kilobytes. Did you mean megabytes?
  2. JSON data size is not equal to memory usage.
  3. If you specify a file match and url only, the schemas will only be loaded when needed. I.e. the entire schema store is loaded by these lines
    fetch('https://www.schemastore.org/api/json/catalog.json').then(async (response) => {
    if (!response.ok) {
    return;
    }
    const catalog = (await response.json()) as JSONSchemaForSchemaStoreOrgCatalogFiles;
    const schemas = [defaultSchema];
    catalog.schemas.sort((a, b) => a.name.localeCompare(b.name));
    for (const { fileMatch, name, url } of catalog.schemas) {
    const match =
    typeof name === 'string' && fileMatch?.find((filename) => /\.ya?ml$/i.test(filename));
    if (!match) {
    continue;
    }
    const option = document.createElement('option');
    option.value = match;
    option.textContent = name;
    select.append(option);
    schemas.push({
    fileMatch: [match],
    uri: url,
    });
    }
    setDiagnosticsOptions({
    validate: true,
    enableSchemaRequest: true,
    format: true,
    hover: true,
    completion: true,
    schemas,
    });
    });
    Just used the network inspector in devtools to see for yourself on https://monaco-yaml.js.org.
  4. The worker is terminated when unused, meaning its memory will be freed.

THanks for the reply:

  1. Yes megabytes I mean, it will be 5 to 25 Mo I think, I have thousands of differents schema (so I will create fileMatch with thousand of entries) + 20 big ones that will contains the thousand schema in 1 schema (with anyOf)
  2. good catch
  3. Thanks for the clarification, it will help I think a lot if not all schema is loaded and I will change it to lazy loading (for now, it's fetch by my app, then forwarded to monaco).
  4. Even in a SPA application like vuejs? I see that monaco-aria-container is always on the dom as soon as I load an editor
  1. Yes, it doesn’t matter if it’s a SPA or not. monaco-yaml doesn’t interact with the DOM, that’s monaco-editor.