Skip to content

Commit

Permalink
fix: do not include unused fragments in emitted JS (#39)
Browse files Browse the repository at this point in the history
* add fragment shaking (?)

* Add tests for printing fragments in JS code

* Add fragments map to PrintFragmentContext

* refactoring
  • Loading branch information
uhyo authored Dec 24, 2023
1 parent b8bfc82 commit 133bba2
Show file tree
Hide file tree
Showing 20 changed files with 318 additions and 124 deletions.
13 changes: 4 additions & 9 deletions crates/checker/src/operation_checker/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ mod operations {

use graphql_type_system::Schema;
use insta::assert_debug_snapshot;
use nitrogql_semantics::{ast_to_type_system, resolve_operation_extensions};
use nitrogql_semantics::ast_to_type_system;

use crate::operation_checker::check_operation_document;
use nitrogql_ast::base::Pos;
use nitrogql_parser::parse_operation_document;

Expand Down Expand Up @@ -301,9 +300,8 @@ mod selection_set {

use graphql_type_system::Schema;
use insta::assert_debug_snapshot;
use nitrogql_semantics::{ast_to_type_system, resolve_operation_extensions};
use nitrogql_semantics::ast_to_type_system;

use crate::operation_checker::check_operation_document;
use nitrogql_ast::base::Pos;
use nitrogql_parser::parse_operation_document;

Expand Down Expand Up @@ -928,12 +926,9 @@ mod imports {

use graphql_type_system::Schema;
use insta::assert_debug_snapshot;
use nitrogql_semantics::{ast_to_type_system, resolve_operation_extensions};
use nitrogql_semantics::ast_to_type_system;

use crate::{
operation_checker::{check_operation_document, tests::test_check},
OperationCheckContext,
};
use crate::operation_checker::tests::test_check;
use nitrogql_ast::base::Pos;
use nitrogql_parser::parse_operation_document;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
source: crates/graphql-loader/src/loader.rs
expression: js
---
const TestQuery = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Test"},"variableDefinitions":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test"},"arguments":[],"directives":[]},{"kind":"FragmentSpread","name":{"kind":"Name","value":"Frag1"},"directives":[]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Frag2"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test3"},"arguments":[],"directives":[]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Frag1"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test2"},"arguments":[],"directives":[]},{"kind":"FragmentSpread","name":{"kind":"Name","value":"Frag2"},"directives":[]}]}}]};
const TestQuery = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Test"},"variableDefinitions":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test"},"arguments":[],"directives":[]},{"kind":"FragmentSpread","name":{"kind":"Name","value":"Frag1"},"directives":[]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Frag1"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test2"},"arguments":[],"directives":[]},{"kind":"FragmentSpread","name":{"kind":"Name","value":"Frag2"},"directives":[]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Frag2"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test3"},"arguments":[],"directives":[]}]}}]};

export { TestQuery as default };

export const Frag2 = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Frag2"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test3"},"arguments":[],"directives":[]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Frag1"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test2"},"arguments":[],"directives":[]},{"kind":"FragmentSpread","name":{"kind":"Name","value":"Frag2"},"directives":[]}]}}]};
export const Frag2 = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Frag2"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test3"},"arguments":[],"directives":[]}]}}]};

export const Frag1 = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Frag1"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test2"},"arguments":[],"directives":[]},{"kind":"FragmentSpread","name":{"kind":"Name","value":"Frag2"},"directives":[]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Frag2"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test3"},"arguments":[],"directives":[]}]}}]};

Expand Down
9 changes: 1 addition & 8 deletions crates/plugin/src/model_plugin/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,7 @@ type User @model(type: "string") {
additional_info: error
.additional_info
.into_iter()
.map(|(pos, message)| {
(
pos,
CheckErrorMessage::Plugin {
message: message.into(),
},
)
})
.map(|(pos, message)| (pos, CheckErrorMessage::Plugin { message }))
.collect(),
}),
);
Expand Down
2 changes: 2 additions & 0 deletions crates/printer/src/json_printer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mod helpers;
mod tests;
mod to_json;

pub use to_json::ExecutableDefinitionRef;

pub fn print_to_json_string<T: JsonPrintable + ?Sized>(ast: &T) -> String {
let mut buf = String::new();
ast.print_json(&mut JSONObjectWriter::new(&mut buf));
Expand Down
20 changes: 19 additions & 1 deletion crates/printer/src/json_printer/to_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl JsonPrintable for OperationDocument<'_> {
}
}

impl JsonPrintable for [&ExecutableDefinition<'_>] {
impl JsonPrintable for [ExecutableDefinitionRef<'_>] {
fn print_json(&self, writer: &mut JSONObjectWriter) {
writer.value("kind", "Document");

Expand Down Expand Up @@ -62,6 +62,24 @@ impl JsonPrintable for ExecutableDefinition<'_> {
}
}

pub enum ExecutableDefinitionRef<'a> {
OperationDefinition(&'a OperationDefinition<'a>),
FragmentDefinition(&'a FragmentDefinition<'a>),
}

impl JsonPrintable for ExecutableDefinitionRef<'_> {
fn print_json(&self, writer: &mut JSONObjectWriter) {
match self {
ExecutableDefinitionRef::OperationDefinition(op) => {
op.print_json(writer);
}
ExecutableDefinitionRef::FragmentDefinition(fragment) => {
fragment.print_json(writer);
}
}
}
}

impl JsonPrintable for OperationDefinition<'_> {
fn print_json(&self, writer: &mut JSONObjectWriter) {
writer.value("kind", "OperationDefinition");
Expand Down
10 changes: 10 additions & 0 deletions crates/printer/src/operation_base_printer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ where
.iter()
.filter(|def| matches!(def, ExecutableDefinition::OperationDefinition(_)))
.count();
let fragments = document
.definitions
.iter()
.filter_map(|def| match def {
ExecutableDefinition::FragmentDefinition(def) => Some((def.name.name, def)),
_ => None,
})
.collect();

for d in document.definitions.iter() {
match d {
Expand All @@ -53,6 +61,7 @@ where
export_input_type: self.options.export_input_type,
export_result_type: self.options.export_result_type,
operation: def,
fragments: &fragments,
};
self.visitor
.print_operation_definition(context, self.writer);
Expand All @@ -71,6 +80,7 @@ where
var_name: &var_name,
exported,
fragment: def,
fragments: &fragments,
};
self.visitor.print_fragment_definition(context, self.writer);
}
Expand Down
6 changes: 6 additions & 0 deletions crates/printer/src/operation_base_printer/visitor.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use nitrogql_ast::operation::{FragmentDefinition, OperationDefinition};
use sourcemap_writer::SourceMapWriter;

Expand Down Expand Up @@ -40,6 +42,8 @@ pub struct PrintOperationContext<'a> {
pub export_result_type: bool,
/// Operation definition.
pub operation: &'a OperationDefinition<'a>,
/// Map from fragment names to fragment definitions.
pub fragments: &'a HashMap<&'a str, &'a FragmentDefinition<'a>>,
}

#[derive(Copy, Clone, Debug)]
Expand All @@ -50,4 +54,6 @@ pub struct PrintFragmentContext<'a> {
pub exported: bool,
/// Fragment definition.
pub fragment: &'a FragmentDefinition<'a>,
/// Map from fragment names to fragment definitions.
pub fragments: &'a HashMap<&'a str, &'a FragmentDefinition<'a>>,
}
2 changes: 1 addition & 1 deletion crates/printer/src/operation_js_printer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub fn print_js_for_operation_document(
writer: &mut impl SourceMapWriter,
) {
let base_options = options.base_options;
let visitor = OperationJSPrinterVisitor::new(operation);
let visitor = OperationJSPrinterVisitor::new();
let mut printer = OperationPrinter::new(base_options, visitor, writer);
printer.print_document(operation);
}
71 changes: 71 additions & 0 deletions crates/printer/src/operation_js_printer/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,77 @@ fn print_nested_fragments() {
assert_snapshot!(print_js(&document));
}

#[test]
fn omit_unused_fragment() {
let document = parse(
r#"
query MyQuery {
user {
id
}
}
fragment Foo on User {
id
name
}
"#,
);

assert_snapshot!(print_js(&document));
}

#[test]
fn can_print_recursive_fragments() {
let document = parse(
r#"
query MyQuery {
user {
...Foo
}
}
fragment Foo on User {
id
user {
...Foo
}
}
"#,
);

assert_snapshot!(print_js(&document));
}

#[test]
fn can_print_mutually_recursive_fragments() {
let document = parse(
r#"
query MyQuery {
user {
...Foo
}
}
fragment Foo on User {
id
user {
...Bar
}
}
fragment Bar on User {
name
user {
...Foo
}
}
"#,
);

assert_snapshot!(print_js(&document));
}

fn parse(str: &str) -> OperationDocument {
let doc = parse_operation_document(str).unwrap();
let (document, _) = resolve_operation_extensions(doc).unwrap();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: crates/printer/src/operation_js_printer/tests/mod.rs
expression: print_js(&document)
---
const MyQueryQuery = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MyQuery"},"variableDefinitions":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[]}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Bar"},"directives":[]}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Bar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[]}]}}]}}]};

export { MyQueryQuery as default };

export const Foo = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Bar"},"directives":[]}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Bar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[]}]}}]}}]};

export const Bar = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Bar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[]}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Bar"},"directives":[]}]}}]}}]};


Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/printer/src/operation_js_printer/tests/mod.rs
expression: print_js(&document)
---
const MyQueryQuery = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MyQuery"},"variableDefinitions":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[]}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[]}]}}]}}]};

export { MyQueryQuery as default };

export const Foo = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[]}]}}]}}]};


Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/printer/src/operation_js_printer/tests/mod.rs
expression: print_js(&document)
---
const MyQueryQuery = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MyQuery"},"variableDefinitions":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]}]}}]}}]};

export { MyQueryQuery as default };

export const Foo = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]}]}}]};


Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export { MyQueryQuery as default };

export const Foo = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"FragmentSpread","name":{"kind":"Name","value":"Bar"},"directives":[]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Bar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]}]}}]};

export const Bar = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Bar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"FragmentSpread","name":{"kind":"Name","value":"Bar"},"directives":[]}]}}]};
export const Bar = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Bar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]}]}}]};


Loading

0 comments on commit 133bba2

Please sign in to comment.