Hero

Crear un sitio Gatsby con multiples tipos de páginas Markdown

Enero 31, 2022

mvessuri
Gatsby
Markdown

En la entrada de blog anterior expliqué cómo exportamos el contenido del antiguo sitio de 7Sabores en Drupal 7 a archivos Markup. En esta entrada de blog voy a revisar cómo fue el proceso para que estos archivos se convertirán en la fuente de contenido de un nuevo sitio Gatsby reemplazando tanto al CMS como a su base de datos.

Una de las mejores cosas de Gataby.js es que ofrece la posibilidad de crear rápidamente páginas a partir de archivos en formato Markdown. Para que Gatsby pueda acceder a los archivos Markdown se necesitan dos plugins.

Plugins

El primer plugin que necesitamos instalar es gatsby-source-filesystem. Con este plugin nuestra aplicación Gatsby podrá utilizar archivos almacenados en el sistema de archivos local como fuente de datos. Por cada archivo se crea un nodo File que puede ser consultado a través de GraphQL en allFile y File.

Para poder utilizar este plugin debemos instalar el paquete del mismo usando NPM (o Yarn).

npm install gatsby-source-filesystem

Una vez que el paquete esté instalado debemos configurar el nuevo plugin en la sección plugins del archivo gatsby-config.js de nuestra aplicación y especificar el directorio en el cuál se encuentran los archivos que vamos a utilizar como contenido.

module.exports = {
  plugins: [
    {
      resolve:  `gatsby-source-filesystem`,
      options: {
        path:  `${__dirname}/content/blog`,
        name: `blog`,
      },
    },
  ],
}

Con esto tenemos acceso a la información de los archivos desde el GraphQL data layer, pero nos debemos utilizar otro plugin para poder acceder al contenido Markdown y mostrarlo en una página de nuestra aplicación. Este segundo plugin es el gatsby-transformer-remark y es un plugin de tipo Transformer, es decir que “transforma” los datos provistos por un source plugin, en nuestro caso el gatsby-source-filesystem, y los transforma en nuevos nodos y campos de nodos.

Con gatsby-transformer-remarkm todos los archivos que tengan extensión .md o .markdown dentro del directorio configurado son procesados y se crean nuevos nodos de tipo MarkdownRemark. El contenido markdown es procesado por el plugin y crea campos html y excerpt en el GraphQL data layer. También se procesa el frontmatter y se crean campos en GraphQL para ellos.

Instalamos el paquete del plugin.

npm install gatsby-transformer-remark

Y luego configuramos el mismo en gatsby-config.js

module.exports = {
  plugins: [
    {
      resolve:  `gatsby-source-filesystem`,
      options: {
        path:  `${__dirname}/content/blog`,
        name: `blog`,
      },
    },
    `gatsby-transformer-remark`,  ],
}

Organizando el contenido

Lo que hemos visto hasta aquí es similar a lo que se puede encontrar en cualquier guía de Gatsby para crear páginas a partir de contenido Markdown, pero en esas guías probablemente digan que el siguiente paso es crear un archivo con el siguiente nombre para crear las páginas.

src/pages/{MarkdownRemark.frontmatter__slug}.js

Esa es una solución simple y funciona muy bien si todas las páginas generadas a partir de archivos markdown en el sitio tienen el mismo formato. En este sitio en particular tenemos archivos con diferentes campos y necesitamos tener más control en cómo se van a mostrar cada uno de los tipos de página.

Creando directorios en el sistema de archivos podemos separar los distintos tipos de páginas que se van a generar a partir de archivos markdown. Para organizar todo el contenido del sitio he creado una carpeta llamada “content” dentro de ella hay carpetas para cada uno de los tipos de página y una carpeta llamada assets que contiene todas las imágenes que están embebidas en el contenido de páginas markdown.

Content folder

