Соединение двух узлов Гэтсби - PullRequest
0 голосов
/ 15 января 2019

Итак, я использую плагин gatsby-mdx для создания сайта из файлов MDX. Я хочу создать связь между объектом SitePage и объектом Mdx, чтобы я мог выполнить один запрос graphQL к краям SitePage для построения навигации по сайту.

Большая часть моего кода написана на TypeScript, поэтому игнорируйте любые аннотации типов, если вам интересно WTF.

Вещи, которые я пробовал

Использование полей

Моей первой мыслью было использование API onCreateNode, захват узла MDX и добавление его в SitePage с помощью действия createNodeField. Все это прекрасно работает, НО плагин gatsby-mdx добавляет кучу другой информации к их узлу позже, используя setFieldsOnGraphQLNodeType API (который происходит после onCreateNode API). Я хочу, чтобы эти поля (например, frontmatter и tableOfContents) были доступны в последующих запросах graphql, но они не используют этот метод.

Реализация моей собственной setFieldsOnGraphQLNodeType

Я подумал, что могу просто расширить объект SitePage так же, как gatsby-mdx расширяет узел Mdx.

Ключевая проблема, с которой я столкнулся, заключалась в том, что я не мог понять, как создать тип узла 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 {}
}

Я также пытался просто передать тип как String ('Mdx'), но это тоже не удалось.

Использование ссылок родитель-потомок

Этот плагин создает родительско-дочернюю ссылку между узлом File и проанализированным MDX-узлом в onCreateNode API, используя действие createParentChildLink ( source ).

Я пытался реализовать это ...

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})
        }
    }
}

Сначала это кажется успешным, но свойство tableOfContents , которое gatsby-mdx добавляет к узлу Mdx, все еще недоступно в запросе graphQL, например:

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

Другая (возможно неактуальная) информация

Я создаю несколько страниц программно в gatsby-node.js.

Я видел предложение для аналогичных случаев использования сопоставления типов узлов , но я, поскольку мое сопоставление между SitePage и объектом MDX требует некоторого изящества (в частности, чтение некоторых вещей из siteMetadata и сравнение строк), я не думаю, что это будет работать для моего варианта использования.

1 Ответ

0 голосов
/ 16 января 2019

Итак, я наконец-то нашел лучшее решение (чем моя предыдущая попытка, включающая накачку узла mdx в context страницы).

У Гэтсби есть недокументированный метод для связи узлов друг с другом:

Да, вы можете использовать createNodeField с еще не документированным синтаксисом ___NODE для создания связей между узлами.

Итак, шаги такие:

  • В createPage сохраните id узла Mdx на узле SitePage.
  • В onCreateNode, если узел SitePage, используйте createNodeField с Mdx___NODE в качестве имени поля и ID узла Mdx в качестве значения.

Мой 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 }
    });
  });
};

Результат:

graphiql

Надеюсь, это поможет!

...