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

perf(gatsby): do not call and iterate getAllNodes(File) for each file #28891

Merged
merged 7 commits into from
Jan 8, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 45 additions & 30 deletions packages/gatsby/src/schema/resolvers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import systemPath from "path"
import normalize from "normalize-path"
import _ from "lodash"
import {
GraphQLList,
GraphQLType,
Expand Down Expand Up @@ -29,6 +28,9 @@ import { IGatsbyNode } from "../redux/types"

type ResolvedLink = IGatsbyNode | Array<IGatsbyNode> | null

type nestedListOfStrings = Array<string | nestedListOfStrings>
type nestedListOfNodes = Array<IGatsbyNode | nestedListOfNodes>
Comment on lines +31 to +32
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funsies. But turns out the resulting nesting structure must match the input nesting structure. So if you have [a, [b, [c, d]]] as input, then your output should be the same except the input strings are replaced with nodes.

This is a tad annoying since it means we can't "just" pass on a flat array of promises to Promise.all() but so be it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realise this is already merged, but types should be PascalCased


export function findMany<TSource, TArgs>(
typeName: string
): GatsbyResolver<TSource, TArgs> {
Expand Down Expand Up @@ -353,15 +355,22 @@ export function fileByPath<TSource, TArgs>(
args,
context,
info
): Promise<any> {
): Promise<IGatsbyNode | nestedListOfNodes | null> {
const resolver = fieldConfig.resolve || context.defaultFieldResolver
const fieldValue = await resolver(source, args, context, {
...info,
from: options.from || info.from,
fromNode: options.from ? options.fromNode : info.fromNode,
})
const fieldValue: nestedListOfStrings = await resolver(
source,
args,
context,
{
...info,
from: options.from || info.from,
fromNode: options.from ? options.fromNode : info.fromNode,
}
)

if (fieldValue == null) return null
if (fieldValue == null) {
return null
}

// Find the File node for this node (we assume the node is something
// like markdown which would be a child node of a File node).
Expand All @@ -370,34 +379,40 @@ export function fileByPath<TSource, TArgs>(
node => node.internal && node.internal.type === `File`
)

const findLinkedFileNode = (relativePath: string): any => {
// Use the parent File node to create the absolute path to
// the linked file.
const fileLinkPath = normalize(
systemPath.resolve(parentFileNode.dir, relativePath)
)
async function queryNodesByPath(
relPaths: nestedListOfStrings
): Promise<nestedListOfNodes> {
const arr: nestedListOfNodes = []
for (let i = 0; i < relPaths.length; ++i) {
arr[i] = await (Array.isArray(relPaths[i])
? queryNodesByPath(relPaths[i] as nestedListOfStrings)
: queryNodeByPath(relPaths[i] as string))
}
return arr
}

// Use that path to find the linked File node.
const linkedFileNode = _.find(
context.nodeModel.getAllNodes({ type: `File` }),
n => n.absolutePath === fileLinkPath
)
return linkedFileNode
function queryNodeByPath(relPath: string): Promise<IGatsbyNode> {
return context.nodeModel.runQuery({
query: {
filter: {
absolutePath: {
eq: normalize(systemPath.resolve(parentFileNode.dir, relPath)),
},
},
},
firstOnly: true,
type: `File`,
})
}

return resolveValue(findLinkedFileNode, fieldValue)
if (Array.isArray(fieldValue)) {
return queryNodesByPath(fieldValue)
} else {
return queryNodeByPath(fieldValue)
}
}
}

function resolveValue(
resolve: (a: any) => any,
value: any | Array<any>
): any | Array<any> {
return Array.isArray(value)
? value.map(v => resolveValue(resolve, v))
: resolve(value)
}

function getProjectedField(
info: GraphQLResolveInfo,
fieldName: string
Expand Down