Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow subobjects in markdown frontmatter arrays to link to files #2255

Merged
merged 2 commits into from
Sep 28, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Allow subobjects in markdown frontmatter arrays to link to files
  • Loading branch information
KyleAMathews committed Sep 28, 2017
commit 2e0ca7aedbcde916e51bd697fac5e05ba4ee8596
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Array [
Object {
"children": Array [],
"frontmatter": Object {
"___PARENT": "whatever",
"date": "2017-09-18T23:19:51.246Z",
"parent": "whatever",
"title": "my little pony",
Expand Down Expand Up @@ -35,6 +36,7 @@ Array [
"child": Object {
"children": Array [],
"frontmatter": Object {
"___PARENT": "whatever",
"date": "2017-09-18T23:19:51.246Z",
"parent": "whatever",
"title": "my little pony",
Expand Down
17 changes: 17 additions & 0 deletions packages/gatsby-transformer-remark/src/on-node-create.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,26 @@ module.exports = async function onCreateNode({
type: `MarkdownRemark`,
},
}

// Add ___PARENT to sub-object in the frontmatter so we can
// use this to find the root markdown node when running GraphQL
// queries. Yes this is lame. But it's because in GraphQL child nodes
// can't access their parent nodes so we use this ___PARENT convention
// to get around this.
_.each(data.data, (v, k) => {
if (_.isArray(v) && _.isObject(v[0])) {
data.data[k] = v.map(o => {
return { ...o, ___PARENT: node.id }
})
}
})

markdownNode.frontmatter = {
title: ``, // always include a title
...data.data,
___PARENT: node.id,
// TODO Depreciate this at v2 as much larger chance of conflicting with a
// user supplied field.
parent: node.id,
}

Expand Down
80 changes: 70 additions & 10 deletions packages/gatsby/src/schema/infer-graphql-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,11 +345,15 @@ function findRootNode(node) {
let rootNode = node
let whileCount = 0
while (
rootNode.parent &&
getNode(rootNode.parent) !== undefined &&
(rootNode.___PARENT || rootNode.parent) &&
(getNode(rootNode.parent) !== undefined || getNode(rootNode.___PARENT)) &&
whileCount < 101
) {
rootNode = getNode(rootNode.parent)
if (rootNode.___PARENT) {
rootNode = getNode(rootNode.___PARENT)
} else {
rootNode = getNode(rootNode.parent)
}
whileCount += 1
if (whileCount > 100) {
console.log(
Expand All @@ -363,13 +367,6 @@ function findRootNode(node) {
}

function shouldInferFile(nodes, key, value) {
// Find the node used for this example.
const node = nodes.find(n => _.get(n, key) === value)

if (!node) {
return false
}

const looksLikeFile =
_.isString(value) &&
mime.lookup(value) !== `application/octet-stream` &&
Expand All @@ -382,6 +379,68 @@ function shouldInferFile(nodes, key, value) {
return false
}

// Find the node used for this example.
let node = nodes.find(n => _.get(n, key) === value)

if (!node) {
// Try another search as our "key" isn't always correct e.g.
// it doesn't support arrays so the right key could be "a.b[0].c" but
// this function will get "a.b.c".
//
// We loop through every value of nodes until we find
// a match.
const visit = (current, selector = [], fn) => {
for (let i = 0, keys = Object.keys(current); i < keys.length; i++) {
const key = keys[i]
const value = current[key]

if (value === undefined || value === null) continue

if (typeof value === "object" || typeof value === "function") {
visit(current[key], selector.concat([key]), fn)
continue
}

let proceed = fn(current[key], key, selector, current)

// returning false (and only false)
// from the visitor function will stop the
// visitations
if (proceed === false) {
break
}
}
}

const isNormalInteger = str => /^\+?(0|[1-9]\d*)$/.test(str)

node = nodes.find(n => {
let isMatch = false
visit(n, [], (v, k, selector, parent) => {
if (v === value) {
const normalizedSelector = selector
.map(s => (isNormalInteger(s) ? `` : s))
.filter(s => s !== ``)
const fullSelector = `${normalizedSelector.join(`.`)}.${k}`
if (fullSelector === key) {
isMatch = true
return false
}
}

// Not a match so we continue
return true
})

return isMatch
})

// Still no node.
if (!node) {
return false
}
}

const rootNode = findRootNode(node)

// Only nodes transformed (ultimately) from a File
Expand Down Expand Up @@ -498,6 +557,7 @@ export function inferObjectStructureFromNodes({
// Third if the field is pointing to a file (from another file).
} else if (
nodes[0].internal.type !== `File` &&
_.isString(value) &&
shouldInferFile(nodes, nextSelector, value)
) {
inferredField = inferFromUri(key, types)
Expand Down