Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f51f5f5
feat(compiler): add __query selection hoisting prototype
captbaritone May 22, 2026
74d9492
test: add e2e test for __query selection hoisting
captbaritone May 22, 2026
6e95ba8
feat: add QueryRootSelection reader node for __query
captbaritone May 22, 2026
8ad950b
refactor: move __query out of graphql-ir, add feature flag
captbaritone May 22, 2026
584c85a
test: add comprehensive skip/include coverage for __query hoisting
captbaritone May 22, 2026
3da2312
fix: propagate all ancestor conditions when hoisting __query
captbaritone May 22, 2026
1b5770b
refactor: clean up hoist_query_selections
captbaritone May 22, 2026
e3de8cb
test: add duplicate root field test for __query hoisting
captbaritone May 22, 2026
6b52b90
test: duplicate root field with mismatched conditions
captbaritone May 22, 2026
d82a6df
fix: namespace __query data under data.__query instead of flat merge
captbaritone May 22, 2026
b67365e
feat: add hover description for __query field
captbaritone May 22, 2026
4c8349e
fix: CI failures — rustfmt + Flow exhaustiveness + Flow type cast
captbaritone May 22, 2026
c7ceb2a
fix: regenerate test files + fix Flow type errors
captbaritone May 23, 2026
6a59edc
fix: add __query to remaining Schema impls and compact serializer
captbaritone May 23, 2026
319fdba
fix: update compact serializer, config schema, and regenerate all tes…
captbaritone May 23, 2026
e2d43f4
fix: regenerate all test files with fixture_tests_bin (complete)
captbaritone May 23, 2026
46b3b46
fix: remove spurious test files from fixture_tests_bin
captbaritone May 23, 2026
18dbe3d
fix: restore hand-written visitor_test.rs (uses two transform functions)
captbaritone May 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions compiler/crates/common/src/feature_flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ pub struct FeatureFlags {
#[serde(default)]
pub allow_resolver_non_nullable_return_type: FeatureFlag,

/// Enable the `__query` selection syntax for hoisting selections to the
/// query root. When enabled, any selection set can include `__query { ... }`
/// to select fields from the query root type.
#[serde(default)]
pub enable_query_root_selection: bool,

/// Disable validating the composite schema (server, client schema
/// extensions, Relay Resolvers) after its built.
#[serde(default)]
Expand Down Expand Up @@ -256,6 +262,7 @@ impl Default for FeatureFlags {
enforce_module_name_prefix_for_non_haste: Default::default(),
emit_nogrep_annotation: Default::default(),
disable_more_precise_abstract_selection_raw_response_type: Default::default(),
enable_query_root_selection: Default::default(),

// enabled-by-default
enforce_fragment_alias_where_ambiguous: FeatureFlag::Enabled,
Expand Down
228 changes: 59 additions & 169 deletions compiler/crates/graphql-text-printer/tests/prettier_printer_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,219 +3,109 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f40808776b8f008ee81128c4dff60891>>
*/

mod prettier_printer;

use fixture_tests::test_fixture;
use prettier_printer::transform_fixture;
use fixture_tests::test_fixture;

#[tokio::test]
async fn query_simple() {
let input = include_str!("prettier_printer/fixtures/query_simple.graphql");
let expected = include_str!("prettier_printer/fixtures/query_simple.expected");
test_fixture(
transform_fixture,
file!(),
"query_simple.graphql",
"prettier_printer/fixtures/query_simple.expected",
input,
expected,
)
.await;
}

#[tokio::test]
async fn query_anonymous() {
let input = include_str!("prettier_printer/fixtures/query_anonymous.graphql");
let expected = include_str!("prettier_printer/fixtures/query_anonymous.expected");
test_fixture(
transform_fixture,
file!(),
"query_anonymous.graphql",
"prettier_printer/fixtures/query_anonymous.expected",
input,
expected,
)
.await;
}

#[tokio::test]
async fn query_with_variables() {
let input = include_str!("prettier_printer/fixtures/query_with_variables.graphql");
let expected = include_str!("prettier_printer/fixtures/query_with_variables.expected");
test_fixture(
transform_fixture,
file!(),
"query_with_variables.graphql",
"prettier_printer/fixtures/query_with_variables.expected",
input,
expected,
)
.await;
async fn argument_values() {
let input = include_str!("prettier_printer/fixtures/argument_values.graphql");
let expected = include_str!("prettier_printer/fixtures/argument_values.expected");
test_fixture(transform_fixture, file!(), "argument_values.graphql", "prettier_printer/fixtures/argument_values.expected", input, expected).await;
}

#[tokio::test]
async fn mutation_with_variables() {
let input = include_str!("prettier_printer/fixtures/mutation_with_variables.graphql");
let expected = include_str!("prettier_printer/fixtures/mutation_with_variables.expected");
test_fixture(
transform_fixture,
file!(),
"mutation_with_variables.graphql",
"prettier_printer/fixtures/mutation_with_variables.expected",
input,
expected,
)
.await;
async fn field_alias() {
let input = include_str!("prettier_printer/fixtures/field_alias.graphql");
let expected = include_str!("prettier_printer/fixtures/field_alias.expected");
test_fixture(transform_fixture, file!(), "field_alias.graphql", "prettier_printer/fixtures/field_alias.expected", input, expected).await;
}

#[tokio::test]
async fn subscription_basic() {
let input = include_str!("prettier_printer/fixtures/subscription_basic.graphql");
let expected = include_str!("prettier_printer/fixtures/subscription_basic.expected");
test_fixture(
transform_fixture,
file!(),
"subscription_basic.graphql",
"prettier_printer/fixtures/subscription_basic.expected",
input,
expected,
)
.await;
async fn field_directives() {
let input = include_str!("prettier_printer/fixtures/field_directives.graphql");
let expected = include_str!("prettier_printer/fixtures/field_directives.expected");
test_fixture(transform_fixture, file!(), "field_directives.graphql", "prettier_printer/fixtures/field_directives.expected", input, expected).await;
}

#[tokio::test]
async fn fragment_basic() {
let input = include_str!("prettier_printer/fixtures/fragment_basic.graphql");
let expected = include_str!("prettier_printer/fixtures/fragment_basic.expected");
test_fixture(
transform_fixture,
file!(),
"fragment_basic.graphql",
"prettier_printer/fixtures/fragment_basic.expected",
input,
expected,
)
.await;
}

#[tokio::test]
async fn fragment_with_directive() {
let input = include_str!("prettier_printer/fixtures/fragment_with_directive.graphql");
let expected = include_str!("prettier_printer/fixtures/fragment_with_directive.expected");
test_fixture(
transform_fixture,
file!(),
"fragment_with_directive.graphql",
"prettier_printer/fixtures/fragment_with_directive.expected",
input,
expected,
)
.await;
test_fixture(transform_fixture, file!(), "fragment_basic.graphql", "prettier_printer/fixtures/fragment_basic.expected", input, expected).await;
}

#[tokio::test]
async fn fragment_spread() {
let input = include_str!("prettier_printer/fixtures/fragment_spread.graphql");
let expected = include_str!("prettier_printer/fixtures/fragment_spread.expected");
test_fixture(
transform_fixture,
file!(),
"fragment_spread.graphql",
"prettier_printer/fixtures/fragment_spread.expected",
input,
expected,
)
.await;
test_fixture(transform_fixture, file!(), "fragment_spread.graphql", "prettier_printer/fixtures/fragment_spread.expected", input, expected).await;
}

#[tokio::test]
async fn inline_fragment() {
let input = include_str!("prettier_printer/fixtures/inline_fragment.graphql");
let expected = include_str!("prettier_printer/fixtures/inline_fragment.expected");
test_fixture(
transform_fixture,
file!(),
"inline_fragment.graphql",
"prettier_printer/fixtures/inline_fragment.expected",
input,
expected,
)
.await;
async fn fragment_with_directive() {
let input = include_str!("prettier_printer/fixtures/fragment_with_directive.graphql");
let expected = include_str!("prettier_printer/fixtures/fragment_with_directive.expected");
test_fixture(transform_fixture, file!(), "fragment_with_directive.graphql", "prettier_printer/fixtures/fragment_with_directive.expected", input, expected).await;
}

#[tokio::test]
async fn field_alias() {
let input = include_str!("prettier_printer/fixtures/field_alias.graphql");
let expected = include_str!("prettier_printer/fixtures/field_alias.expected");
test_fixture(
transform_fixture,
file!(),
"field_alias.graphql",
"prettier_printer/fixtures/field_alias.expected",
input,
expected,
)
.await;
async fn inline_fragment() {
let input = include_str!("prettier_printer/fixtures/inline_fragment.graphql");
let expected = include_str!("prettier_printer/fixtures/inline_fragment.expected");
test_fixture(transform_fixture, file!(), "inline_fragment.graphql", "prettier_printer/fixtures/inline_fragment.expected", input, expected).await;
}

#[tokio::test]
async fn field_directives() {
let input = include_str!("prettier_printer/fixtures/field_directives.graphql");
let expected = include_str!("prettier_printer/fixtures/field_directives.expected");
test_fixture(
transform_fixture,
file!(),
"field_directives.graphql",
"prettier_printer/fixtures/field_directives.expected",
input,
expected,
)
.await;
async fn inline_fragment_directives() {
let input = include_str!("prettier_printer/fixtures/inline_fragment_directives.graphql");
let expected = include_str!("prettier_printer/fixtures/inline_fragment_directives.expected");
test_fixture(transform_fixture, file!(), "inline_fragment_directives.graphql", "prettier_printer/fixtures/inline_fragment_directives.expected", input, expected).await;
}

#[tokio::test]
async fn argument_values() {
let input = include_str!("prettier_printer/fixtures/argument_values.graphql");
let expected = include_str!("prettier_printer/fixtures/argument_values.expected");
test_fixture(
transform_fixture,
file!(),
"argument_values.graphql",
"prettier_printer/fixtures/argument_values.expected",
input,
expected,
)
.await;
async fn mutation_with_variables() {
let input = include_str!("prettier_printer/fixtures/mutation_with_variables.graphql");
let expected = include_str!("prettier_printer/fixtures/mutation_with_variables.expected");
test_fixture(transform_fixture, file!(), "mutation_with_variables.graphql", "prettier_printer/fixtures/mutation_with_variables.expected", input, expected).await;
}

#[tokio::test]
async fn nested_selections() {
let input = include_str!("prettier_printer/fixtures/nested_selections.graphql");
let expected = include_str!("prettier_printer/fixtures/nested_selections.expected");
test_fixture(
transform_fixture,
file!(),
"nested_selections.graphql",
"prettier_printer/fixtures/nested_selections.expected",
input,
expected,
)
.await;
test_fixture(transform_fixture, file!(), "nested_selections.graphql", "prettier_printer/fixtures/nested_selections.expected", input, expected).await;
}

#[tokio::test]
async fn inline_fragment_directives() {
let input = include_str!("prettier_printer/fixtures/inline_fragment_directives.graphql");
let expected = include_str!("prettier_printer/fixtures/inline_fragment_directives.expected");
test_fixture(
transform_fixture,
file!(),
"inline_fragment_directives.graphql",
"prettier_printer/fixtures/inline_fragment_directives.expected",
input,
expected,
)
.await;
async fn query_anonymous() {
let input = include_str!("prettier_printer/fixtures/query_anonymous.graphql");
let expected = include_str!("prettier_printer/fixtures/query_anonymous.expected");
test_fixture(transform_fixture, file!(), "query_anonymous.graphql", "prettier_printer/fixtures/query_anonymous.expected", input, expected).await;
}

#[tokio::test]
async fn query_simple() {
let input = include_str!("prettier_printer/fixtures/query_simple.graphql");
let expected = include_str!("prettier_printer/fixtures/query_simple.expected");
test_fixture(transform_fixture, file!(), "query_simple.graphql", "prettier_printer/fixtures/query_simple.expected", input, expected).await;
}

#[tokio::test]
async fn query_with_variables() {
let input = include_str!("prettier_printer/fixtures/query_with_variables.graphql");
let expected = include_str!("prettier_printer/fixtures/query_with_variables.expected");
test_fixture(transform_fixture, file!(), "query_with_variables.graphql", "prettier_printer/fixtures/query_with_variables.expected", input, expected).await;
}

#[tokio::test]
async fn subscription_basic() {
let input = include_str!("prettier_printer/fixtures/subscription_basic.graphql");
let expected = include_str!("prettier_printer/fixtures/subscription_basic.expected");
test_fixture(transform_fixture, file!(), "subscription_basic.graphql", "prettier_printer/fixtures/subscription_basic.expected", input, expected).await;
}
11 changes: 11 additions & 0 deletions compiler/crates/relay-codegen/src/build_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ use relay_transforms::INTERNAL_METADATA_DIRECTIVE;
use relay_transforms::InlineDirectiveMetadata;
use relay_transforms::ModuleMetadata;
use relay_transforms::NoInlineFragmentSpreadMetadata;
use relay_transforms::QUERY_ROOT_SELECTION_DIRECTIVE_NAME;
use relay_transforms::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE;
use relay_transforms::RefetchableMetadata;
use relay_transforms::RelayDirective;
Expand Down Expand Up @@ -2208,6 +2209,16 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
}
}
}
} else if inline_frag
.directives
.iter()
.any(|d| d.name.item == *QUERY_ROOT_SELECTION_DIRECTIVE_NAME)
{
let selections = self.build_selections(context, inline_frag.selections.iter());
Primitive::Key(self.object(object! {
kind: Primitive::String(CODEGEN_CONSTANTS.query_root_selection),
selections: selections,
}))
} else if
// TODO(T63388023): Use typed custom directives
inline_frag.directives.len() == 1
Expand Down
2 changes: 2 additions & 0 deletions compiler/crates/relay-codegen/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ pub struct CodegenConstants {
pub provided_variables: StringKey,
pub provider: StringKey,
pub query: StringKey,
pub query_root_selection: StringKey,
pub refetch: StringKey,
pub relay_live_resolver: StringKey,
pub relay_resolver_normalization_info: StringKey,
Expand Down Expand Up @@ -219,6 +220,7 @@ lazy_static! {
provided_variables: "providedVariables".intern(),
provider: "provider".intern(),
query: "query".intern(),
query_root_selection: "QueryRootSelection".intern(),
refetch: "refetch".intern(),
relay_live_resolver: "RelayLiveResolver".intern(),
relay_resolver_normalization_info: "normalizationInfo".intern(),
Expand Down
16 changes: 16 additions & 0 deletions compiler/crates/relay-compiler/relay-compiler-config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,11 @@
"type": "boolean",
"default": false
},
"enable_query_root_selection": {
"description": "Enable the `__query` selection syntax for hoisting selections to the\nquery root. When enabled, any selection set can include `__query { ... }`\nto select fields from the query root type.",
"type": "boolean",
"default": false
},
"enable_relay_resolver_mutations": {
"description": "Allow relay resolvers to extend the Mutation type",
"type": "boolean",
Expand Down Expand Up @@ -739,6 +744,13 @@
"kind": "disabled"
}
},
"shard_extra_artifacts": {
"description": "Shard generated extra artifacts into subdirectories under\n`extraArtifactsOutput` that mirror the source file's relative path,\nhonoring `shardOutput` / `shardStripRegex`.",
"$ref": "#/$defs/FeatureFlag",
"default": {
"kind": "disabled"
}
},
"skip_printing_nulls": {
"$ref": "#/$defs/FeatureFlag",
"default": {
Expand Down Expand Up @@ -958,6 +970,7 @@
"enable_3d_branch_arg_generation": false,
"enable_exec_time_resolvers_directive": false,
"enable_fragment_argument_transform": false,
"enable_query_root_selection": false,
"enable_relay_resolver_mutations": false,
"enable_resolver_normalization_ast": false,
"enable_shadow_resolvers": {
Expand All @@ -981,6 +994,9 @@
"relay_resolver_enable_interface_output_type": {
"kind": "disabled"
},
"shard_extra_artifacts": {
"kind": "disabled"
},
"skip_printing_nulls": {
"kind": "disabled"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result<String, String>
prefer_fetchable_in_refetch_queries: fixture
.content
.contains("# prefer_fetchable_in_refetch_queries"),
enable_query_root_selection: fixture.content.contains("# enable_query_root_selection"),
..Default::default()
};

Expand Down
Loading
Loading