Giter Site home page Giter Site logo

acao / codemirror-json-schema Goto Github PK

View Code? Open in Web Editor NEW
43.0 3.0 8.0 395 KB

A JSONSchema enabled mode for codemirror 6, for json4 and json5, inspired by monaco-json

Home Page: https://codemirror-json-schema.netlify.app

License: MIT License

TypeScript 96.69% HTML 3.25% Shell 0.07%
codemirror-mode codemirror6 json-schema json json5

codemirror-json-schema's Introduction

Codemirror 6 extensions that provide full JSON Schema support for @codemirror/lang-json & codemirror-json5 language modes

npm

screenshot of the examples with json4 and json5 support enabled

Features

This is now a full-featured library for both json4 (aka json) and json5 using extensions, so they should compatible with any frontend framework and/or integration library

  • ✅ validation messages
  • ✅ autocompletion with insert text
  • ✅ hover tooltips
  • ✅ dynamic, per-editor-instance schemas using codemirror StateField and linting refresh

Resources

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.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

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.

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

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.

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
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:

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!

Our Goals

  • working GeoJSON spec linter & completion
  • working variables json mode for cm6-graphql, ala monaco-graphql
  • json5 support for graphiql as a plugin!
  • perhaps use @lezer to make a json5 language service for monaco-editor + json5?
  • json5 + json4 json schema features for all!

codemirror-json-schema's People

Contributors

acao avatar boristian avatar github-actions[bot] avatar imolorhe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

codemirror-json-schema's Issues

Add ES modules (ESM) support?

Trying to use with ES modules, I get this error

image
import { Draft07, isJsonError } from "json-schema-library";
         ^^^^^^^
SyntaxError: Named export 'Draft07' not found. The requested module 'json-schema-library' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'json-schema-library';
const { Draft07, isJsonError } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
    at ModuleJob.run (node:internal/modules/esm/module_job:190:5)

Using the following:

"codemirror-json-schema": "^0.7.0"
"vite": "^5.1.5",

tsconfig compilerOptions:

  "compilerOptions": {
    "lib": ["DOM", "DOM.Iterable", "ES2022"],
    "types": ["vitest/globals"],
    "isolatedModules": true,
    "esModuleInterop": true,
    "jsx": "react-jsx",
    "moduleResolution": "Bundler",
    "resolveJsonModule": true,
    "target": "ES2022",
    "strict": true,
    "allowJs": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "baseUrl": ".",
    "paths": {
      "~/*": ["./app/*"]
    },

typedoc on release PR only?

I'm beginning to get annoyed with the unnecessary diff in every PR for API docs
And it only makes sense to re-generate docs for each new release

So, I propose to add this to the version step for changesets instead of pre-commit, and let it churn these changes on merge to main only, so that the docs only change per reach release anyways (which will be even better, because they will reflect the actually released API, not the current iteration which may not yet be published)

jsonschema rules for array type does not get all applied during validation

the json schema definition:

{
    "type": "object",
    "definitions": {
        "type": {
            "type": "string",
            "enum": [
                "object",
                "boolean",
                "integer",
                "number",
                "string"
            ]
        },
        "enum": {
            type: "array",
            uniqueItems: true,
            items: { type: "string" },
            minItems: 1,
        }
    },
    "properties": {
        "type": {
            "$ref": "#/definitions/type"
        },
        "enum": {
            "$ref": "#/definitions/enum"
        }
    },
    "patternProperties": {
        "^.*$": {
            "properties": {
                "type": {
                    "$ref": "#/definitions/type"
                },
                "enum": {
                    "$ref": "#/definitions/enum"
                }
            },
            "additionalProperties": {
                "$ref": "#"
            }
        }
    }
}

and how i initialized the editor

const state = EditorState.create({
    doc: doc,
    extensions: [
        minimalSetup,
        lineNumbers(),
        bracketMatching(),
        closeBrackets(),
        linter(jsonParseLinter(), { delay: 0 }),
        lintGutter(),
        keymap.of([indentWithTab]),
        indentUnit.of("    "),
        json(),
        onUpdate,
        jsonSchema(...);
    ]
})

the enum array is not being fully validated, enum: type: array and enum: minItems: 1 work, but enum: uniqueItems: true and enum: items: type: string are not being enforced.
image

Custom value autocompletion

It would be great to have the ability to specify custom autocompletion sources for specific values. These sources could either be associated with a specific JSON Schema format tag, or specified as an array of completion functions that would receive the schema for the given property.

I'd be interested in implementing this as a PR, but wanted to reach out, as I'm still figuring out how CM6 autocompletions work, much less how they're specifically set up in this extension.

Perhaps something like this?

type JSONCompletionSource = (schema: JSONSchema7, ctx: CompletionContext) => CompletionResult | Promise <CompletionResult | null> | null;

type JSONCompletionOptions = {
  mode?: "json" | "json5";
  custom?: JSONCompletionSource[];
};

Console error (json format error)

Phenomenon: official website demo must appear.
image
Reproduction step: add "," to any value.
Desired: error message syntax highlighting.
image

Module not found - Webpack can't resolve not fully specified paths

Hello, thanks a lot for your work!

I use Webpack v.5.89.0 for bundling and get following errors:
image

It was already mentioned in #57 and seems connected to webpack's resolve.fullySpecified rule defaulting to true - https://webpack.js.org/configuration/module/#resolvefullyspecified. Setting fullySpecified to false solves the problem.

But it looks like more correct solution would be to put .js extensions in those imports as it seems to be recommended practice.

Cannot import codemirror-json-schema

Hi,
I wanted to try this library but cannot import it.

When I try to import with

import { jsonSchema } from 'codemirror-json-schema'

But the browser console says:

client.js:194 Module not found: Error: Can't resolve './json-completion' in '<LOCAL_PATH>/node_modules/codemirror-json-schema/dist'
Did you mean 'json-completion.js'?
BREAKING CHANGE: The request './json-completion' failed to resolve only because it was resolved as fully specified (probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.Add the extension to the request.

I am using node 18 with webpack 5.

Am I doing something wrong?

Thanks in advance

Expected type not working

Repro steps

I am using schemas generated from protobufs via protoc-gen-jsonschema.

I am using JSON4 not JSON5.

Use this schema to reproduce the problem show below:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "definitions": {
        "foo": {
            "properties": {
                "number": {
                    "oneOf": [
                        {
                            "type": "integer"
                        },
                        {
                            "type": "null"
                        }
                    ]
                }
            },
            "additionalProperties": false,
            "oneOf": [
                {
                    "type": "null"
                },
                {
                    "type": "object"
                }
            ]
        }},
    "properties": {
        "foo": {
            "$ref": "#/definitions/foo",
            "additionalProperties": false,
            "oneOf": [
                {
                    "type": "null"
                },
                {}
            ]
        }
    },
    "additionalProperties": false,
    "oneOf": [
        {
            "type": "null"
        },
        {
            "type": "object"
        }
    ]
}

I further narrowed it down to this schema (in JS/JSON5 instead of JSON4, though I am still using the JSON4 plugin):

{
  $schema: 'http://json-schema.org/draft-04/schema#',
  properties: {
    foo: {
      additionalProperties: false,
      properties: {
        number: {
          type: 'integer',
        },
      },
      oneOf: [
        {
          type: 'null',
        },
        {
          type: 'object'
        },
      ],
    },
  },
  additionalProperties: false,
  oneOf: [
    {
      type: 'null',
    },
    {
      type: 'object',
    },
  ],
}

Of note if you remove properties.oneOf and instead just use type: 'object' it will correctly show the error:

{
  $schema: 'http://json-schema.org/draft-04/schema#',
  properties: {
    foo: {
      additionalProperties: false,
      properties: {
        number: {
          type: 'integer',
        },
      },
      type: 'object'
    },
  },
  additionalProperties: false,
  oneOf: [
    {
      type: 'null',
    },
    {
      type: 'object',
    },
  ],
}

Expected Behavior

An error should appear.

image

Actual Behavior

No error appears.

image

support complex base types

This is a pattern that seems fairly common, that appears to not be working with any of the features
image

I think it's because we skip the root node with our utilities (see the failing PR). This will of course break quite a few things, especially in the autocomplete, but I think would be worth it for full spec compliance - this is why a lot of tooling configs don't work currently

tsconfig.json is a good example of this, I believe it uses a root anyOf last I checked

"required" property not checked?

Looking through https://github.com/acao/codemirror-json-schema/blob/main/src/__tests__/json-validation.spec.ts I don't see a test to ensure that required properties within an object are checked for. Moreover, in my application, I'm not seeing any lint errors when I include an object in my json that are missing required properties per my linked schema.

Is this expected behavior? If not, I'm not sure if this repo, https://www.npmjs.com/package/json-schema-library, or https://www.npmjs.com/package/@types/json-schema is the root of the issue.

Error message: can't find module

Hey guys,

I have installed you package codemirror-json-schema as described in your manual and integraded in my code:

<CodeMirror
                value={jsonTemplate}
                theme={dracula}
                indentWithTab
                extensions={[
                    json(),
                    EditorView.lineWrapping,
                    bracketMatching(),
                    /* linter(jsonLinter), */
                    lintGutter(),
                    jsonSchema(schema),
                ]}
                onChange={onChange}
                height='100vh'
            />

As soon as I start my app, everything looks fine. But when I refresh the page, I always get the following error:
Error: Cannot find module '/.../node_modules/codemirror-json-schema/dist/json-completion' imported from /.../node_modules/codemirror-json-schema/dist/index.js

I checked the node_modules and it looks fine...

Am I the only one having this issue?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.