Skip to content

feat: introduce graphql linting#2856

Open
tatomyr wants to merge 17 commits into
mainfrom
feat/lint-graphql
Open

feat: introduce graphql linting#2856
tatomyr wants to merge 17 commits into
mainfrom
feat/lint-graphql

Conversation

@tatomyr

@tatomyr tatomyr commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

What/Why/How?

Added experimental graphql linting.

Reference

Testing

Screenshots (optional)

Check yourself

  • This PR follows the contributing guide
  • All new/updated code is covered by tests
  • Core code changed? - Tested with other Redocly products (internal contributions only)
  • New package installed? - Tested in different environments (browser/node)
  • Documentation update has been considered

Security

  • The security impact of the change has been considered
  • Code follows company security practices and guidelines

Note

Medium Risk
Introduces a new core lint path and graphql dependency, but GraphQL files are gated by extension and do not alter existing OpenAPI/AsyncAPI lint behavior.

Overview
Adds experimental lint support for GraphQL SDL (.graphql / .gql) in redocly lint, documented in a new guide and linked from the lint command docs.

.graphql / .gql files are detected by extension, kept as raw SDL during resolve (not YAML-parsed), and linted through a dedicated path that uses graphql to parse and walk the AST. OpenAPI-style walking and bundle $ref handling are skipped for these files.

Configuration gains a graphql spec version and graphqlRules (merged with shared rules except no-unresolved-refs). Built-in GraphQL rules include struct (syntax + schema validity), no-unused-types, and type-description, with severities wired into existing presets. Plugins may expose rules.graphql.

