Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1c02417

Browse filesBrowse files
evanyeungfacebook-github-bot
authored andcommitted
Add logic to find property lookup resolver docblocks
Summary: This diff builds on the previous one to add a new visitor that finds docblocks for property lookup resolvers. The visitor just looks at the location of the `gqlField` comment nodes and compares an object key to see if the object key should generate a property lookup resolver. For any object key that matches, a new `UnresolvedFieldDefinition` is added. Reviewed By: captbaritone Differential Revision: D67370142 fbshipit-source-id: b3abb2fa3b9ea6da77edcd0202c8887597303b68
1 parent 24951b9 commit 1c02417
Copy full SHA for 1c02417

6 files changed

+354-4Lines changed: 354 additions & 4 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎compiler/crates/relay-schema-generation/src/errors.rs‎

Copy file name to clipboardExpand all lines: compiler/crates/relay-schema-generation/src/errors.rs
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ pub enum SchemaGenerationError {
9898
module_name: StringKey,
9999
import_type: JSImportType,
100100
},
101+
#[error(
102+
"The key of a property lookup resolver defined with @gqlField must be an identifier (not a string or computed value)."
103+
)]
104+
ExpectedPropertyLookupToBeIdentifer,
101105
#[error(
102106
"This field is a property lookup but has an import for a GraphQL fragment used in the resolver. This is not allowed."
103107
)]
Collapse file
+93Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#![deny(warnings)]
9+
#![deny(clippy::all)]
10+
11+
use ::intern::string_key::Intern;
12+
use ::intern::string_key::StringKey;
13+
use common::Diagnostic;
14+
use common::Location;
15+
use common::SourceLocationKey;
16+
use common::Span;
17+
use common::WithLocation;
18+
use docblock_shared::ResolverSourceHash;
19+
use fnv::FnvHashSet;
20+
use hermes_estree::ObjectTypePropertyKey;
21+
use hermes_estree::Range;
22+
use hermes_estree::SourceRange;
23+
use hermes_estree::Visitor;
24+
25+
use crate::FieldDefinitionInfo;
26+
use crate::SchemaGenerationError;
27+
use crate::UnresolvedFieldDefinition;
28+
29+
fn source_range_to_span(source_range: SourceRange) -> Span {
30+
Span {
31+
start: source_range.start,
32+
end: source_range.end,
33+
}
34+
}
35+
36+
pub struct PropertyVisitor<'a> {
37+
pub location: SourceLocationKey,
38+
source_hash: ResolverSourceHash,
39+
pub errors: Vec<Diagnostic>,
40+
entity_name: WithLocation<StringKey>,
41+
resolver_node_ranges: &'a FnvHashSet<SourceRange>,
42+
pub field_definitions: Vec<UnresolvedFieldDefinition>,
43+
}
44+
45+
impl<'a> PropertyVisitor<'a> {
46+
pub fn new(
47+
source_module_path: &str,
48+
source_hash: ResolverSourceHash,
49+
entity_name: WithLocation<StringKey>,
50+
resolver_node_ranges: &'a FnvHashSet<SourceRange>,
51+
) -> Self {
52+
Self {
53+
location: SourceLocationKey::standalone(source_module_path),
54+
source_hash,
55+
errors: vec![],
56+
entity_name,
57+
resolver_node_ranges,
58+
field_definitions: vec![],
59+
}
60+
}
61+
}
62+
63+
impl Visitor<'_> for PropertyVisitor<'_> {
64+
fn visit_object_type_property(&mut self, ast: &'_ hermes_estree::ObjectTypeProperty) {
65+
if self.resolver_node_ranges.contains(&ast.range) {
66+
let field_name = match &ast.key {
67+
ObjectTypePropertyKey::Identifier(id) => WithLocation::from_span(
68+
self.location,
69+
source_range_to_span(id.range),
70+
id.name.clone().intern(),
71+
),
72+
ObjectTypePropertyKey::_Literal(lit) => {
73+
self.errors.push(Diagnostic::error(
74+
SchemaGenerationError::ExpectedPropertyLookupToBeIdentifer,
75+
Location::new(self.location, source_range_to_span(lit.range())),
76+
));
77+
return;
78+
}
79+
};
80+
let field_definition = UnresolvedFieldDefinition {
81+
field_name,
82+
entity_name: Some(self.entity_name),
83+
return_type: ast.value.clone(),
84+
source_hash: self.source_hash,
85+
description: None, // TODO
86+
deprecated: None, // TODO
87+
entity_type: None,
88+
field_info: FieldDefinitionInfo::PropertyLookupInfo,
89+
};
90+
self.field_definitions.push(field_definition);
91+
}
92+
}
93+
}
Collapse file

