From 3361bfe233a594a0a525e8476125aeef20890c59 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sat, 28 Aug 2021 11:44:04 +0200 Subject: [PATCH] Let demo users select a schema from schema store --- examples/demo/package.json | 1 + examples/demo/src/index.css | 8 +++ examples/demo/src/index.ejs | 5 ++ examples/demo/src/index.ts | 106 ++++++++++++++++---------------- examples/demo/src/schema.json | 41 ++++++++++++ examples/demo/src/types.d.ts | 4 ++ examples/demo/webpack.config.js | 6 +- index.d.ts | 39 +++++++----- package-lock.json | 20 +++++- 9 files changed, 159 insertions(+), 71 deletions(-) create mode 100644 examples/demo/src/schema.json create mode 100644 examples/demo/src/types.d.ts diff --git a/examples/demo/package.json b/examples/demo/package.json index 1f24a9e..d0faffa 100644 --- a/examples/demo/package.json +++ b/examples/demo/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "^5.15.4", + "@schemastore/schema-catalog": "^0.0.5", "css-loader": "^6.2.0", "css-minimizer-webpack-plugin": "^3.0.2", "html-webpack-plugin": "^5.3.2", diff --git a/examples/demo/src/index.css b/examples/demo/src/index.css index b9a8879..5670402 100644 --- a/examples/demo/src/index.css +++ b/examples/demo/src/index.css @@ -59,6 +59,14 @@ nav { margin: 1.5rem; } +#schema-selection { + background-color: var(--editor-background); + border: none; + border-bottom: 1px solid var(--shadow-color); + color: var(--foreground-color); + width: 100%; +} + #breadcrumbs { border-bottom: 1px solid var(--shadow-color); color: var(--foreground-color); diff --git a/examples/demo/src/index.ejs b/examples/demo/src/index.ejs index b0c847f..ae4d37e 100644 --- a/examples/demo/src/index.ejs +++ b/examples/demo/src/index.ejs @@ -24,6 +24,11 @@
+
+ +
diff --git a/examples/demo/src/index.ts b/examples/demo/src/index.ts index 888c0a8..3f3ab27 100644 --- a/examples/demo/src/index.ts +++ b/examples/demo/src/index.ts @@ -1,5 +1,6 @@ import './index.css'; +import { JSONSchemaForSchemaStoreOrgCatalogFiles } from '@schemastore/schema-catalog'; import { CancellationToken } from 'monaco-editor/esm/vs/base/common/cancellation'; import { getDocumentSymbols } from 'monaco-editor/esm/vs/editor/contrib/documentSymbols/documentSymbols'; import { @@ -8,13 +9,16 @@ import { languages, Position, Range, + Uri, } from 'monaco-editor/esm/vs/editor/editor.api'; -import { setDiagnosticsOptions } from 'monaco-yaml'; +import { SchemasSettings, setDiagnosticsOptions } from 'monaco-yaml'; // NOTE: This will give you all editor featues. If you would prefer to limit to only the editor // features you want to use, import them each individually. See this example: (https://github.com/microsoft/monaco-editor-samples/blob/main/browser-esm-webpack-small/index.js#L1-L91) import 'monaco-editor'; +import defaultSchemaUri from './schema.json'; + declare global { interface Window { MonacoEnvironment: Environment; @@ -34,63 +38,18 @@ window.MonacoEnvironment = { }, }; +const defaultSchema: SchemasSettings = { + uri: defaultSchemaUri, + fileMatch: ['monaco-yaml.yaml'], +}; + setDiagnosticsOptions({ validate: true, enableSchemaRequest: true, format: true, hover: true, completion: true, - schemas: [ - { - // Id of the first schema - uri: 'https://example.com/example-schema.json', - // Associate with our model - fileMatch: ['*'], - schema: { - // Id of the first schema - id: 'https://example.com/example-schema.json', - type: 'object', - properties: { - property: { - description: 'I have a description', - }, - titledProperty: { - title: 'I have a title', - description: 'I also have a description', - }, - markdown: { - markdownDescription: 'Even **markdown** _descriptions_ `are` ~~not~~ supported!', - }, - enum: { - description: 'Pick your starter', - enum: ['Bulbasaur', 'Squirtle', 'Charmander', 'Pikachu'], - }, - number: { - description: 'Numbers work!', - minimum: 42, - maximum: 1337, - }, - boolean: { - description: 'Are boolean supported?', - type: 'boolean', - }, - string: { - type: 'string', - }, - reference: { - description: 'JSON schemas can be referenced, even recursively', - $ref: 'https://example.com/example-schema.json', - }, - array: { - description: 'It also works in arrays', - items: { - $ref: 'https://example.com/example-schema.json', - }, - }, - }, - }, - }, - ], + schemas: [defaultSchema], }); const value = ` @@ -152,11 +111,50 @@ formatting: Formatting is supported too! Under the hood this is powered by const ed = editor.create(document.getElementById('editor'), { automaticLayout: true, - value, - language: 'yaml', + model: editor.createModel(value, 'yaml', Uri.parse('monaco-yaml.yaml')), theme: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'vs-dark' : 'vs-light', }); +const select = document.getElementById('schema-selection') as HTMLSelectElement; + +fetch('https://www.schemastore.org/api/json/catalog.json').then(async (response) => { + if (!response.ok) { + return; + } + const catalog: JSONSchemaForSchemaStoreOrgCatalogFiles = await response.json(); + const schemas = [defaultSchema]; + catalog.schemas.sort((a, b) => a.name.localeCompare(b.name)); + for (const { fileMatch, name, url } of catalog.schemas) { + const match = + typeof name === 'string' && fileMatch?.find((filename) => /\.ya?ml$/i.test(filename)); + if (!match) { + continue; + } + const option = document.createElement('option'); + option.value = match; + + option.textContent = name; + select.append(option); + schemas.push({ + fileMatch: [match], + uri: url, + }); + } + + setDiagnosticsOptions({ + validate: true, + enableSchemaRequest: true, + format: true, + hover: true, + completion: true, + schemas, + }); +}); + +select.addEventListener('change', () => { + ed.setModel(editor.createModel(ed.getValue(), 'yaml', Uri.parse(select.value))); +}); + function* iterateSymbols( symbols: languages.DocumentSymbol[], position: Position, diff --git a/examples/demo/src/schema.json b/examples/demo/src/schema.json new file mode 100644 index 0000000..38adcdc --- /dev/null +++ b/examples/demo/src/schema.json @@ -0,0 +1,41 @@ +{ + "type": "object", + "properties": { + "property": { + "description": "I have a description" + }, + "titledProperty": { + "title": "I have a title", + "description": "I also have a description" + }, + "markdown": { + "markdownDescription": "Even **markdown** _descriptions_ `are` ~~not~~ supported!" + }, + "enum": { + "description": "Pick your starter", + "enum": ["Bulbasaur", "Squirtle", "Charmander", "Pikachu"] + }, + "number": { + "description": "Numbers work!", + "minimum": 42, + "maximum": 1337 + }, + "boolean": { + "description": "Are boolean supported?", + "type": "boolean" + }, + "string": { + "type": "string" + }, + "reference": { + "description": "JSON schemas can be referenced, even recursively", + "$ref": "#" + }, + "array": { + "description": "It also works in arrays", + "items": { + "$ref": "#" + } + } + } +} diff --git a/examples/demo/src/types.d.ts b/examples/demo/src/types.d.ts new file mode 100644 index 0000000..ade23fa --- /dev/null +++ b/examples/demo/src/types.d.ts @@ -0,0 +1,4 @@ +declare module '*.json' { + declare const uri; + export default uri; +} diff --git a/examples/demo/webpack.config.js b/examples/demo/webpack.config.js index 2aa5c97..bb9816f 100644 --- a/examples/demo/webpack.config.js +++ b/examples/demo/webpack.config.js @@ -22,7 +22,11 @@ module.exports = { { // Monaco editor uses .ttf icons. test: /\.(svg|ttf)$/, - type: 'asset', + type: 'asset/resource', + }, + { + test: /schema\.json$/, + type: 'asset/resource', }, { test: /\.ts$/, diff --git a/index.d.ts b/index.d.ts index 9f922ed..c40f0e0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,6 +1,29 @@ import { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'; import { IEvent, languages } from 'monaco-editor/esm/vs/editor/editor.api'; +export interface SchemasSettings { + /** + * A `Uri` file match which will trigger the schema validation. This may be a glob or an exact + * path. + * + * @example '.gitlab-ci.yml' + * @example 'file://**\/.github/actions/*.yaml' + */ + fileMatch: string[]; + + /** + * The JSON schema which will be used for validation. If not specified, it will be downloaded from + * `uri`. + */ + schema?: JSONSchema4 | JSONSchema6 | JSONSchema7; + + /** + * The source URI of the JSON schema. The JSON schema will be downloaded from here if no schema + * was supplied. It will also be displayed as the source in hover tooltips. + */ + uri: string; +} + declare module 'monaco-editor/esm/vs/editor/editor.api' { namespace languages.yaml { export interface DiagnosticsOptions { @@ -23,21 +46,7 @@ declare module 'monaco-editor/esm/vs/editor/editor.api' { /** * A list of known schemas and/or associations of schemas to file names. */ - readonly schemas?: { - /** - * The URI of the schema, which is also the identifier of the schema. - */ - readonly uri: string; - /** - * A list of file names that are associated to the schema. The '*' wildcard can be used. - * For example '*.schema.json', 'package.json' - */ - readonly fileMatch?: string[]; - /** - * The schema for the given URI. - */ - readonly schema?: JSONSchema4 | JSONSchema6 | JSONSchema7; - }[]; + readonly schemas?: SchemasSettings[]; /** * If set, the schema service would load schema content on-demand with 'fetch' if available diff --git a/package-lock.json b/package-lock.json index a92d4c9..ceb1a75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "version": "1.0.0", "dependencies": { "@fortawesome/fontawesome-free": "^5.15.4", + "@schemastore/schema-catalog": "^0.0.5", "css-loader": "^6.2.0", "css-minimizer-webpack-plugin": "^3.0.2", "html-webpack-plugin": "^5.3.2", @@ -690,6 +691,11 @@ "node": ">= 8" } }, + "node_modules/@schemastore/schema-catalog": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@schemastore/schema-catalog/-/schema-catalog-0.0.5.tgz", + "integrity": "sha512-5+Xo5tNdgdQ6Q2otY3XSs1WZR8J1cYHAmGDDHbCk0C3Qx/ycD6r2Fpzz0X9UFjm/hsbZDBd9fh/xLqdIKzREew==" + }, "node_modules/@trysound/sax": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", @@ -11590,6 +11596,11 @@ "fastq": "^1.6.0" } }, + "@schemastore/schema-catalog": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@schemastore/schema-catalog/-/schema-catalog-0.0.5.tgz", + "integrity": "sha512-5+Xo5tNdgdQ6Q2otY3XSs1WZR8J1cYHAmGDDHbCk0C3Qx/ycD6r2Fpzz0X9UFjm/hsbZDBd9fh/xLqdIKzREew==" + }, "@trysound/sax": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", @@ -13368,6 +13379,7 @@ "version": "file:examples/demo", "requires": { "@fortawesome/fontawesome-free": "^5.15.4", + "@schemastore/schema-catalog": "*", "css-loader": "^6.2.0", "css-minimizer-webpack-plugin": "^3.0.2", "html-webpack-plugin": "^5.3.2", @@ -16139,7 +16151,7 @@ "monaco-editor": "^0.27.0", "path-browserify": "^1.0.1", "prettier": "2.0.5", - "type-fest": "*", + "type-fest": "^2.1.0", "typescript": "^4.3.5", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", @@ -16649,6 +16661,11 @@ "fastq": "^1.6.0" } }, + "@schemastore/schema-catalog": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@schemastore/schema-catalog/-/schema-catalog-0.0.5.tgz", + "integrity": "sha512-5+Xo5tNdgdQ6Q2otY3XSs1WZR8J1cYHAmGDDHbCk0C3Qx/ycD6r2Fpzz0X9UFjm/hsbZDBd9fh/xLqdIKzREew==" + }, "@trysound/sax": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", @@ -18427,6 +18444,7 @@ "version": "file:examples/demo", "requires": { "@fortawesome/fontawesome-free": "^5.15.4", + "@schemastore/schema-catalog": "*", "css-loader": "^6.2.0", "css-minimizer-webpack-plugin": "^3.0.2", "html-webpack-plugin": "^5.3.2",