codemirror-json-schema
Version:
Codemirror 6 extensions that provide full JSONSchema support for `@codemirror/lang-json` and `codemirror-json5`
286 lines (223 loc) • 7.89 kB
Markdown
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>

## 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 /lang-json codemirror-json-schema
```
without `auto-install-peers true`:
```
npm install --save /lang-json codemirror-json-schema @codemirror/language /lint @codemirror/view /state @lezer/common
```
#### Minimal Usage
This sets up `/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 /language @codemirror/lint /view @codemirror/state /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!