‎compiler/crates/relay-schema-generation/src/lib.rs‎

Copy file name to clipboardExpand all lines: compiler/crates/relay-schema-generation/src/lib.rs
+33-3Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#![deny(clippy::all)]
1111

1212
mod errors;
13+
mod find_property_lookup_resolvers;
1314
mod find_resolver_imports;
1415

1516
use std::collections::hash_map::Entry;
@@ -41,6 +42,7 @@ use find_resolver_imports::JSImportType;
4142
use find_resolver_imports::ModuleResolution;
4243
use find_resolver_imports::ModuleResolutionKey;
4344
use fnv::FnvBuildHasher;
45+
use fnv::FnvHashSet;
4446
use graphql_ir::FragmentDefinitionName;
4547
use graphql_syntax::ConstantArgument;
4648
use graphql_syntax::ConstantDirective;
@@ -72,6 +74,7 @@ use hermes_estree::Range;
7274
use hermes_estree::SourceRange;
7375
use hermes_estree::TypeAlias;
7476
use hermes_estree::TypeAnnotationEnum;
77+
use hermes_estree::Visitor;
7578
use hermes_parser::parse;
7679
use hermes_parser::ParseResult;
7780
use hermes_parser::ParserDialect;
@@ -92,6 +95,8 @@ use relay_docblock::WeakObjectIr;
9295
use rustc_hash::FxHashMap;
9396
use schema_extractor::SchemaExtractor;
9497

98+
use crate::find_property_lookup_resolvers::PropertyVisitor;
99+
95100
pub static LIVE_FLOW_TYPE_NAME: &str = "LiveState";
96101

