Skip to content

Commit 146b197

Browse files
authored
fix(gatsby): print childOf directive for implicit child fields (#28483)
* fix(gatsby): print childOf directive for implicit child fields * fix comments * address review comments * be more defensive * Revert "be more defensive" This reverts commit e139486 * fix duplicate types printing in `childOf` * fix invalid deprecation warning when mimeTypes is set
1 parent 3af7182 commit 146b197

File tree

4 files changed

+162
-44
lines changed

4 files changed

+162
-44
lines changed

packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ input Baz {
120120
qux: Boolean
121121
}
122122
123+
type BarChild implements Node @childOf(types: [\\"Test\\"]) @dontInfer {
124+
bar: String
125+
}
126+
127+
type FooChild implements Node @childOf(types: [\\"Test\\"]) @dontInfer {
128+
bar: String
129+
}
130+
123131
type Test implements Node @dontInfer {
124132
foo: Int
125133
}"
@@ -148,6 +156,10 @@ type Nested {
148156
149157
input Baz {
150158
qux: Boolean
159+
}
160+
161+
type BarChild implements Node @childOf(types: [\\"Test\\"]) @dontInfer {
162+
bar: String
151163
}"
152164
`;
153165

@@ -266,6 +278,14 @@ type OneMoreTest implements Node @dontInfer {
266278
267279
union ThisOrThat = AnotherTest | OneMoreTest
268280
281+
type BarChild implements Node @childOf(types: [\\"Test\\"]) @dontInfer {
282+
bar: String
283+
}
284+
285+
type FooChild implements Node @childOf(types: [\\"Test\\"]) @dontInfer {
286+
bar: String
287+
}
288+
269289
type Test implements Node @dontInfer {
270290
foo: Int
271291
}"
@@ -423,6 +443,14 @@ input Baz {
423443
qux: Boolean
424444
}
425445
446+
type BarChild implements Node @childOf(types: [\\"Test\\"]) @dontInfer {
447+
bar: String
448+
}
449+
450+
type FooChild implements Node @childOf(types: [\\"Test\\"]) @dontInfer {
451+
bar: String
452+
}
453+
426454
type Test implements Node @dontInfer {
427455
foo: Int
428456
}"

packages/gatsby/src/schema/__tests__/print.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ const { build } = require(`..`)
22
import { buildObjectType } from "../types/type-builders"
33
const { store } = require(`../../redux`)
44
const { actions } = require(`../../redux/actions/restricted`)
5+
const { actions: publicActions } = require(`../../redux/actions/public`)
6+
const { createParentChildLink } = publicActions
57
const { printTypeDefinitions } = actions
68

79
jest.mock(`fs-extra`)
@@ -41,14 +43,35 @@ jest.spyOn(global.Date.prototype, `toISOString`).mockReturnValue(`2019-01-01`)
4143
describe(`Print type definitions`, () => {
4244
beforeEach(() => {
4345
store.dispatch({ type: `DELETE_CACHE` })
44-
const node = {
46+
const node1 = {
4547
id: `test1`,
4648
internal: {
4749
type: `Test`,
4850
},
51+
children: [],
4952
foo: 26,
5053
}
51-
store.dispatch({ type: `CREATE_NODE`, payload: { ...node } })
54+
const node2 = {
55+
id: `test2`,
56+
parent: `test1`,
57+
internal: {
58+
type: `FooChild`,
59+
},
60+
bar: `bar`,
61+
}
62+
const node3 = {
63+
id: `test3`,
64+
parent: `test1`,
65+
internal: {
66+
type: `BarChild`,
67+
},
68+
bar: `bar`,
69+
}
70+
store.dispatch({ type: `CREATE_NODE`, payload: { ...node1 } })
71+
store.dispatch({ type: `CREATE_NODE`, payload: { ...node2 } })
72+
store.dispatch({ type: `CREATE_NODE`, payload: { ...node3 } })
73+
createParentChildLink({ parent: node1, child: node2 })
74+
createParentChildLink({ parent: node1, child: node3 })
5275
const typeDefs = []
5376
typeDefs.push(`
5477
type AnotherTest implements Node & ITest {
@@ -96,6 +119,18 @@ describe(`Print type definitions`, () => {
96119
types: [`OneMoreTest`],
97120
},
98121
},
122+
}),
123+
buildObjectType({
124+
name: `BarChild`,
125+
fields: {
126+
id: `ID!`,
127+
},
128+
interfaces: [`Node`],
129+
extensions: {
130+
childOf: {
131+
types: [`Test`],
132+
},
133+
},
99134
})
100135
)
101136
store.dispatch({
@@ -108,6 +143,11 @@ describe(`Print type definitions`, () => {
108143
payload: typeDefs[1],
109144
plugin: { name: `gatsby-plugin-another-test` },
110145
})
146+
store.dispatch({
147+
type: `CREATE_TYPES`,
148+
payload: typeDefs[2],
149+
plugin: { name: `gatsby-plugin-another-test` },
150+
})
111151
})
112152

113153
it(`saves type definitions to default file`, async () => {

packages/gatsby/src/schema/extensions/__tests__/child-relations.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,24 @@ describe(`Define parent-child relationships with field extensions`, () => {
186186
)
187187
})
188188

189+
it(`does not show deprecation warning for inferred child fields`, async () => {
190+
dispatch(
191+
createTypes(`
192+
type Parent implements Node @dontInfer @mimeTypes(types: ["application/listenup"]) {
193+
id: ID!
194+
}
195+
type Child implements Node @childOf(mimeTypes: ["application/listenup"]) {
196+
id: ID!
197+
}
198+
type AnotherChild implements Node @childOf(types: ["Parent"]) {
199+
id: ID!
200+
}
201+
`)
202+
)
203+
await buildSchema()
204+
expect(report.warn).toBeCalledTimes(0)
205+
})
206+
189207
it(`adds children[Field] field to parent type with childOf extension`, async () => {
190208
dispatch(
191209
createTypes(`

packages/gatsby/src/schema/schema.js

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ const updateSchemaComposer = async ({
146146
inferenceMetadata,
147147
parentSpan: activity.span,
148148
})
149+
addInferredChildOfExtensions({
150+
schemaComposer,
151+
})
149152
activity.end()
150153

151154
activity = report.phantomActivity(`Processing types`, {
@@ -202,11 +205,6 @@ const processTypeComposer = async ({
202205

203206
if (typeComposer.hasInterface(`Node`)) {
204207
await addNodeInterfaceFields({ schemaComposer, typeComposer, parentSpan })
205-
await addImplicitConvenienceChildrenFields({
206-
schemaComposer,
207-
typeComposer,
208-
parentSpan,
209-
})
210208
}
211209
await determineSearchableFields({
212210
schemaComposer,
@@ -999,15 +997,42 @@ const addConvenienceChildrenFields = ({ schemaComposer }) => {
999997
})
1000998
}
1001999

1002-
const addImplicitConvenienceChildrenFields = ({
1003-
schemaComposer,
1004-
typeComposer,
1005-
}) => {
1000+
const isExplicitChild = ({ typeComposer, childTypeComposer }) => {
1001+
if (!childTypeComposer.hasExtension(`childOf`)) {
1002+
return false
1003+
}
1004+
const childOfExtension = childTypeComposer.getExtension(`childOf`)
1005+
const { types: parentMimeTypes = [] } =
1006+
typeComposer.getExtension(`mimeTypes`) ?? {}
1007+
1008+
return (
1009+
childOfExtension?.types?.includes(typeComposer.getTypeName()) ||
1010+
childOfExtension?.mimeTypes?.some(mimeType =>
1011+
parentMimeTypes.includes(mimeType)
1012+
)
1013+
)
1014+
}
1015+
1016+
const addInferredChildOfExtensions = ({ schemaComposer }) => {
1017+
schemaComposer.forEach(typeComposer => {
1018+
if (
1019+
typeComposer instanceof ObjectTypeComposer &&
1020+
typeComposer.hasInterface(`Node`)
1021+
) {
1022+
addInferredChildOfExtension({
1023+
schemaComposer,
1024+
typeComposer,
1025+
})
1026+
}
1027+
})
1028+
}
1029+
1030+
const addInferredChildOfExtension = ({ schemaComposer, typeComposer }) => {
10061031
const shouldInfer = typeComposer.getExtension(`infer`)
1007-
// In Gatsby v3, when `@dontInfer` is set, children fields will not be
1008-
// created for parent-child relations set by plugins with
1032+
// In Gatsby v3, when `@dontInfer` is set, `@childOf` extension will not be
1033+
// automatically created for parent-child relations set by plugins with
10091034
// `createParentChildLink`. With `@dontInfer`, only parent-child
1010-
// relations explicitly set with the `childOf` extension will be added.
1035+
// relations explicitly set with the `@childOf` extension will be added.
10111036
// if (shouldInfer === false) return
10121037

10131038
const parentTypeName = typeComposer.getTypeName()
@@ -1016,39 +1041,46 @@ const addImplicitConvenienceChildrenFields = ({
10161041
const childNodesByType = groupChildNodesByType({ nodes })
10171042

10181043
Object.keys(childNodesByType).forEach(typeName => {
1019-
// Adding children fields to types with the `@dontInfer` extension is deprecated
1020-
if (shouldInfer === false) {
1021-
const childTypeComposer = schemaComposer.getAnyTC(typeName)
1022-
const childOfExtension = childTypeComposer.getExtension(`childOf`)
1044+
const childTypeComposer = schemaComposer.getAnyTC(typeName)
1045+
let childOfExtension = childTypeComposer.getExtension(`childOf`)
10231046

1024-
// Only warn when the parent-child relation has not been explicitly set with
1025-
if (
1026-
!childOfExtension ||
1027-
!childOfExtension.types.includes(parentTypeName)
1028-
) {
1029-
const childField = fieldNames.convenienceChild(typeName)
1030-
const childrenField = fieldNames.convenienceChildren(typeName)
1031-
const childOfTypes = (childOfExtension?.types ?? [])
1032-
.concat(parentTypeName)
1033-
.map(name => `"${name}"`)
1034-
.join(`,`)
1035-
1036-
report.warn(
1037-
`Deprecation warning: ` +
1038-
`In Gatsby v3 fields \`${parentTypeName}.${childField}\` and \`${parentTypeName}.${childrenField}\` ` +
1039-
`will not be added automatically because ` +
1040-
`type \`${typeName}\` does not explicitly list type \`${parentTypeName}\` in \`childOf\` extension.\n` +
1041-
`Add the following type definition to fix this:\n\n` +
1042-
` type ${typeName} implements Node @childOf(types: [${childOfTypes}]) {\n` +
1043-
` id: ID!\n` +
1044-
` }\n\n` +
1045-
`https://www.gatsbyjs.com/docs/actions/#createTypes`
1046-
)
1047-
}
1047+
if (isExplicitChild({ typeComposer, childTypeComposer })) {
1048+
return
10481049
}
1050+
if (shouldInfer === false) {
1051+
// Adding children fields to types with the `@dontInfer` extension is deprecated
1052+
// Only warn when the parent-child relation has not been explicitly set with `childOf` directive
1053+
const childField = fieldNames.convenienceChild(typeName)
1054+
const childrenField = fieldNames.convenienceChildren(typeName)
1055+
const childOfTypes = (childOfExtension?.types ?? [])
1056+
.concat(parentTypeName)
1057+
.map(name => `"${name}"`)
1058+
.join(`,`)
10491059

1050-
typeComposer.addFields(createChildrenField(typeName))
1051-
typeComposer.addFields(createChildField(typeName))
1060+
report.warn(
1061+
`Deprecation warning: ` +
1062+
`In Gatsby v3 fields \`${parentTypeName}.${childField}\` and \`${parentTypeName}.${childrenField}\` ` +
1063+
`will not be added automatically because ` +
1064+
`type \`${typeName}\` does not explicitly list type \`${parentTypeName}\` in \`childOf\` extension.\n` +
1065+
`Add the following type definition to fix this:\n\n` +
1066+
` type ${typeName} implements Node @childOf(types: [${childOfTypes}]) {\n` +
1067+
` id: ID!\n` +
1068+
` }\n\n` +
1069+
`https://www.gatsbyjs.com/docs/actions/#createTypes`
1070+
)
1071+
}
1072+
// Set `@childOf` extension automatically
1073+
// This will cause convenience children fields like `childImageSharp`
1074+
// to be added in `addConvenienceChildrenFields` method.
1075+
// Also required for proper printing of the `@childOf` directive in the snapshot plugin
1076+
if (!childOfExtension) {
1077+
childOfExtension = {}
1078+
}
1079+
if (!childOfExtension.types) {
1080+
childOfExtension.types = []
1081+
}
1082+
childOfExtension.types.push(parentTypeName)
1083+
childTypeComposer.setExtension(`childOf`, childOfExtension)
10521084
})
10531085
}
10541086

0 commit comments

Comments
 (0)