连接两个 gatsby 节点

jon*_*bot 7 javascript graphql graphql-js gatsby

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

我的大部分代码都在 TypeScript 中,所以如果您想知道 WTF 是什么类型的注释,请忽略任何类型注释。

我尝试过的事情

使用字段

我的第一个想法是使用onCreateNodeAPI,获取 MDX 节点,然后使用createNodeField操作将其添加到 SitePage 。这一切的伟大工程,但盖茨比,MDX插件增加了大量的其他信息,以它们的节点以后使用setFieldsOnGraphQLNodeTypeAPI(发生onCreateNodeAPI)。我希望这些字段(例如 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 {}
}
Run Code Online (Sandbox Code Playgroud)

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

使用父子链接

该插件onCreateNode使用createParentChildLink操作(source)在API 中的 File 节点和解析的 MDX 节点之间创建父子链接。

我尝试实现...

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})
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Run Code Online (Sandbox Code Playgroud)

其他(可能不相关)信息

我正在gatsby-node.js 中以编程方式创建一些页面

我已经看到类似用例使用节点类型映射的建议,但我因为我在 SitePage 和 MDX 对象之间的映射需要一些技巧(特别是,从 siteMetadata 读取一些东西并进行字符串比较),我不认为这不适用于我的用例。

Der*_*yen 9

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

Gatsby 有一种未公开的方法将节点相互链接:

是的,您可以使用 createNodeField 和尚未记录的 ___NODE 语法来创建节点之间的链接。

所以,步骤是这样的:

  • 在 中createPage,将idMdx 节点的存储到 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 }
    });
  });
};
Run Code Online (Sandbox Code Playgroud)

结果:

图形

希望能帮助到你!