ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

monaco-graphql:为 Monaco Editor 构建 GraphQL 语言插件的完整指南

monaco-graphql:为 Monaco Editor 构建 GraphQL 语言插件的完整指南 monaco-graphql为 Monaco Editor 构建 GraphQL 语言插件的完整指南【免费下载链接】graphiqlGraphiQL the GraphQL LSP Reference Ecosystem for building browser IDE tools.项目地址: https://gitcode.com/GitHub_Trending/gr/graphiql导读monaco-graphql是 GraphQL 官方在graphiql仓库中维护的、面向 Monaco Editor 的 GraphQL 语言插件支持 Schema 驱动的代码补全、Hover、校验、格式化、JSON 变量校验等能力用于打造 VSCode / Codespaces 风格的 Web 或桌面 IDE。本文以 packages/monaco-graphql/README.md 为主体结合源码 api.ts、initialize.ts、GraphQLWorker.ts 与 monaco-editor.ts讲透同步/懒加载两种初始化方式、Schema 多模型配置、Variables JSON 校验、lite 裁剪、API 方法集、自定义 Worker 与打包器集成。读完你将能够在一个前端项目中从零接入 GraphQL 语言服务并深入理解其 Web Worker 运行时架构。1. 概览它是什么能做什么monaco-graphql的定位在 README 中说得非常清楚GraphQL 语言插件language plugin目标是让你用任意前端框架React、Vue、Svelte 甚至纯 JavaScript构建 VSCode/Codespaces 风格的 Web 或桌面 IDE。它当前处于为 GraphiQL 2.0.x 铺路的 pre-release 状态同仓库的codemirror-graphql拥有更多特性如 JSON 变量校验且更稳定。在编辑 GraphQL 文件时它提供如下能力对应 README Features 一节可配置的多模型、多 Schema 语言 Worker支持fileMatch表达式Schema 驱动的代码补全Operation 与 SDL 类型均支持并支持叶子类型补全后的自动展开填充Schema 驱动的 Hover支持 Markdown 渲染Schema 驱动的校验diagnosticsSchema 驱动的 JSON 变量校验与语言特性基于 Prettier 的格式化格式化选项可配置基础语法高亮由monaco-editor的内置 basic-languages 提供外部 Fragment 定义注入自定义 Worker可传入自定义 parser、校验规则、schemaBuilder 等语言服务选项。1.1 架构速览主线程 API Web Worker 语言服务从源码结构可以清晰看出分层见 packages/monaco-graphql/srcapi.tsMonacoGraphQLAPI类运行在主线程持有全部可配置状态schemas、modeConfiguration、diagnosticSettings、completionSettings、formattingOptions、experimentalFragmentArguments、externalFragmentDefinitions任何配置变更都会触发_onDidChange事件让 Worker 侧重载相关语言特性。initialize.tsinitializeMode()入口首次调用时创建 API 实例并挂到monaco.languages.graphql同时异步加载 graphqlMode.ts 完成模式注册。graphql.worker.tsWorker 入口通过initialize((ctx, createData) new GraphQLWorker(ctx, createData))构造 Worker 实例。GraphQLWorker.ts封装LanguageService对外暴露doValidation、doComplete、doHover、doGetVariablesJSONSchema、doFormat、doUpdateSchema(s)等方法并负责把 GraphQL 坐标与 Monaco 坐标互相转换utils.ts 中的toGraphQLPosition、toMonacoRange、toMarkerData、toCompletion。LanguageService.ts直接使用graphql-language-service包提供真正的 LSP 能力。语言 ID 当前固定为graphqlinitialize.ts 中LANGUAGE_ID graphqlREADME 说明这是为了等待与官方graphql语言 ID 稳妥对接。2. 快速上手同步初始化Sync Example安装依赖README 给出的 webpack 场景命令yarn add monaco-graphqlREADME 还提示该包的 peer 依赖范围见 package.jsongraphql ^15.5.0 || ^16.0.0 || ^17.0.0、monaco-editor 0.20.0 0.53、prettier ^2.8.0 || ^3.0.0。同步初始化示例完整保留自 READMEimport * as monaco from monaco-editor/esm/vs/editor/editor.api; import { initializeMode } from monaco-graphql/initializeMode; // monaco-graphql/esm/initializeMode 已废弃但依然可用 // 也可以使用 webpack 或 vite 插件来配置 monaco-editor import GraphQLWorker from worker-loader!monaco-graphql/esm/graphql.worker; // 以 schema 实例化 worker 与语言特性 const MonacoGraphQLAPI initializeMode({ // 仅当连接的服务器支持 fragment arguments 时开启 experimentalFragmentArguments: true, schemas: [ { schema: myGraphqlSchema as GraphQLSchema, // 任何 monaco.URI.from() 兼容的内容 uri: https://my-schema.com, uri: /my-schema.graphql, // 匹配该 schema 对应的 monaco 文件 uri // 支持具体 uri 与 picomatch 支持的表达式 // 除中括号正则以外的全部 fileMatch: [**/*.graphql], // 注意若 graphql model 使用 url 作为 uri^ 前缀可能不生效 }, ], }); globalThis.MonacoEnvironment { getWorker(_workerId: string, label: string) { if (label graphql) { return new GraphQLWorker(); } // 若使用 vite 或 webpack 插件会在这里找到它 return new Worker(editor.worker.js); }, }; monaco.editor.create(document.getElementById(someElementId), { value: query { }, language: graphql, formatOnPaste: true, });关键点说明initializeMode(config)一旦调用就同步创建 API 并异步注册语言模式后续对schemas之外任何 API 方法的调用都会触发 Worker 重载。MonacoEnvironment.getWorker必须正确分流graphql标签到 GraphQL Worker其余标签回到editor.worker.js。formatOnPaste: true让粘贴时自动触发 Prettier 格式化。3. 懒加载初始化Lazy Example如果不希望页面加载时就初始化 schema可以先import monaco-graphql注册语言模式之后任意时刻通过monaco.languages.graphql.setSchemaConfig([...])惰性注入 schemaimport * as monaco from monaco-editor/esm/vs/editor/editor.api; // 立即启用我们的语言 worker尽管还没有 schema import monaco-graphql; // 也可以使用 webpack 或 vite 插件配置 import GraphQLWorker from worker-loader!monaco-graphql/esm/graphql.worker; // 随时惰性调用 api 配置方法 monaco.languages.graphql.setSchemaConfig([ { schema: myGraphqlSchema as GraphQLSchema, // 任何 monaco.URI.from() 兼容的内容 uri: https://my-schema.com, uri: /my-schema.graphql, // 匹配该 schema 对应的 monaco 文件 uri // 接受具体 uri 与 picomatch 支持的表达式除中括号正则 fileMatch: [**/*.graphql], }, ]); globalThis.MonacoEnvironment { getWorker(_workerId: string, label: string) { if (label graphql) { return new GraphQLWorker(); } return new Worker(editor.worker.js); }, }; monaco.editor.create(document.getElementById(someElementId), { value: query { }, language: graphql, formatOnPaste: true, });这两种方式都只覆盖默认 introspectionQuery POST这类基础请求要完全自定义 fetcher参见 README 提到的 advanced customization自定义 Schema 加载见第 9 节。4. Schema 配置详解SchemaConfig的多种形态SchemaConfig的类型定义在 typings/index.ts它受monaco-json的 schema 对象启发。每个字段字段类型说明uristring该 schema 的唯一 uri 字符串后续用于 definition 查找时会为这个 URI 设置模型数据fileMatch?string[]与该 schema 关联的 uri/glob 数组使用picomatch支持多数常见表达式不含中括号。仅当提供多个 schema 时才必需否则默认关联唯一的 schemabuildSchemaOptions?BuildSchemaOptions使用buildClientSchema、buildASTSchema等时的自定义选项schema?GraphQLSchemaGraphQLSchema 实例documentString?stringSDL 文档字符串documentAST?DocumentNodeGraphQL DocumentNode ASTintrospectionJSON?IntrospectionQuery解析后的 introspection 结果 JSON 字面量introspectionJSONString?string字符串化的 introspection JSON 结果customScalarSchemas?Recordstring, JSONSchema6自定义标量的 JSON Schema如DateTime: { type: string, format: date-time }对应地默认 schema 加载器 schemaLoader.ts 的解析优先级为schema实例 →introspectionJSONStringJSON.parse后buildClientSchema→documentString parserbuildASTSchema→introspectionJSONbuildClientSchema→documentASTbuildASTSchema→ 全部缺失则抛出No schema supplied。README 提示对于大 schema可以尝试不同格式找出对你最高效的那一种——因为字符串化表示更便于跨主线程/Worker 边界传递这是 monaco worker 运行时中偏向性能的设计。5. 高级用法Variables JSON 支持monaco-graphql0.5.0起提供了getVariablesJSONSchema可为任意给定操作集合生成其声明变量的JSONSchema描述从而在 JSON 编辑器中对 variables 做补全、校验与诊断。5.1 完整同步 DemoFull Sync Demo with Variables JSONimport * as monaco from monaco-editor/esm/vs/editor/editor.api; import { initializeMode } from monaco-graphql/initializeMode; import GraphQLWorker from worker-loader!monaco-graphql/esm/graphql.worker; globalThis.MonacoEnvironment { getWorker(_workerId: string, label: string) { if (label graphql) { return new GraphQLWorker(); } return new Worker(editor.worker.js); }, }; // schema 就绪后语言服务即被实例化 const MonacoGraphQLAPI initializeMode({ schemas: [ { // 任何 monaco.URI.from() 兼容的内容 uri: https://my-schema.com, // 匹配该 schema 对应的 monaco 文件 uri fileMatch: [**/*.graphql], schema: myGraphqlSchema as GraphQLSchema, }, ], }); const operationModel monaco.editor.createModel( query {}, graphql, /operation.graphql, ); const operationEditor monaco.editor.create( document.getElementById(someElementId), { model: operationModel, language: graphql, formatOnPaste: true, }, ); const variablesSchemaUri monaco.editor.URI.file(/variables-schema.json); const variablesModel monaco.editor.createModel( {}, json, /variables.json, ); const variablesEditor monaco.editor.create( document.getElementById(someElementId), { model: variablesModel, language: graphql, formatOnPaste: true, }, ); // 配置 json 变量校验的高层方法 MonacoGraphQLAPI.setDiagnosticSettings({ validateVariablesJson: { // Urls、uris任何 monaco.URI.from() 兼容的内容。 // 把 operation model 与 variables 编辑器配对 // 语言服务会自动监听变更 // 并通过 GraphQLWorker 计算 json schema。 // 在主进程应用于全局 monaco json 设置 // 以借助 monaco-json 内建 JSON Schema 支持完成校验、补全等。 [operationModel.uri.toString()]: [variablesModel.uri.toString()], }, jsonDiagnosticSettings: { allowComments: true, // 允许 json 注释请求时用 jsonc parser 解析 }, }); MonacoGraphQL.setCompletionSettings({ // 自动填充 NonNull 叶子字段 // 以前默认开启但当字段含必填参数时会很烦人 // 希望很快修复这个问题 __experimental__fillLeafsOnComplete: true, });setDiagnosticSettings的validateVariablesJson键是操作模型 URI → 变量模型 URI 列表的映射类型定义见 typings/index.ts注意源码中的键名是validateVariablesJSON。配置后语言服务会监听操作文档变化由 Worker 内的doGetVariablesJSONSchemaGraphQLWorker.ts动态计算 JSON Schema。jsonDiagnosticSettings透传monaco.languages.json.DiagnosticsOptions例如schemaValidation: error默认值见 api.ts、allowComments: true启用 jsonc 编辑、trailingComments默认error可调为warning/ignore。README 还建议可实验内建的jsonc允许注释与尾逗号的 JSON 语法如tsconfig.json以及第三方monaco-yaml模式来做其他变量输入格式的补全也可以自行用编辑器方法把检测到的输入解析为不同格式如yaml粘贴转json。当然你也可以完全不用编辑器改用任意框架生成 variables 输入的 JSON Schema 表单。6. 裁剪版monaco-graphql/litepackages/monaco-graphql/lite 是lite入口手动按需启用 Monaco 特性import { initializeMode } from monaco-graphql/lite; // 启用补全 import monaco-editor/esm/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution; const api initializeMode({ schemas: [ { // 任何 monaco.URI.from() 兼容的内容 uri: schema.graphql, // 匹配该 schema 对应的 monaco 文件 uri fileMatch: [operation.graphql], schema: myGraphqlSchema as GraphQLSchema, }, ], });警告lite 模式下默认只有高亮和校验可用补全等其他特性必须手动 import 对应的 monaco contribution 模块才会生效README 原文by default, completion and other features will not work, only highlighting and validation。7.MonacoGraphQLAPI运行时 API 手册README 明确任何 API 方法在运行时修改语言服务配置都会让 web worker 重载相关语言特性。7.1 获取 API 实例同步import monaco-graphql后可通过全局monaco.languages.graphql.api访问import monaco-graphql; // api 现在挂在 monaco.languages 全局上 const { api } monaco.languages.graphql;或import monaco-graphql; // 这样也行 import { languages } from monaco-editor; const { api } languages.graphql;否则如同步 demo 中使用initializeMode返回值import { initializeMode } from monaco-graphql/initializeMode; const api initializeMode(config);从源码看initialize.tsinitializeMode是幂等的首次调用创建 API 并挂载到monaco.languages.graphql后续调用直接返回既有实例而 monaco.contribution.ts 中languages.onLanguage(LANGUAGE_ID, ...)会在第一个 graphql 语言模型出现时自动initializeMode()。7.2setSchemaConfig([SchemaConfig])覆盖整个 schema 配置可传多个 schema 并用fileMatch映射到各种 uri 目录 glob 或具体文件uri可为 url 或文件路径等任何可解析内容。惰性加载方式// 可以惰性加载 import monaco-graphql; monaco.languages.graphql.api.setSchemaConfig([ { schema: GraphQLSchema, fileMatch: [**/*.graphql], uri: my-schema.graphql, }, ]);或者等拿到 schema 后再加载语言特性import { initializeMode } from monaco-graphql/initializeMode; const schemas [ { schema: GraphQLSchema, fileMatch: [operations/*.graphql], uri: my-schema.graphql, }, ]; const api initializeMode({ schemas }); // 追加另一个 schema。这会导致语言 workers 与特性重置 api.setSchemaConfig([ ...schemas, { introspectionJSON: myIntrospectionJSON, fileMatch: [specific/monaco/uri.graphql], uri: another-schema.graphql, }, ]);或者用单个 schema 整体替换这会完全重建 worker 并重置语言服务api.setSchemaConfig([ { introspectionJSON: myIntrospectionJSON, fileMatch: [**/*.graphql], uri: my-schema.graphql, }, ]);底层实现见 api.tssetSchemaConfig保存数组、按uri建索引到_schemasById并 fire_onDidChange通知 Worker 重载。7.3setModeConfiguration()用于开关 Monaco 语言特性默认全部开启完整默认值见 api.ts 的modeConfigurationDefaultmonaco.languages.graphql.api.setModeConfiguration({ documentFormattingEdits: true, completionItems: true, hovers: true, documentSymbols: true, diagnostics: true, });ModeConfiguration还支持documentRangeFormattingEdits、tokens、colors、foldingRanges、selectionRanges默认均为false。7.4setFormattingOptions()接受{ prettierConfig: prettier.Options }可传任意 Prettier 选项。它不会重载 schema 或语言特性但新的 prettier 选项立即生效该方法会覆盖先前配置且只接受可在主线程/worker 边界间传递的静态值monaco.languages.graphql.api.setFormattingOptions({ // 如果你喜欢这样 prettierOptions: { tabWidth: 2, useTabs: true }, });默认格式化配置api.ts为{ prettierConfig: { tabWidth: 2 } }注释给出的理由是可达性tabs vs spaces 的可访问性讨论。7.5setExternalFragmentDefinitions()追加外部 Fragment供补全与其他语言特性使用。接受包含 fragment 定义的字符串或FragmentDefinitionNode[]api.ts。7.6setDiagnosticSettings()即第 5 节所用方法monaco.languages.graphql.api.setDiagnosticSettings({ validateVariablesJson: { // 把 operation model 与 variables 编辑器配对…… [operationModel.uri.toString()]: [variablesModel.uri.toString()], }, jsonDiagnosticSettings: { allowComments: true, // 允许 json请求时用 jsonc parser 解析 }, });7.7setCompletionSettings()控制补全行为api.ts 与 typings/index.tsMonacoGraphQLAPI.setCompletionSettings({ // 自动填充 NonNull 叶子字段 __experimental__fillLeafsOnComplete: true, });注意__experimental__fillLeafsOnComplete已标记为deprecated建议改用等价的fillLeafsOnCompletegetter 中两者做了兼容合并见 api.ts默认值为false。8. 打包器集成Webpack 与 Vite8.1 Webpack参见完整示例 examples/monaco-graphql-webpack 的 webpack 配置了解如何与官方monaco-editor-webpack-plugin协作也许有更简单的方式配置worker-loader。注意额外特性请在 webpack 插件features中指定或直接 import若想加入typescript语言webpack 插件存在已知 bugmonaco-editor issue #2738可参考 examples/monaco-graphql-nextjs 的 workaround不要指定languages: [typescript]或javascript。8.2 Vite可参照vite-plugin-monaco-editor配置加载monaco-editor的 json 模式与语言 worker。注意vite 插件只允许指定languageWorkers其他编辑器特性与语言模式需手动 import参见 vite 示例中如何加入 typescript 支持。9. 自定义 Web Worker把非静态配置传进 Worker默认 Worker 只能接收静态配置主线程/worker 跨运行时传递的限制。若要传入自定义 parser、校验规则等LanguageServiceConfig选项如schemaLoader需要自建 worker 并把自定义配置放进createData// my-graphql.worker.ts import type * as monaco from monaco-editor; import type { ICreateData } from monaco-graphql; // ts-expect-error -- 忽略缺失类型 import { initialize } from monaco-editor/esm/vs/editor/editor.worker; import { GraphQLWorker } from monaco-graphql/esm/GraphQLWorker; import { GraphQLError, ValidationRule } from graphql; const RequireOperationNameRule: ValidationRule context { return { OperationDefinition(node) { if (node.name) { return; } context.reportError( new GraphQLError(Oops, all operations must be named., { nodes: [node], }), ); }, }; }; globalThis.onmessage () { initialize((ctx: monaco.worker.IWorkerContext, createData: ICreateData) { createData.languageConfig.customValidationRules [ RequireOperationNameRule, ]; return new GraphQLWorker(ctx, createData); }); };应用侧Vite 查询后缀语法import EditorWorker from monaco-editor/esm/vs/editor/editor.worker?worker; import GraphQLWorker from ./my-graphql.worker?worker; globalThis.MonacoEnvironment { getWorker(_workerId: string, label: string) { return label graphql ? new GraphQLWorker() : new EditorWorker(); }, };或 webpack 场景globalThis.MonacoEnvironment { getWorkerUrl(_workerId: string, label: string) { return label graphql ? my-graphql.worker.js : editor.worker.js; }, };vite 中也可用插件注册自定义 workerimport { defineConfig } from vite; import monacoEditorPlugin from vite-plugin-monaco-editor; export default defineConfig({ plugins: [ monacoEditorPlugin({ customWorker: [ { label: graphql, entry: my-graphql.worker.js, }, ], }), ], });GraphQLLanguageConfig允许的自定义点typings/index.ts包括parser、parseOptions、schemaLoader、schemas、externalFragmentDefinitions、customValidationRules、completionSettings、fillLeafsOnComplete、experimentalFragmentArguments。ICreateData则携带languageId、formattingOptions、languageConfig与diagnosticSettings。10. Web 框架集成建议纯 JavaScript 的 webpack 示例 是很好的起点它演示了创建 operation / variables / schema / results 四个模型与编辑器、使用monaco-graphql/monaco-editor导入、Uri.file(/1/operation.graphql)这类文件 URI 与语言 ID 的配合。针对 Reactuse-monaco看起来支持我们所需的自定义语言 worker 配置且构建质量不错自行加载时应在didMount的useEffect中动态 import 模式并/或自行实例化避免破坏 SSR其他库可采用与示例 editors.ts 类似的策略也可提供MonacoEnvironment.getWorkerUrl它更适合以异步 import 方式加载预构建的 worker 文件。11. Monaco 编辑器注意事项如果你熟悉 Codemirror/Atom 时代的术语与特性以下差异值得留意README 原文hinting 在 LSP 术语中叫code completion代码补全linting 在 LSP 术语中叫diagnostics诊断默认按键映射不同更接近 vscode命令面板与右键上下文菜单很重要可以在标准补全、linting 等之上继续扩展例如使用editor.setModelMarkers()。12. 避免打包全部monaco-editor语言导入monaco-editor会静默引入 83 种内置语言typescript、html、css、json 等。而monaco-graphql只需要graphql与json两种。monaco-graphql1.3.0及以后可将所有monaco-editor导入替换为monaco-graphql/monaco-editor以提升性能、只加载这两种语言-import { ... } from monaco-editor import { ... } from monaco-graphql/monaco-editor其实现见 packages/monaco-graphql/src/monaco-editor.ts只 importbasic-languages/graphql与language/json的 contribution其余全部从edcore.main.js导出——typescript、css、html 等都被剔除。monaco-graphql/esm/*导入模式已废弃仅为向后兼容保留通配符导出计划在下一大版本移除。新代码应使用规范子路径monaco-graphql/monaco-editor、monaco-graphql/initializeMode、monaco-graphql/graphql.worker、monaco-graphql/lite这些子路径均在 package.json 的exports中声明。12.1 用 ESLint 拦截误导入通过no-restricted-importsJS 项目或typescript-eslint/no-restricted-importsTS 项目防止误导入monaco-editor{ rules: { // 或 typescript-eslint/no-restricted-imports no-restricted-imports: [ error, { name: monaco-editor, message: monaco-editor imports all languages; use monaco-graphql/monaco-editor instead to import only json and graphql languages, }, ], }, }13. 灵感来源与路线图README 说明microsoft/monaco-json从一开始就是灵感来源当时还是独立仓库项目作者几乎原样拷贝了其中大量文件可以说近乎是它的一个 fork。TODO 状态README 原文variables JSON 校验variables 补全Symbols 与 Definitions文件 uri 驱动的 schema 加载operation ↔ schema 与 schema → schema 引用字段与参数补全的insertText14. 更多示例与参考仓库内可继续深挖的路径完整工程示例monaco-graphql-webpack、monaco-graphql-viteReact含 variables (C)JSON 支持的最小示例、monaco-graphql-nextjs测试用例GraphQLWorker.test.ts、monaco-editor.test.ts 可验证 worker 与编辑器集成行为依赖的底层 LSP 包graphql-language-service语言服务核心、graphql-language-service-serverLSP Server变更记录packages/monaco-graphql/CHANGELOG.md掌握以上 API 与配置后你就能在任意前端栈中构建出带 Schema 驱动补全、校验、格式化与 Variables JSON 支持的 GraphQL 编辑器甚至进一步自定义 worker 与语言服务规则向完整 IDE 体验迈进。【免费下载链接】graphiqlGraphiQL the GraphQL LSP Reference Ecosystem for building browser IDE tools.项目地址: https://gitcode.com/GitHub_Trending/gr/graphiql创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表