如何在 Gatsby 中将多个 yaml 文件映射到 frontmatter

Posted

技术标签:

【中文标题】如何在 Gatsby 中将多个 yaml 文件映射到 frontmatter【英文标题】:How to map multiple yaml files to frontmatter in Gatsby 【发布时间】:2020-10-07 01:08:07 【问题描述】:

问题:

我在将第二个 .yaml 文件映射到 Gatsby 中的第二个 markdown frontmatter 字段时遇到问题。该项目用于杂志,第一个 yaml 文件存储有关每个作者的详细信息,第二个文件存储有关每个问题的详细信息。

author.yaml 文件按预期工作,我可以在每个模板和页面中查询它,但 issue.yaml 文件仅在两个地方有效:我专门在 gatsby 中将其数据作为页面上下文传递的模板-node,并且(莫名其妙地)用于带有frontmatter“特色”字段=== true的markdown文件。对于不存在“featured”或为 false 的映射数组,应从 issue.yaml 中提取数据的每个查询都会给出“TypeError:无法读取属性 'id' of null”。

我怀疑这是因为我的降价文件上的问题字段不是第一个字段(即作者,并且已经映射到 author.yaml 文件)。据我所知,我实现的两个 yaml 文件完全相同。

我尝试过的事情:

我尝试查询我认为应该自动生成的 allAuthorYaml 和 allIssueYaml 节点,但我无法让它们中的任何一个工作。我还尝试在 gatsby-node 中创建模式自定义,但我无法弄清楚如何调整教程以定义我需要的全局节点(超出我的想象)。我在某处读到我可以直接使用import YAMLData from "../data/issue.yaml" 导入 yaml 数据,然后直接创建一个数组,但它也给了我 null/undefined 错误。

我是一个业余爱好者,根本没有编程背景,这可能是问题的很大一部分。不过,我会很感激任何人可能得到的帮助。如果有人碰巧发现我的代码需要改进的其他地方,请务必告诉我!

我使用的启动器是https://www.gatsbyjs.org/starters/JugglerX/gatsby-serif-theme/。 我的存储库位于https://github.com/ljpernic/HQ3.1/tree/HQhelp。

再次感谢!


我的 gatsby 配置:


module.exports = 
  siteMetadata: 
    title: 'Haven Quarterly',
    description: 'a magazine of science fiction and fantasy',
    submit: 
      phone: 'XXX XXX XXX',
      email: 'havenquarterly@gmail.com',
    ,
    menuLinks: [
      
        name: 'Fiction',
        link: '/fiction',
      ,
      
        name: 'Non-fiction',
        link: '/non-fiction',
      ,
      
        name: 'Letters from the Future',
        link: '/future',
      ,
      
        name: 'Full Issues',
        link: '/fullissues',
      ,
      
        name: 'Contributors',
        link: '/contributors',
      ,
      
        name: 'About',
        link: '/about',
      ,
      
        name: 'Support',
        link: '/support',
      ,
      
        name: 'Submit',
        link: '/submit',
      ,
    ],
  ,
  plugins: [
    'gatsby-plugin-sass',
    'gatsby-transformer-json',
    'gatsby-transformer-remark',
    'gatsby-plugin-react-helmet',
    `gatsby-transformer-sharp`, 
    `gatsby-plugin-sharp`,
    `gatsby-plugin-catch-links`,
    
      resolve: 'gatsby-source-filesystem',
      options: 
        path: `$__dirname/src/pages`,
        name: 'pages',
      ,
    ,
/*    
      resolve: 'gatsby-source-filesystem',
      options: 
        path: `$__dirname/src/posts`,
        name: 'posts',
      ,
    ,*/
    
      resolve: 'gatsby-source-filesystem',
      options: 
        path: `$__dirname/src/data`,
        name: 'data',
      ,
    ,
    
      resolve: 'gatsby-source-filesystem',
      options: 
        path: `$__dirname/src/images`,
        name: 'images',
      ,
    ,
    
      resolve: "gatsby-transformer-remark",
      options: 
        plugins: [
          
            resolve: `gatsby-remark-images`,
            options: 
              maxWidth: 1200,
              quality: 95, 
            ,
          ,
        ], 
      ,
    ,
    
      resolve: 'gatsby-plugin-google-analytics',
      options: 
        trackingId: guid ? guid : 'UA-XXX-1',
        // Puts tracking script in the head instead of the body
        head: false,
      ,
    ,
  `gatsby-transformer-yaml`,
  ],
  mapping: 
    // 3. map author to author.yaml
    "MarkdownRemark.frontmatter.author": `AuthorYaml`,
    "MarkdownRemark.frontmatter.issue": `IssueYaml`,
  ,