Para separar los distintos tipos de contenido he configurado a cada carpeta en forma separada en gatsby-config.js.


    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/blog`,
        name: `blog`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/entrevista`,
        name: `interview`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/lesson`,
        name: `lesson`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/glosario`,
        name: `glosary`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/user`,
        name: `user`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `assets`,
        path: `${__dirname}/content/assets`,
      },
    },

Es posible agrupar todas estas configuraciones en una sola que incluya todo dentro de la carpeta content, pero queria tener control de cada nuevo tipo de contenido que se agregaba al sitio.

    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `content`,
        path: `${__dirname}/content`,
      },
    },

Templates para las páginas

El siguiente paso es crear los diferentes templates que necesitamos para nuestras paginas.

Los mismos son archivos .js (o .tsx si estás usando TypeScript) que para mantener las cosas ordenadas vamos a guardar dentro de la carpeta src/templates.

A continuación hay un ejemplo muy simple de un template para blog posts. Este template no es otra cosa que un componente de React que será utilizado al momento de generar la página.

import * as React from "react"
import { graphql } from "gatsby"

const BlogPostTemplate = ({ data }) => {
  const post = data.markdownRemark

  return (
    <section>
      <h1>{ post.title }</h1>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
   </section>
  )
}

export default BlogPostTemplate

export const pageQuery = graphql`
  query BlogPostBySlug(
    $id: String!
  ) {
    markdownRemark(id: { eq: $id }) {
      id
      html
      frontmatter {
        title
      }
    }
  }
`

El template tiene dos partes. El query de GraphQL donde obtenemos todos los datos necesarios para nuestra página de blog. Y la estructura del componente de React donde especificamos la forma en que se va a mostrar el contenido. Un detalle a remarcar es el uso de la propiedad de React dangerouslySetInnerHTML para insertar el contenido HTML convertido desde nuestro Markdown en un elemento del DOM.

Este es un ejemplo simple pero incluye los elementos que se necesitan para crear un template. Si diferentes tipos de página necesitan templates distintos se deben crear archivos de template adicionales.

Generando páginas dinámicamente

Ya tenemos nuestro contenido markdown y los templates que se utilizarán para mostrar las páginas de los diferentes tipos de contenido, pero Gatsby aún no sabe que queremos crear una página para cada uno de los archivos, y tampoco conoce la ruta que debe tener ni el template a utilizar con cada uno.

Eso se hace a través del archivo gatsby-node.js de nuestra aplicación. Si en tu aplicación aún no existe, crea un archivo gatsby-node.js en la raíz del sitio. Dentro de este archivo utilizaremos APIs para decirle a gatsby cómo crear nuestras páginas dinámicamente durante el proceso de build del sitio.

Utilizando el API createPage seleccionamos los nodos de todas las páginas de cada tipo utilizando GraphQL. El filtro con una expresión regular nos permite elegir sólo las páginas que se encuentran en un directorio en particular.

const path = require(`path`)

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions

  // Template para páginas de blog
  const blogPost = path.resolve(`./src/templates/blog-post.js`)


  // Obtenemos todos los nodos de Markdown que posts que tengan un
  const result = await graphql(
    `
      {
        blogPosts: allMarkdownRemark(
          filter: {fileAbsolutePath: {regex: "/\/blog\//"}}
        ) {
          nodes {
            id
            frontmatter {
              path
            }
          }
        }
    `
  )

También definimos el template que vamos a utilizar para este tipo de página. Si vamos a tener muchos tipos de página, cada uno con diferentes templates podemos definirlo varios queries y templates en este mismo lugar.

Por último solo nos queda generar las páginas para cada uno de los resultados devueltos por el query de GraphQL. En cada página definimos el path, en este caso utilizando un campo del Fronmatter que tiene el path que tenía el contenido en el sitio original, y el componente que utilizaremos como template.

  const posts = result.data.blogPosts.nodes

  if (posts.length > 0) {
    posts.forEach((post, index) => {

      createPage({
        path: post.frontmatter.path,
        component: blogPost,
        context: {
          id: post.id,
        },
      })
    })
  }

De esta forma al hacer el build del sitio Gatsby creará una página por cada archivo Markdown dentro del directorio ./content/blog utilizando el template que creamos especialmente para este tipo de contenido. Luego podemos crear templates adicionales y hacer lo mismo para otros tipos de contenido a partir de archivos Markdown almacenados en otros directorios.

Espero que les haya sido útil.

Recibe consejos y oportunidades de trabajo 100% remotas y en dólares de weKnow Inc.