97102
type FnvIndexMap<K, V> = IndexMap<K, V, FnvBuildHasher>;
@@ -241,12 +246,17 @@ impl RelayResolverExtractor {
241246
let module_resolution = import_export_visitor.get_module_resolution(&ast)?;
242247

243248
let attached_comments = find_nodes_after_comments(&ast, &comments);
244-
// TODO: use the gqlField comments
245-
let (_gql_field_comments, attached_comments): (AttachedComments<'_>, AttachedComments<'_>) =
249+
let (gql_field_comments, attached_comments): (AttachedComments<'_>, AttachedComments<'_>) =
246250
attached_comments
247251
.into_iter()
248252
.partition(|(comment, _, _, _)| comment.contains("@gqlField"));
249253

254+
let gql_comments = FnvHashSet::from_iter(
255+
gql_field_comments
256+
.into_iter()
257+
.map(|(_, _, _, node_range)| node_range),
258+
);
259+
250260
let result = try_all(
251261
attached_comments
252262
.into_iter()
@@ -320,14 +330,34 @@ impl RelayResolverExtractor {
320330
type_alias,
321331
}) => {
322332
let name = resolver_value.field_value.unwrap_or(field_name);
333+
let mut prop_visitor = PropertyVisitor::new(
334+
source_module_path,
335+
source_hash,
336+
name,
337+
&gql_comments,
338+
);
339+
prop_visitor.visit_flow_type_annotation(&type_alias);
340+
if !prop_visitor.errors.is_empty() {
341+
return Err(prop_visitor.errors);
342+
}
343+
let field_definitions: Vec<(
344+
UnresolvedFieldDefinition,
345+
SourceLocationKey,
346+
)> = prop_visitor
347+
.field_definitions
348+
.into_iter()
349+
.map(|def| (def, prop_visitor.location))
350+
.collect();
351+
323352
self.add_weak_type_definition(
324353
name,
325354
type_alias,
326355
source_hash,
327356
source_module_path,
328357
description,
329358
false,
330-
)?
359+
)?;
360+
self.unresolved_field_definitions.extend(field_definitions);
331361
}
332362
}
333363
Ok(())
Collapse file
+194Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
==================================== INPUT ====================================
2+
/**
3+
* Copyright (c) Meta Platforms, Inc. and affiliates.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
//- module.js
10+
11+
/**
12+
* @RelayResolver
13+
*/
14+
export type Cat = {
15+
/**
16+
* @gqlField
17+
*/
18+
name: string,
19+
/**
20+
* @gqlField
21+
*/
22+
color: string,
23+
};
24+
==================================== OUTPUT ===================================
25+
Field(
26+
TerseRelayResolver(
27+
TerseRelayResolverIr {
28+
field: FieldDefinition {
29+
name: Identifier {
30+
span: 268:272,
31+
token: Token {
32+
span: 268:272,
33+
kind: Identifier,
34+
},
35+
value: "name",
36+
},
37+
type_: Named(
38+
NamedTypeAnnotation {
39+
name: Identifier {
40+
span: 274:280,
41+
token: Token {
42+
span: 274:280,
43+
kind: Identifier,
44+
},
45+
value: "String",
46+
},
47+
},
48+
),
49+
arguments: None,
50+
directives: [],
51+
description: None,
52+
hack_source: None,
53+
span: 268:272,
54+
},
55+
type_: WithLocation {
56+
location: module.js:231:234,
57+
item: "Cat",
58+
},
59+
root_fragment: None,
60+
deprecated: None,
61+
semantic_non_null: Some(
62+
ConstantDirective {
63+
span: 268:272,
64+
at: Token {
65+
span: 0:0,
66+
kind: Empty,
67+
},
68+
name: Identifier {
69+
span: 268:272,
70+
token: Token {
71+
span: 0:0,
72+
kind: Empty,
73+
},
74+
value: "semanticNonNull",
75+
},
76+
arguments: None,
77+
},
78+
),
79+
live: None,
80+
location: module.js:268:272,
81+
fragment_arguments: None,
82+
source_hash: ResolverSourceHash(
83+
"028500824c2efa57e633f83759af8a99",
84+
),
85+
type_confirmed: true,
86+
is_property_lookup: true,
87+
},
88+
),
89+
)
90+
extend type Cat {
91+
name: String @relay_resolver(fragment_name: "Cat____relay_model_instance", generated_fragment: true, inject_fragment_data: "__relay_model_instance", is_property_lookup: true, type_confirmed: true, has_output_type: true, import_name: "name", import_path: "module.js") @resolver_source_hash(value: "028500824c2efa57e633f83759af8a99") @semanticNonNull
92+
}
93+
94+
95+
Field(
96+
TerseRelayResolver(
97+
TerseRelayResolverIr {
98+
field: FieldDefinition {
99+
name: Identifier {
100+
span: 312:317,
101+
token: Token {
102+
span: 312:317,
103+
kind: Identifier,
104+
},
105+
value: "color",
106+
},
107+
type_: Named(
108+
NamedTypeAnnotation {
109+
name: Identifier {
110+
span: 319:325,
111+
token: Token {
112+
span: 319:325,
113+
kind: Identifier,
114+
},
115+
value: "String",
116+
},
117+
},
118+
),
119+
arguments: None,
120+
directives: [],
121+
description: None,
122+
hack_source: None,
123+
span: 312:317,
124+
},
125+
type_: WithLocation {
126+
location: module.js:231:234,
127+
item: "Cat",
128+
},
129+
root_fragment: None,
130+
deprecated: None,
131+
semantic_non_null: Some(
132+
ConstantDirective {
133+
span: 312:317,
134+
at: Token {
135+
span: 0:0,
136+
kind: Empty,
137+
},
138+
name: Identifier {
139+
span: 312:317,
140+
token: Token {
141+
span: 0:0,
142+
kind: Empty,
143+
},
144+
value: "semanticNonNull",
145+
},
146+
arguments: None,
147+
},
148+
),
149+
live: None,
150+
location: module.js:312:317,
151+
fragment_arguments: None,
152+
source_hash: ResolverSourceHash(
153+
"028500824c2efa57e633f83759af8a99",
154+
),
155+
type_confirmed: true,
156+
is_property_lookup: true,
157+
},
158+
),
159+
)
160+
extend type Cat {
161+
color: String @relay_resolver(fragment_name: "Cat____relay_model_instance", generated_fragment: true, inject_fragment_data: "__relay_model_instance", is_property_lookup: true, type_confirmed: true, has_output_type: true, import_name: "color", import_path: "module.js") @resolver_source_hash(value: "028500824c2efa57e633f83759af8a99") @semanticNonNull
162+
}
163+
164+
165+
Type(
166+
WeakObjectType(
167+
WeakObjectIr {
168+
type_name: Identifier {
169+
span: 231:234,
170+
token: Token {
171+
span: 231:234,
172+
kind: Identifier,
173+
},
174+
value: "Cat",
175+
},
176+
rhs_location: module.js:231:234,
177+
description: None,
178+
hack_source: None,
179+
deprecated: None,
180+
location: module.js:231:234,
181+
implements_interfaces: [],
182+
source_hash: ResolverSourceHash(
183+
"028500824c2efa57e633f83759af8a99",
184+
),
185+
type_confirmed: true,
186+
},
187+
),
188+
)
189+
scalar CatModel @__RelayCustomScalar(path: "module.js", export_name: "Cat")
190+
191+
192+
type Cat @__RelayResolverModel @RelayOutputType @__RelayWeakObject {
193+
__relay_model_instance: CatModel! @resolver_source_hash(value: "028500824c2efa57e633f83759af8a99") @unselectable(reason: "This field is intended only for Relay's internal use")
194+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.