;

我的 gatsby 节点:

const _ = require('lodash');
const fs = require("fs")
const yaml = require("js-yaml")

// Create pages from markdown files
exports.createPages = ( graphql, actions ) => 
  const  createPage  = actions;
  const ymlDoc = yaml.safeLoad(fs.readFileSync("./src/data/author.yaml", "utf-8"))
  const ymlIssueDoc = yaml.safeLoad(fs.readFileSync("./src/data/issue.yaml", "utf-8"))
  return new Promise((resolve, reject) => 
    resolve(
      graphql(
        `
          query 
            fictionarchive: allMarkdownRemark(
              filter:  fileAbsolutePath:  regex: "/fiction/"  
              sort:  fields: [frontmatter___date], order: DESC 
            ) 
              edges 
                node 
                  id
                  frontmatter 
                    category
                    featured
                    path
                    title
                    date(formatString: "DD MMMM YYYY")
                  
                  excerpt
                
              
            

            nonfictionarchive: allMarkdownRemark(
              filter:  fileAbsolutePath:  regex: "/non-fiction/"  
              sort:  fields: [frontmatter___date], order: DESC 
            ) 
              edges 
                node 
                  id
                  frontmatter 
                    category
                    featured
                    path
                    title
                    date(formatString: "DD MMMM YYYY")
                  
                  excerpt
                
              
            

            futurearchive: allMarkdownRemark(
              filter:  fileAbsolutePath:  regex: "/letters/"  
              sort:  fields: [frontmatter___date], order: DESC 
            ) 
              edges 
                node 
                  id
                  frontmatter 
                    category
                    featured
                    path
                    title
                    date(formatString: "DD MMMM YYYY")
                  
                  excerpt
                
              
            

            issuesarchive: allMarkdownRemark(
              filter:  fileAbsolutePath:  regex: "/"  
              sort:  fields: [frontmatter___date], order: DESC 
            ) 
              edges 
                node 
                  id
                  frontmatter 
                    category
                    featured
                    path
                    title
                    date(formatString: "DD MMMM YYYY")
                  
                  excerpt
                
              
            

            authorarchive: allMarkdownRemark(
              filter:  fileAbsolutePath:  regex: "/"  
              sort:  fields: [frontmatter___date], order: DESC 
            ) 
              edges 
                node 
                  id
                  frontmatter 
                    category
                    featured
                    path
                    title
                    date(formatString: "DD MMMM YYYY")
                  
                  excerpt
                
              
            

          
        `,
      ).then((result) => 
        ymlDoc.forEach(element => 
          createPage(
            path: element.idpath,
            component: require.resolve("./src/templates/eachauthor.js"),                     /*creates INDIVIDUAL AUTHOR PAGES*/
            context: 
              idname: element.id,
              bio: element.bio,
              twitter: element.twitter,
              picture: element.picture,
              stories: element.stories,
            ,
          );
        );
        ymlIssueDoc.forEach(element => 
          createPage(
            path: element.idpath,
            component: require.resolve("./src/templates/eachissue.js"),                      /*creates INDIVIDUAL ISSUE PAGES*/
            context: 
              issueidname: element.id,
              text: element.text,
              currentcover: element.currentcover,
              artist: element.artist,
              artistbio: element.artistbio,
              artistimage: element.artistimage,
            ,
          );
        );
        result.data.fictionarchive.edges.forEach(( node ) => 
          const component = path.resolve('src/templates/eachpost.js');                      /*creates INDIVIDUAL FICTION PAGES*/
          createPage(
            path: node.frontmatter.path,
            component,
            context: 
              id: node.id,
            ,
          );
        );
        result.data.nonfictionarchive.edges.forEach(( node ) => 
          const component = path.resolve('src/templates/eachpost.js');                      /*creates INDIVIDUAL NON-FICTION PAGES*/
        createPage(
          path: node.frontmatter.path,
          component,
          context: 
            id: node.id,
          ,
        );
      );
        result.data.futurearchive.edges.forEach(( node ) => 
          const component = path.resolve('src/templates/eachpost.js');                      /*creates INDIVIDUAL LETTER PAGES*/
          createPage(
            path: node.frontmatter.path,
            component,
            context: 
              id: node.id,
            ,
          );
        );
        result.data.issuesarchive.edges.forEach(( node ) => 
          const component = path.resolve('src/templates/eachpost.js');                      /*creates INDIVIDUAL ISSUE PAGES; change template to change every issue page*/
          createPage(
            path: node.frontmatter.path,
            component,
            context: 
              id: node.id,
            ,
          );
        );
        const FICposts = result.data.fictionarchive.edges                                   /*creates FICTION LIST PAGES*/
        const FICpostsPerPage = 10
        const FICnumPages = Math.ceil(FICposts.length / FICpostsPerPage)
        Array.from( length: FICnumPages ).forEach((_, i) => 
          createPage(
            path: i === 0 ? `/fiction` : `/fiction/$i + 1`,
            component: path.resolve('src/templates/fictionarchive.js'),
            context: 
              limit: FICpostsPerPage,
              skip: i * FICpostsPerPage,
              FICnumPages,
              FICcurrentPage: i + 1,
            ,
          );
        );
        const NONFICposts = result.data.nonfictionarchive.edges                             /*creates NON-FICTION LIST PAGES*/
        const NONFICpostsPerPage = 10
        const NONFICnumPages = Math.ceil(NONFICposts.length / NONFICpostsPerPage)
        Array.from( length: NONFICnumPages ).forEach((_, i) => 
          createPage(
            path: i === 0 ? `/non-fiction` : `/non-fiction/$i + 1`,
            component: path.resolve('src/templates/nonfictionarchive.js'),
            context: 
              limit: NONFICpostsPerPage,
              skip: i * NONFICpostsPerPage,
              NONFICnumPages,
              NONFICcurrentPage: i + 1,
            ,
          );
        );
        const FUTposts = result.data.futurearchive.edges                                   /*creates LETTERS FROM THE FUTURE LIST PAGES*/
        const FUTpostsPerPage = 10
        const FUTnumPages = Math.ceil(FUTposts.length / FUTpostsPerPage)
        Array.from( length: FUTnumPages ).forEach((_, i) => 
          createPage(
            path: i === 0 ? `/future` : `/future/$i + 1`,
            component: path.resolve('src/templates/futurearchive.js'),
            context: 
              limit: FUTpostsPerPage,
              skip: i * FUTpostsPerPage,
              FUTnumPages,
              FUTcurrentPage: i + 1,
            ,
          );
        );
        const FULLposts = result.data.issuesarchive.edges                                   /*creates ISSUES LIST PAGES*/
        const FULLpostsPerPage = 10
        const FULLnumPages = Math.ceil(FULLposts.length / FULLpostsPerPage)
        Array.from( length: FULLnumPages ).forEach((_, i) => 
          createPage(
            path: i === 0 ? `/fullissues` : `/fullissues/$i + 1`,
            component: path.resolve('src/templates/issuesarchive.js'),
            context: 
              limit: FULLpostsPerPage,
              skip: i * FULLpostsPerPage,
              FULLnumPages,
              FULLcurrentPage: i + 1,
            ,
          );
        );
        const AUTposts = result.data.authorarchive.edges
        const AUTpostsPerPage = 10
        const AUTnumPages = Math.ceil(AUTposts.length / AUTpostsPerPage)
        Array.from( length: AUTnumPages ).forEach((_, i) => 
          createPage(
            path: i === 0 ? `/contributors` : `/contributors/$i + 1`,
            component: path.resolve('src/templates/authorarchive.js'),
            context: 
              limit: AUTpostsPerPage,
              skip: i * AUTpostsPerPage,
              AUTnumPages,
              AUTcurrentPage: i + 1,
            ,
          );
        );
        resolve();
      ),
    );
  );
;

我的首页(缩短):

import  graphql, withPrefix, Link  from 'gatsby';
import Image from "gatsby-image";
import Helmet from 'react-helmet';
import SEO from '../components/SEO';
import Layout from '../layouts/index';

const Home = (props) =>                                                      //THIS SETS THE FRONT PAGE, including featured story, latest stories, and latest issues
  const json = props.data.allFeaturesJson.edges;
  const posts = props.data.allMarkdownRemark.edges;

  return (
    <Layout bodyClass="page-home">
      <SEO title="Home" />
      <Helmet>
        <meta
          name="Haven Quarterly"
          content="A Magazine of Science Fiction and Fantasy"
        />
      </Helmet>

                                                                                          /*FEATURED*/
      <div className="intro pb-1">
        <div className="container">
          <div className="row2 justify-content-start">
            <div className="grid-container pt-2">
              <div className="wide">
                <div className="col-12">
                  <Link to="/featured">
                      <h4>Featured Story</h4>
                  </Link>
                  <hr />
                </div>
                posts
                  .filter(post => post.node.frontmatter.featured === true)                       /*This looks at only the md file with featured: true*/
                  .map(( node: post ) => 
                    return (
                      <div className="container" key=post.id>
                        <h1 pb>
                          <Link to=post.frontmatter.path>post.frontmatter.title</Link>
                        </h1>
                        <h2>By <Link to=post.frontmatter.author.idpath> post.frontmatter.author.id</Link> in <Link to=post.frontmatter.issue.idpath> post.frontmatter.issue.id</Link></h2>          /*THIS IS THE ONLY PLACE ON THIS PAGE WHERE THE ISSUE YAML ARRAY SEEMS TO WORK. IT ONLY WORKS WHEN featured === true WHICH IS CRAZY*/
                        <p>post.excerpt</p>
                      </div>
                      )
                    )
              </div>      
              <div className="thin">
              posts
                  .filter(post => post.node.frontmatter.featured === true)                     /*This looks at only the md file with featured: true*/
                  .map(( node: post ) => 
                    return (

                      <Link to="/latest">
                        <Image className="topimage"
                          fixed=post.frontmatter.currentcover.childImageSharp.fixed      /*This pulls the image from the md file with featured: true (current cover)*/
                        />
                      </Link>

                      )
                    )
              </div>
            </div>
            <hr />

            <div className="col-12">
              posts
              .filter(post => !post.node.frontmatter.featured)
              .filter(post => post.node.frontmatter.issue === "Issue One Summer 2020")          /*THIS SHOULD FILTER ONLY MD FILES WITH issue: Issue One Summer 2020"*/
              .slice(0, 6)
              .map(( node: post ) => 
                return (
                  <div className="postbody" key=post.id>

                      <h2 pb>
                        <Link to=post.frontmatter.path>post.frontmatter.title</Link> by <Link to=post.frontmatter.author.idpath> post.frontmatter.author.id</Link> (post.frontmatter.category)
                      </h2>
                  </div>
                )
              )
            </div>

            <hr />

          </div>
        </div>
      </div>


    <div className="postbody">
      <div className="container pt-8 pt-md-4">
        <div className="row2 justify-content-start pt-2">
          <div className="col-12">
            <Link to="/fiction">
                <h4>Latest Fiction</h4>
            </Link>
            <hr />
          </div>
                                                                                      /*FICTION*/
          <div className="container">

            posts
              .filter(post => !post.node.frontmatter.featured)
              .filter(post => post.node.frontmatter.category === "fiction")          /*This should only pull from md files with category "fiction", excluding posts marked featured*/
              .slice(0, 6)
              .map(( node: post ) => 
                return (
                  <div className="container" key=post.id>
                      <Image className="inlineimage"
                        fluid=post.frontmatter.cover.childImageSharp.fluid          /*This should pull image from md files with category "fiction"*/
                      />
                      <h1 pb>
                        <Link to=post.frontmatter.path>post.frontmatter.title</Link>
                      </h1>
                      <h2>By <Link to=post.frontmatter.author.idpath> post.frontmatter.author.id</Link> in  <Link to=post.frontmatter.issue> post.frontmatter.issue</Link></h2>
                      <p>post.excerpt</p>
                      <hr />
                  </div>
                )
              )
            <div className="col-12 text-center pb-3">
              <Link className="button button-primary" to="/fiction">
                View All Stories
              </Link>
            </div>
          </div>
        </div>
      </div>
    </div>          
    </Layout>
  );
;

export const query = graphql`
  query 
    allAuthorYaml 
      nodes 
        bio
        id
        idpath
        picture 
          childImageSharp 
            fixed(width: 200) 
              ...GatsbyImageSharpFixed 
            
            fluid(maxWidth: 150, maxHeight: 150) 
              ...GatsbyImageSharpFluid
            
          
        
        stories 
          item
        
        twitter
      
    
    allIssueYaml 
      edges 
        node 
          artist
          artistbio
          id
          idpath
          text
          artistimage 
            childImageSharp 
              fixed(width: 200) 
                ...GatsbyImageSharpFixed 
              
              fluid(maxWidth: 150, maxHeight: 150) 
                ...GatsbyImageSharpFluid
              
            
          
          currentcover 
            childImageSharp 
              fixed(width: 403) 
                ...GatsbyImageSharpFixed 
              
              fluid(maxWidth: 300, maxHeight: 300) 
                ...GatsbyImageSharpFluid
              
            
          
        
      
    
    allMarkdownRemark(
      filter:  fileAbsolutePath:  regex: "/.*.md$/" 
      sort:  fields: [frontmatter___date], order: DESC 
      ) 
      totalCount
      edges 
        node 
          id
          frontmatter 
            featured
            path
            title
            author 
              id
              idpath
              bio
              twitter
              picture 
                childImageSharp 
                  fixed(width: 200) 
                    ...GatsbyImageSharpFixed 
                  
                  fluid(maxWidth: 150, maxHeight: 150) 
                    ...GatsbyImageSharpFluid
                  
                
              
            
            issue 
              id
              idpath
              currentcover 
                childImageSharp 
                  fixed(width: 403) 
                    ...GatsbyImageSharpFixed 
                  
                  fluid(maxWidth: 300) 
                    ...GatsbyImageSharpFluid
                  
                
              
              text
              artist
              artistimage 
                childImageSharp 
                  fixed(width: 200) 
                    ...GatsbyImageSharpFixed 
                  
                  fluid(maxWidth: 150, maxHeight: 150) 
                    ...GatsbyImageSharpFluid
                  
                
              
              artistbio 
            
            date(formatString: "DD MMMM YYYY")
            category
            currentcover 
              childImageSharp 
                fixed(width: 403) 
                  ...GatsbyImageSharpFixed 
                
                fluid(maxWidth: 300) 
                  ...GatsbyImageSharpFluid
                
              
            
            cover 
              childImageSharp 
                fixed(width: 403) 
                  ...GatsbyImageSharpFixed 
                
                fluid(maxWidth: 300) 
                  ...GatsbyImageSharpFluid
                
              
                        
          
          excerpt(pruneLength: 650)
        
      
    
    allFeaturesJson 
      edges 
        node 
          id
          title
          description
          image
        
      
    
  
`;

export default Home;

一个带有损坏数组的典型模板:

import  graphql, Link, withPrefix  from 'gatsby';
import SEO from '../components/SEO';
import Layout from '../layouts/index';
import Helmet from 'react-helmet';
import Image from 'gatsby-image';

export default class Fictionarchive extends React.Component 
  render() 
    const posts = this.props.data.allMarkdownRemark.edges
    const json = this.props.data.allFeaturesJson.edges;

    const  FICcurrentPage, FICnumPages  = this.props.pageContext
    const isFirst = FICcurrentPage === 1
    const isLast = FICcurrentPage === FICnumPages
    const prevPage = FICcurrentPage - 1 === 1 ? "/" : `/fiction/$FICcurrentPage - 1`
    const nextPage = `/fiction/$FICcurrentPage + 1`

    return (
      <Layout bodyClass="page-home">
      <SEO title="Fiction" />
      <Helmet>
        <meta
          name="description"
          content="all fiction of Haven Quarterly"
        />
      </Helmet>

    <div className="postbody">
      <div className="container pt-md-5">
        <div className="row2 justify-content-start">
          <div className="col-12">
                <h3>Latest Fiction</h3>
            <hr />
          </div>

          <div className="container">
          posts
              .filter(post => post.node.frontmatter.category === "fiction")
              .map(( node: post ) => 
                return (
                  <div className="container" key=post.id>
                      <Image className="inlineimage"
                        fluid=post.frontmatter.cover.childImageSharp.fluid
                      />
                      <h1 pb>
                        <Link to=post.frontmatter.path>post.frontmatter.title</Link>
                      </h1>
                      <h2>By <Link to=post.frontmatter.author.idpath> post.frontmatter.author.id</Link> in  <Link to=post.frontmatter.issue.idpath> post.frontmatter.issue.id</Link></h2>
                      <p>post.excerpt</p>
                      <hr />
                  </div>
                )
              )
              <div className="container">
                <div className="row">
                  <div className="col-sm">
                    <p className="text-left">
                      !isFirst && (
                        <Link to=prevPage rel="prev">
                          ← Previous Page
                        </Link>
                      )
                    </p>
                  </div>            
                </div>         
              </div>
          </div>
        </div>
      </div>
    </div>


    </Layout>
    )
  


export const fictionarchiveQuery = graphql`
  query fictionarchiveQuery($skip: Int!, $limit: Int!) 
    allMarkdownRemark(
      filter:  frontmatter: category:eq:"fiction"  
      sort:  fields: [frontmatter___date], order: DESC 
      limit: $limit
      skip: $skip
    ) 
      edges 
        node 
          excerpt(pruneLength: 750)    
          frontmatter 
            category
            featured
            path
            title
            author 
              id
              idpath
              bio
              twitter
              picture 
                childImageSharp 
                  fixed(width: 400)                                            
                    ...GatsbyImageSharpFixed 
                  
                  fluid(maxWidth: 400, maxHeight: 400) 
                    ...GatsbyImageSharpFluid
                  
                
              
            
            issue 
              id
              idpath
              currentcover 
                childImageSharp 
                  fixed(width: 403) 
                    ...GatsbyImageSharpFixed 
                  
                  fluid(maxWidth: 300) 
                    ...GatsbyImageSharpFluid
                  
                
              
              text
              artist
              artistimage 
                childImageSharp 
                  fixed(width: 200) 
                    ...GatsbyImageSharpFixed 
                  
                  fluid(maxWidth: 150, maxHeight: 150) 
                    ...GatsbyImageSharpFluid
                  
                
              
              artistbio 
            
            date(formatString: "DD MMMM YYYY")
            cover 
              childImageSharp 
                fixed(width: 322) 
                  ...GatsbyImageSharpFixed 
                
                fluid(maxWidth: 450) 
                  ...GatsbyImageSharpFluid
                
              
            
          
          html
        
      
    
    allFeaturesJson 
      edges 
        node 
          id
          title
          description
          image
        
      
    
  
`

【问题讨论】:

【参考方案1】:

解决方案

比我聪明,确保你已经在你的 yaml 文件中定义了你在 markdown 文件中的所有值。

更长的解释

我在 Gatsby 的 GitHub 存储库上询问,结果发现解决方案非常简单:

一切都或多或少地正确设置。问题是在我的第二个 yaml 文件中,我没有定义我在 markdown 文件中列出的所有值。因此,在 30 多个 .md 文件之间,问题字段总共有四个值,但在我的 issue.yaml 文件中,我只定义了其中两个。它返回了一个空错误,因为当它循环浏览降价文件时,它说,“我有第三个值,但在 yaml 文件中没有对应的值。”

回答我问题的人说有两种解决方案:

    在 issue.yaml 中定义所有问题 ID,以便您至少获得该 ID 返回 在帖子的前端定义一个替代问题标题 并检查问题是否返回 null。如果是,请隐藏您的组件并 改用 issueTitle

我认为这无关紧要,因为我认为我只是从带有我定义的字段的 markdown 文件中调用数据(换句话说,我认为我已经排除了那些我没有在我的yaml),但这是错误的。

其他注意事项

另外两个让我感到困惑的简单问题:1. 确保 yaml 文件中定义的内容与 markdown 文件相应字段中提供的内容之间的拼写完全相同。 2. 确保在将 yaml 文件映射到 frontmatter 字段时使用 id 字段,如 this suggestion。

最后的想法

原来我不知道自己在做什么。但我希望这个答案能帮助那些在 Gatsby 周围闲逛的人遇到同样的问题。

LekoArts 提供的更完整答案的链接:https://github.com/gatsbyjs/gatsby/issues/25373

【讨论】:

以上是关于如何在 Gatsby 中将多个 yaml 文件映射到 frontmatter的主要内容,如果未能解决你的问题,请参考以下文章

在 SpringBoot 中将 yaml 映射到对象 hashmap

如何从 Gatsby.js 中的 YAML 数组中获取图像

如何在 azure devops YAML 管道中将单个代理用于多个作业/阶段

如何在 Gatsby 中将 markdown frontmatter 显示为 HTML

如何在 bash 中将 json 响应转换为 yaml

如何使用 gatsby-image 从数组中查询多个图像?