Как автоматически добавить тег «slug» в файлы уценки с помощью NetlifyCMS и Gatsby?

Ссылка на Codesandbox здесь.

Каждый раз, когда я пытаюсь опубликовать новую запись в блоге с помощью NetlifyCMS, она сообщает, что публикуется. Однако моя сборка Netlify дает сбой и фактически не публикует сообщения в блоге.

Вот ошибка, которую я получаю:

12:44:22 PM: error Your site's "gatsby-node.js" must set the page path when creating a page.
12:44:22 PM: The page object passed to createPage:
12:44:22 PM: {
12:44:22 PM:     "path": null,
12:44:22 PM:     "component": "/opt/build/repo/src/templates/blogTemplate.js",
12:44:22 PM:     "context": {
12:44:22 PM:         "slug": null
12:44:22 PM:     }
12:44:22 PM: }
12:44:22 PM: See the documentation for the "createPage" action — https://www.gatsbyjs.org/docs/actions/#createPage
12:44:22 PM: not finished createPages - 0.042s

Причина, по которой я получаю эту ошибку, заключается в том, что при публикации новых сообщений в файл уценки нового сообщения в блоге не добавляется автоматически тег «slug». Пример:

---
title: 10 of the best SEO strategies for 2021
slug: /posts/10-best-seo-strategies-2021/ <-- I had to manually add this in the markdown file. This line is completely missing when pushing new blog posts live. This is causing the site build to fail.
date: 2021-03-26T23:53:24.128Z
excerpt: >-
  In this post, we go over 10 of the best SEO strategies for 2021. If you want
  more business, read more now!
---

Как только я вручную добавляю сообщение в блоге в виде файла уценки за пределами NetlifyCMS, добавляю тег slug и нажимаю на мастер, он успешно строится. Очевидно, я не хочу делать это каждый раз, я хочу, чтобы мой сайт нормально публиковался из NetlifyCMS.

gatsby-node.js:

exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions
  const blogPostTemplate = require.resolve(`./src/templates/blogTemplate.js`)
  const result = await graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        edges {
          node {
            frontmatter {
              slug
            }
          }
        }
      }
    }
  `)
  // Handle errors
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }
  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    createPage({
      path: node.frontmatter.slug,
      component: blogPostTemplate,
      context: {
        // additional data can be passed via context
        slug: node.frontmatter.slug,
      },
    })
  })
}

GraphQL pageQuery в моем файле /src/pages/posts.js:

export const pageQuery = graphql`
  query {
    allMarkdownRemark(sort: { order: DESC, fields: [frontmatter___date] }) {
      edges {
        node {
          id
          excerpt(pruneLength: 250)
          frontmatter {
            date(formatString: "MMMM DD, YYYY")
            slug
            title
          }
        }
      }
    }
  }
`

Конфиг.yml:

backend:
  name: github
  repo: my-repo

media_folder: uploads
public_folder: /uploads

collections:
  - name: "posts"
    label: "Posts"
    folder: "posts"
    create: true
    slug: "{{slug}}"
    fields:
      - { label: "Title", name: "title", widget: "string" }
      - { label: "Publish Date", name: "date", widget: "date" }
      - { label: "Excerpt", name: "excerpt", widget: "string" }
      - { label: "Body", name: "body", widget: "markdown" }

blogTemplate.js file:

export const pageQuery = graphql`
  query($slug: String!) {
    markdownRemark(frontmatter: { slug: { eq: $slug } }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        slug
        title
        excerpt
      }
    }
  }
`

Любая идея, почему это может происходить?

🤔 А знаете ли вы, что...
Node.js позволяет создавать RESTful API с помощью библиотеки Restify.


1
1 402
1

Ответ:

Решено

Любая идея, почему это может происходить?

Что ж, вы пытаетесь запросить поле slug, а оно никогда не устанавливалось (по крайней мере, вначале). Ваш frontmatter имеет следующие поля:

  • Заголовок
  • Публиковать
  • Выдержка
  • Тело

Но не slug.

Стандартный способ — добавить его в свой config.yml:

- { name: slug, label: Slug, required: true, widget: string }

Добавив это, ваш запрос будет работать автоматически.

Другой метод — использовать встроенные прослушиватели и преобразователи (API-интерфейсы узлов) от Gatsby для создания slug на основе ранее установленного параметра, но вам нужно будет изменить свой запрос. В свой gatsby-node.js добавьте:

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

  if (node.internal.type === `MarkdownRemark`) {
    let value = createFilePath({ node, getNode });

    createNodeField({
      name: `slug`,
      node,
      value,
    });
  }
};

С помощью onCreateNode вы создаете новый узел на основе некоторых правил (подробнее). Это создаст новую коллекцию для запроса с именем fields и slug внутри. Поэтому вам нужно только адаптировать его, например:

exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions
  const blogPostTemplate = require.resolve(`./src/templates/blogTemplate.js`)
  const result = await graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        edges {
          node {
            fields{
              slug
            }
            frontmatter {
              slug // not needed now
            }
          }
        }
      }
    }
  `)
  // Handle errors
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }
  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    createPage({
      path: node.fields.slug,
      component: blogPostTemplate,
      context: {
        // additional data can be passed via context
        slug: node.frontmatter.slug,
      },
    })
  })
}

Не существует «автоматического» способа добиться этого без погружения в дополнительные схемы Node. Вы только создаете файл уценки и запрашиваете его содержимое. Какова логика создания slug с нуля? slug поля всегда должны быть обязательными.

Вы можете попробовать изменить следующее:

createNodeField({
  name: `slug`,
  node,
  value,
});

Чтобы добавить пользовательский value на основе некоторой логики, если slug не определен.


Другое дело вне темы. Вы создаете дубликат excerpt:

  • Один в вашей уценке (из CMS Netlify):

    - { label: "Excerpt", name: "excerpt", widget: "string" }
    
  • Один создается автоматически в вашем запросе GraphQL. Файловая система GraphQL + Gatsby добавляет пользовательское поле excerpt, которое получается в результате разделения содержимого body с помощью фильтрации pruneLength вне frontmatter:

      export const pageQuery = graphql`
        query {
          allMarkdownRemark(sort: { order: DESC, fields: [frontmatter___date] }) {
            edges {
              node {
                id
                excerpt(pruneLength: 250)
                frontmatter {
                  date(formatString: "MMMM DD, YYYY")
                  slug
                  title
                }
              }
            }
          }
        }
      `
    

Я думаю, что вы смешиваете вещи здесь, я бы рекомендовал использовать только один из них, чтобы избежать недоразумений в вашем коде.