Gatsby: Pergunta - Como ler Markdown do frontmatter

Criado em 17 abr. 2018  ·  41Comentários  ·  Fonte: gatsbyjs/gatsby

Descrição

Quero ler o conteúdo de markdown do frontmatter. Por exemplo:

 halfBlocks:
 - título: Este é o primeiro título
 conteúdo:> -
 ### Este é o conteúdo real no formato ** MarkDown **.

 - Esta é a primeira linha
 - Esta é a segunda linha
 - Esta é a terceira linha
 - título: Este é o segundo título
 conteúdo:> -
 ### Este é o conteúdo real no formato ** MarkDown **.

 - Esta é a primeira linha
 - Esta é a segunda linha
 - Esta é a terceira linha

Estou usando o seguinte graphql:

 halfBlocks {
 título
 imagem
 conteúdo
 }

Como leio conteúdo convertido em HTML ou exibo como HTML?

Resultado esperado

Espero ser capaz de ler isso em um arquivo de marcação, como 'index.md', e renderizá-lo como HTML.

Resultado atual

A marcação é exibida como está, sem interpretação.

Meio Ambiente

  • Versão Gatsby ( npm list gatsby ): gatsby@^1.9.247
  • versão gatsby-cli ( gatsby --version ): 1.1.50
question or discussion

Comentários muito úteis

Isso poderia estar totalmente errado, mas você não poderia simplesmente criar um componente de redução como abaixo que você poderia usar em seus modelos em qualquer lugar que a redução seja necessária para ser convertida para 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 comentários

O tutorial explica isso, especialmente as partes 5-7 https://www.gatsbyjs.org/tutorial/

Você também pode querer começar com um dos iniciadores - muitos dos quais já possuem suporte a markdown configurado - https://www.gatsbyjs.org/docs/gatsby-starters/

A questão é mais sutil e não é abordada pelo tutorial, então vou reabri-la.

Na minha opinião, você tem 2 opções:

  1. Você pode separar o conteúdo do markdown em arquivos separados e usar links de caminho:
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. Outra abordagem seria lidar com isso de forma programática - criando nós de redução para seus campos de frontmatter e adicionando-os por meio de createNodeField . Isso é mais envolvente. Você provavelmente precisaria navegar pelo plug-in de origem Contentful para ver como criar o nó MarkdownRemark.

Olá @KyleAMathews, obrigado pela sua sugestão. Eu os li, mas foi difícil para mim entender completamente como fazer isso. Estou usando um iniciador, mas isso foi mais complicado. Starter usado: https://github.com/v4iv/gatsby-starter-business

@pieh Muito obrigado por me guiar. Você está certo. Achei que terei muitos arquivos pequenos, então coloquei isso funcionando com o código abaixo. Estou documentando isso aqui para que, se outra pessoa tiver o mesmo problema, ela também possa ver isso.

Etapa 1: Leitura do conteúdo

Estou lendo o conteúdo normalmente via Graphql. Isso me dá a redução como uma string. Eu ainda preciso converter isso.

Etapa 2: convertendo para HTML

Para isso decidi deixar o conteúdo de uma string até chegarmos ao componente real que vai exibir isso. Lá, eu converto em redução de preço.

Adicione uma observação para fazer isso programaticamente. Você pode ignorar a observação predefinida-recomendada-lint:

Instalar Observação

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';

Render
Mais tarde na parte de renderização, assumindo que content é a redução que foi lida como string:

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

Agora posso reinterpretar o conteúdo como HTML.

Etapa 3: Adicionar conteúdo na redução

Há mais um problema que eu encontrei A formatação não era adequada quando eu estava usando multilinhas com >- :

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

Mas com o símbolo de tubo | funciona muito bem.

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

Por enquanto estou encerrando isso. Sinta-se à vontade para reabrir se desejar.

Obrigado!!

Eu gostaria de usar o markdown para o frontmatter (título e trecho, para ser exato) também, e acho que deve ser suportado por padrão.

Seria ótimo ter uma convenção de nomenclatura, de forma que gatsby-transformer-remark pudesse entender que, por exemplo, title.md é um campo de marcação.

@omeid @ thorn0 pode ser algo que suportamos diretamente no gatsby-transformer -note, mas enquanto isso, você pode criar um plugin que faz isso para você, por exemplo, https://github.com/gatsbyjs/gatsby/issues/5729#issuecomment -395701042 e createNodeField

Peço desculpas por comentar sobre um problema já encerrado, mas só queria compartilhar um snippet que usei em meu próprio gatsby-node.js que funcionou para mim, seguindo o que todos vocês referiram:

// 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;
};

Então, é correto fazer isso sem usar createNodeField ? Estou confuso.

@ thorn0 é melhor usar createNodeField vez de node.frontmatter.my_field = porque a mutação de node pode resultar em bugs difíceis de depurar

@amitjindal @nshki Está funcionando bem, mas

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)

Tentando excluir a biblioteca do Webpack não está funcionando ( @see https://github.com/gatsbyjs/gatsby/issues/7599)

Olá, David (@comxd), Desculpe, eu estava viajando.
Infelizmente, não tenho nenhuma visão sobre isso. Tentei verificar o código. O compilador em null parece estar vindo da biblioteca de comentários.

Você parece estar usando um loop em seu arquivo gatsby-node.js.
Pode estar relacionado a algum conteúdo recebido que não é marcado ou, pior, vazio e você está tentando processá-lo. Tente colocar algumas instruções console.log nele e veja se você encontra um padrão onde algo vazio está causando isso.

Isso poderia estar totalmente errado, mas você não poderia simplesmente criar um componente de redução como abaixo que você poderia usar em seus modelos em qualquer lugar que a redução seja necessária para ser convertida para 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 não totalmente desligado! É uma abordagem razoável.

Dito isso, um dos grandes benefícios do Gatsby é que você faz essas operações em tempo de construção, o que é bom porque não precisamos comprar um analisador Markdown para o usuário final!

@DSchau O analisador Markdown não será enviado ao usuário quando o aplicativo for reidratado?

@blakenoll Ame o

Temos algo disponível para obter todos os plug-ins / configuração de observação que incluímos em gatsby-config.js para que não precisemos duplicar toda a funcionalidade feita nos bastidores que a implementação de observação de Gatsby está nos fornecendo ? Isso tornaria a geração dos campos de nó um pouco mais fácil; mas incrivelmente complicado quando você pensa em campos aninhados e repetíveis e na variação de conteúdo por página.

@blakenoll Muito obrigado pela sua dica de confronto. Super útil e fiz o que precisava para usar HTML no frontmatter. Dito isso, parece uma abordagem desajeitada de usar o Markdown para criar uma página da web que precisa passar diferentes partes do conteúdo para diferentes partes da página.

Existe alguma maneira de aplicar algum tipo de função markdownParser na seção frontmatter de nossa consulta graphQL (semelhante à maneira como podemos manipular imagens) que irá analisar a string de markdown recebida no frontmatter e convertê-la em HTML? Não sou um especialista em GraphQL ... apenas tentando pensar.

Este é um problema importante IMO b / c se alguém estiver usando Gatsby com Netifly CMS, Netifly fornece a opção de ter vários campos de frontmatter aceitando Markdown como valores. Mas ao consultar esses campos, eles são retornados como marcação em uma string em vez de analisados ​​em HTML. As soluções de @amitjindal e @blakenoll funcionam, mas como @DSchau mencionou, não é preferível enviar uma análise de redução para o usuário se pudermos evitá-lo. Algum pensamento de alguém que está mais familiarizado com Gatbsy do que eu?

@skylarweaver Estou definitivamente na mesma página que você. Embora eu compreenda a natureza da criação de um campo de nó que pode ter essas informações analisadas, também se torna um pouco complicado com dados CMS que podem ter campos repetíveis e uma grande quantidade de variações de nomes de campo para filtrar; além de não haver uma maneira clara de reutilizar qualquer / todos os plug-ins Gatsby Remark naquele momento.

+1 o que @skylarweaver disse!

@amitjindal Talvez uma pergunta estúpida, mas o que o "> -" ainda faz? Usando o iniciador cms Netlify e parece não fazer absolutamente nenhuma diferença na saída gerada se eu tenho>,> -, | ou nada.

@ nol13 Veja os escalares de bloco , trata-se de novas linhas.

Como posso inserir uma mesa? Isso não funciona

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

@ qnguyen12 Eu tentaria usar uma ferramenta como https://jmalarcon.github.io/markdowntables/ para ajudá-lo com a conversão.

Sim, quero dizer, renderiza como fonte, não tabela
Por exemplo:

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|

gera:
teste

Este é o conteúdo real no formato MarkDown .

| Mês | Poupança | Gastos | | --- | --- | --- | | Janeiro | $ 100 | $ 900 | | Julho | $ 750 | $ 1000 | | Dezembro | $ 250 | $ 300 | | Abril | $ 400 | $ 700 |

@KyleAMathews Obrigado, esse tipo de trabalho, mas obviamente você precisa responder a configuração do plug-in de comentário e plug-ins ou obter resultados diferentes. Algum plano para apoiar o frontmatter de markdown? Talvez um campo frontmattermd junto com o campo frontmatter bruto?

@omeid Eu não tenho planos não - este seria um ótimo plugin para alguém criar e compartilhar com a comunidade!

Eu criei um plugin que deve fazer isso: gatsby-transformer-Observação-frontmatter . Parece funcionar nos meus testes e estou planejando usá-lo em um projeto que estou fazendo para um cliente, mas agradeceria se vocês dessem uma olhada e me dissessem se há algo que parece incorreto , já que é a primeira vez que escrevo um plugin gatsby. Ele segue a rota sugerida por @omeid e adiciona um campo frontmattermd ao nó MarkdownRemark.

Inicialmente, antes de perceber que poderia apenas criar novos nós de arquivo de marcação para o consumo de gatsby-transformador-observação, eu vim com uma solução realmente hacky envolvendo chamar os resolvedores exportados pela função setFieldsOnGraphQLNodeType de gatsby-transformer-observação e passando em uma nova função nó markdown criado em outro resolvedor. Isso permitiu consultar qualquer campo no nó MarkdownRemark usando um enum de campo como é usado para a função de grupo, o que eu realmente gostei, mas parecia muito um hack para realmente usar para qualquer coisa. Guardei aqui para a posteridade.

oi @WhiteAbeLincoln tentei instalar e testar:
npm i gatsby-transformer-remark-frontmatter npm ERR! code ENOVERSIONS npm ERR! No valid versions available for gatsby-transformer-remark-frontmatter

Desculpe, percebi que ainda não publiquei no npm. Vou publicá-lo assim que sair do trabalho e avisar você.

- Abe White

Em 17 de junho de 2019, às 22:53, broeker [email protected] escreveu:

oi @WhiteAbeLincoln tentei instalar e testar:
npm i gatsby-transformador-observação-frontmatter npm ERR! código ENOVERSIONS npm ERR! Nenhuma versão válida disponível para gatsby-transformador-observação-frontmatter

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub ou ignore a conversa.

@WhiteAbeLincoln Tentei gatsby-transformador-observação-frontmatter, mas

ERROR # 11325

"Gatsby-node.js" do seu site criou uma página com um componente que não existe.

Você recebeu este erro?

Foi originalmente relatado por @obeid no registro de problemas do seu repo.

Talvez eu não esteja usando corretamente. Então, alguma ajuda é apreciada.

Aproveitando a resposta @nshki e com o comentário @pieh sobre a mutação do nó. Isso funciona totalmente para mim:

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 Como posso fazer a mesma coisa, mas para um campo aninhado em array?

---
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'
---

Preciso converter cada pressEventsList[i].body .

@alexeychikk Acho que você pode procurar pressEventList e mapear o conteúdo para criar uma série 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
  });
}
};

Estou interessado em criar um plug-in para analisar tags YAML personalizadas para atingir o acima sem usar createNodeField (da mesma forma que o sharp analisa URLs de imagem).
Alguém pode me indicar o código onde os URLs das imagens são analisados ​​para ver um exemplo de como isso é feito atualmente com o Sharp?

👋 Para aqueles que usam MDX, criei um plug-in para adicionar suporte a frontmatter https://www.gatsbyjs.org/packages/gatsby-plugin-mdx-frontmatter/

@zslabs , não é sempre que você vê uma solução postada "9 horas atrás"! Vou dar uma volta! Bom trabalho.

Tenho lutado com isso desde que queria usar uma estrutura de dados mais complexa para uma de minhas páginas.
No frontmatter eu tinha uma série de seções, com alguns campos como título e imagem em destaque, e então em cada uma eu tinha um corpo feito com markdown.
Usar createNodeField não estava funcionando para mim porque tive problemas para vinculá-los logicamente, já que são criados em seu próprio campo, não anexados à estrutura de frontmatter existente.
Acabei usando createFieldExtension para que, quando meu section.body for consultado, ele seja retornado em HTML.
Por favor, alguém me corrija se esta não for uma boa solução, parece funcionar para mim, mas tenho aquela sensação incômoda de que é a maneira errada de fazer isso.

minha estrutura de frontmatter se parece com isto:

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

E o código que usei em 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 qualquer outra pessoa interessada, resolvi usando um tipo YAML personalizado para permitir a análise de qualquer campo arbitrário como marcação da seguinte maneira:

---
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 fazer isso, crie um tipo personalizado e, em seguida, substitua o analisador YAML da matéria cinzenta:

// 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") },
      },
    }
  ]
}

Eu resolvi isso um pouco diferente. Eu criei uma extensão de campo chamada @md e usei-a na definição de tipo de frontmatter combinada com a renomeação de campo para que pudéssemos alcançar a abstração desejada.

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

aqui está um exemplo de uso:

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

Eu resolvi isso um pouco diferente.

Isso não funciona muito bem para mim. Primeiro recebo um erro ao rejeitar defaultValue: true para o argumento from - deve ser uma string. Alterando isso para defaultValue: '' , recebo este erro:

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

Isso eu não sei como resolver.

Para qualquer outra pessoa interessada, resolvi usando um tipo YAML personalizado para permitir a análise de qualquer campo arbitrário como marcação da seguinte maneira:

---
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 fazer isso, crie um tipo personalizado e, em seguida, substitua o analisador YAML da matéria cinzenta:

// 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") },
      },
    }
  ]
}

Recebo 'Opções de plug-in inválidas para "gatsby-transformer-Observação":' se eu tentar esse método?

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

theduke picture theduke  ·  3Comentários

jimfilippou picture jimfilippou  ·  3Comentários

Oppenheimer1 picture Oppenheimer1  ·  3Comentários

dustinhorton picture dustinhorton  ·  3Comentários

ferMartz picture ferMartz  ·  3Comentários