From a44772d6af97254c4f159ea7237e842a3e3719e8 Mon Sep 17 00:00:00 2001 From: Rikki Schulte Date: Tue, 7 Dec 2021 22:51:45 +0100 Subject: [PATCH] fix: import LS only for monaco-graphql (#2103) --- .changeset/cuddly-moons-shave.md | 6 + examples/monaco-graphql-webpack/src/schema.ts | 2 +- .../graphql-language-service/package.json | 3 +- .../graphql-language-service/src/index.ts | 11 +- .../src/schemaLoader.ts | 77 ----------- packages/monaco-graphql/package.json | 7 +- packages/monaco-graphql/src/GraphQLWorker.ts | 12 +- .../src/LanguageService.ts | 92 +++++-------- packages/monaco-graphql/src/api.ts | 2 +- packages/monaco-graphql/src/schemaLoader.ts | 32 +++++ packages/monaco-graphql/src/typings/index.ts | 121 +++++++++++++++++- .../src/typings/picomatch-browser.ts | 1 + packages/monaco-graphql/src/utils.ts | 2 +- yarn.lock | 6 +- 14 files changed, 205 insertions(+), 169 deletions(-) create mode 100644 .changeset/cuddly-moons-shave.md delete mode 100644 packages/graphql-language-service/src/schemaLoader.ts rename packages/{graphql-language-service => monaco-graphql}/src/LanguageService.ts (76%) create mode 100644 packages/monaco-graphql/src/schemaLoader.ts create mode 100644 packages/monaco-graphql/src/typings/picomatch-browser.ts diff --git a/.changeset/cuddly-moons-shave.md b/.changeset/cuddly-moons-shave.md new file mode 100644 index 00000000000..bfecd7c459c --- /dev/null +++ b/.changeset/cuddly-moons-shave.md @@ -0,0 +1,6 @@ +--- +'graphql-language-service': patch +'monaco-graphql': patch +--- + +LangugeService should not be imported by `codemirror-graphql`, and thus `picomatch` should not be imported. diff --git a/examples/monaco-graphql-webpack/src/schema.ts b/examples/monaco-graphql-webpack/src/schema.ts index a15ba59a5c7..2b1036865a0 100644 --- a/examples/monaco-graphql-webpack/src/schema.ts +++ b/examples/monaco-graphql-webpack/src/schema.ts @@ -1,5 +1,5 @@ import { getIntrospectionQuery } from 'graphql'; -import type { SchemaConfig } from 'graphql-language-service'; +import type { SchemaConfig } from 'monaco-graphql/src/typings'; import { Uri } from 'monaco-editor'; const SCHEMA_URL = 'https://api.github.com/graphql'; diff --git a/packages/graphql-language-service/package.json b/packages/graphql-language-service/package.json index 6a1ab124cda..4ec435a4f6c 100644 --- a/packages/graphql-language-service/package.json +++ b/packages/graphql-language-service/package.json @@ -34,8 +34,7 @@ "graphql-language-service-interface": "^2.10.1", "graphql-language-service-parser": "^1.10.4", "graphql-language-service-types": "^1.8.7", - "graphql-language-service-utils": "^2.7.1", - "picomatch-browser": "^2.2.5" + "graphql-language-service-utils": "^2.7.1" }, "devDependencies": { "@types/picomatch": "^2.3.0", diff --git a/packages/graphql-language-service/src/index.ts b/packages/graphql-language-service/src/index.ts index 665598c0dbc..25701c8917b 100644 --- a/packages/graphql-language-service/src/index.ts +++ b/packages/graphql-language-service/src/index.ts @@ -12,16 +12,6 @@ * * TODO: retire `graphql-language-service-{parser,interface,types,utils}` and merge with this workspace */ -export type { - SchemaConfig, - BaseSchemaConfig, - SchemaLoader, -} from './schemaLoader'; -export { defaultSchemaLoader } from './schemaLoader'; - -export type { GraphQLLanguageConfig } from './LanguageService'; -export { LanguageService } from './LanguageService'; - /** * A whole bunch of the key language services */ @@ -43,6 +33,7 @@ export { GraphQLLanguageService, SEVERITY, Severity, + HoverConfig, SeverityEnum, DIAGNOSTIC_SEVERITY, DefinitionQueryResult, diff --git a/packages/graphql-language-service/src/schemaLoader.ts b/packages/graphql-language-service/src/schemaLoader.ts deleted file mode 100644 index 14c674fdad1..00000000000 --- a/packages/graphql-language-service/src/schemaLoader.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { - IntrospectionQuery, - DocumentNode, - BuildSchemaOptions, - buildClientSchema, - buildASTSchema, - GraphQLSchema, -} from 'graphql'; - -import { LanguageService } from './LanguageService'; - -export type BaseSchemaConfig = { - buildSchemaOptions?: BuildSchemaOptions; - schema?: GraphQLSchema; - documentString?: string; - documentAST?: DocumentNode; - introspectionJSON?: IntrospectionQuery; - introspectionJSONString?: string; -}; - -export type SchemaConfig = { - /** - * A unique URI for this schema. - * Model data will be set - */ - uri: string; - fileMatch?: string[]; - buildSchemaOptions?: BuildSchemaOptions; - schema?: GraphQLSchema; - documentString?: string; - documentAST?: DocumentNode; - introspectionJSON?: IntrospectionQuery; - introspectionJSONString?: string; -}; - -/** - * This schema loader is focused on performance for the monaco worker runtime - * We favor taking in stringified schema representations as they can be used to communicate - * Across the main/webworker process boundary - * - * @param schemaConfig {SchemaConfig} - * @param parser {LanguageService['parse']} - * @returns {GraphQLSchema} - */ -export type SchemaLoader = ( - schemaConfig: SchemaConfig, - parser: LanguageService['parse'], -) => GraphQLSchema; - -export const defaultSchemaLoader: SchemaLoader = (schemaConfig, parser) => { - const { - schema, - documentAST, - introspectionJSON, - introspectionJSONString, - buildSchemaOptions, - documentString, - } = schemaConfig; - if (schema) { - return schema; - } - if (introspectionJSONString) { - const introspectionJSONResult = JSON.parse(introspectionJSONString); - return buildClientSchema(introspectionJSONResult, buildSchemaOptions); - } - if (documentString) { - const docAST = parser(documentString); - return buildASTSchema(docAST, buildSchemaOptions); - } - if (introspectionJSON) { - return buildClientSchema(introspectionJSON, buildSchemaOptions); - } - if (documentAST) { - return buildASTSchema(documentAST, buildSchemaOptions); - } - throw Error('no schema supplied'); -}; diff --git a/packages/monaco-graphql/package.json b/packages/monaco-graphql/package.json index 4d2f4b968d1..3ec29b3ad8c 100644 --- a/packages/monaco-graphql/package.json +++ b/packages/monaco-graphql/package.json @@ -5,7 +5,7 @@ "license": "MIT", "main": "dist/monaco.contribution.js", "module": "esm/monaco.contribution.js", - "types": "dist/monaco.contribution.d.ts", + "types": "esm/monaco.contribution.d.ts", "contributors": [ { "name": "Peng Lyu", @@ -26,8 +26,9 @@ "src" ], "dependencies": { - "graphql-language-service": "^4.1.2", - "graphql-language-service-utils": "^2.7.1" + "graphql-language-service": "^4.1.1", + "graphql-language-service-utils": "^2.7.1", + "picomatch-browser": "^2.2.5" }, "devDependencies": { "graphql": "16.0.0-experimental-stream-defer.5", diff --git a/packages/monaco-graphql/src/GraphQLWorker.ts b/packages/monaco-graphql/src/GraphQLWorker.ts index 01e732a8d1a..87f3ac22f31 100644 --- a/packages/monaco-graphql/src/GraphQLWorker.ts +++ b/packages/monaco-graphql/src/GraphQLWorker.ts @@ -5,15 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -import { FormattingOptions, ICreateData } from './typings'; +import { FormattingOptions, ICreateData, SchemaConfig } from './typings'; import type { worker, Position } from 'monaco-editor'; -import { - getRange, - LanguageService, - SchemaConfig, -} from 'graphql-language-service'; +import { getRange } from 'graphql-language-service'; + +import { LanguageService } from './LanguageService'; import { toGraphQLPosition, @@ -27,7 +25,6 @@ export type MonacoCompletionItem = monaco.languages.CompletionItem & { isDeprecated?: boolean; deprecationReason?: string | null; }; - export class GraphQLWorker { private _ctx: worker.IWorkerContext; private _languageService: LanguageService; @@ -133,6 +130,7 @@ export class GraphQLWorker { ...this._formattingOptions?.prettierConfig, }); } + /** * TODO: store this in a proper document cache in the language service */ diff --git a/packages/graphql-language-service/src/LanguageService.ts b/packages/monaco-graphql/src/LanguageService.ts similarity index 76% rename from packages/graphql-language-service/src/LanguageService.ts rename to packages/monaco-graphql/src/LanguageService.ts index 5079b4882ac..c80d541b4c9 100644 --- a/packages/graphql-language-service/src/LanguageService.ts +++ b/packages/monaco-graphql/src/LanguageService.ts @@ -12,17 +12,18 @@ import { FragmentDefinitionNode, visit, DocumentNode, + Source, } from 'graphql'; -import { default as picomatch } from 'picomatch'; +import picomatch from 'picomatch-browser'; -import type { IPosition } from 'graphql-language-service-types'; +import type { IPosition } from 'graphql-language-service'; import { getAutocompleteSuggestions, getDiagnostics, getHoverInformation, HoverConfig, -} from 'graphql-language-service-interface'; +} from 'graphql-language-service'; import { getVariablesJSONSchema, @@ -30,51 +31,18 @@ import { JSONSchemaOptions, } from 'graphql-language-service-utils'; -import { - defaultSchemaLoader, - SchemaConfig, - SchemaLoader, -} from './schemaLoader'; +import { defaultSchemaLoader } from './schemaLoader'; -/** - * For the `monaco-graphql` language worker, these must be specified - * in a custom webworker. see the readme. - */ -export type GraphQLLanguageConfig = { - /** - * Provide a parser that matches `graphql` `parse()` signature - * Used for internal document parsing operations - * for autocompletion and hover, `graphql-language-service-parser ` is used via `graphql-language-service-interface` - */ - parser?: typeof parse; - /** - * Custom options passed to `parse`, whether `graphql` parse by default or custom parser - */ - parseOptions?: ParseOptions; - /** - * Take a variety of schema inputs common for the language worker, and transform them - * to at least a `schema` if not other easily available implementations - */ - schemaLoader?: SchemaLoader; - /** - * An array of schema configurations from which to match files for language features - */ - schemas?: SchemaConfig[]; - /** - * External fragments to be used with completion and validation - */ - exteralFragmentDefinitions?: FragmentDefinitionNode[] | string; - /** - * Custom validation rules following `graphql` `ValidationRule` signature - */ - customValidationRules?: ValidationRule[]; -}; +import { SchemaConfig, SchemaLoader, GraphQLLanguageConfig } from './typings'; type SchemaCacheItem = Omit & { schema: GraphQLSchema }; type SchemaCache = Map; const schemaCache: SchemaCache = new Map(); +/** + * Currently only used by the `monaco-graphql` worker + */ export class LanguageService { private _parser: typeof parse = parse; private _schemas: SchemaConfig[] = []; @@ -128,28 +96,36 @@ export class LanguageService { schema, }); } + /** * Provide a model uri path, and see if a schema config has a `fileMatch` to match it * @param uri {string} * @returns {SchemaCacheItem | undefined} */ public getSchemaForFile(uri: string): SchemaCacheItem | undefined { - const schema = this._schemas.find(schemaConfig => { - if (!schemaConfig.fileMatch) { - return false; - } - return schemaConfig.fileMatch.some(glob => { - const isMatch = picomatch(glob); - return isMatch(uri); + if (!this._schemas || !this._schemas.length) { + return; + } + if (this._schemas.length === 1) { + return this._schemaCache.get(this._schemas[0].uri); + } else { + const schema = this._schemas.find(schemaConfig => { + if (!schemaConfig.fileMatch) { + return false; + } + return schemaConfig.fileMatch.some(glob => { + const isMatch = picomatch(glob); + return isMatch(uri); + }); }); - }); - if (schema) { - const cacheEntry = this._schemaCache.get(schema.uri); - if (cacheEntry) { - return cacheEntry; + if (schema) { + const cacheEntry = this._schemaCache.get(schema.uri); + if (cacheEntry) { + return cacheEntry; + } + const cache = this._cacheSchema(schema); + return cache.get(schema.uri); } - const cache = this._cacheSchema(schema); - return cache.get(schema.uri); } } @@ -212,11 +188,11 @@ export class LanguageService { } /** * Uses the configured parser - * @param text - * @param options + * @param text {string | Source} + * @param options {ParseOptions} * @returns {DocumentNode} */ - public parse(text: string, options?: ParseOptions): DocumentNode { + public parse(text: string | Source, options?: ParseOptions): DocumentNode { return this._parser(text, options || this._parseOptions); } /** diff --git a/packages/monaco-graphql/src/api.ts b/packages/monaco-graphql/src/api.ts index a3a7c98adb0..92fe3616519 100644 --- a/packages/monaco-graphql/src/api.ts +++ b/packages/monaco-graphql/src/api.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import { SchemaConfig } from 'graphql-language-service'; import { Emitter } from 'monaco-editor'; import type { IEvent } from 'monaco-editor'; @@ -15,6 +14,7 @@ import type { FormattingOptions, ModeConfiguration, MonacoGraphQLInitializeConfig, + SchemaConfig, } from './typings'; export type MonacoGraphQLAPIOptions = { diff --git a/packages/monaco-graphql/src/schemaLoader.ts b/packages/monaco-graphql/src/schemaLoader.ts new file mode 100644 index 00000000000..b9ce739b31e --- /dev/null +++ b/packages/monaco-graphql/src/schemaLoader.ts @@ -0,0 +1,32 @@ +import { buildClientSchema, buildASTSchema } from 'graphql'; + +import type { SchemaLoader } from './typings'; + +export const defaultSchemaLoader: SchemaLoader = (schemaConfig, parser) => { + const { + schema, + documentAST, + introspectionJSON, + introspectionJSONString, + buildSchemaOptions, + documentString, + } = schemaConfig; + if (schema) { + return schema; + } + if (introspectionJSONString) { + const introspectionJSONResult = JSON.parse(introspectionJSONString); + return buildClientSchema(introspectionJSONResult, buildSchemaOptions); + } + if (documentString && parser) { + const docAST = parser(documentString); + return buildASTSchema(docAST, buildSchemaOptions); + } + if (introspectionJSON) { + return buildClientSchema(introspectionJSON, buildSchemaOptions); + } + if (documentAST) { + return buildASTSchema(documentAST, buildSchemaOptions); + } + throw Error('no schema supplied'); +}; diff --git a/packages/monaco-graphql/src/typings/index.ts b/packages/monaco-graphql/src/typings/index.ts index 512e9945bd6..ced897edc33 100644 --- a/packages/monaco-graphql/src/typings/index.ts +++ b/packages/monaco-graphql/src/typings/index.ts @@ -1,17 +1,126 @@ -import type { - SchemaConfig as SchemaConfiguration, - GraphQLLanguageConfig, -} from 'graphql-language-service'; import type { languages } from 'monaco-editor'; +import { + IntrospectionQuery, + DocumentNode, + BuildSchemaOptions, + parse, + ParseOptions, + GraphQLSchema, + ValidationRule, + FragmentDefinitionNode, +} from 'graphql'; + import type { Options as PrettierConfig } from 'prettier'; +export type BaseSchemaConfig = { + buildSchemaOptions?: BuildSchemaOptions; + schema?: GraphQLSchema; + documentString?: string; + documentAST?: DocumentNode; + introspectionJSON?: IntrospectionQuery; + introspectionJSONString?: string; +}; + +/** + * Inspired by the `monaco-json` schema object in `DiagnosticSettings["schemas"]`, + * which we use :) + * + * You have many schema format options to provide, choose one! + * + * For large schemas, try different formats to see what is most efficient for you. + */ +export type SchemaConfig = { + /** + * A unique uri string for this schema. + * Model data will evenetually be set for this URI for definition lookup + */ + uri: string; + /** + * An array of URIs or globs to associate with this schema in the language worker + * Uses `picomatch` which supports many common expressions except brackets + * Only necessary if you provide more than one schema, otherwise it defaults to the sole schema + */ + fileMatch?: string[]; + /** + * provide custom options when using `buildClientSchema`, `buildASTSchema`, etc + */ + buildSchemaOptions?: BuildSchemaOptions; + /** + * A GraphQLSchema instance + */ + schema?: GraphQLSchema; + /** + * An SDL document string + */ + documentString?: string; + /** + * A GraphQL DocumentNode AST + */ + documentAST?: DocumentNode; + /** + * A parsed JSON literal of the introspection results + */ + introspectionJSON?: IntrospectionQuery; + /** + * A stringified introspection JSON result + */ + introspectionJSONString?: string; +}; + +/** + * This schema loader is focused on performance for the monaco worker runtime + * We favor taking in stringified schema representations as they can be used to communicate + * Across the main/webworker process boundary + * + * @param schemaConfig {SchemaConfig} + * @param parser {LanguageService['parse']} + * @returns {GraphQLSchema} + */ +export type SchemaLoader = ( + schemaConfig: SchemaConfig, + parser: GraphQLLanguageConfig['parser'], +) => GraphQLSchema; + +/** + * For the `monaco-graphql` language worker, these must be specified + * in a custom webworker. see the readme. + */ +export type GraphQLLanguageConfig = { + /** + * Provide a parser that matches `graphql` `parse()` signature + * Used for internal document parsing operations + * for autocompletion and hover, `graphql-language-service-parser ` is used via `graphql-language-service-interface` + */ + parser?: typeof parse; + /** + * Custom options passed to `parse`, whether `graphql` parse by default or custom parser + */ + parseOptions?: ParseOptions; + /** + * Take a variety of schema inputs common for the language worker, and transform them + * to at least a `schema` if not other easily available implementations + */ + schemaLoader?: SchemaLoader; + /** + * An array of schema configurations from which to match files for language features + * You can provide many formats, see the config for details! + */ + schemas?: SchemaConfig[]; + /** + * External fragments to be used with completion and validation + */ + exteralFragmentDefinitions?: FragmentDefinitionNode[] | string; + /** + * Custom validation rules following `graphql` `ValidationRule` signature + */ + customValidationRules?: ValidationRule[]; +}; + export interface IDisposable { dispose(): void; } -export type SchemaConfig = SchemaConfiguration; - export type JSONDiagnosticOptions = languages.json.DiagnosticsOptions; export interface IEvent { (listener: (e: T) => any, thisArg?: any): IDisposable; diff --git a/packages/monaco-graphql/src/typings/picomatch-browser.ts b/packages/monaco-graphql/src/typings/picomatch-browser.ts new file mode 100644 index 00000000000..4bff9a9802c --- /dev/null +++ b/packages/monaco-graphql/src/typings/picomatch-browser.ts @@ -0,0 +1 @@ +declare module 'picomatch-browser'; diff --git a/packages/monaco-graphql/src/utils.ts b/packages/monaco-graphql/src/utils.ts index 33350326967..3024f4c84ea 100644 --- a/packages/monaco-graphql/src/utils.ts +++ b/packages/monaco-graphql/src/utils.ts @@ -5,12 +5,12 @@ * LICENSE file in the root directory of this source tree. */ +import { SchemaConfig } from './typings'; import type { IRange as GraphQLRange, IPosition as GraphQLPosition, Diagnostic, CompletionItem as GraphQLCompletionItem, - SchemaConfig, } from 'graphql-language-service'; import { buildASTSchema, printSchema } from 'graphql'; diff --git a/yarn.lock b/yarn.lock index 5c2e6ca4e57..c65f398f5fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11047,16 +11047,16 @@ grapheme-splitter@^1.0.4: integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== "graphiql@file:packages/graphiql": - version "1.5.13" + version "1.5.14" dependencies: "@graphiql/toolkit" "^0.4.2" codemirror "^5.58.2" - codemirror-graphql "^1.2.8" + codemirror-graphql "^1.2.9" copy-to-clipboard "^3.2.0" dset "^3.1.0" entities "^2.0.0" escape-html "^1.0.3" - graphql-language-service "^4.1.1" + graphql-language-service "^4.1.2" markdown-it "^12.2.0" graphql-config@^4.1.0: