Skip to content

Commit

Permalink
Fix dangling inline fragments when flattening fragments with unions (#…
Browse files Browse the repository at this point in the history
…836)

* fix dangling inline fragments when flattening fragments with unions

* changeset
  • Loading branch information
AlecAivazis authored Jan 21, 2023
1 parent 44da563 commit 0f1f0b4
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/green-walls-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'houdini': patch
---

Fix syntax error when generating artifacts for queries that contain fragments with direct inline fragment children
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as graphql from 'graphql'
import { test, expect } from 'vitest'

import { mockCollectedDoc, testConfig } from '../../../test'
import { flattenSelections } from '../../utils'
import selection from './selection'

test('fragments of unions inject correctly', function () {
const document = graphql.parse(`
query {
entities {
...EntityInfo
}
}
fragment EntityInfo on Entity {
... on User {
firstName
}
... on Cat {
name
}
}
`)

const config = testConfig()
const fragmentDefinitions = {
EntityInfo: document.definitions.find(
(def): def is graphql.FragmentDefinitionNode => def.kind === 'FragmentDefinition'
)!,
}

const flat = flattenSelections({
config,
filepath: '',
selections: document.definitions.find(
(def): def is graphql.OperationDefinitionNode => def.kind === 'OperationDefinition'
)!.selectionSet.selections,
fragmentDefinitions,
ignoreMaskDisable: true,
applyFragments: true,
})

const artifactSelection = selection({
config,
filepath: '',
rootType: 'Query',
operations: {},
selections: flat,
includeFragments: false,
document: mockCollectedDoc(`
query Query {
entities {
...EntityInfo
}
}`),
})

expect(artifactSelection).toMatchInlineSnapshot(`
{
"fields": {
"entities": {
"type": "Entity",
"keyRaw": "entities",
"selection": {
"abstractFields": {
"fields": {
"User": {
"firstName": {
"type": "String",
"keyRaw": "firstName"
}
},
"Cat": {
"name": {
"type": "String",
"keyRaw": "name"
}
}
},
"typeMap": {}
}
},
"abstract": true
}
}
}
`)
})
20 changes: 17 additions & 3 deletions packages/houdini/src/codegen/utils/flattenSelections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ class FieldCollection {
}
}

get size() {
return (
Object.keys(this.fields).length +
Object.keys(this.inlineFragments).length +
Object.keys(this.fragmentSpreads).length
)
}

add(selection: graphql.SelectionNode) {
// how we handle the field depends on what kind of field it is
if (selection.kind === 'Field') {
Expand Down Expand Up @@ -153,7 +161,6 @@ class FieldCollection {
}

// instead of adding the field on directly, let's turn the external fragment into an inline fragment

this.add({
kind: 'InlineFragment',
typeCondition: {
Expand All @@ -173,10 +180,17 @@ class FieldCollection {

toSelectionSet(): graphql.SelectionNode[] {
return Object.values(this.inlineFragments)
.map<graphql.SelectionNode>((fragment) => {
.flatMap<graphql.SelectionNode>((fragment) => {
// if there are no selections in the fragment, skip it
if (fragment.selection.size === 0) {
return []
}

// convert the selection to a real selection set
fragment.astNode.selectionSet.selections = fragment.selection.toSelectionSet()

return fragment.astNode
// return the value
return [fragment.astNode]
})
.concat(
Object.values(this.fields).map((field) => {
Expand Down

0 comments on commit 0f1f0b4

Please sign in to comment.