连接两个 gatsby 节点

Posted

技术标签:

【中文标题】连接两个 gatsby 节点【英文标题】:Connecting two gatsby nodes 【发布时间】:2019-06-09 17:37:45 【问题描述】:

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

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

我尝试过的事情

使用字段

我的第一个想法是使用 onCreateNode API,获取 MDX 节点,然后使用 createNodeField 操作将其添加到 SitePage。这一切都很好,B-U-T gatsby-mdx 插件adds a bunch of other info to their node 稍后使用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 操作 (source) 在 onCreateNode 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)
        
    

起初,这似乎是成功的,但 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
                
            
        
    


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

我是 gatsby-node.js 中的 creating some pages programmatically。

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

【问题讨论】:

也许我遗漏了一些东西,但不应该所有 mdx 文件都已经是 sitePage 节点的一部分吗?如果您的目的是构建站点导航,您将无法在其中找到所有生成的页面吗? 因此,SitePage 节点不会在没有干预的情况下直接链接到 graphql 查询中与其关联的 Mdx 节点。我想要导航的 Mdx 节点上有一些特定信息:frontmatter 和 tableOfContents。这些不在 SitePage 节点本身。虽然我可以对allSitePageallMdx 进行一次大查询,然后使用 javascript 将它们组合在一起,但……嗯,有点糟糕!从表面上看,我应该能够操纵 SitePage 节点以包含我想要的信息,但是各个阶段的时间安排使事情变得复杂。 createParentChildLink 操作似乎是最合适的解决方案。我可能只需要对 TypeError: Cannot read property 'internal' of undefined 消息进行深入的调试。 更新了问题,因为在我清除 .cache 文件夹并重新启动后,该神秘错误消失了。 感谢您提供额外的上下文,我从未处理过此问题,但知道如何处理可能会很好。我刚刚在下面分享了我的发现作为答案 【参考方案1】:

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

Gatsby 有一个 undocumented method 用于将节点相互链接:

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

所以,步骤是这样的:

createPage中,将Mdx节点的id存储到SitePage节点。 在onCreateNode 中,如果节点为SitePage,则使用createNodeField,以Mdx___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 
    );
  );
;

结果:

希望对你有帮助!

【讨论】:

确实如此!谢谢。

以上是关于连接两个 gatsby 节点的主要内容,如果未能解决你的问题,请参考以下文章

gatsby-source-wordpress 插件:我无法将数据从 WP 拉入 Gatsby(连接错误)

尝试在 GraphCMS api 上连接时出现 Gatsby Graphql 问题

单个 GraphQL 查询中的多个源 (Gatsby)

确定层次结构中的两个节点是不是已连接

如何使用匹配节点连接两个 XML 文件

如何从两个不同的中继连接中删除共享节点?