Gatsby: Pregunta: Cómo leer Markdown de frontmatter

Creado en 17 abr. 2018  ·  41Comentarios  ·  Fuente: gatsbyjs/gatsby

Descripción

Quiero leer contenido de rebajas de frontmatter. Por ejemplo:

 HalfBlocks:
 - título: este es el primer título
 contenido:> -
 ### Este es el contenido real en formato ** MarkDown **.

 - Esta es la primera fila
 - Esta es la segunda fila
 - Esta es la tercera fila
 - título: este es el segundo título
 contenido:> -
 ### Este es el contenido real en formato ** MarkDown **.

 - Esta es la primera fila
 - Esta es la segunda fila
 - Esta es la tercera fila

Estoy usando el siguiente graphql:

 halfBlocks {
 título
 imagen
 contenido
 }

¿Cómo leo contenido convertido a HTML o que se muestra como HTML?

Resultado Esperado

Espero poder leer esto desde un archivo de rebajas, decir 'index.md' y representarlo como HTML.

Resultado actual

Markdown se muestra como está sin interpretación.

Medio ambiente

  • Versión de Gatsby ( npm list gatsby ): gatsby@^1.9.247
  • versión gatsby-cli ( gatsby --version ): 1.1.50
question or discussion

Comentario más útil

Esto podría estar totalmente fuera de lugar, pero ¿no podría simplemente crear un componente de rebajas como el que se muestra a continuación que podría usar en sus plantillas en cualquier lugar donde sea necesario convertir las rebajas a HTML?

import React from 'react'
import PropTypes from 'prop-types'
import showdown from 'showdown'

const converter = new showdown.Converter()

const MarkdownContent = ({ content, className }) => (
  <div className={className} dangerouslySetInnerHTML={{ __html: converter.makeHtml(content) }} />
)

MarkdownContent.propTypes = {
  content: PropTypes.string,
  className: PropTypes.string,
}


export default MarkdownContent

Todos 41 comentarios

El tutorial explica esto, en particular las partes 5-7 https://www.gatsbyjs.org/tutorial/

También es posible que desee comenzar con uno de los principiantes, muchos de los cuales ya tienen configurado el soporte de rebajas: https://www.gatsbyjs.org/docs/gatsby-starters/

La pregunta tiene más matices y no está cubierta por el tutorial, así que volveré a abrir esto.

En mi opinión tienes 2 opciones:

  1. Puede separar su contenido de rebajas para separar archivos y usar enlaces de ruta:
Separate file - let's call it `someContent.md`
```md
### This is the actual content in **MarkDown** format.

- This is the first row
- This is second row
- This is third row
```
and reference that file in your main file (by relative path):
```md
halfBlocks:
  - title: This is first title
    content: "./someContent.md"
```
then in query you could
```
halfBlocks {
  content {
    childMarkdownRemark {
      html
    }
}
```
  1. Otro enfoque sería manejar esto programáticamente: crear nodos de rebajas para sus campos de frontmatter y agregarlos a través de createNodeField . Esto es más complicado. Probablemente necesite navegar por el complemento de fuente Contentful para ver cómo crear el nodo MarkdownRemark.

Hola @KyleAMathews, gracias por tu sugerencia. Los leí, pero fue difícil para mí entender completamente cómo hacerlo. Estoy usando un motor de arranque, pero esto fue más complicado. Motor de arranque utilizado: https://github.com/v4iv/gatsby-starter-business

@pieh Muchas gracias por guiarme. Tienes razón. Pensé que tendría demasiados archivos pequeños, así que hice que esto funcionara con el siguiente código. Estoy documentando esto aquí para que si alguien más tiene el mismo problema, también pueda verlo.

Paso 1: lectura de contenido

Estoy leyendo el contenido normalmente a través de Graphql. Esto me da la rebaja como una cadena. Todavía necesito convertir eso.

Paso 2: conversión a HTML

Para esto, decidí dejar el contenido como una cadena hasta que alcancemos el componente real que lo mostrará. Allí lo convierto en rebajas.

