├── .nvmrc
├── src
├── constants.ts
├── fillers
│ ├── lodash.ts
│ ├── ajv.ts
│ ├── schemaSelectionHandlers.ts
│ └── vscode-nls.ts
├── index.ts
├── yamlMode.ts
├── languageFeatures.ts
└── yaml.worker.ts
├── .npmrc
├── .remarkrc.yaml
├── .prettierignore
├── .prettierrc.yaml
├── .gitignore
├── .eslintrc.yaml
├── examples
├── monaco-editor-webpack-plugin
│ ├── src
│ │ ├── index.ejs
│ │ └── index.js
│ ├── package.json
│ ├── webpack.config.js
│ └── README.md
├── vite-example
│ ├── index.html
│ ├── package.json
│ ├── README.md
│ └── index.js
└── demo
│ ├── package.json
│ ├── src
│ ├── types.d.ts
│ ├── icon.svg
│ ├── schema.json
│ ├── index.ejs
│ ├── index.css
│ └── index.ts
│ ├── README.md
│ └── webpack.config.js
├── netlify.toml
├── tsconfig.json
├── .editorconfig
├── playwright.config.ts
├── test
├── index.html
├── build.js
├── marker-data.test.ts
├── index.ts
└── serve.js
├── CONTRIBUTING.md
├── LICENSE.md
├── package.json
├── .github
└── workflows
│ └── ci.yaml
├── index.d.ts
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const languageId = 'yaml'
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | install-links = false
2 | lockfile-version = 3
3 |
--------------------------------------------------------------------------------
/.remarkrc.yaml:
--------------------------------------------------------------------------------
1 | plugins:
2 | - remark-preset-remcohaszing
3 |
--------------------------------------------------------------------------------
/src/fillers/lodash.ts:
--------------------------------------------------------------------------------
1 | export const cloneDeep = structuredClone
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist/
2 | /index.js
3 | /yaml.worker.js
4 | test/out/
5 | playwright-report/
6 |
--------------------------------------------------------------------------------
/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | proseWrap: always
2 | semi: false
3 | singleQuote: true
4 | trailingComma: none
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | node_modules/
3 | test/out/
4 | playwright-report/
5 | /index.js
6 | /yaml.worker.js
7 | *.log
8 | *.map
9 | *.tgz
10 |
--------------------------------------------------------------------------------
/.eslintrc.yaml:
--------------------------------------------------------------------------------
1 | extends:
2 | - remcohaszing
3 | - remcohaszing/typechecking
4 | rules:
5 | import/no-extraneous-dependencies: off
6 |
7 | jsdoc/require-jsdoc: off
8 |
9 | n/no-extraneous-import: off
10 |
--------------------------------------------------------------------------------
/src/fillers/ajv.ts:
--------------------------------------------------------------------------------
1 | import { type ValidateFunction } from 'ajv'
2 |
3 | export default class AJVStub {
4 | // eslint-disable-next-line class-methods-use-this
5 | compile(): ValidateFunction {
6 | return () => true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/fillers/schemaSelectionHandlers.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a stub for `monaco-yaml/lib/esm/schemaSelectionHandlers.js`.
3 | */
4 | // eslint-disable-next-line @typescript-eslint/no-empty-function
5 | export function JSONSchemaSelection(): void {}
6 |
--------------------------------------------------------------------------------
/examples/monaco-editor-webpack-plugin/src/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Monaco YAML
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = 'examples/demo/dist/'
3 | command = 'npm ci && npm run prepack && npm --workspace demo run build'
4 |
5 | [[headers]]
6 | for = '/*'
7 | [headers.values]
8 | Content-Security-Policy = "default-src 'self'; connect-src https:; img-src 'self' data:; style-src 'self' 'unsafe-inline'"
9 |
--------------------------------------------------------------------------------
/examples/vite-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Monaco YAML
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "forceConsistentCasingInFileNames": true,
4 | "module": "nodenext",
5 | "noEmit": true,
6 | "resolveJsonModule": true,
7 | "skipLibCheck": true,
8 | "sourceMap": true,
9 | "strict": true,
10 | "target": "es2017",
11 | "types": []
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 2
8 | indent_style = space
9 | insert_final_newline = true
10 | max_line_length = 100
11 | trim_trailing_whitespace = true
12 |
13 | [COMMIT_EDITMSG]
14 | max_line_length = 72
15 |
--------------------------------------------------------------------------------
/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import { type PlaywrightTestConfig } from '@playwright/test'
2 |
3 | const config: PlaywrightTestConfig = {
4 | reporter: 'html',
5 | timeout: 120_000,
6 | webServer: {
7 | command: 'node test/serve.js',
8 | port: 3000,
9 | reuseExistingServer: !process.env.CI
10 | }
11 | }
12 | export default config
13 |
--------------------------------------------------------------------------------
/examples/vite-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-example",
3 | "version": "1.0.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "start": "vite",
8 | "build": "vite build"
9 | },
10 | "dependencies": {
11 | "monaco-editor": "^0.38.0",
12 | "monaco-yaml": "file:../..",
13 | "vite": "^4.0.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/build.js:
--------------------------------------------------------------------------------
1 | import { fileURLToPath } from 'node:url'
2 |
3 | import { build } from 'esbuild'
4 |
5 | await build({
6 | entryPoints: {
7 | 'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
8 | 'yaml.worker': fileURLToPath(new URL('../yaml.worker.js', import.meta.url)),
9 | index: fileURLToPath(new URL('index.ts', import.meta.url))
10 | },
11 | bundle: true,
12 | format: 'iife',
13 | outdir: fileURLToPath(new URL('out/', import.meta.url)),
14 | loader: {
15 | '.ttf': 'file'
16 | }
17 | })
18 |
--------------------------------------------------------------------------------
/examples/monaco-editor-webpack-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "monaco-editor-webpack-plugin-example",
3 | "version": "1.0.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "start": "webpack serve --open --mode development",
8 | "build": "webpack --mode production"
9 | },
10 | "dependencies": {
11 | "css-loader": "^6.0.0",
12 | "html-webpack-plugin": "^5.0.0",
13 | "monaco-editor": "^0.38.0",
14 | "monaco-editor-webpack-plugin": "^7.0.0",
15 | "monaco-yaml": "file:../..",
16 | "style-loader": "^3.0.0",
17 | "webpack": "^5.0.0",
18 | "webpack-cli": "^5.0.0",
19 | "webpack-dev-server": "^4.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/fillers/vscode-nls.ts:
--------------------------------------------------------------------------------
1 | interface LocalizeInfo {
2 | key: string
3 | comment: string[]
4 | }
5 | type LocalizeFunc = (info: LocalizeInfo | string, message: string, ...args: string[]) => string
6 | type LoadFunc = (file?: string) => LocalizeFunc
7 |
8 | function format(message: string, args: string[]): string {
9 | return args.length === 0
10 | ? message
11 | : message.replace(/{(\d+)}/g, (match, [index]: number[]) =>
12 | index in args ? args[index] : match
13 | )
14 | }
15 |
16 | const localize: LocalizeFunc = (key, message, ...args) => format(message, args)
17 |
18 | export function loadMessageBundle(): LocalizeFunc {
19 | return localize
20 | }
21 |
22 | export function config(): LoadFunc {
23 | return loadMessageBundle
24 | }
25 |
--------------------------------------------------------------------------------
/examples/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "1.0.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "start": "webpack serve --open --mode development",
8 | "build": "webpack --mode production"
9 | },
10 | "dependencies": {
11 | "@fortawesome/fontawesome-free": "^6.0.0",
12 | "@schemastore/schema-catalog": "^0.0.6",
13 | "css-loader": "^6.0.0",
14 | "css-minimizer-webpack-plugin": "^5.0.0",
15 | "html-webpack-plugin": "^5.0.0",
16 | "mini-css-extract-plugin": "^2.0.0",
17 | "monaco-editor": "^0.38.0",
18 | "monaco-yaml": "file:../..",
19 | "ts-loader": "^9.0.0",
20 | "webpack": "^5.0.0",
21 | "webpack-cli": "^5.0.0",
22 | "webpack-dev-server": "^4.0.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/demo/src/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'monaco-editor/esm/vs/editor/common/services/languageFeatures.js' {
2 | export const ILanguageFeaturesService: { documentSymbolProvider: unknown }
3 | }
4 |
5 | declare module 'monaco-editor/esm/vs/editor/contrib/documentSymbols/browser/outlineModel.js' {
6 | import { type editor, type languages } from 'monaco-editor'
7 |
8 | export abstract class OutlineModel {
9 | static create(registry: unknown, model: editor.ITextModel): Promise
10 |
11 | asListOfDocumentSymbols(): languages.DocumentSymbol[]
12 | }
13 | }
14 |
15 | declare module 'monaco-editor/esm/vs/editor/standalone/browser/standaloneServices.js' {
16 | export const StandaloneServices: {
17 | get: (id: unknown) => { documentSymbolProvider: unknown }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/vite-example/README.md:
--------------------------------------------------------------------------------
1 | # Vite Example
2 |
3 | This minimal example shows how `monaco-yaml` can be used with [Vite](https://vitejs.dev).
4 |
5 | ## Table of Contents
6 |
7 | - [Prerequisites](#prerequisites)
8 | - [Setup](#setup)
9 | - [Running](#running)
10 |
11 | ## Prerequisites
12 |
13 | - [NodeJS](https://nodejs.org) 16 or higher
14 | - [npm](https://github.com/npm/cli) 8.1.2 or higher
15 |
16 | ## Setup
17 |
18 | To run the project locally, clone the repository and set it up:
19 |
20 | ```sh
21 | git clone https://github.com/remcohaszing/monaco-yaml
22 | cd monaco-yaml
23 | npm ci
24 | npm run prepack
25 | ```
26 |
27 | ## Running
28 |
29 | To start it, simply run:
30 |
31 | ```sh
32 | npm --workspace vite-example start
33 | ```
34 |
35 | The demo will be available on .
36 |
--------------------------------------------------------------------------------
/test/marker-data.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, type Page, test } from '@playwright/test'
2 |
3 | let page: Page
4 |
5 | test.beforeAll(async ({ browser }) => {
6 | page = await browser.newPage()
7 | await page.goto('')
8 | })
9 |
10 | test.afterAll(async () => {
11 | await page.close()
12 | })
13 |
14 | test.describe('Marker data', () => {
15 | test('should display warning when type is incorrect', async () => {
16 | await page.locator('.view-lines').hover({ position: { x: 30, y: 10 } })
17 | const warn = page.locator('.marker.hover-contents > span:nth-child(1)')
18 | await expect(warn).toHaveText('Incorrect type. Expected "number".')
19 | })
20 |
21 | test('should underline warning when type is incorrect', async () => {
22 | await expect(page.locator('.cdr.squiggly-warning')).toHaveCount(2)
23 | })
24 | })
25 |
--------------------------------------------------------------------------------
/examples/monaco-editor-webpack-plugin/webpack.config.js:
--------------------------------------------------------------------------------
1 | import HtmlWebPackPlugin from 'html-webpack-plugin'
2 | import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'
3 |
4 | export default {
5 | module: {
6 | rules: [
7 | {
8 | test: /\.css$/,
9 | use: ['style-loader', 'css-loader']
10 | },
11 | {
12 | test: /\.ttf$/,
13 | type: 'asset'
14 | }
15 | ]
16 | },
17 | plugins: [
18 | new HtmlWebPackPlugin(),
19 | new MonacoWebpackPlugin({
20 | languages: ['yaml'],
21 | customLanguages: [
22 | {
23 | label: 'yaml',
24 | entry: 'monaco-yaml',
25 | worker: {
26 | id: 'monaco-yaml/yamlWorker',
27 | entry: 'monaco-yaml/yaml.worker'
28 | }
29 | }
30 | ]
31 | })
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/examples/demo/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | This demo is deployed to [monaco-yaml.js.org](https://monaco-yaml.js.org). It shows how
4 | `monaco-editor` and `monaco-yaml` can be used with
5 | [Webpack 5](https://webpack.js.org/concepts/entry-points).
6 |
7 | ## Table of Contents
8 |
9 | - [Prerequisites](#prerequisites)
10 | - [Setup](#setup)
11 | - [Running](#running)
12 |
13 | ## Prerequisites
14 |
15 | - [NodeJS](https://nodejs.org) 16 or higher
16 | - [npm](https://github.com/npm/cli) 8.1.2 or higher
17 |
18 | ## Setup
19 |
20 | To run the project locally, clone the repository and set it up:
21 |
22 | ```sh
23 | git clone https://github.com/remcohaszing/monaco-yaml
24 | cd monaco-yaml
25 | npm ci
26 | npm run prepack
27 | ```
28 |
29 | ## Running
30 |
31 | To start it, simply run:
32 |
33 | ```sh
34 | npm --workspace demo start
35 | ```
36 |
37 | The demo will open in your browser.
38 |
--------------------------------------------------------------------------------
/test/index.ts:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor'
2 | import { type SchemasSettings, setDiagnosticsOptions } from 'monaco-yaml'
3 |
4 | self.MonacoEnvironment = {
5 | getWorkerUrl(workerId, label) {
6 | if (label === 'yaml') {
7 | return 'out/yaml.worker.js'
8 | }
9 | return 'out/editor.worker.js'
10 | }
11 | }
12 |
13 | const schema: SchemasSettings = {
14 | fileMatch: ['*'],
15 | uri: 'http://schemas/my-schema.json',
16 | schema: {
17 | type: 'object',
18 | properties: {
19 | p1: {
20 | description: 'number property',
21 | type: 'number'
22 | },
23 | p2: {
24 | type: 'boolean'
25 | }
26 | }
27 | }
28 | }
29 |
30 | setDiagnosticsOptions({
31 | schemas: [schema]
32 | })
33 | const value = 'p1: \np2: \n'
34 |
35 | monaco.editor.create(document.querySelector('#editor')!, {
36 | language: 'yaml',
37 | tabSize: 2,
38 | value
39 | })
40 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Table of Contents
4 |
5 | - [Prerequisites](#prerequisites)
6 | - [Getting started](#getting-started)
7 | - [Building](#building)
8 | - [Running](#running)
9 |
10 | ## Prerequisites
11 |
12 | The following are required to start working on this project:
13 |
14 | - [Git](https://git-scm.com)
15 | - [NodeJS](https://nodejs.org) 16 or higher
16 | - [npm](https://github.com/npm/cli) 8.1.2 or higher
17 |
18 | ## Getting started
19 |
20 | To get started with contributing, clone the repository and install its dependencies.
21 |
22 | ```sh
23 | git clone https://github.com/remcohaszing/monaco-yaml
24 | cd monaco-yaml
25 | npm ci
26 | ```
27 |
28 | ## Building
29 |
30 | To build the repository, run:
31 |
32 | ```sh
33 | npm run prepack
34 | ```
35 |
36 | ## Running
37 |
38 | To test it, run one of the
39 | [examples](https://github.com/remcohaszing/monaco-yaml/tree/main/examples).
40 |
41 | ```sh
42 | npm --workspace demo start
43 | ```
44 |
--------------------------------------------------------------------------------
/examples/demo/webpack.config.js:
--------------------------------------------------------------------------------
1 | import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
2 | import HtmlWebPackPlugin from 'html-webpack-plugin'
3 | import MiniCssExtractPlugin from 'mini-css-extract-plugin'
4 |
5 | export default {
6 | output: {
7 | filename: '[contenthash].js'
8 | },
9 | devtool: 'source-map',
10 | resolve: {
11 | extensions: ['.mjs', '.js', '.ts']
12 | },
13 | module: {
14 | rules: [
15 | {
16 | test: /\.css$/,
17 | use: [MiniCssExtractPlugin.loader, 'css-loader']
18 | },
19 | {
20 | // Monaco editor uses .ttf icons.
21 | test: /\.(svg|ttf)$/,
22 | type: 'asset/resource'
23 | },
24 | {
25 | test: /\.ts$/,
26 | loader: 'ts-loader',
27 | options: { transpileOnly: true }
28 | }
29 | ]
30 | },
31 | optimization: {
32 | minimizer: ['...', new CssMinimizerPlugin()]
33 | },
34 | plugins: [new HtmlWebPackPlugin(), new MiniCssExtractPlugin({ filename: '[contenthash].css' })]
35 | }
36 |
--------------------------------------------------------------------------------
/test/serve.js:
--------------------------------------------------------------------------------
1 | import { readFile } from 'node:fs/promises'
2 | import http from 'node:http'
3 | import { extname } from 'node:path'
4 |
5 | import './build.js'
6 |
7 | const contentTypes = {
8 | '.html': 'text/html',
9 | '.js': 'text/javascript',
10 | '.css': 'text/css',
11 | '.ttf': 'font/ttf'
12 | }
13 |
14 | http
15 | .createServer(async (req, res) => {
16 | const file = req.url === '/' ? 'index.html' : req.url.slice(1)
17 | const url = new URL(file, import.meta.url)
18 | const ext = extname(url.pathname)
19 | try {
20 | const content = await readFile(url)
21 | res.setHeader('Content-type', contentTypes[ext] ?? 'text/plain')
22 | res.end(content)
23 | } catch (err) {
24 | if (err.code === 'ENOENT') {
25 | res.writeHead(404, 'Not Found')
26 | } else {
27 | res.writeHead(500, 'Internal server error')
28 | }
29 | res.end(String(err))
30 | }
31 | })
32 | // eslint-disable-next-line no-console
33 | .listen(3000, () => console.log('listening...'))
34 |
--------------------------------------------------------------------------------
/examples/demo/src/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
15 |
--------------------------------------------------------------------------------
/examples/demo/src/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "property": {
5 | "description": "I have a description"
6 | },
7 | "titledProperty": {
8 | "title": "I have a title",
9 | "description": "I also have a description"
10 | },
11 | "markdown": {
12 | "markdownDescription": "Even **markdown** _descriptions_ `are` ~~not~~ supported!"
13 | },
14 | "enum": {
15 | "description": "Pick your starter",
16 | "enum": ["Bulbasaur", "Squirtle", "Charmander", "Pikachu"]
17 | },
18 | "number": {
19 | "description": "Numbers work!",
20 | "minimum": 42,
21 | "maximum": 1337
22 | },
23 | "boolean": {
24 | "description": "Are boolean supported?",
25 | "type": "boolean"
26 | },
27 | "string": {
28 | "type": "string"
29 | },
30 | "reference": {
31 | "description": "JSON schemas can be referenced, even recursively",
32 | "$ref": "#"
33 | },
34 | "array": {
35 | "description": "It also works in arrays",
36 | "items": {
37 | "$ref": "#"
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Microsoft Corporation
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6 | associated documentation files (the “Software”), to deal in the Software without restriction,
7 | including without limitation the rights to use, copy, modify, merge, publish, distribute,
8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all copies or substantial
12 | portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
17 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 |
--------------------------------------------------------------------------------
/examples/monaco-editor-webpack-plugin/README.md:
--------------------------------------------------------------------------------
1 | # Monaco Editor Webpack Loader Plugin Example
2 |
3 | This demo demonstrates how bundle `monaco-editor` and `monaco-yaml` with
4 | [monaco-editor-webpack-plugin](https://github.com/microsoft/monaco-editor/tree/main/webpack-plugin).
5 | The build output is
6 | [esm library](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). Example is
7 | based on
8 | [link](https://github.com/microsoft/monaco-editor/tree/main/samples/browser-esm-webpack-monaco-plugin).
9 | To start it, simply run:
10 |
11 | ## Table of Contents
12 |
13 | - [Prerequisites](#prerequisites)
14 | - [Setup](#setup)
15 | - [Running](#running)
16 |
17 | ## Prerequisites
18 |
19 | - [NodeJS](https://nodejs.org) 16 or higher
20 | - [npm](https://github.com/npm/cli) 8.1.2 or higher
21 |
22 | ## Setup
23 |
24 | To run the project locally, clone the repository and set it up:
25 |
26 | ```sh
27 | git clone https://github.com/remcohaszing/monaco-yaml
28 | cd monaco-yaml
29 | npm ci
30 | npm run prepack
31 | ```
32 |
33 | ## Running
34 |
35 | To start it, simply run:
36 |
37 | ```sh
38 | npm --workspace monaco-editor-webpack-plugin-example start
39 | ```
40 |
41 | The demo will open in your browser.
42 |
--------------------------------------------------------------------------------
/examples/monaco-editor-webpack-plugin/src/index.js:
--------------------------------------------------------------------------------
1 | import { editor, Uri } from 'monaco-editor'
2 | import { setDiagnosticsOptions } from 'monaco-yaml'
3 |
4 | // The uri is used for the schema file match.
5 | const modelUri = Uri.parse('a://b/foo.yaml')
6 |
7 | setDiagnosticsOptions({
8 | enableSchemaRequest: true,
9 | hover: true,
10 | completion: true,
11 | validate: true,
12 | format: true,
13 | schemas: [
14 | {
15 | // Id of the first schema
16 | uri: 'http://myserver/foo-schema.json',
17 | // Associate with our model
18 | fileMatch: [String(modelUri)],
19 | schema: {
20 | type: 'object',
21 | properties: {
22 | p1: {
23 | enum: ['v1', 'v2']
24 | },
25 | p2: {
26 | // Reference the second schema
27 | $ref: 'http://myserver/bar-schema.json'
28 | }
29 | }
30 | }
31 | },
32 | {
33 | // Id of the first schema
34 | uri: 'http://myserver/bar-schema.json',
35 | schema: {
36 | type: 'object',
37 | properties: {
38 | q1: {
39 | enum: ['x1', 'x2']
40 | }
41 | }
42 | }
43 | }
44 | ]
45 | })
46 |
47 | const value = 'p1: \np2: \n'
48 |
49 | editor.create(document.getElementById('editor'), {
50 | automaticLayout: true,
51 | model: editor.createModel(value, 'yaml', modelUri)
52 | })
53 |
--------------------------------------------------------------------------------
/examples/demo/src/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Monaco YAML
7 |
8 |
9 |
10 |
11 |
38 |
39 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/examples/vite-example/index.js:
--------------------------------------------------------------------------------
1 | import { editor, Uri } from 'monaco-editor'
2 | import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
3 | import { setDiagnosticsOptions } from 'monaco-yaml'
4 | import YamlWorker from 'monaco-yaml/yaml.worker?worker'
5 |
6 | window.MonacoEnvironment = {
7 | getWorker(moduleId, label) {
8 | switch (label) {
9 | case 'editorWorkerService':
10 | return new EditorWorker()
11 | case 'yaml':
12 | return new YamlWorker()
13 | default:
14 | throw new Error(`Unknown label ${label}`)
15 | }
16 | }
17 | }
18 |
19 | // The uri is used for the schema file match.
20 | const modelUri = Uri.parse('a://b/foo.yaml')
21 |
22 | setDiagnosticsOptions({
23 | enableSchemaRequest: true,
24 | hover: true,
25 | completion: true,
26 | validate: true,
27 | format: true,
28 | schemas: [
29 | {
30 | // Id of the first schema
31 | uri: 'http://myserver/foo-schema.json',
32 | // Associate with our model
33 | fileMatch: [String(modelUri)],
34 | schema: {
35 | type: 'object',
36 | properties: {
37 | p1: {
38 | enum: ['v1', 'v2']
39 | },
40 | p2: {
41 | // Reference the second schema
42 | $ref: 'http://myserver/bar-schema.json'
43 | }
44 | }
45 | }
46 | },
47 | {
48 | // Id of the first schema
49 | uri: 'http://myserver/bar-schema.json',
50 | schema: {
51 | type: 'object',
52 | properties: {
53 | q1: {
54 | enum: ['x1', 'x2']
55 | }
56 | }
57 | }
58 | }
59 | ]
60 | })
61 |
62 | const value = 'p1: \np2: \n'
63 |
64 | editor.create(document.getElementById('editor'), {
65 | automaticLayout: true,
66 | model: editor.createModel(value, 'yaml', modelUri)
67 | })
68 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "monaco-yaml",
3 | "version": "4.0.4",
4 | "description": "YAML plugin for the Monaco Editor",
5 | "homepage": "https://monaco-yaml.js.org",
6 | "scripts": {
7 | "prepack": "node build.js",
8 | "start": "npm --workspace demo start",
9 | "test": "playwright test"
10 | },
11 | "type": "module",
12 | "main": "./index.js",
13 | "typings": "./index.d.ts",
14 | "files": [
15 | "index.js",
16 | "index.js.map",
17 | "index.d.ts",
18 | "yaml.worker.js",
19 | "yaml.worker.js.map"
20 | ],
21 | "workspaces": [
22 | "examples/*"
23 | ],
24 | "author": "Kevin Decker (http://incaseofstairs.com)",
25 | "maintainers": [
26 | "Remco Haszing (https://github.com/remcohaszing)"
27 | ],
28 | "license": "MIT",
29 | "repository": "remcohaszing/monaco-yaml",
30 | "bugs": "https://github.com/remcohaszing/monaco-yaml/issues",
31 | "funding": "https://github.com/sponsors/remcohaszing",
32 | "keywords": [
33 | "editor",
34 | "frontend",
35 | "front-end",
36 | "monaco",
37 | "monaco-editor",
38 | "yaml"
39 | ],
40 | "dependencies": {
41 | "@types/json-schema": "^7.0.0",
42 | "jsonc-parser": "^3.0.0",
43 | "monaco-languageserver-types": "^0.2.0",
44 | "monaco-marker-data-provider": "^1.0.0",
45 | "monaco-worker-manager": "^2.0.0",
46 | "path-browserify": "^1.0.0",
47 | "prettier": "^2.0.0",
48 | "vscode-languageserver-textdocument": "^1.0.0",
49 | "vscode-languageserver-types": "^3.0.0",
50 | "vscode-uri": "^3.0.0",
51 | "yaml": "^2.0.0"
52 | },
53 | "peerDependencies": {
54 | "monaco-editor": ">=0.36"
55 | },
56 | "devDependencies": {
57 | "@playwright/test": "^1.0.0",
58 | "esbuild": "^0.17.0",
59 | "eslint": "^8.0.0",
60 | "eslint-config-remcohaszing": "^9.0.0",
61 | "monaco-editor": "^0.38.0",
62 | "remark-cli": "^11.0.0",
63 | "remark-preset-remcohaszing": "^1.0.0",
64 | "typescript": "^5.0.0",
65 | "yaml-language-server": "^1.0.0"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
2 | import { setMonaco } from 'monaco-languageserver-types'
3 | import { type DiagnosticsOptions, type LanguageServiceDefaults } from 'monaco-yaml'
4 |
5 | import { languageId } from './constants.js'
6 | import { setupMode } from './yamlMode.js'
7 |
8 | // --- YAML configuration and defaults ---------
9 |
10 | const diagnosticDefault: DiagnosticsOptions = {
11 | completion: true,
12 | customTags: [],
13 | enableSchemaRequest: false,
14 | format: true,
15 | isKubernetes: false,
16 | hover: true,
17 | schemas: [],
18 | validate: true,
19 | yamlVersion: '1.2'
20 | }
21 |
22 | setMonaco(monaco)
23 |
24 | export function createLanguageServiceDefaults(
25 | initialDiagnosticsOptions: DiagnosticsOptions
26 | ): LanguageServiceDefaults {
27 | const onDidChange = new monaco.Emitter()
28 | let diagnosticsOptions = initialDiagnosticsOptions
29 |
30 | const languageServiceDefaults: LanguageServiceDefaults = {
31 | get onDidChange() {
32 | return onDidChange.event
33 | },
34 |
35 | get diagnosticsOptions() {
36 | return diagnosticsOptions
37 | },
38 |
39 | setDiagnosticsOptions(options) {
40 | diagnosticsOptions = { ...diagnosticDefault, ...options }
41 | onDidChange.fire(languageServiceDefaults)
42 | }
43 | }
44 |
45 | return languageServiceDefaults
46 | }
47 |
48 | export const yamlDefaults = createLanguageServiceDefaults(diagnosticDefault)
49 |
50 | // --- Registration to monaco editor ---
51 |
52 | monaco.languages.register({
53 | id: languageId,
54 | extensions: ['.yaml', '.yml'],
55 | aliases: ['YAML', 'yaml', 'YML', 'yml'],
56 | mimetypes: ['application/x-yaml']
57 | })
58 |
59 | monaco.languages.onLanguage('yaml', () => {
60 | setupMode(yamlDefaults)
61 | })
62 |
63 | /**
64 | * Configure `monaco-yaml` diagnostics options.
65 | *
66 | * @param options The options to set.
67 | */
68 | export function setDiagnosticsOptions(options: DiagnosticsOptions = {}): void {
69 | yamlDefaults.setDiagnosticsOptions(options)
70 | }
71 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches: [main]
7 | tags: ['*']
8 |
9 | jobs:
10 | eslint:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: actions/setup-node@v3
15 | with: { node-version: 18 }
16 | - run: npm ci
17 | - run: npx eslint .
18 |
19 | examples:
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: actions/checkout@v3
23 | - uses: actions/setup-node@v3
24 | with: { node-version: 18 }
25 | - run: npm ci
26 | - run: npm run prepack
27 | - run: npm run build --workspaces
28 |
29 | pack:
30 | runs-on: ubuntu-latest
31 | steps:
32 | - uses: actions/checkout@v3
33 | - uses: actions/setup-node@v3
34 | with: { node-version: 18 }
35 | - run: npm ci
36 | - run: npm pack
37 | - uses: actions/upload-artifact@v3
38 | with:
39 | name: package
40 | path: '*.tgz'
41 |
42 | prettier:
43 | runs-on: ubuntu-latest
44 | steps:
45 | - uses: actions/checkout@v3
46 | - uses: actions/setup-node@v3
47 | with: { node-version: 18 }
48 | - run: npm ci
49 | - run: npx prettier --check .
50 |
51 | remark:
52 | runs-on: ubuntu-latest
53 | steps:
54 | - uses: actions/checkout@v3
55 | - uses: actions/setup-node@v3
56 | with: { node-version: 18 }
57 | - run: npm ci
58 | - run: npx remark --frail .
59 |
60 | tsc:
61 | runs-on: ubuntu-latest
62 | steps:
63 | - uses: actions/checkout@v3
64 | - uses: actions/setup-node@v3
65 | with: { node-version: 18 }
66 | - run: npm ci
67 | - run: npx tsc
68 |
69 | playwright:
70 | runs-on: ubuntu-latest
71 | steps:
72 | - uses: actions/checkout@v3
73 | - uses: actions/setup-node@v3
74 | with: { node-version: 18 }
75 | - run: npm ci
76 | - run: npx playwright install --with-deps
77 | - run: npm run prepack
78 | - run: npx playwright test
79 | - uses: actions/upload-artifact@v3
80 | if: always()
81 | with:
82 | name: playwright-report
83 | path: playwright-report/
84 | retention-days: 30
85 |
86 | release:
87 | runs-on: ubuntu-latest
88 | needs:
89 | - eslint
90 | - examples
91 | - pack
92 | - playwright
93 | - prettier
94 | - remark
95 | - tsc
96 | if: startsWith(github.ref, 'refs/tags/')
97 | permissions:
98 | id-token: write
99 | steps:
100 | - uses: actions/setup-node@v3
101 | with:
102 | node-version: 18
103 | registry-url: https://registry.npmjs.org
104 | - uses: actions/download-artifact@v3
105 | with:
106 | name: package
107 | - run: npm publish *.tgz --provenance
108 | env:
109 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
110 |
--------------------------------------------------------------------------------
/examples/demo/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --background-color: hsl(0, 0%, 96%);
3 | --editor-background: hsl(60, 100%, 100%);
4 | --error-color: hsl(0, 85%, 62%);
5 | --foreground-color: hsl(0, 0%, 0%);
6 | --primary-color: hsl(189, 100%, 63%);
7 | --shadow-color: hsla(0, 0%, 27%, 0.239);
8 | --scrollbar-color: hsla(0, 0%, 47%, 0.4);
9 | --warning-color: hsl(49, 100%, 40%);
10 | }
11 |
12 | @media (prefers-color-scheme: dark) {
13 | :root {
14 | --background-color: hsl(0, 0%, 23%);
15 | --editor-background: hsl(0, 0%, 12%);
16 | --foreground-color: hsl(0, 0%, 100%);
17 | --shadow-color: hsl(0, 0%, 43%);
18 | }
19 | }
20 |
21 | body {
22 | background: var(--background-color);
23 | display: flex;
24 | flex-flow: column;
25 | font-family: sans-serif;
26 | height: 100vh;
27 | margin: 0;
28 | }
29 |
30 | h1 {
31 | margin: 0 auto 0 1rem;
32 | }
33 |
34 | nav {
35 | align-items: center;
36 | background: var(--primary-color);
37 | box-shadow: 0px 5px 5px var(--shadow-color);
38 | display: flex;
39 | flex: 0 0 auto;
40 | height: 3rem;
41 | justify-content: space-between;
42 | }
43 |
44 | .nav-icon {
45 | margin-right: 1rem;
46 | text-decoration: none;
47 | }
48 |
49 | .nav-icon > img {
50 | vertical-align: middle;
51 | }
52 |
53 | main {
54 | background: var(--editor-background);
55 | box-shadow: 0 0 10px var(--shadow-color);
56 | display: flex;
57 | flex: 1 1 auto;
58 | flex-flow: column;
59 | margin: 1.5rem;
60 | }
61 |
62 | #schema-selection {
63 | background-color: var(--editor-background);
64 | border: none;
65 | border-bottom: 1px solid var(--shadow-color);
66 | color: var(--foreground-color);
67 | width: 100%;
68 | }
69 |
70 | #breadcrumbs {
71 | border-bottom: 1px solid var(--shadow-color);
72 | color: var(--foreground-color);
73 | flex: 0 0 1rem;
74 | }
75 |
76 | .breadcrumb {
77 | cursor: pointer;
78 | }
79 |
80 | #breadcrumbs::before,
81 | .breadcrumb:not(:last-child)::after {
82 | content: '›';
83 | margin: 0 0.2rem;
84 | }
85 |
86 | .breadcrumb.array::before {
87 | content: '[]';
88 | }
89 |
90 | .breadcrumb.object::before {
91 | content: '{}';
92 | }
93 |
94 | #editor {
95 | flex: 1 1 auto;
96 | }
97 |
98 | #problems {
99 | border-top: 1px solid var(--shadow-color);
100 | flex: 0 0 20vh;
101 | color: var(--foreground-color);
102 | overflow-y: scroll;
103 | }
104 |
105 | .problem {
106 | align-items: center;
107 | cursor: pointer;
108 | display: flex;
109 | padding: 0.25rem;
110 | }
111 |
112 | .problem:hover {
113 | background-color: var(--shadow-color);
114 | }
115 |
116 | .problem-text {
117 | margin-left: 0.5rem;
118 | }
119 |
120 | .problem .codicon-warning {
121 | color: var(--warning-color);
122 | }
123 |
124 | .problem .codicon-error {
125 | color: var(--error-color);
126 | }
127 |
128 | *::-webkit-scrollbar {
129 | box-shadow: 1px 0 0 0 var(--scrollbar-color) inset;
130 | width: 14px;
131 | }
132 |
133 | *::-webkit-scrollbar-thumb {
134 | background: var(--scrollbar-color);
135 | }
136 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import { type JSONSchema4, type JSONSchema6, type JSONSchema7 } from 'json-schema'
2 | import { type IEvent } from 'monaco-editor'
3 |
4 | export interface SchemasSettings {
5 | /**
6 | * A `Uri` file match which will trigger the schema validation. This may be a glob or an exact
7 | * path.
8 | *
9 | * @example '.gitlab-ci.yml'
10 | * @example 'file://**\/.github/actions/*.yaml'
11 | */
12 | fileMatch: string[]
13 |
14 | /**
15 | * The JSON schema which will be used for validation. If not specified, it will be downloaded from
16 | * `uri`.
17 | */
18 | schema?: JSONSchema4 | JSONSchema6 | JSONSchema7
19 |
20 | /**
21 | * The source URI of the JSON schema. The JSON schema will be downloaded from here if no schema
22 | * was supplied. It will also be displayed as the source in hover tooltips.
23 | */
24 | uri: string
25 | }
26 |
27 | export interface DiagnosticsOptions {
28 | /**
29 | * If set, enable schema based autocompletion.
30 | *
31 | * @default true
32 | */
33 | readonly completion?: boolean
34 |
35 | /**
36 | * A list of custom tags.
37 | *
38 | * @default []
39 | */
40 | readonly customTags?: string[]
41 |
42 | /**
43 | * If set, the schema service would load schema content on-demand with 'fetch' if available
44 | *
45 | * @default false
46 | */
47 | readonly enableSchemaRequest?: boolean
48 |
49 | /**
50 | * If true, formatting using Prettier is enabled. Setting this to `false` does **not** exclude
51 | * Prettier from the bundle.
52 | *
53 | * @default true
54 | */
55 | readonly format?: boolean
56 |
57 | /**
58 | * If set, enable hover typs based the JSON schema.
59 | *
60 | * @default true
61 | */
62 | readonly hover?: boolean
63 |
64 | /**
65 | * If true, a different diffing algorithm is used to generate error messages.
66 | *
67 | * @default false
68 | */
69 | readonly isKubernetes?: boolean
70 |
71 | /**
72 | * A list of known schemas and/or associations of schemas to file names.
73 | *
74 | * @default []
75 | */
76 | readonly schemas?: SchemasSettings[]
77 |
78 | /**
79 | * If set, the validator will be enabled and perform syntax validation as well as schema
80 | * based validation.
81 | *
82 | * @default true
83 | */
84 | readonly validate?: boolean
85 |
86 | /**
87 | * The YAML version to use for parsing.
88 | *
89 | * @default '1.2'
90 | */
91 | readonly yamlVersion?: '1.1' | '1.2'
92 | }
93 |
94 | export interface LanguageServiceDefaults {
95 | readonly onDidChange: IEvent
96 | readonly diagnosticsOptions: DiagnosticsOptions
97 | setDiagnosticsOptions: (options: DiagnosticsOptions) => void
98 | }
99 |
100 | export function createLanguageServiceDefaults(
101 | initialDiagnosticsOptions: DiagnosticsOptions
102 | ): LanguageServiceDefaults
103 |
104 | export const yamlDefaults: LanguageServiceDefaults
105 |
106 | /**
107 | * Configure `monaco-yaml` diagnostics options.
108 | *
109 | * @param options The options to set.
110 | */
111 | export function setDiagnosticsOptions(options?: DiagnosticsOptions): void
112 |
--------------------------------------------------------------------------------
/src/yamlMode.ts:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
2 | import { registerMarkerDataProvider } from 'monaco-marker-data-provider'
3 | import { createWorkerManager } from 'monaco-worker-manager'
4 | import { type LanguageServiceDefaults } from 'monaco-yaml'
5 |
6 | import { languageId } from './constants.js'
7 | import {
8 | createCodeActionProvider,
9 | createCompletionItemProvider,
10 | createDefinitionProvider,
11 | createDocumentFormattingEditProvider,
12 | createDocumentSymbolProvider,
13 | createHoverProvider,
14 | createLinkProvider,
15 | createMarkerDataProvider
16 | } from './languageFeatures.js'
17 | import { type CreateData, type YAMLWorker } from './yaml.worker.js'
18 |
19 | const richEditConfiguration: monaco.languages.LanguageConfiguration = {
20 | comments: {
21 | lineComment: '#'
22 | },
23 | brackets: [
24 | ['{', '}'],
25 | ['[', ']'],
26 | ['(', ')']
27 | ],
28 | autoClosingPairs: [
29 | { open: '{', close: '}' },
30 | { open: '[', close: ']' },
31 | { open: '(', close: ')' },
32 | { open: '"', close: '"' },
33 | { open: "'", close: "'" }
34 | ],
35 | surroundingPairs: [
36 | { open: '{', close: '}' },
37 | { open: '[', close: ']' },
38 | { open: '(', close: ')' },
39 | { open: '"', close: '"' },
40 | { open: "'", close: "'" }
41 | ],
42 |
43 | onEnterRules: [
44 | {
45 | beforeText: /:\s*$/,
46 | action: { indentAction: monaco.languages.IndentAction.Indent }
47 | }
48 | ]
49 | }
50 |
51 | export function setupMode(defaults: LanguageServiceDefaults): void {
52 | const worker = createWorkerManager(monaco, {
53 | label: 'yaml',
54 | moduleId: 'monaco-yaml/yaml.worker',
55 | createData: {
56 | languageSettings: defaults.diagnosticsOptions,
57 | enableSchemaRequest: defaults.diagnosticsOptions.enableSchemaRequest
58 | }
59 | })
60 |
61 | defaults.onDidChange(() => {
62 | worker.updateCreateData({
63 | languageSettings: defaults.diagnosticsOptions,
64 | enableSchemaRequest: defaults.diagnosticsOptions.enableSchemaRequest
65 | })
66 | })
67 |
68 | monaco.languages.registerCompletionItemProvider(
69 | languageId,
70 | createCompletionItemProvider(worker.getWorker)
71 | )
72 | monaco.languages.registerHoverProvider(languageId, createHoverProvider(worker.getWorker))
73 | monaco.languages.registerDefinitionProvider(
74 | languageId,
75 | createDefinitionProvider(worker.getWorker)
76 | )
77 | monaco.languages.registerDocumentSymbolProvider(
78 | languageId,
79 | createDocumentSymbolProvider(worker.getWorker)
80 | )
81 | monaco.languages.registerDocumentFormattingEditProvider(
82 | languageId,
83 | createDocumentFormattingEditProvider(worker.getWorker)
84 | )
85 | monaco.languages.registerLinkProvider(languageId, createLinkProvider(worker.getWorker))
86 | monaco.languages.registerCodeActionProvider(
87 | languageId,
88 | createCodeActionProvider(worker.getWorker)
89 | )
90 | monaco.languages.setLanguageConfiguration(languageId, richEditConfiguration)
91 |
92 | let markerDataProvider = registerMarkerDataProvider(
93 | monaco,
94 | languageId,
95 | createMarkerDataProvider(worker.getWorker)
96 | )
97 | defaults.onDidChange(() => {
98 | markerDataProvider.dispose()
99 | markerDataProvider = registerMarkerDataProvider(
100 | monaco,
101 | languageId,
102 | createMarkerDataProvider(worker.getWorker)
103 | )
104 | })
105 | }
106 |
--------------------------------------------------------------------------------
/src/languageFeatures.ts:
--------------------------------------------------------------------------------
1 | import { type languages } from 'monaco-editor/esm/vs/editor/editor.api.js'
2 | import {
3 | fromMarkerData,
4 | fromPosition,
5 | fromRange,
6 | toCodeAction,
7 | toCompletionList,
8 | toDocumentSymbol,
9 | toHover,
10 | toLink,
11 | toLocationLink,
12 | toMarkerData,
13 | toTextEdit
14 | } from 'monaco-languageserver-types'
15 | import { type MarkerDataProvider } from 'monaco-marker-data-provider'
16 | import { type WorkerGetter } from 'monaco-worker-manager'
17 |
18 | import { languageId } from './constants.js'
19 | import { type YAMLWorker } from './yaml.worker.js'
20 |
21 | export type WorkerAccessor = WorkerGetter
22 |
23 | export function createMarkerDataProvider(getWorker: WorkerAccessor): MarkerDataProvider {
24 | return {
25 | owner: languageId,
26 | async provideMarkerData(model) {
27 | const worker = await getWorker(model.uri)
28 | const diagnostics = await worker.doValidation(String(model.uri))
29 |
30 | return diagnostics?.map((diagnostic) => toMarkerData(diagnostic))
31 | },
32 |
33 | async doReset(model) {
34 | const worker = await getWorker(model.uri)
35 | await worker.resetSchema(String(model.uri))
36 | }
37 | }
38 | }
39 |
40 | export function createCompletionItemProvider(
41 | getWorker: WorkerAccessor
42 | ): languages.CompletionItemProvider {
43 | return {
44 | triggerCharacters: [' ', ':'],
45 |
46 | async provideCompletionItems(model, position) {
47 | const resource = model.uri
48 |
49 | const worker = await getWorker(resource)
50 | const info = await worker.doComplete(String(resource), fromPosition(position))
51 | if (!info) {
52 | return
53 | }
54 |
55 | const wordInfo = model.getWordUntilPosition(position)
56 |
57 | return toCompletionList(info, {
58 | range: {
59 | startLineNumber: position.lineNumber,
60 | startColumn: wordInfo.startColumn,
61 | endLineNumber: position.lineNumber,
62 | endColumn: wordInfo.endColumn
63 | }
64 | })
65 | }
66 | }
67 | }
68 |
69 | export function createDefinitionProvider(getWorker: WorkerAccessor): languages.DefinitionProvider {
70 | return {
71 | async provideDefinition(model, position) {
72 | const resource = model.uri
73 |
74 | const worker = await getWorker(resource)
75 | const locationLinks = await worker.doDefinition(String(resource), fromPosition(position))
76 |
77 | return locationLinks?.map(toLocationLink)
78 | }
79 | }
80 | }
81 |
82 | export function createHoverProvider(getWorker: WorkerAccessor): languages.HoverProvider {
83 | return {
84 | async provideHover(model, position) {
85 | const resource = model.uri
86 |
87 | const worker = await getWorker(resource)
88 | const info = await worker.doHover(String(resource), fromPosition(position))
89 | if (!info) {
90 | return
91 | }
92 |
93 | return toHover(info)
94 | }
95 | }
96 | }
97 |
98 | export function createDocumentSymbolProvider(
99 | getWorker: WorkerAccessor
100 | ): languages.DocumentSymbolProvider {
101 | return {
102 | async provideDocumentSymbols(model) {
103 | const resource = model.uri
104 |
105 | const worker = await getWorker(resource)
106 | const items = await worker.findDocumentSymbols(String(resource))
107 |
108 | return items?.map(toDocumentSymbol)
109 | }
110 | }
111 | }
112 |
113 | export function createDocumentFormattingEditProvider(
114 | getWorker: WorkerAccessor
115 | ): languages.DocumentFormattingEditProvider {
116 | return {
117 | async provideDocumentFormattingEdits(model) {
118 | const resource = model.uri
119 |
120 | const worker = await getWorker(resource)
121 | const edits = await worker.format(String(resource), {})
122 |
123 | return edits?.map(toTextEdit)
124 | }
125 | }
126 | }
127 |
128 | export function createLinkProvider(getWorker: WorkerAccessor): languages.LinkProvider {
129 | return {
130 | async provideLinks(model) {
131 | const resource = model.uri
132 |
133 | const worker = await getWorker(resource)
134 | const links = await worker.findLinks(String(resource))
135 |
136 | if (!links) {
137 | return
138 | }
139 |
140 | return {
141 | links: links.map(toLink)
142 | }
143 | }
144 | }
145 | }
146 |
147 | export function createCodeActionProvider(getWorker: WorkerAccessor): languages.CodeActionProvider {
148 | return {
149 | async provideCodeActions(model, range, context) {
150 | const resource = model.uri
151 |
152 | const worker = await getWorker(resource)
153 | const codeActions = await worker.getCodeAction(
154 | String(resource),
155 | fromRange(range),
156 | context.markers.map(fromMarkerData)
157 | )
158 |
159 | if (!codeActions) {
160 | return
161 | }
162 |
163 | return {
164 | actions: codeActions.map((codeAction) => toCodeAction(codeAction)),
165 | dispose() {
166 | // This is required by the TypeScript interface, but it’s not implemented.
167 | }
168 | }
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/yaml.worker.ts:
--------------------------------------------------------------------------------
1 | import { initialize } from 'monaco-worker-manager/worker'
2 | import { TextDocument } from 'vscode-languageserver-textdocument'
3 | import {
4 | type CodeAction,
5 | type CompletionList,
6 | type Diagnostic,
7 | type DocumentLink,
8 | type DocumentSymbol,
9 | type Hover,
10 | type LocationLink,
11 | type Position,
12 | type Range,
13 | type TextEdit
14 | } from 'vscode-languageserver-types'
15 | import { type Telemetry } from 'yaml-language-server/lib/esm/languageservice/telemetry.js'
16 | import {
17 | type CustomFormatterOptions,
18 | getLanguageService,
19 | type LanguageSettings,
20 | type WorkspaceContextService
21 | } from 'yaml-language-server/lib/esm/languageservice/yamlLanguageService.js'
22 |
23 | import { languageId } from './constants.js'
24 |
25 | async function schemaRequestService(uri: string): Promise {
26 | const response = await fetch(uri)
27 | if (response.ok) {
28 | return response.text()
29 | }
30 | throw new Error(`Schema request failed for ${uri}`)
31 | }
32 |
33 | export interface CreateData {
34 | languageSettings: LanguageSettings
35 | enableSchemaRequest?: boolean
36 | }
37 |
38 | export interface YAMLWorker {
39 | doValidation: (uri: string) => Diagnostic[] | undefined
40 |
41 | doComplete: (uri: string, position: Position) => CompletionList | undefined
42 |
43 | doDefinition: (uri: string, position: Position) => LocationLink[] | undefined
44 |
45 | doHover: (uri: string, position: Position) => Hover | null | undefined
46 |
47 | format: (uri: string, options: CustomFormatterOptions) => TextEdit[] | undefined
48 |
49 | resetSchema: (uri: string) => boolean | undefined
50 |
51 | findDocumentSymbols: (uri: string) => DocumentSymbol[] | undefined
52 |
53 | findLinks: (uri: string) => DocumentLink[] | undefined
54 |
55 | getCodeAction: (uri: string, range: Range, diagnostics: Diagnostic[]) => CodeAction[] | undefined
56 | }
57 |
58 | const telemetry: Telemetry = {
59 | // eslint-disable-next-line @typescript-eslint/no-empty-function
60 | send() {},
61 | sendError(name, properties) {
62 | // eslint-disable-next-line no-console
63 | console.error('monaco-yaml', name, properties)
64 | },
65 | // eslint-disable-next-line @typescript-eslint/no-empty-function
66 | sendTrack() {}
67 | }
68 |
69 | const workspaceContext: WorkspaceContextService = {
70 | resolveRelativePath(relativePath, resource) {
71 | return String(new URL(relativePath, resource))
72 | }
73 | }
74 |
75 | initialize((ctx, { enableSchemaRequest, languageSettings }) => {
76 | const languageService = getLanguageService({
77 | // @ts-expect-error Type definitions are wrong. This may be null.
78 | schemaRequestService: enableSchemaRequest ? schemaRequestService : null,
79 | telemetry,
80 | workspaceContext
81 | })
82 | languageService.configure(languageSettings)
83 |
84 | const getTextDocument = (uri: string): TextDocument | undefined => {
85 | const models = ctx.getMirrorModels()
86 | for (const model of models) {
87 | if (String(model.uri) === uri) {
88 | return TextDocument.create(uri, languageId, model.version, model.getValue())
89 | }
90 | }
91 | }
92 |
93 | return {
94 | doValidation(uri) {
95 | const document = getTextDocument(uri)
96 | if (document) {
97 | return languageService.doValidation(document, Boolean(languageSettings.isKubernetes))
98 | }
99 | },
100 |
101 | doComplete(uri, position) {
102 | const document = getTextDocument(uri)
103 | if (document) {
104 | return languageService.doComplete(
105 | document,
106 | position,
107 | Boolean(languageSettings.isKubernetes)
108 | )
109 | }
110 | },
111 |
112 | doDefinition(uri, position) {
113 | const document = getTextDocument(uri)
114 | if (document) {
115 | return languageService.doDefinition(document, { position, textDocument: { uri } })
116 | }
117 | },
118 |
119 | doHover(uri, position) {
120 | const document = getTextDocument(uri)
121 | if (document) {
122 | return languageService.doHover(document, position)
123 | }
124 | },
125 |
126 | format(uri, options) {
127 | const document = getTextDocument(uri)
128 | if (document) {
129 | return languageService.doFormat(document, options)
130 | }
131 | },
132 |
133 | resetSchema(uri) {
134 | return languageService.resetSchema(uri)
135 | },
136 |
137 | findDocumentSymbols(uri) {
138 | const document = getTextDocument(uri)
139 | if (document) {
140 | return languageService.findDocumentSymbols2(document, {})
141 | }
142 | },
143 |
144 | findLinks(uri) {
145 | const document = getTextDocument(uri)
146 | if (document) {
147 | return languageService.findLinks(document)
148 | }
149 | },
150 |
151 | getCodeAction(uri, range, diagnostics) {
152 | const document = getTextDocument(uri)
153 | if (document) {
154 | return languageService.getCodeAction(document, {
155 | range,
156 | textDocument: { uri },
157 | context: { diagnostics }
158 | })
159 | }
160 | }
161 | }
162 | })
163 |
--------------------------------------------------------------------------------
/examples/demo/src/index.ts:
--------------------------------------------------------------------------------
1 | import { type JSONSchemaForSchemaStoreOrgCatalogFiles } from '@schemastore/schema-catalog'
2 | import { editor, languages, MarkerSeverity, type Position, Range, Uri } from 'monaco-editor'
3 | import { ILanguageFeaturesService } from 'monaco-editor/esm/vs/editor/common/services/languageFeatures.js'
4 | import { OutlineModel } from 'monaco-editor/esm/vs/editor/contrib/documentSymbols/browser/outlineModel.js'
5 | import { StandaloneServices } from 'monaco-editor/esm/vs/editor/standalone/browser/standaloneServices.js'
6 | import { type SchemasSettings, setDiagnosticsOptions } from 'monaco-yaml'
7 |
8 | import './index.css'
9 | import schema from './schema.json'
10 |
11 | window.MonacoEnvironment = {
12 | getWorker(moduleId, label) {
13 | switch (label) {
14 | case 'editorWorkerService':
15 | return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url))
16 | case 'yaml':
17 | return new Worker(new URL('monaco-yaml/yaml.worker', import.meta.url))
18 | default:
19 | throw new Error(`Unknown label ${label}`)
20 | }
21 | }
22 | }
23 |
24 | const defaultSchema: SchemasSettings = {
25 | uri: 'https://github.com/remcohaszing/monaco-yaml/blob/HEAD/examples/demo/src/schema.json',
26 | // @ts-expect-error TypeScript can’t narrow down the type of JSON imports
27 | schema,
28 | fileMatch: ['monaco-yaml.yaml']
29 | }
30 |
31 | setDiagnosticsOptions({
32 | schemas: [defaultSchema]
33 | })
34 |
35 | const value = `
36 | # Property descriptions are displayed when hovering over properties using your cursor
37 | property: This property has a JSON schema description
38 |
39 |
40 | # Titles work too!
41 | titledProperty: Titles work too!
42 |
43 |
44 | # Even markdown descriptions work
45 | markdown: hover me to get a markdown based description 😮
46 |
47 |
48 | # Enums can be autocompleted by placing the cursor after the colon and pressing Ctrl+Space
49 | enum:
50 |
51 |
52 | # Unused anchors will be reported
53 | unused anchor: &unused anchor
54 |
55 |
56 | # Of course numbers are supported!
57 | number: 12
58 |
59 |
60 | # As well as booleans!
61 | boolean: true
62 |
63 |
64 | # And strings
65 | string: I am a string
66 |
67 |
68 | # This property is using the JSON schema recursively
69 | reference:
70 | boolean: Not a boolean
71 |
72 |
73 | # Also works in arrays
74 | array:
75 | - string: 12
76 | enum: Mewtwo
77 | reference:
78 | reference:
79 | boolean: true
80 |
81 |
82 | # JSON referenses can be clicked for navigation
83 | pointer:
84 | $ref: '#/array'
85 |
86 |
87 | # This anchor can be referenced
88 | anchorRef: &anchor can be clicked as well
89 |
90 |
91 | # Press control while hovering over the anchor
92 | anchorPointer: *anchor
93 |
94 |
95 | formatting: Formatting is supported too! Under the hood this is powered by Prettier. Just press Ctrl+Shift+I or right click and press Format to format this document.
96 |
97 |
98 |
99 |
100 |
101 |
102 | `.replace(/:$/m, ': ')
103 |
104 | const ed = editor.create(document.getElementById('editor')!, {
105 | automaticLayout: true,
106 | model: editor.createModel(value, 'yaml', Uri.parse('monaco-yaml.yaml')),
107 | theme: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'vs-dark' : 'vs-light'
108 | })
109 |
110 | const select = document.getElementById('schema-selection') as HTMLSelectElement
111 |
112 | // eslint-disable-next-line unicorn/prefer-top-level-await
113 | fetch('https://www.schemastore.org/api/json/catalog.json').then(async (response) => {
114 | if (!response.ok) {
115 | return
116 | }
117 | const catalog = (await response.json()) as JSONSchemaForSchemaStoreOrgCatalogFiles
118 | const schemas = [defaultSchema]
119 | catalog.schemas.sort((a, b) => a.name.localeCompare(b.name))
120 | for (const { fileMatch, name, url } of catalog.schemas) {
121 | const match =
122 | typeof name === 'string' && fileMatch?.find((filename) => /\.ya?ml$/i.test(filename))
123 | if (!match) {
124 | continue
125 | }
126 | const option = document.createElement('option')
127 | option.value = match
128 |
129 | option.textContent = name
130 | select.append(option)
131 | schemas.push({
132 | fileMatch: [match],
133 | uri: url
134 | })
135 | }
136 |
137 | setDiagnosticsOptions({
138 | validate: true,
139 | enableSchemaRequest: true,
140 | format: true,
141 | hover: true,
142 | completion: true,
143 | schemas
144 | })
145 | })
146 |
147 | select.addEventListener('change', () => {
148 | const oldModel = ed.getModel()
149 | const newModel = editor.createModel(oldModel?.getValue() ?? '', 'yaml', Uri.parse(select.value))
150 | ed.setModel(newModel)
151 | oldModel?.dispose()
152 | })
153 |
154 | function* iterateSymbols(
155 | symbols: languages.DocumentSymbol[],
156 | position: Position
157 | ): Iterable {
158 | for (const symbol of symbols) {
159 | if (Range.containsPosition(symbol.range, position)) {
160 | yield symbol
161 | if (symbol.children) {
162 | yield* iterateSymbols(symbol.children, position)
163 | }
164 | }
165 | }
166 | }
167 |
168 | ed.onDidChangeCursorPosition(async (event) => {
169 | const breadcrumbs = document.getElementById('breadcrumbs')!
170 | const { documentSymbolProvider } = StandaloneServices.get(ILanguageFeaturesService)
171 | const outline = await OutlineModel.create(documentSymbolProvider, ed.getModel()!)
172 | const symbols = outline.asListOfDocumentSymbols()
173 | while (breadcrumbs.lastChild) {
174 | breadcrumbs.lastChild.remove()
175 | }
176 | for (const symbol of iterateSymbols(symbols, event.position)) {
177 | const breadcrumb = document.createElement('span')
178 | breadcrumb.setAttribute('role', 'button')
179 | breadcrumb.classList.add('breadcrumb')
180 | breadcrumb.textContent = symbol.name
181 | breadcrumb.title = symbol.detail
182 | if (symbol.kind === languages.SymbolKind.Array) {
183 | breadcrumb.classList.add('array')
184 | } else if (symbol.kind === languages.SymbolKind.Module) {
185 | breadcrumb.classList.add('object')
186 | }
187 | breadcrumb.addEventListener('click', () => {
188 | ed.setPosition({
189 | lineNumber: symbol.range.startLineNumber,
190 | column: symbol.range.startColumn
191 | })
192 | ed.focus()
193 | })
194 | breadcrumbs.append(breadcrumb)
195 | }
196 | })
197 |
198 | editor.onDidChangeMarkers(([resource]) => {
199 | const problems = document.getElementById('problems')!
200 | const markers = editor.getModelMarkers({ resource })
201 | while (problems.lastChild) {
202 | problems.lastChild.remove()
203 | }
204 | for (const marker of markers) {
205 | if (marker.severity === MarkerSeverity.Hint) {
206 | continue
207 | }
208 | const wrapper = document.createElement('div')
209 | wrapper.setAttribute('role', 'button')
210 | const codicon = document.createElement('div')
211 | const text = document.createElement('div')
212 | wrapper.classList.add('problem')
213 | codicon.classList.add(
214 | 'codicon',
215 | marker.severity === MarkerSeverity.Warning ? 'codicon-warning' : 'codicon-error'
216 | )
217 | text.classList.add('problem-text')
218 | text.textContent = marker.message
219 | wrapper.append(codicon, text)
220 | wrapper.addEventListener('click', () => {
221 | ed.setPosition({ lineNumber: marker.startLineNumber, column: marker.startColumn })
222 | ed.focus()
223 | })
224 | problems.append(wrapper)
225 | }
226 | })
227 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Monaco YAML
2 |
3 | [](https://github.com/remcohaszing/monaco-yaml/actions/workflows/ci.yaml)
4 | [](https://www.npmjs.com/package/monaco-yaml)
5 | [](https://prettier.io)
6 | [](https://monaco-yaml.js.org)
7 | [](https://app.netlify.com/sites/monaco-yaml/deploys)
8 |
9 | YAML language plugin for the Monaco Editor. It provides the following features when editing YAML
10 | files:
11 |
12 | - Code completion, based on JSON schemas or by looking at similar objects in the same file
13 | - Hovers, based on JSON schemas
14 | - Validation: Syntax errors and schema validation
15 | - Formatting using Prettier
16 | - Document Symbols
17 | - Automatically load remote schema files (by enabling DiagnosticsOptions.enableSchemaRequest)
18 | - Links from JSON references.
19 | - Links and hover effects from YAML anchors.
20 |
21 | Schemas can also be provided by configuration. See
22 | [here](https://github.com/remcohaszing/monaco-yaml/blob/main/index.d.ts) for the API that the plugin
23 | offers to configure the YAML language support.
24 |
25 | ## Table of Contents
26 |
27 | - [Installation](#installation)
28 | - [Usage](#usage)
29 | - [Examples](#examples)
30 | - [FAQ](#faq)
31 | - [Does this work with the Monaco UMD bundle?](#does-this-work-with-the-monaco-umd-bundle)
32 | - [Does this work with Monaco Editor from a CDN?](#does-this-work-with-monaco-editor-from-a-cdn)
33 | - [Does this work with `@monaco-editor/loader` or `@monaco-editor/react`?](#does-this-work-with-monaco-editorloader-or-monaco-editorreact)
34 | - [Is the web worker necessary?](#is-the-web-worker-necessary)
35 | - [Does it work without a bundler?](#does-it-work-without-a-bundler)
36 | - [How do I integrate `monaco-yaml` with a framework? (Angular, React, Vue, etc.)](#how-do-i-integrate-monaco-yaml-with-a-framework-angular-react-vue-etc)
37 | - [Does `monaco-yaml` work with `create-react-app`?](#does-monaco-yaml-work-with-create-react-app)
38 | - [Why doesn’t it work with Vite?](#why-doesnt-it-work-with-vite)
39 | - [Why isn’t `monaco-yaml` working? Official Monaco language extensions do work.](#why-isnt-monaco-yaml-working-official-monaco-language-extensions-do-work)
40 | - [Using Monaco webpack loader plugin](#using-monaco-webpack-loader-plugin)
41 | - [Why does it try to download my schema even when I provided one as an object?](#why-does-it-try-to-download-my-schema-even-when-i-provided-one-as-an-object)
42 | - [Contributing](#contributing)
43 | - [Credits](#credits)
44 | - [License](#license)
45 |
46 | ## Installation
47 |
48 | ```sh
49 | npm install monaco-yaml
50 | ```
51 |
52 | ## Usage
53 |
54 | Import `monaco-yaml` and configure it before an editor instance is created.
55 |
56 | ```typescript
57 | import { editor, Uri } from 'monaco-editor'
58 | import { setDiagnosticsOptions } from 'monaco-yaml'
59 |
60 | // The uri is used for the schema file match.
61 | const modelUri = Uri.parse('a://b/foo.yaml')
62 |
63 | setDiagnosticsOptions({
64 | enableSchemaRequest: true,
65 | hover: true,
66 | completion: true,
67 | validate: true,
68 | format: true,
69 | schemas: [
70 | {
71 | // Id of the first schema
72 | uri: 'http://myserver/foo-schema.json',
73 | // Associate with our model
74 | fileMatch: [String(modelUri)],
75 | schema: {
76 | type: 'object',
77 | properties: {
78 | p1: {
79 | enum: ['v1', 'v2']
80 | },
81 | p2: {
82 | // Reference the second schema
83 | $ref: 'http://myserver/bar-schema.json'
84 | }
85 | }
86 | }
87 | },
88 | {
89 | // Id of the first schema
90 | uri: 'http://myserver/bar-schema.json',
91 | schema: {
92 | type: 'object',
93 | properties: {
94 | q1: {
95 | enum: ['x1', 'x2']
96 | }
97 | }
98 | }
99 | }
100 | ]
101 | })
102 |
103 | editor.create(document.createElement('editor'), {
104 | // Monaco-yaml features should just work if the editor language is set to 'yaml'.
105 | language: 'yaml',
106 | model: editor.createModel('p1: \n', 'yaml', modelUri)
107 | })
108 | ```
109 |
110 | Also make sure to register the web worker. When using Webpack 5, this looks like the code below.
111 | Other bundlers may use a different syntax, but the idea is the same. Languages you don’t used can be
112 | omitted.
113 |
114 | ```js
115 | window.MonacoEnvironment = {
116 | getWorker(moduleId, label) {
117 | switch (label) {
118 | case 'editorWorkerService':
119 | return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url))
120 | case 'css':
121 | case 'less':
122 | case 'scss':
123 | return new Worker(new URL('monaco-editor/esm/vs/language/css/css.worker', import.meta.url))
124 | case 'handlebars':
125 | case 'html':
126 | case 'razor':
127 | return new Worker(
128 | new URL('monaco-editor/esm/vs/language/html/html.worker', import.meta.url)
129 | )
130 | case 'json':
131 | return new Worker(
132 | new URL('monaco-editor/esm/vs/language/json/json.worker', import.meta.url)
133 | )
134 | case 'javascript':
135 | case 'typescript':
136 | return new Worker(
137 | new URL('monaco-editor/esm/vs/language/typescript/ts.worker', import.meta.url)
138 | )
139 | case 'yaml':
140 | return new Worker(new URL('monaco-yaml/yaml.worker', import.meta.url))
141 | default:
142 | throw new Error(`Unknown label ${label}`)
143 | }
144 | }
145 | }
146 | ```
147 |
148 | ## Examples
149 |
150 | A demo is available on [monaco-yaml.js.org](https://monaco-yaml.js.org).
151 |
152 | Some usage examples can be found in the
153 | [examples](https://github.com/remcohaszing/monaco-yaml/tree/main/examples) directory.
154 |
155 | ## FAQ
156 |
157 | ### Does this work with the Monaco UMD bundle?
158 |
159 | No. Only ESM is supported.
160 |
161 | ### Does this work with Monaco Editor from a CDN?
162 |
163 | No, because these use a UMD bundle, which isn’t supported.
164 |
165 | ### Does this work with `@monaco-editor/loader` or `@monaco-editor/react`?
166 |
167 | No. These packages pull in the Monaco UMD bundle from a CDN. Because UMD isn’t supported, neither
168 | are these packages.
169 |
170 | ### Is the web worker necessary?
171 |
172 | Yes. The web worker provides the core functionality of `monaco-yaml`.
173 |
174 | ### Does it work without a bundler?
175 |
176 | No. `monaco-yaml` uses dependencies from `node_modules`, so they can be deduped and your bundle size
177 | is decreased. This comes at the cost of not being able to use it without a bundler.
178 |
179 | ### How do I integrate `monaco-yaml` with a framework? (Angular, React, Vue, etc.)
180 |
181 | `monaco-yaml` only uses the Monaco Editor. It’s not tied to a framework, all that’s needed is a DOM
182 | node to attach the Monaco Editor to. See the
183 | [Monaco Editor examples](https://github.com/microsoft/monaco-editor/tree/main/monaco-editor-samples)
184 | for examples on how to integrate Monaco Editor in your project, then configure `monaco-yaml` as
185 | described above.
186 |
187 | ### Does `monaco-yaml` work with `create-react-app`?
188 |
189 | Yes, but you’ll have to eject. See
190 | [#92 (comment)](https://github.com/remcohaszing/monaco-yaml/issues/92#issuecomment-905836058) for
191 | details.
192 |
193 | ### Why doesn’t it work with Vite?
194 |
195 | Some users have experienced the following error when using Vite:
196 |
197 | ```
198 | Uncaught (in promise) Error: Unexpected usage
199 | at EditorSimpleWorker.loadForeignModule (editorSimpleWorker.js)
200 | at webWorker.js
201 | ```
202 |
203 | As a workaround, create a file named `yaml.worker.js` in your own project with the following
204 | contents:
205 |
206 | ```js
207 | import 'monaco-yaml/yaml.worker.js'
208 | ```
209 |
210 | Then in your Monaco environment `getWorker` function, reference this file instead of referencing
211 | `monaco-yaml/yaml.worker.js` directly:
212 |
213 | ```js
214 | import YamlWorker from './yaml.worker.js?worker'
215 |
216 | window.MonacoEnvironment = {
217 | getWorker(moduleId, label) {
218 | switch (label) {
219 | // Handle other cases
220 | case 'yaml':
221 | return new YamlWorker()
222 | default:
223 | throw new Error(`Unknown label ${label}`)
224 | }
225 | }
226 | }
227 | ```
228 |
229 | ### Why isn’t `monaco-yaml` working? Official Monaco language extensions do work.
230 |
231 | This is most likely due to the fact that `monaco-yaml` is using a different instance of the
232 | `monaco-editor` package than you are. This is something you’ll want to avoid regardless of
233 | `monaco-editor`, because it means your bundle is significantly larger than it needs to be. This is
234 | likely caused by one of the following issues:
235 |
236 | - A code splitting misconfiguration
237 |
238 | To solve this, try inspecting your bundle using for example
239 | [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer). If
240 | `monaco-editor` is in there twice, this is the issue. It’s up to you to solve this, as it’s
241 | project-specific.
242 |
243 | - You’re using a package which imports `monaco-editor` for you, but it’s using a different version.
244 |
245 | You can find out why the `monaco-editor` is installed using `npm ls monaco-editor` or
246 | `yarn why monaco-editor`. It should exist only once, but it’s ok if it’s deduped.
247 |
248 | You may be able to solve this by deleting your `node_modules` folder and `package-lock.json` or
249 | `yarn.lock`, then running `npm install` or `yarn install` respectively.
250 |
251 | ### Using Monaco webpack loader plugin
252 |
253 | If you’re using
254 | [monaco webpack plugin](https://github.com/microsoft/monaco-editor/tree/main/webpack-plugin), then
255 | instead of the above code, you can extend the plugin’s configuration. Extend your
256 | `webpack.config.js` file with the following:
257 |
258 | ```js
259 | import { MonacoWebpackPlugin } from 'monaco-editor-webpack-plugin'
260 |
261 | export default {
262 | // ...the rest of your webpack configuration...
263 | plugins: [
264 | new MonacoWebpackPlugin({
265 | languages: ['yaml'],
266 | customLanguages: [
267 | {
268 | label: 'yaml',
269 | entry: 'monaco-yaml',
270 | worker: {
271 | id: 'monaco-yaml/yamlWorker',
272 | entry: 'monaco-yaml/yaml.worker'
273 | }
274 | }
275 | ]
276 | })
277 | ]
278 | }
279 | ```
280 |
281 | You can also refer to the
282 | [example](https://github.com/remcohaszing/monaco-yaml/tree/main/examples/monaco-editor-webpack-plugin)
283 | of a complete project.
284 |
285 | ### Why does it try to download my schema even when I provided one as an object?
286 |
287 | You may have provided a schema configured like this:
288 |
289 | ```Javascript
290 | {
291 | uri: "http://example.com",
292 | fileMatch: ["file_name.yml"],
293 | schema: {
294 | $schema: "http://json-schema.org/draft-07/schema#",
295 | $id: "http://example.com",
296 | title: "placeholder title",
297 | description: "placeholder description",
298 | type: "object",
299 | properties: {
300 | name: {
301 | description: "name property description",
302 | type: "string",
303 | },
304 | },
305 | required: ["name"],
306 | },
307 | }
308 | ```
309 |
310 | And would be surprised to see the error:
311 |
312 | > `Unable to load schema from '': Failed to fetch.`
313 |
314 | It happens because plugin uses schema URI not only as the URL to download the schema from, but also
315 | to determine the schema name. To fix this, change the `uri` parameter to
316 | `http://example.com/schema-name.json`.
317 |
318 | ## Contributing
319 |
320 | Please see our [contributing guidelines](CONTRIBUTING.md)
321 |
322 | ## Credits
323 |
324 | Originally [@kpdecker](https://github.com/kpdecker) forked this repository from
325 | [`monaco-json`](https://github.com/microsoft/monaco-json) by
326 | [@microsoft](https://github.com/microsoft) and rewrote it to work with
327 | [`yaml-language-server`](https://github.com/redhat-developer/yaml-language-server) instead. Later
328 | the repository maintenance was taken over by [@pengx17](https://github.com/pengx17). Eventually the
329 | repository was tranferred to the account of [@remcohaszing](https://github.com/remcohaszing), who is
330 | currently maintaining this repository with the help of [@fleon](https://github.com/fleon) and
331 | [@yazaabed](https://github.com/yazaabed).
332 |
333 | The heavy processing is done in
334 | [`yaml-language-server`](https://github.com/redhat-developer/yaml-language-server), best known for
335 | being the backbone for [`vscode-yaml`](https://github.com/redhat-developer/vscode-yaml). This
336 | repository provides a thin layer to add functionality provided by `yaml-language-server` into
337 | `monaco-editor`.
338 |
339 | ## License
340 |
341 | [MIT](https://github.com/remcohaszing/monaco-yaml/blob/main/LICENSE.md)
342 |
--------------------------------------------------------------------------------