Skip to content

YAML validation for Monaco Editor #228

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

Open
vrabota opened this issue Apr 16, 2021 · 12 comments
Open

YAML validation for Monaco Editor #228

vrabota opened this issue Apr 16, 2021 · 12 comments

Comments

@vrabota
Copy link

vrabota commented Apr 16, 2021

For YAML seems like we have indentation and code highlighting, but unfortunately, we can't validate the code (indentation format or naming, duplications of the fields).

I found a library (https://github.com/pengx17/monaco-yaml) that can maybe help, but I have issues to integrate with create-react-app.

I also tried this solution (#68 (comment)) but unfortunately nothing works for create-react-app application.

Maybe someone integrated YAML validations and can share some knowledge.

@suren-atoyan
Copy link
Owner

this can be helpful. I'll try to find some time to make a working example

@vrabota
Copy link
Author

vrabota commented Apr 18, 2021

It's hard to understand something in this example and how to integrate with monaco-react. Seems like all the code should be placed in public/index.html and we don't need monaco-react at all.

@efreila
Copy link

efreila commented Jun 9, 2021

An update on this would be greatly appreciated

@Chethannp
Copy link

@suren-atoyan could you please provide an update on this.

@Nivl
Copy link

Nivl commented Oct 15, 2021

@suren-atoyan Did you manage to get some time to look into it? :)

@suren-atoyan
Copy link
Owner

@Nivl @Chethannp @efreila guys sorry, but this package isn't compatible with this yaml-monaco implmenetation. To avoid using webpack configuration in this package we download monaco sources from CDN. The mentioned yaml-monaco implementation heavily depends on the ESM version of monaco, which requires a specific webpack configuration

@huruji
Copy link

huruji commented Jun 12, 2022

is there any example with monaco-yaml?

@suren-atoyan
Copy link
Owner

this might make it possible to use monaco-yaml with monaco-react

@jason51553262
Copy link

jason51553262 commented Jun 18, 2022

@suren-atoyan Cheers for the suggestion. I did manage to get it working using the webpack config from https://github.com/remcohaszing/monaco-yaml
and the following component:

import React, {FC, useEffect} from 'react';

import * as monaco from "monaco-editor";
import Editor, { loader } from "@monaco-editor/react";
loader.config({ monaco });
import {setDiagnosticsOptions} from 'monaco-yaml';

// @ts-ignore
window.MonacoEnvironment = {
    getWorker(moduleId: any, label: string) {
        switch (label) {
            case 'editorWorkerService':
                // @ts-ignore
                return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url));
            case 'css':
            case 'less':
            case 'scss':
                // @ts-ignore
                return new Worker(new URL('monaco-editor/esm/vs/language/css/css.worker', import.meta.url));
            case 'handlebars':
            case 'html':
            case 'razor':
                return new Worker(
                    // @ts-ignore
                    new URL('monaco-editor/esm/vs/language/html/html.worker', import.meta.url),
                );
            case 'json':
                return new Worker(
                    // @ts-ignore
                    new URL('monaco-editor/esm/vs/language/json/json.worker', import.meta.url),
                );
            case 'javascript':
            case 'typescript':
                return new Worker(
                    // @ts-ignore
                    new URL('monaco-editor/esm/vs/language/typescript/ts.worker', import.meta.url),
                );
            case 'yaml':
                // @ts-ignore
                return new Worker(new URL('monaco-yaml/yaml.worker', import.meta.url));
            default:
                throw new Error(`Unknown label ${label}`);
        }
    },
};

interface CodeEditorProps {
    language: string;
    value: any;
    disabled?: boolean;
    onChange(value: string|undefined): void;
    className?: string;
    width?: string;
    height?: string;
}

export const CodeEditor: FC<CodeEditorProps> = (props) => {
    const {language, value, disabled, onChange, className, width, height} = props;

    const handleOnChange = (value: string|undefined) => {
        onChange(value);
    }

    const handleEditorValidation = (markers: any) => {
        // model markers
        markers.forEach((marker: any) => console.log("onValidate:", marker.message));
    }

    useEffect(() => {
        setDiagnosticsOptions({
            // Have to set an empty Diagnostics options to get syntax checking
        });
    }, [])

    return (
        <div style={{border: "1px solid #ccc"}} className={className}>
            <Editor
                options={{
                    readOnly: disabled,
                    lineDecorationsWidth: 5,
                    lineNumbersMinChars: 0,
                    glyphMargin: false,
                    folding: false,
                    lineNumbers: 'off',
                    minimap: {
                        enabled: false
                    },
                    fontSize: 11,
                }}
                width={width}
                height={height}
                language={language}
                value={value}
                onValidate={handleEditorValidation}
                onChange={handleOnChange}
            />
        </div>
    );
}

@josiext
Copy link

josiext commented Aug 2, 2023

@suren-atoyan Cheers for the suggestion. I did manage to get it working using the webpack config from https://github.com/remcohaszing/monaco-yaml and the following component:

import React, {FC, useEffect} from 'react';

import * as monaco from "monaco-editor";
import Editor, { loader } from "@monaco-editor/react";
loader.config({ monaco });
import {setDiagnosticsOptions} from 'monaco-yaml';

// @ts-ignore
window.MonacoEnvironment = {
    getWorker(moduleId: any, label: string) {
        switch (label) {
            case 'editorWorkerService':
                // @ts-ignore
                return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url));
            case 'css':
            case 'less':
            case 'scss':
                // @ts-ignore
                return new Worker(new URL('monaco-editor/esm/vs/language/css/css.worker', import.meta.url));
            case 'handlebars':
            case 'html':
            case 'razor':
                return new Worker(
                    // @ts-ignore
                    new URL('monaco-editor/esm/vs/language/html/html.worker', import.meta.url),
                );
            case 'json':
                return new Worker(
                    // @ts-ignore
                    new URL('monaco-editor/esm/vs/language/json/json.worker', import.meta.url),
                );
            case 'javascript':
            case 'typescript':
                return new Worker(
                    // @ts-ignore
                    new URL('monaco-editor/esm/vs/language/typescript/ts.worker', import.meta.url),
                );
            case 'yaml':
                // @ts-ignore
                return new Worker(new URL('monaco-yaml/yaml.worker', import.meta.url));
            default:
                throw new Error(`Unknown label ${label}`);
        }
    },
};

interface CodeEditorProps {
    language: string;
    value: any;
    disabled?: boolean;
    onChange(value: string|undefined): void;
    className?: string;
    width?: string;
    height?: string;
}

export const CodeEditor: FC<CodeEditorProps> = (props) => {
    const {language, value, disabled, onChange, className, width, height} = props;

    const handleOnChange = (value: string|undefined) => {
        onChange(value);
    }

    const handleEditorValidation = (markers: any) => {
        // model markers
        markers.forEach((marker: any) => console.log("onValidate:", marker.message));
    }

    useEffect(() => {
        setDiagnosticsOptions({
            // Have to set an empty Diagnostics options to get syntax checking
        });
    }, [])

    return (
        <div style={{border: "1px solid #ccc"}} className={className}>
            <Editor
                options={{
                    readOnly: disabled,
                    lineDecorationsWidth: 5,
                    lineNumbersMinChars: 0,
                    glyphMargin: false,
                    folding: false,
                    lineNumbers: 'off',
                    minimap: {
                        enabled: false
                    },
                    fontSize: 11,
                }}
                width={width}
                height={height}
                language={language}
                value={value}
                onValidate={handleEditorValidation}
                onChange={handleOnChange}
            />
        </div>
    );
}

Throws:

Error: Target container is not a DOM element.

In a Next.js app :(

@binhtran04
Copy link

I was able to configure @monaco-editor/react to work with monaco-yaml:

yaml.worker.ts

import "monaco-yaml/yaml.worker.js";

setupMonaco.ts

import { loader } from "@monaco-editor/react";
import * as monaco from "monaco-editor";
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
import yamlWorker from "./yaml.worker.js?worker";

/** Configure Monaco editor for Vite */
self.MonacoEnvironment = {
  getWorker(_, label) {
    if (label === "json") {
      return new jsonWorker();
    }
    if (label === "css" || label === "scss" || label === "less") {
      return new cssWorker();
    }
    if (label === "html" || label === "handlebars" || label === "razor") {
      return new htmlWorker();
    }
    if (label === "typescript" || label === "javascript") {
      return new tsWorker();
    }
    if (label === "yaml") {
      return new yamlWorker();
    }
    return new editorWorker();
  },
};

loader.config({ monaco });

  1. import "./setupMonaco" to index.tsx
  2. YamlEditor.tsx
// In the docs, it is stated that there should only be one monaco yaml instance configured at a time
let monacoYamlInstance: MonacoYaml | undefined;

export function YamlEditor({
  schemaUri,
  path,
  ...props
}: YamlEditorProps) {
  useEffect(() => {
    if (schemaUri) {
      monacoYamlInstance?.update({
        enableSchemaRequest: true,
        schemas: [
          {
            fileMatch: [path ? `*/**${path}` : "*"],
            uri: schemaUri,
          },
        ],
      });
    }
  }, [path, schemaUri]);

  const handleEditorBeforeMount = (monaco: Monaco) => {
    if (!monacoYamlInstance) {
      monacoYamlInstance = configureMonacoYaml(monaco, {
        hover: true,
        completion: true,
        validate: true,
        format: true,
        enableSchemaRequest: true,
        schemas: schemaUri
          ? [
              {
                fileMatch: [path ? `*/**${path}` : "*"],
                uri: schemaUri,
              },
            ]
          : undefined,
      });
    }
  };


  return (
    <Editor
      defaultLanguage="yaml"
      path={path}
      beforeMount={handleEditorBeforeMount}
      {...props}
    />
  );
}

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

10 participants