npm package: @samnoyes/codemirror-json-schema
Description: Codemirror 6 extensions that provide full JSONSchema support for `@codemirror/lang-json` and `codemirror-json5`
Latest version: 0.7.9

Codemirror 6 extensions that provide full [JSON Schema](https://json-schema.org/) support for `@codemirror/lang-json` & `codemirror-json5` language modes

<a href="https://npmjs.com/codemirror-json-schema">
<img alt="npm" src="https://img.shields.io/npm/dm/codemirror-json-schema?label=npm%20downloads">
</a>

![screenshot of the examples with json4 and json5 support enabled](./dev/public/example.png)

## Features

This is now a full-featured library for json schema for `json`, `json5` and `yaml` as cm6 extensions!

- ✅ lint validation messages from json schema
- ✅ autocompletion with insert text from json schema
- ✅ hover tooltips
- ✅ dynamic, per-editor-instance schemas using codemirror `StateField` and linting refresh
- ✅ markdown rendering for `schema.description` and custom `formatHover` and `formatError` configuration

## Resources

- [Changelog](./CHANGELOG.md)
- [Comprehensive example](https://github.com/acao/cm6-json-schema/blob/main/dev/index.ts)
- [API Docs](./docs/)

## Usage

To give you as much flexibility as possible, everything codemirror related is a peer or optional dependency

Based on whether you want to support json4, json5 or both, you will need to install the relevant language mode for our library to use.

### Breaking Changes:

- 0.7.0 - this version introduces markdown rendering in place of returning html strings, so any usage of `formatHover` and/or `formatError` configuration will be passed to `markdown-it` which doesn't handle html by default.
- 0.5.0 - this breaking change only impacts those following the "custom usage" approach, it _does not_ effect users using the high level, "bundled" `jsonSchema()` or `json5Schema()` modes. See the custom usages below to learn how to use the new `stateExtensions` and `handleRefresh` exports.

### json4

with `auto-install-peers true` or similar:

```
npm install --save @codemirror/lang-json codemirror-json-schema
```

without `auto-install-peers true`:

```
npm install --save @codemirror/lang-json codemirror-json-schema @codemirror/language @codemirror/lint @codemirror/view @codemirror/state @lezer/common
```

#### Minimal Usage

This sets up `@codemirror/lang-json` and our extension for you.
If you'd like to have more control over the related configurations, see custom usage below

```ts
import { EditorState } from "@codemirror/state";
import { jsonSchema } from "codemirror-json-schema";

const schema = {
  type: "object",
  properties: {
    example: {
      type: "boolean",
    },
  },
};

const json5State = EditorState.create({
  doc: "{ example: true }",
  extensions: [jsonSchema(schema)],
});
```

#### Custom Usage

This approach allows you to configure the json mode and parse linter, as well as our linter, hovers, etc more specifically.

```ts
import { EditorState } from "@codemirror/state";
import { linter } from "@codemirror/lint";
import { hoverTooltip } from "@codemirror/view";
import { json, jsonParseLinter, jsonLanguage } from "@codemirror/lang-json";

import {
  jsonSchemaLinter,
  jsonSchemaHover,
  jsonCompletion,
  stateExtensions,
  handleRefresh
} from "codemirror-json-schema";

const schema = {
  type: "object",
  properties: {
    example: {
      type: "boolean",
    },
  },
};

const state = EditorState.create({
  doc: `{ "example": true }`,
  extensions: [
    json(),
    linter(jsonParseLinter(), {
      // default is 750ms
      delay: 300
    }),
    linter(jsonSchemaLinter(), {
      needsRefresh: handleRefresh,
    }),
    jsonLanguage.data.of({
      autocomplete: jsonCompletion(),
    }),
    hoverTooltip(jsonSchemaHover()),
    stateExtensions(schema)
  ];
})
```

### json5

with `auto-install-peers true` or similar:

```
npm install --save codemirror-json5 codemirror-json-schema
```

without `auto-install-peers true`:

```
npm install --save codemirror-json5 codemirror-json-schema @codemirror/language @codemirror/lint @codemirror/view @codemirror/state @lezer/common
```

#### Minimal Usage

This sets up `codemirror-json5` mode for you.
If you'd like to have more control over the related configurations, see custom usage below

```ts
import { EditorState } from "@codemirror/state";
import { json5Schema } from "codemirror-json-schema/json5";

const schema = {
  type: "object",
  properties: {
    example: {
      type: "boolean",
    },
  },
};

const json5State = EditorState.create({
  doc: `{
    example: true,
    // json5 is awesome!
  }`,
  extensions: [json5Schema(schema)],
});
```

#### Custom Usage

This approach allows you to configure the json5 mode and parse linter, as well as our linter, hovers, etc more specifically.

```ts
import { EditorState } from "@codemirror/state";
import { linter } from "@codemirror/lint";
import { json5, json5ParseLinter, json5Language } from "codemirror-json5";
import {
  json5SchemaLinter,
  json5SchemaHover,
  json5Completion,
} from "codemirror-json-schema/json5";
import { stateExtensions, handleRefresh } from "codemirror-json-schema";

const schema = {
  type: "object",
  properties: {
    example: {
      type: "boolean",
    },
  },
};

const json5State = EditorState.create({
  doc: `{
    example: true,
    // json5 is awesome!
  }`,
  extensions: [
    json5(),
    linter(json5ParseLinter(), {
      // the default linting delay is 750ms
      delay: 300,
    }),
    linter(
      json5SchemaLinter({
        needsRefresh: handleRefresh,
      })
    ),
    hoverTooltip(json5SchemaHover()),
    json5Language.data.of({
      autocomplete: json5Completion(),
    }),
    stateExtensions(schema),
  ],
});
```

### Dynamic Schema

If you want to, you can provide schema dynamically, in several ways.
This works the same for either json or json5, using the underlying codemirror 6 StateFields, via the `updateSchema` method export.

In this example

- the initial schema state is empty
- schema is loaded dynamically based on user input
- the linting refresh will be handled automatically, because it's built into our bundled `jsonSchema()` and `json5Schema()` modes

```ts
import { EditorState } from "@codemirror/state";
import { EditorView } from "@codemirror/view";

import { json5Schema } from "codemirror-json-schema/json5";

import { updateSchema } from "codemirror-json-schema";

const json5State = EditorState.create({
  doc: `{
    example: true,
    // json5 is awesome!
  }`,
  // note: you can still provide initial
  // schema when creating state
  extensions: [json5Schema()],
});

const editor = new EditorView({ state: json5State });

const schemaSelect = document.getElementById("schema-selection");

schemaSelect!.onchange = async (e) => {
  const val = e.target!.value!;
  if (!val) {
    return;
  }
  // parse the remote schema spec to json
  const data = await (
    await fetch(`https://json.schemastore.org/${val}`)
  ).json();
  // this will update the schema state field, in an editor specific way
  updateSchema(editor, data);
};
```

if you are using the "custom path" with this approach, you will need to configure linting refresh as well:

```ts
import { linter } from "@codemirror/lint";
import { json5SchemaLinter } from "codemirror-json-schema/json5";
import { handleRefresh } from "codemirror-json-schema";

const state = EditorState.create({
  // ...
  extensions: [
    linter(json5SchemaLinter(), {
      needsRefresh: handleRefresh,
    })
  ];
}
```

## Current Constraints:

- currently only tested with standard schemas using json4 spec. results may vary
- doesn't place cursor inside known insert text yet
- currently you can only override the texts and rendering of a hover. we plan to add the same for validation errors and autocomplete

## Inspiration

`monaco-json` and `monaco-yaml` both provide json schema features for json, cson and yaml, and we want the nascent codemirror 6 to have them as well!

Also, json5 is slowly growing in usage, and it needs full language support for the browser!