Skip to content

Commit f1cce18

Browse files
committed
feat(graphql_analyze): implement useInputName
1 parent a3a1ad2 commit f1cce18

File tree

13 files changed

+288
-49
lines changed

13 files changed

+288
-49
lines changed

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

Lines changed: 69 additions & 48 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: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_graphql_analyze/src/lint/nursery.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ use biome_analyze::declare_lint_group;
66
pub mod no_empty_source;
77
pub mod use_consistent_graphql_descriptions;
88
pub mod use_deprecated_date;
9+
pub mod use_input_name;
910
pub mod use_unique_argument_names;
1011
pub mod use_unique_field_definition_names;
1112
pub mod use_unique_graphql_operation_name;
1213
pub mod use_unique_input_field_names;
1314
pub mod use_unique_variable_names;
14-
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_argument_names :: UseUniqueArgumentNames , self :: use_unique_field_definition_names :: UseUniqueFieldDefinitionNames , self :: use_unique_graphql_operation_name :: UseUniqueGraphqlOperationName , self :: use_unique_input_field_names :: UseUniqueInputFieldNames , self :: use_unique_variable_names :: UseUniqueVariableNames ,] } }
15+
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_input_name :: UseInputName , self :: use_unique_argument_names :: UseUniqueArgumentNames , self :: use_unique_field_definition_names :: UseUniqueFieldDefinitionNames , self :: use_unique_graphql_operation_name :: UseUniqueGraphqlOperationName , self :: use_unique_input_field_names :: UseUniqueInputFieldNames , self :: use_unique_variable_names :: UseUniqueVariableNames ,] } }
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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::{
6+
GraphqlFieldDefinition, GraphqlFieldDefinitionList, GraphqlFieldsDefinition,
7+
GraphqlObjectTypeDefinition, GraphqlObjectTypeExtension,
8+
};
9+
use biome_rowan::{AstNode, declare_node_union};
10+
use biome_rule_options::use_input_name::UseInputNameOptions;
11+
12+
declare_lint_rule! {
13+
/// Require mutation argument to be always called “input” and input type to be called Mutation name + “Input”.
14+
///
15+
/// Require mutation argument to be always called “input” and input type to be called Mutation name + “Input”.
16+
/// Using the same name for all input parameters will make your schemas easier to consume and more predictable.
17+
/// Using the same name as mutation for InputType will make it easier to find mutations that InputType belongs to.
18+
///
19+
/// ## Examples
20+
///
21+
/// ### Invalid
22+
///
23+
/// ```graphql,expect_diagnostic
24+
/// type Mutation {
25+
/// SetMessage(message: InputMessage): String
26+
/// }
27+
/// ```
28+
///
29+
/// ### Valid
30+
///
31+
/// ```graphql
32+
/// type Mutation {
33+
/// SetMessage(input: SetMessageInput): String
34+
/// }
35+
/// ```
36+
///
37+
pub UseInputName {
38+
version: "next",
39+
name: "useInputName",
40+
language: "graphql",
41+
recommended: false,
42+
sources: &[RuleSource::EslintGraphql("input-name").inspired()],
43+
}
44+
}
45+
46+
impl Rule for UseInputName {
47+
type Query = Ast<GraphqlFieldDefinition>;
48+
type State = ();
49+
type Signals = Option<Self::State>;
50+
type Options = UseInputNameOptions;
51+
52+
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
53+
let node = ctx.query();
54+
55+
let def_list = node
56+
.syntax()
57+
.parent()
58+
.and_then(GraphqlFieldDefinitionList::cast)?;
59+
let fields_def = def_list
60+
.syntax()
61+
.parent()
62+
.and_then(GraphqlFieldsDefinition::cast)?;
63+
let is_query = fields_def.syntax().parent().is_some(|parent| {})?;
64+
65+
if !is_query {
66+
return None;
67+
}
68+
69+
None
70+
}
71+
72+
fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
73+
let span = ctx.query().range();
74+
Some(
75+
RuleDiagnostic::new(
76+
rule_category!(),
77+
span,
78+
markup! {
79+
"Unexpected empty block is not allowed"
80+
},
81+
)
82+
.note(markup! {
83+
"This note will give you more information."
84+
}),
85+
)
86+
}
87+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# should generate diagnostics
2+
type Mutation { SetMessage(message: String): String }
3+
4+
type Mutation { SetMessage(input: String): String }
5+
6+
type Mutation { SetMessage(hello: SetMessageInput): String }
7+
8+
type Mutation { userCreate(record: CreateOneUserInput!): CreateOneUserPayload }
9+
10+
type Mutation { userCreate(record: [CreateOneUserInput]!): CreateOneUserPayload }
11+
12+
type Mutation { userCreate(record: [CreateOneUserInput!]!): CreateOneUserPayload }
13+
14+
type Mutation { userCreate(record: [CreateOneUserInput!]): CreateOneUserPayload }
15+
16+
type Mutation { userCreate(record: String, test: String): String }
17+
18+
type Mutation { userCreate(input: UserCreateInput): String }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# should not generate diagnostics
2+
type Mutation { SetMessage(input: SetMessageInput): String }
3+
4+
type Mutation { CreateMessage(input: CreateMessageInput): String DeleteMessage(input: DeleteMessageInput): Boolean }
5+
6+
type Mutation { CreateMessage(input: CreateMessageInput!): String }
7+
8+
type Mutation { CreateMessage(input: [CreateMessageInput]): String }
9+
10+
type Mutation { userCreate(input: userCreateInput): String }
11+
12+
type Mutation { CreateMessage(input: String): String }
13+
extend type Mutation { CreateMessage(input: String): String }
14+
type Query { message(id: ID): Message }
15+
extend type Query { message(id: ID): Message }

crates/biome_graphql_syntax/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#[macro_use]
88
mod generated;
99
mod file_source;
10+
mod object_ext;
1011
pub mod string_value_ext;
1112
mod syntax_node;
1213

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use crate::{GraphqlObjectTypeDefinition, GraphqlObjectTypeExtension};
2+
3+
impl GraphqlObjectTypeDefinition {
4+
pub fn is_mutation(&self) -> bool {
5+
if let Some(name) = self.name().ok()
6+
&& let Some(value_token) = name.value_token().ok()
7+
&& value_token.to_string() == "Mutation"
8+
{
9+
return true;
10+
}
11+
12+
false
13+
}
14+
15+
pub fn is_query(&self) -> bool {
16+
if let Some(name) = self.name().ok()
17+
&& let Some(value_token) = name.value_token().ok()
18+
&& value_token.to_string() == "Query"
19+
{
20+
return true;
21+
}
22+
23+
false
24+
}
25+
}
26+
27+
impl GraphqlObjectTypeExtension {
28+
pub fn is_mutation(&self) -> bool {
29+
if let Some(name) = self.name().ok()
30+
&& let Some(value_token) = name.value_token().ok()
31+
&& value_token.to_string() == "Mutation"
32+
{
33+
return true;
34+
}
35+
36+
false
37+
}
38+
39+
pub fn is_query(&self) -> bool {
40+
if let Some(name) = self.name().ok()
41+
&& let Some(value_token) = name.value_token().ok()
42+
&& value_token.to_string() == "Query"
43+
{
44+
return true;
45+
}
46+
47+
false
48+
}
49+
}

crates/biome_rule_options/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ pub mod use_image_size;
330330
pub mod use_import_extensions;
331331
pub mod use_import_type;
332332
pub mod use_index_of;
333+
pub mod use_input_name;
333334
pub mod use_is_array;
334335
pub mod use_is_nan;
335336
pub mod use_iterable_callback_return;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use biome_deserialize_macros::{Deserializable, Merge};
2+
use serde::{Deserialize, Serialize};
3+
#[derive(Default, Clone, Debug, Deserialize, Deserializable, Merge, Eq, PartialEq, Serialize)]
4+
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
5+
#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
6+
pub struct UseInputNameOptions {}

0 commit comments

Comments
 (0)