Agregue un comentario para hacer esto mediante programación. Es posible que pueda ignorar comment-preset-lint-recommended:

Instalar Observación

yarn add remark remark-preset-lint-recommended remark-html

Importar

import remark from 'remark';
import recommended from 'remark-preset-lint-recommended';
import remarkHtml from 'remark-html';

Hacer
Más adelante en la parte de render asumiendo que content es la rebaja que se leyó como cadena:

content = remark()
      .use(recommended)
      .use(remarkHtml)
      .processSync(content).toString();

Ahora puedo reinterpretar el contenido como HTML.

Paso 3: agregar contenido en la rebaja

Hay una trampa más con la que me encontré. El formato no era adecuado cuando estaba usando multilínea con >- :

content: >-
    ### This is the actual content in **MarkDown** format.
    - This is the first row
    - This is second row

Pero con el símbolo de tubería | funciona muy bien.

content: |
    ### This is the actual content in **MarkDown** format.
    - This is the first row
    - This is second row

Por ahora estoy cerrando esto. No dude en volver a abrir si lo desea.

¡¡Gracias!!

También me gustaría usar markdown para el frontmatter (título y extracto para ser exactos), y creo que debería ser compatible de forma predeterminada.

Sería genial tener una convención de nomenclatura, de modo que gatsby-transformer-remark pudiera entender que, por ejemplo, title.md es un campo de rebajas.

@omeid @ thorn0, esto podría ser algo que https://github.com/gatsbyjs/gatsby/issues/5729#issuecomment -395701042 y createNodeField

Disculpas por comentar sobre un problema ya cerrado, pero solo quería compartir un fragmento que usé en mi propio gatsby-node.js que funcionó para mí, siguiendo lo que todos habéis hecho referencia:

// Need to `yarn add remark remark-html`, then include the following code in
// gatsby-node.js.
const remark = require('remark');
const remarkHTML = require('remark-html');

exports.onCreateNode = ({ node }) => {
  // Conditionals, etc. can be used here, but I omitted those just for example's sake.
  const markdown = node.frontmatter.my_field;
  node.frontmatter.my_field = remark()
    .use(remarkHTML)
    .processSync(markdown)
    .toString();
  return node;
};

Entonces, ¿está bien hacerlo sin usar createNodeField ? Estoy confundido.

@ thorn0 mejor use createNodeField lugar de node.frontmatter.my_field = porque la mutación de node puede resultar en errores difíciles de depurar

@amitjindal @nshki Está funcionando bien, pero

success delete html and css files from previous builds — 0.626 s
success open and validate gatsby-config — 0.018 s
success copy gatsby files — 0.075 s
success onPreBootstrap — 2.782 s
error UNHANDLED EXCEPTION


  TypeError: Cannot set property 'Compiler' of null

  - index.js:16 plugin
    [blog]/[remark-html]/index.js:16:17

  - index.js:271 Function.use
    [blog]/[unified]/index.js:271:25

  - gatsby-node.js:63 exports.onCreateNode.postscriptumsMarkdown.forEach.postscr    iptum
    /home/projects/blog/gatsby-node.js:63:12

  - Array.forEach

  - gatsby-node.js:61 Object.exports.onCreateNode
    /home/projects/blog/gatsby-node.js:61:29

  - api-runner-node.js:110 runAPI
    [blog]/[gatsby]/dist/utils/api-runner-node.js:110:36

  - api-runner-node.js:187 
    [blog]/[gatsby]/dist/utils/api-runner-node.js:187:33

  - map.js:27 
    [blog]/[async]/internal/map.js:27:9

  - eachOfLimit.js:66 replenish
    [blog]/[async]/internal/eachOfLimit.js:66:17

  - eachOfLimit.js:50 iterateeCallback
    [blog]/[async]/internal/eachOfLimit.js:50:17

  - onlyOnce.js:12 module.exports
    [blog]/[async]/internal/onlyOnce.js:12:16

  - map.js:29 
    [blog]/[async]/internal/map.js:29:13

  - util.js:16 tryCatcher
    [blog]/[bluebird]/js/release/util.js:16:23

  - nodeify.js:23 Promise.successAdapter
    [blog]/[bluebird]/js/release/nodeify.js:23:30

  - promise.js:566 Promise.module.exports.Promise._settlePromise
    [blog]/[bluebird]/js/release/promise.js:566:21

  - promise.js:606 Promise.module.exports.Promise._settlePromiseCtx
    [blog]/[bluebird]/js/release/promise.js:606:10


Waiting for the debugger to disconnect...

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

Intentar excluir la biblioteca de Webpack no funciona ( @see https://github.com/gatsbyjs/gatsby/issues/7599)

Hola David (@comxd), Lo siento, estaba viajando.
Desafortunadamente, no tengo ninguna idea de esto. Traté de verificar el código. El compilador en null parece provenir de la biblioteca de comentarios.

Parece que está utilizando un bucle en su archivo gatsby-node.js.
Puede estar relacionado con algún contenido entrante que no está rebajado o, peor aún, está vacío y está tratando de procesarlo. Intente poner algunas declaraciones de console.log en eso y vea si encuentra un patrón donde algo vacío está causando esto.

Esto podría estar totalmente fuera de lugar, pero ¿no podría simplemente crear un componente de rebajas como el que se muestra a continuación que podría usar en sus plantillas en cualquier lugar donde sea necesario convertir las rebajas a HTML?

import React from 'react'
import PropTypes from 'prop-types'
import showdown from 'showdown'

const converter = new showdown.Converter()

const MarkdownContent = ({ content, className }) => (
  <div className={className} dangerouslySetInnerHTML={{ __html: converter.makeHtml(content) }} />
)

MarkdownContent.propTypes = {
  content: PropTypes.string,
  className: PropTypes.string,
}


export default MarkdownContent

@blakenoll definitivamente no del todo! Es un enfoque razonable.

Dicho esto, uno de los grandes beneficios de Gatsby es que realiza esas operaciones en el momento de la compilación, lo cual es bueno porque entonces no necesitamos comprar un analizador Markdown para el usuario final.

@DSchau ¿No se enviará realmente el analizador Markdown al usuario cuando la aplicación se rehidrate?

@blakenoll ¡ Me encanta el

¿Tenemos algo disponible para incorporar todos los complementos / configuraciones de comentarios que incluimos dentro de gatsby-config.js para que no necesitemos duplicar toda la funcionalidad realizada detrás de escena que la implementación de comentarios de Gatsby nos proporciona? ? Eso facilitaría un poco la generación de los campos de nodo; pero increíblemente engorroso cuando se piensa en campos anidados y repetibles y en la variación del contenido por página.

@blakenoll Muchas gracias por tu consejo de enfrentamiento. Muy útil e hice lo que necesitaba para usar HTML en frontmatter. Dicho esto, parece un enfoque torpe para usar Markdown para crear una página web que necesita pasar diferentes piezas de contenido a diferentes partes de la página.

¿Hay alguna forma de que podamos aplicar algún tipo de función markdownParser dentro de la sección frontmatter de nuestra consulta graphQL (similar a la forma en que podemos manipular imágenes) que analizará la cadena de markdown entrante en el frontmatter y la convertirá a HTML? No soy un experto en graphQL ... solo intento pensar.

Este es un problema importante en mi opinión b / c si uno está usando Gatsby con Netifly CMS, Netifly ofrece la opción de que varios campos de frontmatter acepten Markdown como valores. Pero al consultar esos campos, se devuelven como rebajas en una cadena en lugar de analizarse en HTML. Las soluciones de @amitjindal y @blakenoll funcionan, pero como mencionó @DSchau , no es preferible enviar un análisis de rebajas al usuario si podemos evitarlo. ¿Algún pensamiento de alguien que esté más familiarizado con Gatbsy que yo?

@skylarweaver Definitivamente estoy en la misma página que tú. Si bien entiendo la naturaleza de crear un campo de nodo que puede tener esta información analizada, también se vuelve un poco difícil de manejar con datos de CMS que pueden tener campos repetibles y una gran cantidad de variaciones de nombres de campo para examinar; además de no tener una forma clara de reutilizar alguno o todos los complementos de Gatsby Remark en ese momento.

+1 lo que dijo @skylarweaver !

@amitjindal Quizás una pregunta estúpida, pero ¿qué hace el "> -"? Usar el iniciador netlify cms y parece no hacer absolutamente ninguna diferencia en la salida generada si tengo>,> -, | o nada en absoluto.

@ nol13 Ver escalares de bloque , se trata de nuevas líneas.

¿Cómo podría insertar una mesa? Esto no funciona

content: |
        |   |   |   |   |   |
        |---|---|---|---|---|
        |   |   |   |   |   |
        |   |   |   |   |   |
        |   |   |   |   |   |

@ qnguyen12 Intentaría usar una herramienta como https://jmalarcon.github.io/markdowntables/ para ayudarte con la conversión.

Sí, me refiero a que se renderiza como fuente, no como tabla
Por ejemplo:

text: |
        test   
        ### This is the actual content in **MarkDown** format.  
        |Month|Savings|Spending|
        |--- |--- |--- |
        |January|$100|$900|
        |July|$750|$1000|
        |December|$250|$300|
        |April|$400|$700|

genera:
prueba

Este es el contenido real en formato MarkDown .

| Mes | Ahorros | Gastos | | --- | --- | --- | | Enero | $ 100 | $ 900 | | Julio | $ 750 | $ 1000 | | Diciembre | $ 250 | $ 300 | | Abril | $ 400 | $ 700 |

@KyleAMathews Gracias, ese tipo de trabajo, pero obviamente necesitas responder la configuración del complemento de comentarios y los complementos o obtendrás resultados diferentes. ¿Algún plan para apoyar el frontmatter de rebajas? ¿Quizás un campo frontmattermd junto con el frontmatter procesar?

@omeid No tengo ningún plan, no, ¡este sería un gran complemento para que alguien lo cree y lo comparta con la comunidad!

Creé un complemento que debería hacer esto: @omeid y agrega un campo frontmattermd al nodo MarkdownRemark.

Inicialmente, antes de darme cuenta de que podía crear nuevos nodos de archivo de rebajas para que los consumiera gatsby-transformer-comment, se me ocurrió una solución realmente hacky que implicaba llamar a los resolutores exportados por la función setFieldsOnGraphQLNodeType de gatsby-transformer-comment y pasar una nueva nodo de rebajas creado dentro de otro solucionador. Esto permitió consultar cualquier campo en el nodo MarkdownRemark usando una enumeración de campo como la que se usa para la función de grupo, que realmente me gustó, pero se sintió como un truco para usar realmente para cualquier cosa. Lo guardé aquí para la posteridad.

hola @WhiteAbeLincoln intenté instalar y probar:
npm i gatsby-transformer-remark-frontmatter npm ERR! code ENOVERSIONS npm ERR! No valid versions available for gatsby-transformer-remark-frontmatter

Lo siento, me di cuenta de que aún no he publicado en npm. Lo publicaré una vez que salga del trabajo y te lo haré saber.

- Abe White

El 17 de junio de 2019, a las 22:53, el corredor de [email protected] escribió:

hola @WhiteAbeLincoln intenté instalar y probar:
npm i gatsby-transformer-comment-frontmatter npm ERR! código ENOVERSIONES npm ERR! No hay versiones válidas disponibles para gatsby-transformer-comment-frontmatter

-
Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub o silencia el hilo.

@WhiteAbeLincoln Probé gatsby-transformer-comment-frontmatter pero me dio un error.

ERROR # 11325

El "gatsby-node.js" de su sitio creó una página con un componente que no existe.

¿Recibes este error?

Originalmente fue reportado por @obeid en el registro de problemas de su repositorio.

Quizás no lo esté usando correctamente. Así que se agradece un poco de ayuda.

Sobre la base de la respuesta de @nshki y con el comentario de @pieh sobre la mutación del nodo. Esto funciona totalmente para mí:

const remark = require("remark");
const remarkHTML = require("remark-html");

exports.onCreateNode = ({ node, actions: { createNodeField } }) => {
  const my_field = node.frontmatter.my_field;

  if (my_field) {
    const value = remark()
      .use(remarkHTML)
      .processSync(my_field)
      .toString();

    // new node at:
    // fields {
    //   my_field_html
    // }
    createNodeField({
      name: `my_field_html`,
      node,
      value
    });
  }
};

editar: my_field => my_field_html

@aziaziazi ¿Cómo puedo hacer lo mismo pero para un campo anidado en una matriz?

---
pressEventsList:
  - body: >-
      *My md content...*
    image: 'https://res.cloudinary.com/press/01.jpg'
  - body: >-
      *My md content...*
    image: 'https://res.cloudinary.com/press/02.jpg'
---

Necesito convertir cada pressEventsList[i].body .

@alexeychikk supongo que puede buscar pressEventList y luego mapear el contenido para crear una matriz de resultados:

const remark = require("remark");
const remarkHTML = require("remark-html");

exports.onCreateNode = ({ node, actions: { createNodeField } }) => {
const pressEventList = node.frontmatter.pressEventList;

if (pressEventList) {
  const value = pressEventList.map(event =>
     remark()
    .use(remarkHTML)
    .processSync(event.body)
    .toString()
  )

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

Estoy interesado en crear un complemento para analizar etiquetas YAML personalizadas para lograr lo anterior sin usar createNodeField (de la misma manera que Sharp analiza las URL de las imágenes).
¿Alguien puede señalarme el código donde se analizan las URL de las imágenes para ver un ejemplo de cómo se hace esto actualmente con sharp?

👋 Para aquellos que usan MDX, creé un complemento para agregar soporte de frontmatter https://www.gatsbyjs.org/packages/gatsby-plugin-mdx-frontmatter/

@zslabs , ¡no es frecuente que vea una solución publicada "hace 9 horas"! ¡Le daré una vuelta! Buen trabajo.

He estado luchando con esto desde que quería usar una estructura de datos más compleja para una de mis páginas.
En frontmatter tenía una serie de secciones, con algunos campos como el título y la imagen destacada, y luego en cada una tenía un cuerpo hecho con markdown.
El uso de createNodeField no me sirvió porque tuve problemas para vincularlos lógicamente, ya que se crean en su propio campo, no se añaden a la estructura de frontmatter existente.
Terminé usando createFieldExtension para que cuando se consulta mi section.body, se devuelve en HTML.
Por favor, que alguien me corrija si esta no es una buena solución, parece funcionar para mí, pero tengo la persistente sensación de que es la forma incorrecta de hacerlo.

mi estructura de frontmatter se ve así:

templateKey: project-entry
date: 2020-06-22T13:16:57.702Z
featuredproject: true
title: Project title
description: Description for listing the project on other pages
featuredimage: Image for listing the project on other pages
featuredpost: false
sections:
  - heading: Section heading
    standout: false
    intro: >-
      Introduction to be displayed separately to body
    body: >-
       ## section title
       * bullet point
       * bullet point
       Some other text here

Y el código que usé en gatsby-node.js

exports.createSchemaCustomization = ({actions}) => {
  const { createTypes, createFieldExtension} = actions

  createFieldExtension({
    name: 'toHTML',
    extend:() => ({
        resolve(source) {
          return remark().use(remarkHTML).processSync(source.body).toString()
        }
      })
  })
  const typeDefs = `
  type MarkdownRemark implements Node {
    frontmatter: Frontmatter
  }
  type Frontmatter <strong i="14">@infer</strong> {
    sections: [section]
  }
  type section <strong i="15">@infer</strong> {
    body: String <strong i="16">@toHTML</strong>
  }
  `
  createTypes(typeDefs)
}

Para cualquier otra persona que esté interesada, lo resolví usando un tipo YAML personalizado para permitir analizar cualquier campo arbitrario como rebaja de esta manera:

---
title: My Page
inline: !md Some **bold** and _italic_ text
block: !md |
  ## I'm a H2 title
  [I'm an inline-style link](https://www.google.com)
---

Para hacerlo, cree un tipo personalizado y luego anule el analizador YAML de la materia gris:

// custom-yaml.js
const yaml = require('js-yaml')
const remark = require('remark')
const remarkHTML = require('remark-html')

const MarkdownYamlType = new yaml.Type('!md', {
  kind: 'scalar',
  construct: data => remark().use(remarkHTML).processSync(data).toString(),
})

const MARKDOWN_SCHEMA = yaml.Schema.create(MarkdownYamlType)

module.exports = doc => yaml.safeLoad(doc, { schema: MARKDOWN_SCHEMA })
// gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        engines: { yaml: require("path/to/custom-yaml.js") },
      },
    }
  ]
}

Resolví esto un poco diferente. Creé una extensión de campo llamada

exports.createSchemaCustomization = ({ actions }) => {
  actions.createFieldExtension({
    name: "md",
    args: {
      from: {
        type: "String!",
        defaultValue: true,
      },
    },

    extend() {
      return {
        args: {
          from: "String!",
        },
        resolve(source, args) {
          const fieldValue = source[args.from]
          return convertToHTML(fieldValue)
        },
      }
    },
  })
  const typeDefs = `
    type MarkdownRemark implements Node <strong i="7">@infer</strong> {
      frontmatter: Frontmatter
    }
    type Frontmatter {
      markdownField: String! <strong i="8">@md</strong>
    }
  `
  actions.createTypes(typeDefs)
}

aquí hay un ejemplo de uso:

...
frontmatter {
        title: markdownField(from: "title")
        subtitle: markdownField(from: "subtitle")
}

Resolví esto un poco diferente.

Esto no funciona para mí. Primero recibo un error al rechazar defaultValue: true para el argumento from - debe ser una cadena. Al cambiar eso a defaultValue: '' , aparece este error:

Encountered an error parsing the provided GraphQL type definitions:
Argument "from" of required type "String!" was not provided.

  1 |
  2 |     type MarkdownRemark implements Node <strong i="11">@infer</strong> {
  3 |       frontmatter: Frontmatter
  4 |     }
  5 |     type Frontmatter {
> 6 |       markdownField: String! <strong i="12">@md</strong>
    |                              ^
  7 |     }

Esto no sé cómo resolverlo.

Para cualquier otra persona que esté interesada, lo resolví usando un tipo YAML personalizado para permitir analizar cualquier campo arbitrario como rebaja de esta manera:

---
title: My Page
inline: !md Some **bold** and _italic_ text
block: !md |
  ## I'm a H2 title
  [I'm an inline-style link](https://www.google.com)
---

Para hacerlo, cree un tipo personalizado y luego anule el analizador YAML de la materia gris:

// custom-yaml.js
const yaml = require('js-yaml')
const remark = require('remark')
const remarkHTML = require('remark-html')

const MarkdownYamlType = new yaml.Type('!md', {
  kind: 'scalar',
  construct: data => remark().use(remarkHTML).processSync(data).toString(),
})

const MARKDOWN_SCHEMA = yaml.Schema.create(MarkdownYamlType)

module.exports = doc => yaml.safeLoad(doc, { schema: MARKDOWN_SCHEMA })
// gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        engines: { yaml: require("path/to/custom-yaml.js") },
      },
    }
  ]
}

Recibo 'Opciones de complemento no válidas para "gatsby-transformer-comment":' si intento este método?

¿Fue útil esta página
0 / 5 - 0 calificaciones