Skip to content

Commit 4bef9d1

Browse files
authored
For Contentful, filter out unresolvable entries and create markdown text nodes (#1202)
1 parent 60015c3 commit 4bef9d1

File tree

8 files changed

+86
-28
lines changed

8 files changed

+86
-28
lines changed

docs/docs/plugins.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ you can place the files in a `src` subfolder and build them to the plugin folder
9494
* [gatsby-remark-responsive-image](/docs/packages/gatsby-remark-responsive-image/)
9595
* [gatsby-remark-smartypants](/docs/packages/gatsby-remark-smartypants/)
9696
* [gatsby-sharp](/docs/packages/gatsby-sharp/)
97+
* [gatsby-source-contentful](/docs/packages/gatsby-source-contentful/)
9798
* [gatsby-source-drupal](/docs/packages/gatsby-source-drupal/)
9899
* [gatsby-source-filesystem](/docs/packages/gatsby-source-filesystem/)
99100
* [gatsby-source-hacker-news](/docs/packages/gatsby-source-hacker-news/)

packages/gatsby-plugin-offline/src/app-shell.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from "react"
22

33
class AppShell extends React.Component {
44
componentDidMount() {
5+
// TODO check if page exists and if not,
6+
// force a hard reload.
57
window.___navigateTo(this.props.location.pathname)
68
}
79

packages/gatsby-source-contentful/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"axios": "^0.16.1",
1818
"bluebird": "^3.4.6",
1919
"contentful": "^4.3.0",
20+
"json-stringify-safe": "^5.0.1",
2021
"lodash": "^4.17.2"
2122
}
2223
}

packages/gatsby-source-contentful/src/gatsby-node.js

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
const contentful = require(`contentful`)
22
const crypto = require(`crypto`)
3+
const stringify = require("json-stringify-safe")
4+
5+
const digest = str => crypto.createHash(`md5`).update(str).digest(`hex`)
36

47
const typePrefix = `contentful__`
58
const conflictFieldPrefix = `contentful`
@@ -58,6 +61,19 @@ exports.sourceNodes = async (
5861
console.log(`assets fetched`, assets.items.length)
5962
console.timeEnd(`fetch Contentful data`)
6063

64+
// Create map of not resolvable ids so we can filter them out while creating
65+
// links.
66+
const notResolvable = new Map()
67+
entryList.forEach(ents => {
68+
if (ents.errors) {
69+
ents.errors.forEach(error => {
70+
if (error.sys.id === `notResolvable`) {
71+
notResolvable.set(error.details.id, error.details)
72+
}
73+
})
74+
}
75+
})
76+
6177
const contentTypeItems = contentTypes.items
6278

6379
// Build foreign reference map before starting to insert any nodes
@@ -75,6 +91,11 @@ exports.sourceNodes = async (
7591
entryItemFieldValue[0].sys.id
7692
) {
7793
entryItemFieldValue.forEach(v => {
94+
// Don't create link to an unresolvable field.
95+
if (notResolvable.has(v.sys.id)) {
96+
return
97+
}
98+
7899
if (!foreignReferenceMap[v.sys.id]) {
79100
foreignReferenceMap[v.sys.id] = []
80101
}
@@ -87,7 +108,8 @@ exports.sourceNodes = async (
87108
} else if (
88109
entryItemFieldValue.sys &&
89110
entryItemFieldValue.sys.type &&
90-
entryItemFieldValue.sys.id
111+
entryItemFieldValue.sys.id &&
112+
!notResolvable.has(entryItemFieldValue.sys.id)
91113
) {
92114
if (!foreignReferenceMap[entryItemFieldValue.sys.id]) {
93115
foreignReferenceMap[entryItemFieldValue.sys.id] = []
@@ -101,6 +123,26 @@ exports.sourceNodes = async (
101123
})
102124
})
103125

126+
function createTextNode(node, text, createNode) {
127+
const textNode = {
128+
id: `${node.id}TextNode`,
129+
parent: node.id,
130+
children: [],
131+
text,
132+
internal: {
133+
type: `ComponentDescription`,
134+
mediaType: `text/x-markdown`,
135+
content: text,
136+
contentDigest: digest(text),
137+
},
138+
}
139+
140+
node.children = node.children.concat([textNode.id])
141+
createNode(textNode)
142+
143+
return textNode.id
144+
}
145+
104146
contentTypeItems.forEach((contentTypeItem, i) => {
105147
const contentTypeItemId = contentTypeItem.sys.id
106148

@@ -138,13 +180,17 @@ exports.sourceNodes = async (
138180
) {
139181
entryItemFields[
140182
`${entryItemFieldKey}___NODE`
141-
] = entryItemFieldValue.map(v => v.sys.id)
183+
] = entryItemFieldValue
184+
.filter(v => !notResolvable.has(v.sys.id))
185+
.map(v => v.sys.id)
186+
142187
delete entryItemFields[entryItemFieldKey]
143188
}
144189
} else if (
145190
entryItemFieldValue.sys &&
146191
entryItemFieldValue.sys.type &&
147-
entryItemFieldValue.sys.id
192+
entryItemFieldValue.sys.id &&
193+
!notResolvable.has(entryItemFieldValue.sys.id)
148194
) {
149195
entryItemFields[`${entryItemFieldKey}___NODE`] =
150196
entryItemFieldValue.sys.id
@@ -167,30 +213,42 @@ exports.sourceNodes = async (
167213
})
168214
}
169215

170-
const entryNode = {
216+
let entryNode = {
171217
id: entryItem.sys.id,
172218
parent: contentTypeItemId,
173219
children: [],
174-
...entryItemFields,
175220
internal: {
176221
type: `${makeTypeName(contentTypeItemId)}`,
177-
content: JSON.stringify(entryItem),
178222
mediaType: `application/json`,
179223
},
180224
}
181225

226+
// Replace text fields with text nodes so we can process their markdown
227+
// into HTML.
228+
Object.keys(entryItemFields).forEach(entryItemFieldKey => {
229+
if (entryItemFieldKey === `text`) {
230+
entryItemFields[`${entryItemFieldKey}___NODE`] = createTextNode(
231+
entryNode,
232+
entryItemFields[entryItemFieldKey],
233+
createNode
234+
)
235+
236+
delete entryItemFields[entryItemFieldKey]
237+
}
238+
})
239+
240+
entryNode = { ...entryItemFields, ...entryNode }
241+
182242
// Get content digest of node.
183-
const contentDigest = crypto
184-
.createHash(`md5`)
185-
.update(JSON.stringify(entryNode))
186-
.digest(`hex`)
243+
const contentDigest = digest(stringify(entryNode))
187244

188245
entryNode.internal.contentDigest = contentDigest
189246

190247
return entryNode
191248
})
249+
192250
// Create a node for each content type
193-
const contentTypeItemStr = JSON.stringify(contentTypeItem)
251+
const contentTypeItemStr = stringify(contentTypeItem)
194252

195253
const contentTypeNode = {
196254
id: contentTypeItemId,
@@ -201,16 +259,12 @@ exports.sourceNodes = async (
201259
description: contentTypeItem.description,
202260
internal: {
203261
type: `${makeTypeName(`ContentType`)}`,
204-
content: contentTypeItemStr,
205-
mediaType: `application/json`,
262+
mediaType: `text/x-contentful`,
206263
},
207264
}
208265

209266
// Get content digest of node.
210-
const contentDigest = crypto
211-
.createHash(`md5`)
212-
.update(JSON.stringify(contentTypeNode))
213-
.digest(`hex`)
267+
const contentDigest = digest(stringify(contentTypeNode))
214268

215269
contentTypeNode.internal.contentDigest = contentDigest
216270

@@ -222,7 +276,7 @@ exports.sourceNodes = async (
222276

223277
assets.items.forEach(assetItem => {
224278
// Create a node for each asset. They may be referenced by Entries
225-
const assetItemStr = JSON.stringify(assetItem)
279+
const assetItemStr = stringify(assetItem)
226280

227281
const assetNode = {
228282
id: assetItem.sys.id,
@@ -231,16 +285,12 @@ exports.sourceNodes = async (
231285
...assetItem.fields,
232286
internal: {
233287
type: `${makeTypeName(`Asset`)}`,
234-
content: assetItemStr,
235-
mediaType: `application/json`,
288+
mediaType: `text/x-contentful`,
236289
},
237290
}
238291

239292
// Get content digest of node.
240-
const contentDigest = crypto
241-
.createHash(`md5`)
242-
.update(JSON.stringify(assetNode))
243-
.digest(`hex`)
293+
const contentDigest = digest(stringify(assetNode))
244294

245295
assetNode.internal.contentDigest = contentDigest
246296

packages/gatsby/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"is-relative-url": "^2.0.0",
5656
"joi": "^9.1.1",
5757
"json-loader": "^0.5.2",
58+
"json-stringify-safe": "^5.0.1",
5859
"json5": "^0.5.0",
5960
"loader-utils": "^0.2.16",
6061
"lodash": "^4.17.4",

packages/gatsby/src/internal-plugins/dev-404-page/raw_dev-404-page.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Dev404Page extends React.Component {
3535
<h2>Pages ({this.props.data.allSitePage.totalCount})</h2>
3636
<ul>
3737
{this.props.data.allSitePage.edges.map(({ node }) =>
38-
<li><Link to={node.path}>{node.path}</Link></li>
38+
<li key={node.path}><Link to={node.path}>{node.path}</Link></li>
3939
)}
4040
</ul>
4141
</div>}

packages/gatsby/src/redux/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const _ = require(`lodash`)
44
const { composeWithDevTools } = require(`remote-redux-devtools`)
55
const fs = require(`fs`)
66
const EventEmitter = require(`eventemitter2`)
7+
const stringify = require(`json-stringify-safe`)
78

89
// Create event emitter for actions
910
const emitter = new EventEmitter()
@@ -47,7 +48,7 @@ const saveState = _.debounce(state => {
4748
const pickedState = _.pick(state, [`nodes`, `status`, `pageDataDependencies`])
4849
fs.writeFile(
4950
`${process.cwd()}/.cache/redux-state.json`,
50-
JSON.stringify(pickedState, null, 2),
51+
stringify(pickedState, null, 2),
5152
() => {}
5253
)
5354
}, 1000)

packages/gatsby/src/schema/infer-graphql-type.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ function inferFromFieldName(value, selector, types): GraphQLFieldConfig<*, *> {
253253
const [, , linkedField] = key.split(`___`)
254254

255255
const linkedNode = findLinkedNode(value, linkedField)
256+
256257
invariant(
257258
linkedNode,
258259
oneLine`
@@ -464,9 +465,10 @@ export function inferObjectStructureFromNodes({
464465
shouldInferFile(nodes, nextSelector, value)
465466
) {
466467
inferredField = inferFromUri(key, types)
468+
}
467469

468-
// Finally our automatic inference of field value type.
469-
} else {
470+
// Finally our automatic inference of field value type.
471+
if (!inferredField) {
470472
inferredField = inferGraphQLType({
471473
nodes,
472474
types,

0 commit comments

Comments
 (0)