Skip to content

Replace GraphiQL+Monaco with a cm6-graphql editor (prototype)#11616

Merged
nbudin merged 20 commits into
mainfrom
cm6-graphql-editor
Jun 4, 2026
Merged

Replace GraphiQL+Monaco with a cm6-graphql editor (prototype)#11616
nbudin merged 20 commits into
mainfrom
cm6-graphql-editor

Conversation

@nbudin
Copy link
Copy Markdown
Contributor

@nbudin nbudin commented Jun 4, 2026

Purpose

GraphiQL's dependency on @graphiql/react 0.37+ pulls in monaco-editor as a static import, and Rolldown's chunk optimizer co-locates Monaco with the modulepreload polyfill in an eagerly-loaded shared chunk. The result is ~2.4 MB of Monaco downloaded on every page load — for a feature only CMS admins use.

This replaces the GraphiQL component in the CMS GraphQL query admin with a lightweight CodeMirror-based editor. The CMS admin still gets:

  • GraphQL syntax highlighting
  • Schema-aware autocompletion (schema fetched once via introspection on first render)
  • Inline validation
  • JSON syntax highlighting in the variables panel
  • A read-only JSON response panel
  • Two-column layout mirroring GraphiQL's general feel

What's gone: the schema docs explorer. We decided that was an acceptable tradeoff given how rarely CMS admins need it for this specific use case.

Changes

💻 Engineer-facing

  • New GraphQLQueryEditor.tsx — three CodeMirror instances (query, variables, response) plus fetch-based execution; all peer deps were already in the bundle
  • cm6-graphql, @codemirror/autocomplete, and @codemirror/lint added (~242 KB new code, all lazily loaded)
  • graphiql and @graphiql/toolkit no longer imported anywhere in the CMS admin (both packages still present in package.json for DevModeGraphiql.tsx)
  • Monaco is completely absent from the production build

Risks

This is a prototype. The GraphiQL replacement covers the happy path but hasn't been tested under edge cases (network errors during query execution, malformed variables JSON, etc.). Worth a closer look before merging to main.

🚢

🤖 Generated with Claude Code

nbudin and others added 12 commits June 4, 2026 09:45
…ype)

GraphiQL uses @graphiql/react 0.37.4 which has static imports of
monaco-editor utilities, causing ~2.4MB of Monaco to be eagerly
downloaded on every page load due to Rolldown's chunk optimization.

This replaces the GraphiQL component in the CMS GraphQL query admin
with a lightweight CodeMirror-based editor:
- cm6-graphql provides GraphQL syntax highlighting, schema-aware
  autocompletion, and inline validation
- Schema is fetched once via introspection on first render
- Query execution is handled with a simple fetch
- All peer deps (CodeMirror 6, graphql-language-service) were already
  present in the bundle

Result: Monaco is completely absent from the production build.
GraphQLQueryEditor loads lazily at 84KB only when navigating to
the CMS GraphQL query admin pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…idth

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…raphiql/toolkit

graphiql and @graphiql/toolkit are no longer used anywhere. DevModeGraphiql
now uses the same CodeMirror-based editor as the CMS admin, with
useLayoutEffect to ensure tokens are set before the introspection fetch fires.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The graphiql layout was hardcoding /packs/dev-mode-graphiql.js (the
production output path). In dev mode, Vite serves entries at their
source path, so the JS was 404ing. Added dev_mode_graphiql_entry_path
helper matching the application_entry_path pattern.

Also removed the stylesheet_link_tag for dev-mode-graphiql.css — there
are no CSS imports left in DevModeGraphiql.tsx — and deleted the now-
orphaned dev-mode-graphiql.scss file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this, @vitejs/plugin-react-swc throws "can't detect preamble"
when component files (like GraphQLQueryEditor.tsx) attempt to use
React Refresh hooks. Mirrors what cdn_spa_shell.html.erb already does.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Silent render errors leave a blank screen. ErrorBoundary will display
the error message and stack trace instead so we can see what's wrong.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Blank screen was a Vite module cache issue after rapid changes — hard
reload resolves it. ErrorBoundary stays since it makes render errors
visible rather than silently blanking.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this, the /graphiql dev page has no Bootstrap or app styles.
Vite injects CSS imports as <style> tags in dev mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
if (this.state.error) {
return (
<div style={{ padding: '1rem', fontFamily: 'monospace', whiteSpace: 'pre-wrap', color: 'red' }}>
<strong>Error rendering GraphQL editor:</strong>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [eslint] <i18next/no-literal-string> reported by reviewdog 🐶
disallow literal string: Error rendering GraphQL editor:

nbudin and others added 8 commits June 4, 2026 10:28
CodeMirror 6 defaults to content height. Adding EditorView.theme with
height: 100% and overflow: auto on the scroller makes them fill whatever
container they're placed in. Variables editor keeps its line-count height.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- "Query" label above the query editor
- aria-labelledby on both editors' content areas via EditorView.contentAttributes
- Run query button moved to a centered strip between the two panels

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
flex-grow: 1 shares remaining space but not from a common baseline.
flex: 1 1 0 sets flex-basis to 0 so both columns grow from nothing and
end up exactly equal regardless of content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds cms.graphqlQueries.editor.* keys to en.json and uses useTranslation
to render them, removing all i18next/no-literal-string suppressions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Visits the convention root page, waits for the React app to mount, then
samples PerformanceResourceTiming to sum the compressed transfer sizes of
all eagerly-loaded JS and CSS. Fails if the total exceeds 700 KB.

Without a large dependency like Monaco in the initial bundle the payload
sits well under 500 KB compressed; with Monaco it exceeds 900 KB. The
threshold is set between those two states with headroom on each side.

The minitest-system CI job already runs yarn build before test:system, so
the test measures real production-minified assets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 4, 2026

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
app/graphql/concerns/cms_parent_implementation.rb 🔴 40.54% 🔴 43.24% 🟢 2.7%
app/graphql/sources/cms_page_content.rb 🔴 0% 🟢 100% 🟢 100%
app/graphql/types/page_type.rb 🟢 88.89% 🟢 100% 🟢 11.11%
app/helpers/application_helper.rb 🟢 85.19% 🟢 86.21% 🟢 1.02%
app/javascript/BuiltInFormControls/AddFileModal.tsx 🔴 0% 🔴 33.33% 🟢 33.33%
app/javascript/BuiltInFormControls/LiquidInput.tsx 🟠 57.5% 🟠 60% 🟢 2.5%
app/policies/application_policy.rb 🟢 90.91% 🟢 93.94% 🟢 3.03%
app/services/cms_content_finder.rb 🟢 80.77% 🟢 84.62% 🟢 3.85%
test/system/page_weight_test.rb 🔴 0% 🟢 96.3% 🟢 96.3%
Overall Coverage 🟢 52.78% 🟢 52.83% 🟢 0.05%

Minimum allowed coverage is 0%, this run produced 52.83%

@nbudin nbudin marked this pull request as ready for review June 4, 2026 19:23
@nbudin nbudin merged commit 887fd48 into main Jun 4, 2026
25 of 26 checks passed
@nbudin nbudin deleted the cm6-graphql-editor branch June 4, 2026 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant