Skip to content

Instantly share code, notes, and snippets.

@rmosolgo
Last active May 30, 2022 18:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rmosolgo/9250e2ff27640265576e81e60e09b95e to your computer and use it in GitHub Desktop.
Save rmosolgo/9250e2ff27640265576e81e60e09b95e to your computer and use it in GitHub Desktop.
Inline Fragments
require "bundler/inline"
gemfile do
gem "graphql", "~>2.0"
end
# This visitor will inline any fragment definitions in the document.
# It assumes that the document is valid; it will :boom: if there are
# cycles in the fragment spreads.
#
# It doesn't deduplicate selections.
class InlineFragments < GraphQL::Language::Visitor
def on_document(node, _parent)
# First, build up this map so we can access it later:
@fragment_definition_nodes = {}
node.definitions.each do |defn_node|
if defn_node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
@fragment_definition_nodes[defn_node.name] = defn_node
end
end
# And go ahead and flatten definitions
@fragment_definition_nodes.keys.each do |k|
@fragment_definition_nodes[k] = replace_fragment_spreads(@fragment_definition_nodes[k])
end
super
end
def on_operation_definition(node, _parent)
modified_node = replace_fragment_spreads(node)
super(modified_node, _parent)
end
def on_fragment_definition(node, _parent)
super(DELETE_NODE, _parent)
end
def on_field(node, _parent)
modified_node = replace_fragment_spreads(node)
super(modified_node, _parent)
end
def on_inline_fragment(node, _parent)
modified_node = replace_fragment_spreads(node)
super(modified_node, _parent)
end
private
def replace_fragment_spreads(node)
new_selections = []
substituted = false
node.selections.each do |selection|
if selection.is_a?(GraphQL::Language::Nodes::FragmentSpread)
substituted = true
defn_node = @fragment_definition_nodes[selection.name]
inline_frag_node = GraphQL::Language::Nodes::InlineFragment.new(
directives: [],
selections: defn_node.selections,
type: defn_node.type,
)
new_selections << inline_frag_node
else
new_selections << selection
end
end
if substituted
node.merge(selections: new_selections)
else
node
end
end
end
# Example:
doc = GraphQL.parse("
{
...A1
...B1
field1 {
field2 {
...C1
}
}
}
fragment A2 on T {
fieldA3
...C1
}
fragment A1 on T {
fieldA1
fieldA2 {
...A2
}
}
fragment B1 on T {
fieldB1
...A1
}
fragment C1 on T {
... {
fieldC1
fieldC2 {
... {
fieldC3
...C2
}
}
}
}
fragment C2 on T {
fieldC4
}
")
visitor = InlineFragments.new(doc)
visitor.visit
puts visitor.result.to_query_string
# query {
# ... on T {
# fieldA1
# fieldA2 {
# ... on T {
# fieldA3
# ... on T {
# ... {
# fieldC1
# fieldC2 {
# ... {
# fieldC3
# ... on T {
# fieldC4
# }
# }
# }
# }
# }
# }
# }
# }
# ... on T {
# fieldB1
# ... on T {
# fieldA1
# fieldA2 {
# ... on T {
# fieldA3
# ... on T {
# ... {
# fieldC1
# fieldC2 {
# ... {
# fieldC3
# ... on T {
# fieldC4
# }
# }
# }
# }
# }
# }
# }
# }
# }
# field1 {
# field2 {
# ... on T {
# ... {
# fieldC1
# fieldC2 {
# ... {
# fieldC3
# ... on T {
# fieldC4
# }
# }
# }
# }
# }
# }
# }
# }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment