Skip to content

Commit 2df62ed

Browse files
committed
feat(graphql_analyze): implement useUniqueVariableNames
1 parent 0196c0e commit 2df62ed

File tree

13 files changed

+270
-26
lines changed

13 files changed

+270
-26
lines changed

.changeset/public-walls-buy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Added the nursery rule [`useUniqueVariableNames`](https://biomejs.dev/linter/rules/use-unique-variable-names/). Enforce unique variable names for GraphQL operations.

crates/biome_configuration/src/analyzer/linter/rules.rs

Lines changed: 44 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_diagnostics_categories/src/categories.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ define_categories! {
187187
"lint/nursery/noParametersOnlyUsedInRecursion": "https://biomejs.dev/linter/rules/no-parameters-only-used-in-recursion",
188188
"lint/nursery/noProto": "https://biomejs.dev/linter/rules/no-proto",
189189
"lint/nursery/noReactForwardRef": "https://biomejs.dev/linter/rules/no-react-forward-ref",
190-
"lint/nursery/noScriptUrl": "https://biomejs.dev/linter/rules/no-script-url",
191190
"lint/nursery/noReturnAssign": "https://biomejs.dev/linter/rules/no-return-assign",
191+
"lint/nursery/noScriptUrl": "https://biomejs.dev/linter/rules/no-script-url",
192192
"lint/nursery/noShadow": "https://biomejs.dev/linter/rules/no-shadow",
193193
"lint/nursery/noSyncScripts": "https://biomejs.dev/linter/rules/no-sync-scripts",
194194
"lint/nursery/noTernary": "https://biomejs.dev/linter/rules/no-ternary",
@@ -230,6 +230,7 @@ define_categories! {
230230
"lint/nursery/useSortedClasses": "https://biomejs.dev/linter/rules/use-sorted-classes",
231231
"lint/nursery/useSpread": "https://biomejs.dev/linter/rules/no-spread",
232232
"lint/nursery/useUniqueGraphqlOperationName": "https://biomejs.dev/linter/rules/use-unique-graphql-operation-name",
233+
"lint/nursery/useUniqueVariableNames": "https://biomejs.dev/linter/rules/use-unique-variable-names",
233234
"lint/nursery/useVueConsistentDefinePropsDeclaration": "https://biomejs.dev/linter/rules/use-vue-consistent-define-props-declaration",
234235
"lint/nursery/useVueDefineMacrosOrder": "https://biomejs.dev/linter/rules/use-vue-define-macros-order",
235236
"lint/nursery/useVueHyphenatedAttributes": "https://biomejs.dev/linter/rules/use-vue-hyphenated-attributes",

crates/biome_graphql_analyze/src/lint/nursery.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ pub mod no_empty_source;
77
pub mod use_consistent_graphql_descriptions;
88
pub mod use_deprecated_date;
99
pub mod use_unique_graphql_operation_name;
10-
declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_empty_source :: NoEmptySource , self :: use_consistent_graphql_descriptions :: UseConsistentGraphqlDescriptions , self :: use_deprecated_date :: UseDeprecatedDate , self :: use_unique_graphql_operation_name :: UseUniqueGraphqlOperationName ,] } }
10+
pub mod use_unique_variable_names;
11+
declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_empty_source :: NoEmptySource , self :: use_consistent_graphql_descriptions :: UseConsistentGraphqlDescriptions , self :: use_deprecated_date :: UseDeprecatedDate , self :: use_unique_graphql_operation_name :: UseUniqueGraphqlOperationName , self :: use_unique_variable_names :: UseUniqueVariableNames ,] } }
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use biome_analyze::{
2+
Ast, Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule,
3+
};
4+
use biome_console::markup;
5+
use biome_graphql_syntax::GraphqlVariableDefinitions;
6+
use biome_rowan::AstNode;
7+
use biome_rule_options::use_unique_variable_names::UseUniqueVariableNamesOptions;
8+
9+
declare_lint_rule! {
10+
/// Require all variable definitions to be unique.
11+
///
12+
/// A GraphQL operation is only valid if all its variables are uniquely named.
13+
///
14+
/// ## Examples
15+
///
16+
/// ### Invalid
17+
///
18+
/// ```graphql,expect_diagnostic
19+
/// query C($x: Int, $x: Int) {
20+
/// __typename
21+
/// }
22+
/// ```
23+
///
24+
/// ### Valid
25+
///
26+
/// ```graphql
27+
/// query C($x: Int, $y: Int) {
28+
/// __typename
29+
/// }
30+
/// ```
31+
///
32+
pub UseUniqueVariableNames {
33+
version: "next",
34+
name: "useUniqueVariableNames",
35+
language: "graphql",
36+
recommended: false,
37+
sources: &[RuleSource::EslintGraphql("unique-variable-names").same()],
38+
}
39+
}
40+
41+
impl Rule for UseUniqueVariableNames {
42+
type Query = Ast<GraphqlVariableDefinitions>;
43+
type State = ();
44+
type Signals = Option<Self::State>;
45+
type Options = UseUniqueVariableNamesOptions;
46+
47+
fn run(ctx: &RuleContext<Self>) -> Option<Self::State> {
48+
let node = ctx.query();
49+
let mut found: Vec<String> = Vec::new();
50+
51+
for element in node.elements() {
52+
if let Some(variable) = element.variable().ok()
53+
&& let Some(name) = variable.name().ok()
54+
&& let Some(value_token) = name.value_token().ok()
55+
{
56+
let string = value_token.to_string();
57+
if found.contains(&string) {
58+
return Some(());
59+
} else {
60+
found.push(string);
61+
}
62+
}
63+
}
64+
65+
None
66+
}
67+
68+
fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
69+
let span = ctx.query().range();
70+
Some(
71+
RuleDiagnostic::new(
72+
rule_category!(),
73+
span,
74+
markup! {
75+
"Duplicate variable name."
76+
},
77+
)
78+
.note(markup! {
79+
"A GraphQL operation is only valid if all its variables are uniquely named. Make sure to name every variable differently."
80+
}),
81+
)
82+
}
83+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# should generate diagnostics
2+
query A($x: Int, $x: Int, $x: String) { __typename }
3+
query B($x: String, $x: Int) { __typename }
4+
query C($x: Int, $x: Int) { __typename }
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
source: crates/biome_graphql_analyze/tests/spec_tests.rs
3+
expression: invalid.graphql
4+
---
5+
# Input
6+
```graphql
7+
# should generate diagnostics
8+
query A($x: Int, $x: Int, $x: String) { __typename }
9+
query B($x: String, $x: Int) { __typename }
10+
query C($x: Int, $x: Int) { __typename }
11+
12+
```
13+
14+
# Diagnostics
15+
```
16+
invalid.graphql:2:8 lint/nursery/useUniqueVariableNames ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17+
18+
i Duplicate variable name.
19+
20+
1 │ # should generate diagnostics
21+
> 2 │ query A($x: Int, $x: Int, $x: String) { __typename }
22+
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23+
3 │ query B($x: String, $x: Int) { __typename }
24+
4 │ query C($x: Int, $x: Int) { __typename }
25+
26+
i A GraphQL operation is only valid if all its variables are uniquely named. Make sure to name every variable differently.
27+
28+
i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.
29+
30+
31+
```
32+
33+
```
34+
invalid.graphql:3:8 lint/nursery/useUniqueVariableNames ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
35+
36+
i Duplicate variable name.
37+
38+
1 │ # should generate diagnostics
39+
2 │ query A($x: Int, $x: Int, $x: String) { __typename }
40+
> 3 │ query B($x: String, $x: Int) { __typename }
41+
│ ^^^^^^^^^^^^^^^^^^^^^
42+
4 │ query C($x: Int, $x: Int) { __typename }
43+
5 │
44+
45+
i A GraphQL operation is only valid if all its variables are uniquely named. Make sure to name every variable differently.
46+
47+
i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.
48+
49+
50+
```
51+
52+
```
53+
invalid.graphql:4:8 lint/nursery/useUniqueVariableNames ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
54+
55+
i Duplicate variable name.
56+
57+
2 │ query A($x: Int, $x: Int, $x: String) { __typename }
58+
3 │ query B($x: String, $x: Int) { __typename }
59+
> 4 │ query C($x: Int, $x: Int) { __typename }
60+
│ ^^^^^^^^^^^^^^^^^^
61+
5 │
62+
63+
i A GraphQL operation is only valid if all its variables are uniquely named. Make sure to name every variable differently.
64+
65+
i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.
66+
67+
68+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# should not generate diagnostics
2+
query A($x: Int, $y: String) { __typename }
3+
query B($x: String, $y: Int) { __typename }
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
source: crates/biome_graphql_analyze/tests/spec_tests.rs
3+
expression: valid.graphql
4+
---
5+
# Input
6+
```graphql
7+
# should not generate diagnostics
8+
query A($x: Int, $y: String) { __typename }
9+
query B($x: String, $y: Int) { __typename }
10+
11+
```

crates/biome_rule_options/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ pub mod use_trim_start_end;
384384
pub mod use_unified_type_signatures;
385385
pub mod use_unique_element_ids;
386386
pub mod use_unique_graphql_operation_name;
387+
pub mod use_unique_variable_names;
387388
pub mod use_valid_anchor;
388389
pub mod use_valid_aria_props;
389390
pub mod use_valid_aria_role;

0 commit comments

Comments
 (0)