Repository: gatsbyjs/gatsby
File: packages/gatsby-source-contentful/src/extend-node-type.js (JavaScript)

Code before:
exports.extendNodeType = ({ type, store, cache }) => {
  if (type.name.match(/contentful.*RichTextNode/)) {
    return {
      nodeType: {
        type: GraphQLString,
        deprecationReason: `This field is deprecated, please use 'raw' instead. @todo add link to migration steps.`,
      },
      json: {
        type: GraphQLString,
        deprecationReason: `This field is deprecated, please use 'raw' instead. @todo add link to migration steps.`,
      },
      references: {
        type: [`ContentfulReference`],
        async resolve(source, args, context, info) {
          const parent = await context.nodeModel.findRootNodeAncestor(source)

          const rawReferences = { Entry: new Set(), Asset: new Set() }

          // Locate all Contentful Links within the rich text data
          const traverse = obj => {
            for (let k in obj) {
              const value = obj[k]
              if (value && value.sys && value.sys.type === `Link`) {
                rawReferences[value.sys.linkType].add(value.sys.contentful_id)
              } else if (value && typeof value === `object`) {
                traverse(value)
              }
            }
          }

          traverse(JSON.parse(source.raw))

          if (!rawReferences.Entry.size && !rawReferences.Asset.size) {
            return []
          }

          const rawEntries = Array.from(rawReferences.Entry)
          const rawAssets = Array.from(rawReferences.Asset)

          // Query for referenced nodes
          const resultEntries = await context.nodeModel.runQuery({
            query: {
              filter: {
                contentful_id: { in: rawEntries },
              },
            },
            type: `ContentfulEntry`,
          })

          const resultAssets = await context.nodeModel.runQuery({
            query: {

Review comment (performance):
Please make sure to test the performance of this at reasonable scale. I'm curious whether this performs well on a 15k page site. Let alone a 100k page site, if you even have any of those (or prio those at all).

Brute force traversal on an unknown object can be very expensive

Code after:
        }
      }),
  }
}

exports.extendNodeType = ({ type, store, cache }) => {
  if (type.name.match(/contentful.*RichTextNode/)) {
    return {
      nodeType: {
        type: GraphQLString,
        deprecationReason: `This field is deprecated, please use 'raw' instead. @todo add link to migration steps.`,
      },
      json: {
        type: GraphQLString,
        deprecationReason: `This field is deprecated, please use 'raw' instead. @todo add link to migration steps.`,
      },
      references: {
        type: [`ContentfulReference`],
        async resolve(source, args, context, info) {
          const parent = await context.nodeModel.findRootNodeAncestor(source)

          const rawReferences = { Entry: new Set(), Asset: new Set() }

          // Locate all Contentful Links within the rich text data
          const traverse = obj => {
            for (let k in obj) {
              const value = obj[k]
              if (value && value.sys && value.sys.type === `Link`) {
                rawReferences[value.sys.linkType].add(value.sys.contentful_id)
              } else if (value && typeof value === `object`) {
                traverse(value)
              }
            }
          }

          traverse(JSON.parse(source.raw))

          if (!rawReferences.Entry.size && !rawReferences.Asset.size) {
            return []
          }

          const rawEntries = Array.from(rawReferences.Entry)
          const rawAssets = Array.from(rawReferences.Asset)

          // Query for referenced nodes
          const resultEntries = await context.nodeModel.runQuery({
            query: {
              filter: {
                contentful_id: { in: rawEntries },
              },
            },