如何在 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
如何在 azure devops YAML 管道中将单个代理用于多个作业/阶段