ruru 2.x 演进全解析:Grafast 版 GraphiQL 从单体 HTML 到 Monaco 编辑器架构的重构之路

发布时间:2026/9/23 5:14:09
ruru 2.x 演进全解析:Grafast 版 GraphiQL 从单体 HTML 到 Monaco 编辑器架构的重构之路 ruru 2.x 演进全解析Grafast 版 GraphiQL 从单体 HTML 到 Monaco 编辑器架构的重构之路【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址: https://gitcode.com/gh_mirrors/cry/crystal导读本文以 grafast/ruru/CHANGELOG.md 为主线系统梳理 ruru——Graphile Crystal 单仓库中面向 Grafast 的 GraphiQL 发行版——从 2.0.0-0.0 到 2.0.1 的完整演进脉络并结合 src、scripts 与 Grafserv 集成源码还原每一次破坏性变更背后的架构决策。读完本文你将掌握 ruru 的 CLI 与服务器模式配置、htmlParts定制体系、静态资源服务原理、Grafserv 集成方式以及它在 Monaco 编辑器、plan diagram、WebSocket 订阅等能力上的实现细节。一、ruru 是什么Grafast 风味的 GraphiQL 发行版ruru 是 Graphile Crystal 单仓库Graphile 系列核心包Grafast、PostGraphile、pg-introspection、pg-sql2 等的家园中的一个独立包定位在 README.md 中写得很明确A distribution of GraphiQL thats easy to use and embeds both the popular GraphiQL Explorer plugin and Grafast-related tools such as viewing of plan diagrams and explaining executed operations. (Ruru can be used without Grafast.)也就是说它基于 GraphiQL 二次封装默认内置 GraphiQL Explorer 文档探索插件并集成 Grafast 专属工具——执行计划图plan diagram查看与操作 Explain。ruru 可以脱离 Grafast 独立使用仅作为普通 GraphQL IDE。从 package.json 可以确认其工程定位包名为rurudescription为 Grafast-flavoured GraphiQL distributiontype为commonjs通过 exports 字段暴露四个子路径ruru根入口、ruru/server、ruru/static、ruru/cli根入口dist/index.js实际上只是一个错误提示见 src/index.tsYou might have meant to import from ruru/server, or perhaps youre after the ruru-components package?——这提醒开发者面向服务端的使用应导入ruru/serverbin指向./dist/cli-run.js即npx ruru命令的入口运行环境要求 Node.js 22engines字段peerDependencies 包含graphile-config、graphql ^16.9.0以及可选的react/react-dom^18 || ^19发布文件清单files为ruru.html、dist、static——这正好对应 CHANGELOG 中反复出现的三种形态可自托管的静态 HTML、服务端代码、静态资源。二、四种使用方式总览按 README.mdruru 提供四种灵活的使用形态CLI临时或安装后运行npx ruru立即起一个 IDE 服务Node.js 中间件通过ruru/server在任意 JS 服务端框架中挂载静态 HTML 文件构建产物ruru.html可托管或自托管嵌入将 Ruru 渲染进你已有的 HTML 页面。其中 CLI 的体验入口如 README 所示npx ruru -SPe https://countries.trevorblades.com/graphql-S开启订阅支持、-P代理 GraphQL 请求这两个开关不是该示例 API 所必需的但通常是你自己的 API 需要的。CLI 的构建与发布流程可在 package.json 的scripts中看到build依次执行 webpack 打包生产模式、tsc -b编译、生成dist/cli-run.js最后通过make-ruru-html脚本生成静态 HTML并同步写入website/static/myruru/index.html供官网演示使用。三、CLI 的完整参数体系与配置加载CLI 的全部参数定义在 src/cli.ts 中经graphile-config/cli的runCli(options, run)装配见 src/cli-run.ts参数别名类型说明--endpoint-estringquery 与 mutation 操作的 GraphQL 端点--port-pnumber服务器监听端口--proxy-Pboolean代理请求以绕过 CORS 问题--subscriptions-Sboolean开启订阅将--endpoint转换为ws://URL--subscription-endpoint-sstring订阅操作专用端点覆盖-S--config-Cstringgraphile.config.mjs或类似配置文件路径配置加载是 2.0.1 版本的核心变更点CLI 现在能正确自动导入graphile.config.mts文件此前只有graphile.config.ts生效.mts会被忽略。CHANGELOG 同时给出官方建议鉴于当前主流 Node.js 大版本都已原生支持类型剥离type stripping与require(esm)官方推荐将配置文件迁移为 TypeScriptESM 仅可擦除语法。实现上run()通过loadConfig(configFileLocation)读取用户 preset再与命令行参数合成的ruru配置经resolvePreset()合并见 src/cli.ts命令行参数的优先级高于配置文件。run()中的默认值值得记录见 src/cli.tsport默认1337endpoint默认http://localhost:5678/graphqlsubscriptions默认false静态资源路径默认/ruru-static/常量DEFAULT_STATIC_PATH。代理行为src/cli.ts当启用-P时CLI 会尝试动态加载http-proxy模块tryLoadHttpProxyCreateProxyServer以endpoint为 target、ws: true、changeOrigin: true创建代理若加载失败会抛出明确错误提示安装http-proxy。代理模式下一个细节是HTTP 页面请求走proxy.web而 WebSocket 升级通过server.on(upgrade)转发到endpointWsBase将 http 协议自动换算为 ws/wss。若未启用代理非首页请求会返回308重定向到/。四、服务器模式ruruHTML、makeHTMLParts 与配置结构服务器模式的核心是ruru/server子路径实现在 src/server.ts。4.1 两个核心函数makeHTMLParts(config)根据服务端配置生成完整的 HTML 部件集合RuruHTMLParts默认部件包括 meta 标签、标题、样式、RURU_CONFIG注入脚本、Monaco worker 环境初始化脚本、body 骨架含#ruru-root挂载点与启动脚本ruruHTML(config)将makeHTMLParts的结果拼装为完整 HTML 文档返回。它还保留了一个已弃用的第二参数deprecatedHTMLParts用于向后兼容2.0.0-beta.27 引入的兼容策略。4.2 RuruHTMLParts 的八个部件在 src/interfaces.ts 中定义开发者可通过htmlParts逐块定制部件默认内容定制用途metaTagsmeta charsetutf-8 /与ruru.js的 modulepreload追加 viewport、SEO 等 metatitleTagtitleRuru - GraphQL/Grafast IDE/title自定义页面标题styleTagsruru.css链接 基础布局样式换肤、自定义样式configScript注入RURU_CONFIG序列化的 clientConfig修改下发到客户端的配置headerScripts配置 MonacoMonacoEnvironment.getWorker预加载 prettier、加载远程 workerbodyContent含#ruru-root的加载骨架自定义挂载结构必须保留挂载点bodyScriptsRuru 打包脚本替换为自己的 bundle 以优化缓存bodyInitScriptcreateRoot(...).render(...)启动代码自定义启动时机htmlParts的每个键既可以是字符串也可以是回调函数(original, clientConfig, serverConfig, htmlParts, key) string回调拿到原始部件与完整配置后可做任意变换见 src/server.ts。4.3 迁移示例从 defaultHTMLParts 到 htmlParts 回调2.0.0-beta.25 是 ruru 的重建大版本defaultHTMLParts被移除改为config.htmlPartsGraphile Config 用户为preset.ruru.htmlParts且条目支持回调以减少样板代码。CHANGELOG 给出了官方迁移 diff-import { defaultHTMLParts } from ruru/server; const config { htmlParts: { - metaTags: defaultHTMLParts.metaTags !-- local override --, metaTags: (base) base !-- local override --, } }亦可改用makeHTMLParts(config)后自行修改结果。4.4 早期形态的配置与插件定制早在 2.0.0-alpha.2ruru 就支持通过 preset 或插件定制 HTML。preset 方式import { defaultHTMLParts } from ruru/server; const preset: GraphileConfig.Preset { //... ruru: { htmlParts: { titleTag: titleGraphiQL with Grafast support - Ruru!/title, metaTags: defaultHTMLParts.metaTags meta nameviewport contentwidthdevice-width, initial-scale1 /, }, }, };插件方式可以做到按请求按用户粒度定制并通过extra.request读取请求详情const RuruMetaPlugin: GraphileConfig.Plugin { name: RuruMetaPlugin, version: 0.0.0, grafserv: { hooks: { ruruHTMLParts(_info, parts, extra) { // extra.request gives you access to request details, so you can customize parts for the user parts.metaTags meta nameviewport contentwidthdevice-width, initial-scale1 /; }, }, }, };需要说明的是这一ruruHTMLPartshook 在 2.0.0-beta.25 中被重命名为ruruHTML语义从产出部件变为包裹整个 HTML 生成详见本文第六节。4.5 clientConfig显式区分发给客户端的配置2.0.0-beta.25 新增RuruConfig.clientConfig用于显式声明要序列化并发送到浏览器的 props。同时RuruServerConfig顶层的历史配置项editorTheme、debugTools、eventSourceInit被标记为弃用deprecated应迁移到clientConfig内从而覆盖更多 props见 src/server.ts。迁移示例const config { endpoint: /graphql, clientConfig: { editorTheme: dark, }, }在makeHTMLParts中可以看到这三项 legacy props 会被烘焙进BakedRuruClientConfigeditorTheme、debugTools、eventSourceInit再与用户clientConfig及服务端注入的staticPath、endpoint、subscriptionEndpoint合并后序列化为RURU_CONFIG见 src/server.ts。4.6 RuruConfig 完整字段RuruConfig定义于 src/interfaces.ts汇总如下staticPath静态资源根目录 URL必须以/结尾服务端模式默认https://unpkg.com/ruru${version}/static/CLI 默认/ruru-static/因为 CLI 自托管文件portCLI 监听端口endpointGraphQL 端点http/httpssubscriptionEndpoint订阅端点ws/wsssubscriptions是否开启订阅CLI-SenableProxy是否开启代理CLI-PhtmlParts上述八个 HTML 部件的字符串或回调覆盖clientConfig下发客户端的RuruClientConfig。RuruClientConfig从RuruProps定义于 ruru-types选取了一组白名单 props包括editorTheme、defaultTheme、forcedTheme、initialHeaders、defaultHeaders、defaultQuery、initialQuery、initialVariables、responseTooltip、maxHistoryLength、schemaDescription、inputValueDeprecation、showPersistHeadersSettings、isHeadersEditorEnabled、className、debugTools、eventSourceInit等而ruru-types中的RuruProps还额外覆盖onEditQuery/onEditVariables/onEditHeaders、defaultEditorToolsVisibility、confirmCloseTab、fetcher等 GraphiQL 透传能力。2.0.0-beta.5 起 ruru 拆分出独立的ruru-types包只保留类型定义比ruru-components更轻量避免客户端引入不必要的运行时依赖。五、静态资源服务ruru/static 的底层原理由于 ruru 自 2.0.0-beta.25 起不再以单 HTML 文件形态分发Monaco 编辑器依赖 worker 文件静态资源服务成为必需。ruru/static子路径实现在 src/static.ts提供两个核心 APIgetStaticFile({ staticPath, urlPath, acceptEncoding, disallowDevAssets })按 URL 路径查找静态文件返回文件内容与响应头serveStatic(staticPath)返回一个兼容 Node、Connect、Express 的中间件函数内部先剥离staticPath前缀再查文件。实现要点均有源码依据内联打包 内存缓存静态文件bundleCode.ts与源码 mapbundleMeta.ts通过 webpack 内联生成createStaticFileLoader以惰性 Promise 缓存加载加载 bundle 代码约增 ~4MB 内存加载 source maps 约增 ~10MB源码注释明示Brotli 预压缩文件以 brotli 压缩后的 Buffer 存储响应头含content-encoding: br若客户端Accept-Encoding不含br则即时解压并修正content-lengthETag 协商缓存serveStatic用if-none-match与etag比对命中返回304 Not Modified远程 worker 加载技巧在 src/server.ts 中若staticPath以/开头本地路径worker 通过new URL(staticPath file, import.meta.url)加载若是远程 URL如 unpkg则用URL.createObjectURL(new Blob([import ...], ...))包一层 import 文件来规避跨域安全限制——这正是 2.0.0-beta.26 修复从远程 URL 加载 worker 脚本的落地实现内容类型白名单MIME_TYPES仅覆盖txt/ts/js/ttf/map/css五种扩展名未知扩展名会抛错。六、Grafserv 集成GraphiQL 处理器与 ruruHTML 中间件ruru 在 Graphile 生态中的典型宿主是 GrafservGrafast 的 HTTP 服务层。集成代码位于 grafast/grafserv/src/middleware/graphiql.ts这里有两个关键事实按需加载Grafserv 用import(ruru/server)与import(ruru/static)做惰性加载loadRuruServer/loadRuruStatic只有访问到 GraphiQL 相关路由才会真正拉起 ruru 模块配置映射makeGraphiQLHandler将 preset 中的resolvedPreset.ruru展开把staticPath覆盖为 Grafserv 的dynamicOptions.graphiqlStaticPathendpoint设为graphqlPath并将dynamicOptions.explain映射为clientConfig.debugToolsexplain true时下发[explain, plan]false时下发[]否则透传数组见 graphiql.ts——这印证了 2.0.0-0.6 默认开启 explain 的行为debug 工具SQL explain 输出与 plan 图是 Grafast 排障的核心能力。ruruHTML 中间件Grafserv 的 middleware 体系把 ruruHTML 作为中间件事件运行事件对象携带{ htmlParts, request }等上下文。2.0.0-beta.25 将plugin.grafserv.middleware.ruruHTMLParts重命名为ruruHTML并明确生成 HTML 的包裹逻辑官方迁移 diffconst plugin { grafserv: { middleware: { - ruruHTMLParts(next, event) { ruruHTML(next, event) { const { htmlParts, request } event; htmlParts.titleTag title${escapeHTML( Ruru | request.getHeader(host), )}/title; return next(); }, }, }, };注意规则调用next()必须是函数最后一行通过request.getHeader(host)可在服务端拿到请求信息实现按请求定制。此外Grafserv 还会对 GraphiQL HTML 做 brotli 压缩质量级别 5源码注释给出了各级别的耗时与体积权衡并在Accept-Encoding含br时返回content-encoding: br的原始响应见 graphiql.ts。七、客户端能力演进从编辑器到调试工具结合 CHANGELOG 各版本与 ruru-typesruru 客户端能力经历了清晰的分阶段升级编辑器内核VSCode 同款体验2.0.0-beta.24升级到 GraphiQL v4样式路径graphiql/graphiql.css→graphiql/style.css升级 React 19、graphql-ws v62.0.0-beta.25重建——迁移到 GraphiQL v5 Monaco 编辑器与 VSCode 同源获得熟悉的快捷键与更多特性如按 F1 呼出命令面板、可在变量 JSON 中写注释prettier 与 mermaid 改为按需加载并支持离线工作所有编辑器不仅是 GraphQL 编辑器都会被格式化格式化后光标位置保持不变Ctrl-Shift-P/Meta-Shift-P/Cmd-Shift-P唤起;2.0.0-beta.28新增condensed紧凑模式且默认开启可在编辑器视图的设置齿轮中取消勾选2.0.0-beta.23升级 Mermaid 11plan diagram 中的多态渲染更精炼。Grafast 调试工具plan diagram计划图是 ruru 的招牌能力2.0.0-beta.14 重构了一元步骤unary steps在计划图中的渲染修复副作用步骤的显示同版本还将隐式副作用渲染为依赖边2.0.0-alpha.3 增加将 mermaid plan diagram 导出为 SVG 下载的能力explain 输出2.0.0-0.6 起默认启用 explain2.0.0-beta.23 修复增量投递结果中 explain 输出未隐藏的问题2.0.0-beta.11支持已弃用参数deprecated arguments的展示并修复 Explorer 插件中输入字符被覆盖的问题2.0.0-0.10为 ruru 增加 WebSocket 订阅支持后随 beta.24 升级到 graphql-ws v62.0.0-beta.17修复 EventSource 断连导致的白屏之死改为优雅错误处理并允许覆盖 EventSource 配置eventSourceInit支持reconnectInterval、maxReconnectAttempts等实现级扩展2.0.0-rc.4ruru 新增将 schema 导出为 SDLGraphQL Schema Definition Language带选项的能力。状态注入与回调2.0.0-beta.6可通过插件系统设置初始 query 与 variables例如根据 query string 初始化2.0.0-beta.7可透传onEdit回调如将当前编辑器状态同步到 URL search params2.0.0-beta.26加载态loading state匹配当前主题浅色/深色2.0.0-beta.29修复折叠模式下侧边栏边框。主题体系2.0.0-beta.25新增defaultTheme与forcedThemeprops透传给 GraphiQL与既有editorTheme一起构成三级主题控制默认主题 / 强制主题 / 编辑器主题加载骨架脚本会读取localStorage中的graphiql:theme否则跟随系统prefers-color-scheme见 src/server.ts。健壮性修复2.0.0-beta.22升级 GraphiQL 并修复重复模块问题2.0.0-rc.4修复 WebSocket 意外断开时输出{isTrusted: true}假错误的问题2.0.0-rc.6消除悬空 Promisedangling promises降低因未处理的 Promise rejection 导致进程退出的概率2.0.0-0.1修复暗色模式下文字颜色。八、工程化与兼容性演进从 CHANGELOG 可以还原 ruru 的工程演进路线模块体系2.0.0-alpha.2由 ESM 转为CommonJS模块解决与下游 CommonJS 工程的兼容问题2.0.0-beta.3模块转为 ESM 以兼容 GraphiQL并迁移到 React 182.0.0-beta.24升级 React 192.0.0-rc.5统一类型导出语法启用 TypeScript 的rewriteRelativeImportExtensions与erasableSyntaxOnly源码中直接使用.ts扩展名导入并引入ruru-types轻量类型包2.0.0-0.3曾因 npm 无限循环 bugnpm/cli#5322临时降级到 React 16后续版本随上游修复持续升级。依赖治理2.0.0-alpha.7 / beta.2 / beta.24反复调整 peerDependencies 与 dependencies 的边界目标是消除 duplicate modules 错误2.0.0-beta.30移除 peer dependency 的可选性以满足 pnpm 的安装算法beta.25 则将所有peerDependenciesdependencies的模块标记为 optional peerDependencies2.0.0-rc.7清理 package.json对 peer dependencies 使用固定标识符除非它们同时也是显式 dependencies并转向 trusted publishing2.0.0-rc.4TypeScript 配置升级为支持 Node 22 最低版本并明确 Node v22 is required for this module2.0.0-1.1TypeScript v5 成为必需同版本增加 SQL 别名SQL aliases格式化支持2.0.0-beta.2 曾修复 SQL 别名检测问题2.0.0-beta.20ruru/server不再通过fs从磁盘读取 data/version改为源码内联方便他人对 ruru 做打包bundling。配置能力2.0.0-alpha.2Ruru CLI 支持从graphile.config.ts读取选项2.0.1 扩展为正确加载graphile.config.mts2.0.0-0.13修复 header 保存Fix header saving问题2.0.0-beta.31更新graphql版本范围至 ^16。渲染与稳定性2.0.0-0.6修复 fetcher 修改不可变对象的问题修复 Grafast 官网 playground2.0.0-rc.3修复 Ruru bundling 的 bug2.0.0-beta.23修复增量投递结果中 explain 输出未隐藏问题2.0.0-beta.28 前的 beta.27改进ruruHTML向后兼容性显式弃用htmlParts参数。九、版本里程碑速览从 CHANGELOG.md 可提炼出以下关键里程碑版本里程碑意义2.0.0-0.0首个 changesets 发布Major Changes2.0.0-alpha.2转为 CommonJSCLI 读取graphile.config.ts引入htmlPartspreset/插件定制2.0.0-0.10支持 WebSocket 订阅2.0.0-beta.25大版本重建迁移 GraphiQL v5 Monacoruru/static拆分clientConfig引入ruruHTMLParts→ruruHTML2.0.0-rc.4明确 Node 22schema 导出 SDL修复 websocket 假错误2.0.0与 2.0.0-rc.7 完全一致Identical to 2.0.0-rc.72.0.1CLI 支持graphile.config.mts推荐迁移 TS ESM 配置十、小结从单体 HTML 文件到CLI server 中间件 静态资源服务 可自托管 HTML的多形态分发从 React 16/18/19 到 Monaco 编辑器内核ruru 的每一次破坏性变更都在 CHANGELOG 中留下了可执行的迁移指南与 diff 示例。对使用者而言理解本文梳理的RuruConfig配置面、htmlParts八部件定制、clientConfig客户端下发边界、ruru/static的 brotli/ETag 服务机制以及 Grafserv 的ruruHTML中间件约定即可在任何 JS 服务端自由嵌入一套具备 Grafast 计划图与 explain 能力的 GraphQL IDE对想深入源码的读者src/cli.ts、src/server.ts、src/static.ts 与 grafserv/src/middleware/graphiql.ts 是继续阅读的最佳起点。【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址: https://gitcode.com/gh_mirrors/cry/crystal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考