Configurable rule/* assertions now accept GraphQL AST kinds as subject.type (via GraphqlAssertions), including where filtering. Config lint suggests valid GraphQL kinds on typos and shortens invalid subject.type errors with a documentation link.

CLI ruleset-empty checks include GraphQL (and OpenRPC) rules. Minor struct enum validation messages link to docs when the allowed-value list is large.

Reviewed by Cursor Bugbot for commit 73e2aff. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Jun 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 73e2aff

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@redocly/openapi-core Minor
@redocly/cli Minor
@redocly/respect-core Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@tatomyr tatomyr self-assigned this Jun 5, 2026
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 81.68% (🎯 81%) 7578 / 9277
🔵 Statements 81.03% (🎯 80%) 7885 / 9730
🔵 Functions 84.94% (🎯 84%) 1524 / 1794
🔵 Branches 73.18% (🎯 72%) 5115 / 6989
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/cli/src/utils/miscellaneous.ts 61.67% 58.2% 78.04% 60.97% 75, 140, 197-247, 273-274, 284-285, 300, 304, 307, 313, 317-320, 328-361, 372, 397, 406, 414-452, 567, 576
packages/core/src/detect-spec.ts 95.34% 95.31% 100% 95.34% 32, 40
packages/core/src/lint.ts 91.89% 85.71% 100% 91.66% 108-119, 142
packages/core/src/oas-types.ts 100% 100% 100% 100%
packages/core/src/resolve.ts 95.37% 94.69% 100% 95.23% 93, 140, 284, 459-467
packages/core/src/bundle/bundle-visitor.ts 72.72% 68.87% 100% 72.72% 37, 41-49, 60-64, 71, 80, 88, 93-118, 188-200, 217-218, 236-237, 251-252, 285
packages/core/src/config/all.ts 100% 100% 100% 100%
packages/core/src/config/builtIn.ts 100% 100% 100% 100%
packages/core/src/config/config-resolvers.ts 76.21% 60.98% 94.11% 77.01% 73, 101-104, 154, 193, 201, 257, 268, 277, 290, 300, 303-307, 312-321, 345-347, 357, 360, 363, 366, 369, 372, 385-387, 391, 397, 400, 403-406, 409-412, 415-418, 432-434, 441, 444, 447, 450, 453, 456, 481-483, 488-494
packages/core/src/config/config.ts 62.96% 64.58% 75.6% 63.09% 179-209, 235, 239, 243-264, 296, 314-334, 418-448
packages/core/src/config/minimal.ts 100% 100% 100% 100%
packages/core/src/config/recommended-strict.ts 100% 100% 100% 100%
packages/core/src/config/recommended.ts 100% 100% 100% 100%
packages/core/src/config/rules.ts 100% 100% 100% 100%
packages/core/src/config/spec.ts 100% 100% 100% 100%
packages/core/src/config/utils.ts 97.4% 81.81% 100% 98.68% 35, 84-86
packages/core/src/graphql/assertions.ts 95% 87.23% 100% 98.11% 39, 42, 63-65
packages/core/src/graphql/detect-graphql.ts 100% 100% 100% 100%
packages/core/src/graphql/lint-graphql.ts 95.23% 50% 100% 95% 32
packages/core/src/graphql/visitor.ts 92% 81.48% 100% 100% 54, 111
packages/core/src/rules/utils.ts 92% 88.7% 100% 91.3% 65, 99, 109-113, 129, 140-144, 237
packages/core/src/rules/common/struct.ts 97.22% 91.78% 100% 97.01% 77-81, 188-192
packages/core/src/rules/common/assertions/utils.ts 84% 65.51% 69.23% 88.29% 50, 54, 58, 83-85, 89-91, 142, 163-165, 172, 184-192, 252, 272-273, 289
packages/core/src/rules/graphql/index.ts 100% 100% 100% 100%
packages/core/src/rules/graphql/no-unused-types.ts 100% 73.07% 100% 100%
packages/core/src/rules/graphql/struct.ts 66.66% 33.33% 75% 66.66% 18, 27, 32-42
packages/core/src/rules/graphql/type-description.ts 100% 100% 100% 100%
packages/core/src/types/redocly-yaml.ts 92.92% 84.9% 100% 92.7% 412, 444, 450, 494-501, 503, 662-667, 670-675
Generated in workflow #10274 for commit 73e2aff by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Performance Benchmark (Lower is Faster)

CLI Version Bundle Lint Check Config
cli-latest ▓ 1.00x (Fastest) ▓▓▓ 1.07x ± 0.01 ▓ 1.00x (Fastest)
cli-next ▓ 1.02x ± 0.01 ▓ 1.00x (Fastest) ▓ 1.01x ± 0

Comment thread packages/core/src/rules/graphql/type-pascal-case.ts Outdated
Comment thread .changeset/graphql-sdl-linting.md Outdated
'@redocly/cli': minor
---

Added initial support for linting GraphQL SDL schema files (`.graphql` / `.gql`).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's mark it as experimental for now

"operation-summary": "error",
},
"graphql": {
"no-empty-servers": "error",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what is this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Those are unused rules propagated from the root rules config section.

Comment thread packages/core/src/config/config.ts Outdated
Comment thread packages/core/src/lint-graphql.ts Outdated
import type { Config } from './config/index.js';
import { initRules } from './config/rules.js';
import { isGraphqlRef } from './graphql/extensions.js';
import { runGraphqlRules, type InitializedGraphqlRule } from './graphql/run.js';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

let's use dynamic import here so it won't load for everyone and slow them down

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I haven't noticed any perf degradation so far. Would you still like to have it imported dynamcally?

@tatomyr tatomyr requested a review from RomanHotsiy June 12, 2026 08:07
@tatomyr tatomyr force-pushed the feat/lint-graphql branch from 7f3750d to 7bd109c Compare June 12, 2026 08:15
@tatomyr tatomyr marked this pull request as ready for review June 12, 2026 08:15
@tatomyr tatomyr requested review from a team as code owners June 12, 2026 08:16
Comment thread packages/core/src/rules/graphql/no-unused-types.ts
Comment thread packages/core/src/graphql/visitor.ts
Comment thread packages/core/src/config/config.ts
Comment thread docs/@v2/guides/lint-graphql.md Outdated
Comment thread docs/@v2/guides/lint-graphql.md Outdated
Comment thread docs/@v2/guides/lint-graphql.md Outdated
Comment thread docs/@v2/guides/lint-graphql.md Outdated
Comment thread docs/@v2/guides/lint-graphql.md Outdated
Comment thread docs/@v2/guides/lint-graphql.md Outdated
Comment thread docs/@v2/guides/lint-graphql.md Outdated
Comment thread docs/@v2/guides/lint-graphql.md Outdated
Comment thread docs/@v2/guides/lint-graphql.md Outdated
Comment thread docs/@v2/guides/lint-graphql.md Outdated
@tatomyr tatomyr force-pushed the feat/lint-graphql branch from 94e4609 to b2ce0d4 Compare June 12, 2026 15:09
@tatomyr tatomyr force-pushed the feat/lint-graphql branch from 8723a0d to 73e2aff Compare June 16, 2026 08:18

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 73e2aff. Configure here.


function singleProperty(property: Assertion['subject']['property']): string | undefined {
return Array.isArray(property) ? property[0] : property;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Array property silently drops all but first element

Medium Severity

The singleProperty helper returns only the first element when property is an array, silently discarding the rest. Configurable rules support property as a string[], where assertions are meant to evaluate against each listed property. In the GraphQL adapter, a rule like property: [name, description] only ever checks name, and the description assertion is quietly skipped. This causes incomplete lint coverage without any warning to the user.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 73e2aff. Configure here.

return [syntaxErrorToProblem(e, source)];
}
throw e;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Syntax error path bypasses addProblemToIgnore processing

Low Severity

When a GraphQL parse error occurs, lintGraphqlDocument returns directly with the syntaxErrorToProblem result, bypassing the config.addProblemToIgnore call that all other problems go through on line 37. This is inconsistent with the normal flow and breaks the contract that all returned problems are processed through the ignore pipeline.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 73e2aff. Configure here.

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.

3 participants