连接两个 gatsby 节点

Connecting two gatsby nodes

因此,我正在使用 gatsby-mdx 插件从 MDX 文件创建站点。我想在 SitePage 对象和 Mdx 对象之间创建一个关联,这样我就可以对 SitePage 边缘执行一个 graphQL 查询以构建站点导航。

我的大部分代码都是用 TypeScript 编写的,所以如果您想知道这些是什么鬼,请忽略任何类型注释。

我尝试过的事情

使用字段

我的第一个想法是使用 onCreateNode API, grab the MDX node, and add it to the SitePage using the createNodeField action. That all works great, B-U-T the gatsby-mdx plugin adds a bunch of other info to their node later using the setFieldsOnGraphQLNodeType API(在 onCreateNode API 之后出现)。我希望这些字段(例如 frontmatter 和 tableOfContents)在以后的 graphql 查询中可用,但他们没有使用这种方法。

实现我自己的setFieldsOnGraphQLNodeType

我想我可以像 gatsby-mdx 扩展 Mdx 节点一样扩展 SitePage 对象。

我 运行 在这里遇到的关键问题是我不知道如何创建 Mdx GraphQL 节点类型。

export const setFieldsOnGraphQLNodeType = ({type, actions, getNodes}: any, pluginOptions: any) => {
    if (type.name === "SitePage") {
        const {createParentChildLink} = actions
        return new Promise((resolve) => {
            return resolve({
                "childMdx": {
                    type: new GraphQLObjectType({
                        name: 'Mdx'
                    }),
                    async resolve(sitePageNode: any) {
                        const allNodes = getNodes()
                        if (sitePageNode.component &&
                            (sitePageNode.component.endsWith(".mdx") || sitePageNode.component === DefaultLayout)
                        ) {
                            const associatedMdx = allNodes.find((mdxNode: any) =>
                                mdxNode.internal.type === 'Mdx' && mdxNode.fileAbsolutePath === sitePageNode.component
                            )
                            if (associatedMdx) {
                                console.log("Found associated MDX node", associatedMdx.id)
                                console.log("Adding it to the sitepage node", sitePageNode.id)
                                return associatedMdx
                            }
                        }
                    }
                }
            })
        })
    }
    return {}
}

我也试过简单地将类型作为字符串传递 ('Mdx'),但也失败了。

使用父子链接

该插件使用 createParentChildLink action (source).[=] 在 onCreateNode API 中的文件节点和已解析的 MDX 节点之间创建父子 link。 26=]

我尝试实施那个...

export const onCreateNode = ({node, actions, getNodes}: OnCreateNodeArgument) => {
    const {createParentChildLink} = actions
    const allNodes = getNodes()
    if (node.internal && node.internal.type === 'SitePage' && node.component &&
        (node.component.endsWith(".mdx") || node.component === DefaultLayout)
    ) {
        const associatedMdx = allNodes.find((mdxNode: any) =>
            mdxNode && mdxNode.internal && mdxNode.internal.type === 'Mdx' &&
                (mdxNode.fileAbsolutePath === node.component || mdxNode.fileAbsolutePath === node.context.fileAbsolutePath)
        )
        if (associatedMdx) {
            console.log("Found associated MDX node", associatedMdx.id)
            console.log("Adding it to the sitepage node as a child", node.id)
            createParentChildLink({parent: node, child: associatedMdx})
        }
    }
}

起初,这似乎成功了,但是 gatsby-mdx 添加到 Mdx 节点的 tableOfContents property 在 graphQL 查询中仍然不可用,例如:

{
    allSitePage(filter: {fields: {childMdx: {id: {ne: null}}}}) {
        edges {
            node {
                path
                fields{
                    childMdx {
                        tableOfContents
                        fileAbsolutePath
                        frontmatter {
                            title
                        }
                    }
                }
                context {
                    roughFilePath
                    id
                }
            }
        }
    }
}

其他(可能不相关的)信息

我是盖茨比 creating some pages programmatically-node.js.

我看到了关于使用类似用例的建议 node type mappings,但是我因为我在 SitePage 和 MDX 对象之间的映射需要一些技巧(具体来说,从 siteMetadata 读取一些东西并做字符串比较),我认为这不适用于我的用例。

所以我终于找到了一个更好的解决方案(比我之前的尝试,它涉及将 mdx 节点泵入页面的 context)。

Gatsby 有一个 undocumented method 到 link 个节点:

Yes, you can can use createNodeField with the not-yet-documented ___NODE syntax to create links between nodes.

所以,步骤是这样的:

  • createPage中,将Mdx节点的id存储到SitePage节点。
  • onCreateNode中,如果节点是SitePage,使用createNodeFieldMdx___NODE作为字段名,Mdx节点的id作为值。

我的gatsby-node.js:

const path = require("path")
const { createFilePath } = require("gatsby-source-filesystem")

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions

  if (node.internal.type === "SitePage" && node.context && node.context.id) {

    createNodeField({
      name: "Mdx___NODE",
      value: node.context.id,
      node,
    })
  }

  if (node.internal.type === "Mdx") {
    const value = createFilePath({ node, getNode })
    createNodeField({
      // 1) this is the name of the field you are adding,
      name: "slug",
      // 2) this node refers to each individual MDX
      node,
      value: `/blog${value}`
    })
  }
}


exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;
  const { data, errors } = await graphql(`
    {
      allMdx {
        edges {
          node {
            id
            fields {
              slug
            }
          }
        }
      }
    }
  `)

  if (errors) throw errors
  data.allMdx.edges.forEach(({ node }) => {
    createPage({
      path: node.fields.slug,
      component: path.resolve(`./src/components/posts-page-layout.js`),
      context: { id: node.id }
    });
  });
};

结果:

希望对您有所帮助!