「はじめてつくるJamstackサイト with ヘッドレスCMS」コード

はじめに

なし

第1章 基礎編

なし

第2章 開発編 その1

(#2-s1)

jamstack-site
├── node_modules               
├── src    
├── .gitignore            
├── .prettierrc             
├── gatsby-browser.js       ←削除
├── gatsby-config.js           
├── gatsby-node.js  
├── gatsby-ssr.js           ←削除
├── LICENSE 
├── package-lock.json
├── package.json     
└── README.md            
(#2-s2)

jamstack-site
├── node_modules               
├── src    
├── .gitignore            
├── .prettierrc             
├── gatsby-config.js           
├── gatsby-node.js  
├── LICENSE 
├── package-lock.json
├── package.json     
└── README.md            
(#2-s3)

src
├── components   
│           ├── header.js             ←削除
│           ├── layout.css            ←削除
│           ├── layout.js             ←削除
│           └── seo.js                ←削除
│
├── images  
│           ├── gatsby-astronaut.png  ←削除
│           └── gatsby-icon.png    
│
├── pages 
│           ├── 404.js                ←削除
│           ├── index.js 
│           ├── page-2.js             ←削除
│           ├── using-ssr.js          ←削除
│           └── using-typescript.tsx  ←削除  
│
└── templates 
            └── using-dsg.js          ←削除  
(#2-s4)

src
├── components   
│
├── images
│           └── gatsby-icon.png     
│
├── pages  
│           └──index.js
│
└── templates          
// gatsby-node.js(#2-j1)

↓ 削除
exports.createPages = async ({ actions }) => {
  const { createPage } = actions
  createPage({
    path: "/using-dsg",
    component: require.resolve("./src/templates/using-dsg.js"),
    context: {},
    defer: true,
  })
}
↑ 削除
// index.js(#2-j2)

import * as React from "react"

const Index = () => {
    return (

    )
}

export default Index
// index.js(#2-j3)

import * as React from "react"

const Index = () => {
    return (
        <h1>こんにちは</h1>   // 追加
    )
}

export default Index

// style.css(#2-c1)

.h1-style {
    color: red;
    letter-spacing: 20px;
}
(#2-s5)

src
├── components   
│
├── images  
│           └── gatsby-icon.png   
│
├── pages 
│           └── index.js
│
├── styles      ←作成
│
└── templates
          
(#2-s6)

src
├── components   
│
├── images  
│           └── gatsby-icon.png   
│
├── pages 
│           └── index.js
│
├── styles
│           └── index.module.css      ←作成
│
└── templates
          
// index.module.css(#2-c2)

.h1Text{
    color: red;
    letter-spacing: 20px;
}
//index.js(#2-j4)

import * as React from "react"
import * as style from "../styles/index.module.css"   // 追加

const Index = () => {
    return (
        <h1>こんにちは</h1> 
    )
}

export default Index

//index.js(#2-j5)

import * as React from "react"
import * as style from "../styles/index.module.css"  

const Index = () => {
    return (
        <h1 className={style.h1Text}>こんにちは</h1>  // 追加
    )
}

export default Index
(#2-s7)

src
├── components   
│
├── images  
│           └── gatsby-icon.png   
│
├── pages 
│           ├── blog.js     ←作成
│           └── index.js
│
├── styles
│           └── index.module.css
│
└── templates
          
// blog.js(#2-j6)

import * as React from "react"
 
const Blog = () => {
    return (
        <h1>ブログページ</h1>  
    )
}

export default Blog
(#2-s8)

src
├── components   
│
├── images  
│           └── gatsby-icon.png   
│
├── pages 
│           ├── blog.js     
│           ├── contact.js  ←作成
│           └── index.js
│
├── styles
│           └── index.module.css
│
└── templates     
          
// contact.js(#2-j7)

import * as React from "react"
 
const Contact = () => {
    return (
        <h1>コンタクトページ</h1>  
    )
}

export default Contact

//index.js(#2-j8)

import * as React from "react"
import { Link } from "gatsby"   // 追加
import * as style from "../styles/index.module.css"  

const Index = () => {
    return (
        <h1 className={style.h1Text}>こんにちは</h1> 
    )
}

export default Index
//index.js(#2-j9)

import * as React from "react"
import { Link } from "gatsby"
import * as style from "../styles/index.module.css"  

const Index = () => {
    return (
        <h1 className={style.h1Text}>こんにちは</h1> 
        <Link></Link>       // 追加
    )
}

export default Index
//index.js(#2-j10)

import * as React from "react"
import { Link } from "gatsby"
import * as style from "../styles/index.module.css"  

const Index = () => {
    return (
        <h1 className={style.h1Text}>こんにちは</h1> 
        <Link to="/contact">Contactページへ移動</Link>     // 追加  
    )
}

export default Index
//index.js(#2-j11)

import * as React from "react"
import { Link } from "gatsby"
import * as style from "../styles/index.module.css"  

const Index = () => {
    return (
        <div>   // 追加
            <h1 className={style.h1Text}>こんにちは</h1> 
            <Link to="/contact">Contactページへ移動</Link>       
        </div>  // 追加
    )
}

export default Index
(#2-s9)

src
├── components   
│
├── data      ←作成
│
├── images
│           └── gatsby-icon.png     
│
├── pages 
│           ├── blog.js 
│           ├── contact.js
│           └── index.js
│
├── styles
│           └── index.module.css
│
└── templates    
          
(#2-s10)

src
├── components   
│
├── data  
│           └── first-blog.md     ←作成
│
├── images
│           └── gatsby-icon.png     
│
├── pages 
│           ├── blog.js 
│           ├── contact.js
│           └── index.js
│
├── styles
│           └── index.module.css
│
└── templates   
          
// first-blog.md(#2-m1)

---
id: "1"
title: "1つ目の記事"
date: "2031-03-01"
image: ""
excerpt: ""
---

これが1つ目のブログ記事です。
(#2-s11)

src
├── components   
│
├── data  
│           ├── first-blog.md    
│           ├── second-blog.md    ←作成
│           ├── third-blog.md     ←作成
│           ├── fourth-blog.md    ←作成
│           ├── fifth-blog.md     ←作成
│           └── sixth-blog.md     ←作成
│
├── images
│           └── gatsby-icon.png     
│
├── pages 
│           ├── blog.js 
│           ├── contact.js
│           └── index.js
│
├── styles
│           └── index.module.css
│
└── templates     
          
// second-blog.md(#2-m2)

---
id: "2"
title: "2つ目の記事"
date: "2031-03-02"
image: ""
excerpt: ""
---

これが2つ目のブログ記事です。
// third-blog.md(#2-m3)

---
id: "3"
title: "3つ目の記事"
date: "2031-03-03"
image: ""
excerpt: ""
---

これが3つ目のブログ記事です。
// fourth-blog.md(#2-m4)

---
id: "4"
title: "4つ目の記事"
date: "2031-03-04"
image: ""
excerpt: ""
---

これが4つ目のブログ記事です。
// fifth-blog.md(#2-m5)

---
id: "5"
title: "5つ目の記事"
date: "2031-03-05"
image: ""
excerpt: ""
---

これが5つ目のブログ記事です。
// sixth-blog.md(#2-m6)

---
id: "6"
title: "6つ目の記事"
date: "2031-03-06"
image: ""
excerpt: ""
---

これが6つ目のブログ記事です。
// gatsby-config.js(#2-j12)

module.exports = {
  siteMetadata: {

  },
  plugins: [

  ],
}
// gatsby-config.js(#2-j13)

...

  plugins: [
    `gatsby-transformer-remark`,    // 追加
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,

...
// gatsby-config.js(#2-j14)

...

    `gatsby-plugin-image`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    `gatsby-transformer-sharp`,

...
// gatsby-config.js(#2-j15)

{
    resolve: `gatsby-source-filesystem`,
    options: {
        name: `blog`,
        path: `${__dirname}/src/data`,
    },
},
// gatsby-config.js(#2-j16)

module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
    siteUrl: `https://gatsbystarterdefaultsource.gatsbyjs.io/`,
  },
  plugins: [
    `gatsby-transformer-remark`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `blog`,
        path: `${__dirname}/src/data`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        // This will impact how browsers show your PWA/website
        // https://css-tricks.com/meta-theme-color-and-trickery/
        // theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.dev/offline
    // `gatsby-plugin-offline`,
  ],
}
// blog.js(#2-j17)

import * as React from "react"
import { graphql } from "gatsby"    // 追加
 
const Blog = () => {
    return (
        <h1>ブログページ</h1>  
    )
}

export default Blog
// blog.js(#2-j18)

...

export default Blog

export const query = graphql``
// blog.js(#2-j19)

...
export default Blog

export const query = graphql`
    query BlogQuery {
        allMarkdownRemark {
            edges {
                node {
                    frontmatter {
                        date
                        excerpt
                        id
                        image
                        title
                    }
                }
            }
        }
    }
`
// blog.js(#2-j20)

import * as React from "react"
import { graphql } from "gatsby"
 
const Blog = () => {
    return (
        <h1>ブログページ</h1>  
    )
}

export default Blog

export const query = graphql`
    query BlogQuery {
        allMarkdownRemark {
            edges {
                node {
                    frontmatter {
                        date
                        excerpt
                        id
                        image
                        title
                    }
                }
            }
        }
    }
`
// blog.js(#2-j21)

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

const Blog = (props) => {    // 追加
    return (
        <h1>ブログページ</h1>  
    )
}

...
// blog.js(#2-j22)

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

const Blog = (props) => {   
    console.log(props)      // 追加
    return (
        <h1>ブログページ</h1>  
    )
}

...
// blog.js(#2-j23)

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

const Blog = (props) => {
    console.log(props)      
    return (
        <h1>ブログページ</h1>
        {props}       // 追加
    )
}

...
// blog.js(#2-j24)

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

const Blog = (props) => {
    console.log(props)      
    return (
        <h1>ブログページ</h1>
        {props.data.allMarkdownRemark.edges}       // 追加
    )
}

...
// blog.js(#2-j25)

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

const Blog = (props) => {
    console.log(props)      
    return (
        <h1>ブログページ</h1>
        {props.data.allMarkdownRemark.edges.map((singleBlog, index))}     // 追加
    )
}

...
// blog.js(#2-j26)

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

const Blog = (props) => {
    console.log(props)      
    return (
        <h1>ブログページ</h1>
        {props.data.allMarkdownRemark.edges.map((singleBlog, index) =>   // 追加
            {console.log(singleBlog)}   // 追加
        )}   
    )
}

...
// blog.js(#2-j27)

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

const Blog = (props) => {
    console.log(props)      
    return (
        <div>    // 追加
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                {console.log(singleBlog)}   
            )}  
        </div>   // 追加
    )
}

...
// blog.js(#2-j28)

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

const Blog = (props) => {
    console.log(props)    // 削除  
    return (
        <div>    
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                {console.log(singleBlog)}    // 削除
            )}  
        </div>  
    )
}

...
// blog.js(#2-j29)

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

const Blog = (props) => {
    return (
        <div>    
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                <div>               // 追加
                    <h2></h2>       // 追加
                    <p></p>         // 追加
                </div>              // 追加
            )}  
        </div>  
    )
}

...
// blog.js(#2-j30)

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

const Blog = (props) => {
    return (
        <div>    
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                <div>               
                    <h2>{singleBlog.node.frontmatter.title}</h2>       // 追加
                    <p>{singleBlog.node.frontmatter.date}</p>           // 追加
                </div>              
            )}  
        </div>  
    )
}

...
// blog.js(#2-j31)

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

const Blog = (props) => {
    return (
        <div>    
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                <div key={index}>               // 追加
                    <h2>{singleBlog.node.frontmatter.title}</h2>       
                    <p>{singleBlog.node.frontmatter.date}</p>         
                </div>              
            )}  
        </div>  
    )
}

...
// blog.js(#2-j32)

import * as React from "react"
import { graphql } from "gatsby"
 
const Blog = (props) => {
    return (
        <div>    
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                <div key={index}>              
                    <h2>{singleBlog.node.frontmatter.title}</h2>       
                    <p>{singleBlog.node.frontmatter.date}</p>         
                </div>              
            )}  
        </div>  
    )
}

export default Blog

export const query = graphql`
    query BlogQuery {
        allMarkdownRemark {
            edges {
                node {
                    frontmatter {
                        date
                        excerpt
                        id
                        image
                        title
                    }
                }
            }
        }
    }
`
(#2-s12)

src
├── components   
│
├── data  
│           ├── first-blog.md    
│           ├── second-blog.md    
│           ├── third-blog.md    
│           ├── fourth-blog.md    
│           ├── fifth-blog.md     
│           └── sixth-blog.md      
│
├── images
│           └── gatsby-icon.png     
│
├── pages 
│           ├── blog.js 
│           └── index.js
│
├── styles
│           └── index.module.css      
│ 
└── templates
            └── single-blog.js      ←追加        
/// single-blog.js(#2-j33)

import * as React from "react"
 
const SingleBlog = () => {
    return (
        <h1>記事ページ</h1>  
    )
}

export default SingleBlog
// gatsby-node.js(#2-j34)

exports.onCreateNode = ({ node }) => {

}
// gatsby-node.js(#2-j35)

exports.onCreateNode = ({ node }) => {
  if (node.internal.type === `MarkdownRemark`) {   // 追加
  }   // 追加
}
// gatsby-node.js(#2-j36)

const { createFilePath } = require(`gatsby-source-filesystem`)   // 追加

exports.onCreateNode = ({ node, getNode, actions }) => {  // 追加
    const { createNodeField } = actions         // 追加
  
    if (node.internal.type === `MarkdownRemark`) {

    }
}
// gatsby-node.js(#2-j37)

const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
    const { createNodeField } = actions
  
    if (node.internal.type === `MarkdownRemark`) {
      const slug = createFilePath({ node, getNode })   // 追加
      createNodeField({         // 追加
        node,                   // 追加
        name: `slug`,           // 追加
        value: slug,            // 追加
      })
    }
}
// gatsby-node.js(#2-j38)

const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
    ...
}

exports.createPages = async () => {  // 追加
}                                    // 追加
// gatsby-node.js(#2-j39)

const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
    ...
}

exports.createPages = async ({ graphql }) => {  // 追加
    ↓追加
    const result = await graphql(`              
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `)
   ↑追加
}
// gatsby-node.js(#2-j40)

const path = require(`path`)     // 追加
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
    ...
}

exports.createPages = async ({ graphql, actions }) => {    // 追加
    const { createPage } = actions      // 追加
    const result = await graphql(`              
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `)
  
  ↓追加
  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
        createPage({
            path: node.fields.slug,
            component: path.resolve(`./src/templates/single-blog.js`),
            context: {
                slug: node.fields.slug,
            },
        })
  })
  ↑追加

}
// gatsby-node.js(#2-j41)

const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
    const { createNodeField } = actions
  
    if (node.internal.type === `MarkdownRemark`) {
      const slug = createFilePath({ node, getNode })   
      createNodeField({         
        node,                   
        name: `slug`,           
        value: slug,            
      })
    }
}

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

    const result = await graphql(`              
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `)
  
    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
            createPage({
                path: node.fields.slug,
                component: path.resolve(`./src/templates/single-blog.js`),
                context: {
                    slug: node.fields.slug,
                },
            })
    })
}
// single-blog.js(#2-j42)

import * as React from "react"
import { graphql } from "gatsby"     // 追加
 
const SingleBlog = () => {
    return (
        <h1>記事ページ</h1>  
    )
}

export default SingleBlog

export const query = graphql``      // 追加
// single-blog.js(#2-j43)

import * as React from "react"
import { graphql } from "gatsby" 
 
const SingleBlog = () => {
    return (
        <h1>記事ページ</h1>  
    )
}

export default SingleBlog

export const query = graphql`
    ↓追加
    query SingleBlogQuery {
        markdownRemark {
            frontmatter {
                date
                excerpt
                id
                image
                title
            }
            html
        }
    }
    ↑追加
`
// single-blog.js(#2-j44)

import * as React from "react"
import { graphql } from "gatsby" 
 
const SingleBlog = (props) => {     // 追加
    return (
        <div>                       // 追加
            <h1>記事ページ</h1> 
            {console.log(props)}    // 追加
        </div>                      // 追加
    )
}

export default SingleBlog

export const query = graphql`
    query SingleBlogQuery {
        markdownRemark {
            frontmatter {
                date
                excerpt
                id
                image
                title
            }
            html
        }
    }
`
// single-blog.js(#2-j45)

...

export default SingleBlog

export const query = graphql`
    query SingleBlogQuery ($slug: String!) {     // 追加
        markdownRemark(fields: { slug: { eq: $slug } }) {   // 追加
            frontmatter {
                date
                excerpt
                id
                image
                title
            }
            html
        }
    }
`
// single-blog.js(#2-j46)

...
 
const SingleBlog = (props) => {   
    return (
        <div>                       
            <h1>{props.data.markdownRemark.frontmatter.title}</h1>  // 追加
            <p>{props.data.markdownRemark.frontmatter.date}</p>    // 追加
        </div>                     
    )
}

export default SingleBlog

...
// single-blog.js(#2-j47)

...
 
const SingleBlog = (props) => {   
    return (
        <div>                       
            <h1>{props.data.markdownRemark.frontmatter.title}</h1>
            <p>{props.data.markdownRemark.frontmatter.date}</p> 
            <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} />    // 追加
        </div>                     
    )
}

export default SingleBlog

...
// blog.js(#2-j48)

import * as React from "react"
import { graphql } from "gatsby"
 
const Blog = (props) => {
    return (
        <div>    
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                <div key={index}>              
                    <h2>{singleBlog.node.frontmatter.title}</h2>       
                    <p>{singleBlog.node.frontmatter.date}</p>         
                </div>              
            )}  
        </div>  
    )
}

export default Blog

export const query = graphql`
    ↓置き換え
    query BlogQuery {
        allMarkdownRemark {
            edges {
                node {
                    fields {
                        slug
                    }
                    frontmatter {
                        date
                        excerpt
                        id
                        image
                        title
                    }
                }
            }
        }
    }
    ↑置き換え
`
// blog.js(#2-j49)

import * as React from "react"
import { graphql, Link } from "gatsby"  // Linkを追加
 
const Blog = (props) => {
    return (
        <div>    
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                <div key={index}>              
                    <Link to={singleBlog.node.fields.slug}><h2>{singleBlog.node.frontmatter.title}</h2></Link>    // 追加      
                    <p>{singleBlog.node.frontmatter.date}</p>         
                </div>              
            )}  
        </div>  
    )
}

...
// blog.js(#2-j50)

...

export default Blog

export const query = graphql`
query BlogQuery {
    allMarkdownRemark(sort: {fields: frontmatter___id, order: DESC}) { // 追加
      edges {
        node {
          frontmatter {
            date
            excerpt
            id
            image
            title
          }
          fields {
            slug
          }
        }
      }
    }
  }
`
// blog.js(#2-j51)

import * as React from "react"
import { graphql, Link } from "gatsby"
 
const Blog = (props) => {
    return (
        <div>    
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                <div key={index}>              
                    <Link to={singleBlog.node.fields.slug}><h2>{singleBlog.node.frontmatter.title}</h2></Link>        
                    <p>{singleBlog.node.frontmatter.date}</p>         
                </div>              
            )}  
        </div> 
    )
}

export default Blog

export const query = graphql`
    query BlogQuery {
        allMarkdownRemark(sort: {fields: frontmatter___id, order: DESC}) {
            edges {
                node {
                    fields {
                        slug
                    }
                    frontmatter {
                        date
                        excerpt
                        id
                        image
                        title
                    }
                }
            }
        }
    }
`
// single-blog.js(#2-j52)

import * as React from "react"
import { graphql } from "gatsby"
 
const SingleBlog = (props) => {
    return (
        <div>                       
            <h1>{props.data.markdownRemark.frontmatter.title}</h1> 
            <p>{props.data.markdownRemark.frontmatter.date}</p> 
            <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} />  
        </div>     
    )
}

export default SingleBlog

export const query = graphql`
    query SingleBlogQuery($slug: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) {
            frontmatter {
                date
                title
                excerpt
                id
                image
            }
            html
        }
    }
`
// gatsby-node.js(#2-j53)

const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
    const { createNodeField } = actions
  
    if (node.internal.type === `MarkdownRemark`) {
        const slug = createFilePath({ node, getNode })   
        createNodeField({         
            node,                   
            name: `slug`,           
            value: slug,            
        })
    }
}

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

    const result = await graphql(`              
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `)
  
    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
        createPage({
            path: node.fields.slug,
            component: path.resolve(`./src/templates/single-blog.js`),
            context: {
                slug: node.fields.slug,
            },
        })
    })
}
// gatsby-config.js(#2-j54)

module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
    siteUrl: `https://gatsbystarterdefaultsource.gatsbyjs.io/`,
  },
  plugins: [
    `gatsby-transformer-remark`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
          name: `blog`,
          path: `${__dirname}/src/data`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        // This will impact how browsers show your PWA/website
        // https://css-tricks.com/meta-theme-color-and-trickery/
        // theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.dev/offline
    // `gatsby-plugin-offline`,
  ],
}

第3章 開発編 その2

(#3-s1)

src
├── components   
│
├── data  
│           ├── fifth-blog.md     ←置き換え
│           ├── first-blog.md     ←置き換え
│           ├── fourth-blog.md    ←置き換え
│           ├── second-blog.md    ←置き換え
│           ├── sixth-blog.md     ←置き換え
│           └── third-blog.md     ←置き換え
│
├── images  
│           ├── arrow-left.svg       ←追加
│           ├── arrow-right.svg      ←追加
│           ├── company.jpg          ←追加
│           ├── facebook.svg         ←追加
│           ├── gatsby-icon.png      
│           ├── gatsby.svg           ←追加
│           ├── github.svg           ←追加
│           ├── index-hero.jpg       ←追加
│           ├── javascript.svg       ←追加
│           ├── linkedin.svg         ←追加
│           ├── logo.png             ←追加
│           ├── next.svg             ←追加
│           ├── pic1.jpg             ←追加
│           ├── pic2.jpg             ←追加
│           ├── pic3.jpg             ←追加
│           ├── pic4.jpg             ←追加
│           ├── pic5.jpg             ←追加
│           ├── pic6.jpg             ←追加
│           ├── react.svg            ←追加
│           ├── social-card.png      ←追加
│           └── twitter.svg          ←追加
│
├── pages 
│           ├── blog.js
│           ├── contact.js
│           └── index.js
│
├── styles
│           ├── all.scss             ←追加 
│           ├── blog.module.scss     ←追加  
│           ├── common.module.scss   ←追加  
│           ├── contact.module.scss  ←追加 
│           ├── index.module.css        ←削除
│           ├── index.module.scss    ←追加    
│           ├── singleBlog.module.scss    ←追加 
│           └── variables.scss       ←追加 
│
└── templates 
            └── single-blog.js

// (#3-j1)

import Img from "gatsby-image"

const OldGatsbyImage = () => {
    return <Img fixed={data.file.childImageSharp.fixed}  />
}

...

export const query = graphql`
  {
    file(relativePath: { eq: "images/example.jpg" }) {
      childImageSharp {
        fixed {
          ...GatsbyImageSharpFixed
        }
      }
    }
  }
`
// blog.js(#3-j2)

...

export const query = graphql`
    query BlogQuery {
        allMarkdownRemark(sort: {fields: frontmatter___id, order: DESC}) {
            edges {
                node {
                    frontmatter {
                        date
                        excerpt
                        id
                        image       // 削除
                        title
                    }
                    fields {
                        slug
                    }
                }
            }
        }
    }
`
/// single-blog.js(#3-j3)

...

export const query = graphql`
    query SingleBlogQuery ($slug: String!) {     
        markdownRemark(fields: { slug: { eq: $slug } }) {   
            frontmatter {
                date
                excerpt
                id
                image       // 削除
                title
            }
            html
        }
    }
`
// index.js(#3-j4)

import * as React from "react"
import { Link } from "gatsby"
import * as style from "../styles/index.module.css"    // 削除

const Index = () => {
    return (
        <div>      
            <h1 className={style.h1Text}>こんにちは</h1>    // 削除
            <Link to="/contact">Contactページへ移動</Link>  // 削除  
        </div>    
    )
}

export default Index
// index.js(#3-j5)

import * as React from "react"
import { Link } from "gatsby"    
 
const Index = () => {
    return(
    // ↓追加
    <>
        <div>
            <div>
                <h1>Jack of All Trades</h1>
                <h3>World's Largest Provider</h3>
            </div>
        </div>
        <div>
            <div>
                <div>
                    <h2>弊社について</h2>
                    <p>Last Update: 2031/03/14</p>
                    <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
                </div>
            </div>
            <div>
                <h2>Service</h2>
                <div>
                    <div><span>JavaScript / 10 years</span></div>
                    <div><span>React / 5 years</span></div>
                    <div><span>Gatsby / 3 years</span></div>
                    <div><span>Next.JS / 3 years</span></div>
                </div>
            </div>
            <div>
                <Link to="/contact">Contact Us!</Link>
            </div>
        </div>
    </>
    // ↑追加
    )
}

export default Index
// index.js(#3-j6)

import * as React from "react"
import { Link } from "gatsby"
import { StaticImage } from "gatsby-plugin-image"  // 追加

const Index = () => {

...
// index.js(#3-j7)

import * as React from "react"
import { Link } from "gatsby"  
import { StaticImage } from "gatsby-plugin-image"   
 
const Index = () => {
    return (
    <>
        <div>
            <StaticImage />    // 追加
            <div>
                <h1>Jack of All Trades</h1>
                <h3>World's Largest Provider</h3>
            </div>

...
// index.js(#3-j8)

import * as React from "react"
import { Link } from "gatsby"  
import { StaticImage } from "gatsby-plugin-image" 

const Index = () => {
    return (
    <>
        <div>
            <StaticImage src="../images/index-hero.jpg" alt="hero" />    // 追加
            <div>
                <h1>Jack of All Trades</h1>
                <h3>World's Largest Provider</h3>
            </div>

...
// index.js(#3-j9)

import * as React from "react"
import { Link } from "gatsby"  
import { StaticImage } from "gatsby-plugin-image" 
 
const Index = () => {
    return (
    <>
        <div>
            <StaticImage src="../images/index-hero.jpg" alt="hero" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} />    // 追加
            <div>
                <h1>Jack of All Trades</h1>
                <h3>World's Largest Provider</h3>
            </div>

...
// index.js(#3-j10)

...

    <div>
        <div>
            <h2>弊社について</h2>
            <p>Last Update: 2031/03/14</p>
            <p>Lorem Ipsum is simply dummy text of ....</p>
        </div>
        <StaticImage src="../images/company.jpg" alt="profile" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} />   // 追加
    </div>

...
// index.js(#3-j11)

import * as React from "react"
import { Link } from "gatsby"  
import { StaticImage } from "gatsby-plugin-image"  
import JSLogo from "../images/javascript.svg"   // 追加
import ReactLogo from "../images/react.svg"     // 追加
import GatsbyLogo from "../images/gatsby.svg"   // 追加
import NextLogo from "../images/next.svg"       // 追加
 
const Index = () => {

...
// index.js(#3-j12)

...

    <div>
        <h2>Service</h2>
        <div>
            <div><img src={JSLogo} alt="javascript"/><span>JavaScript / 10 years</span></div>   // 追加
            <div><img src={ReactLogo} alt="react"/><span>React / 5 years</span></div>   // 追加
            <div><img src={GatsbyLogo} alt="gatsby"/><span>Gatsby / 3 years</span></div>    // 追加
            <div><img src={NextLogo} alt="next"/><span>Next.JS / 3 years</span></div>   // 追加
        </div>
    </div>

...
// index.js(#3-j13)

import * as React from "react"
import { Link } from "gatsby"  
import { StaticImage } from "gatsby-plugin-image" 
import JSLogo from "../images/javascript.svg"  
import ReactLogo from "../images/react.svg"    
import GatsbyLogo from "../images/gatsby.svg"   
import NextLogo from "../images/next.svg"      

const Index = () => {
    return (
        <>    
            <div>
                <StaticImage src="../images/index-hero.jpg" alt="hero" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} /> 
                <div>
                    <h1>Jack of All Trades</h1>
                    <h3>World's Largest Provider</h3>
                </div>
            </div>
            <div>
                <div>
                    <div>
                        <h2>弊社について</h2>
                        <p>Last Update: 2031/03/14</p>
                        <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
                    </div>
                    <StaticImage src="../images/company.jpg" alt="profile" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} />  
                </div>
                <div>
                    <h2>Service</h2>
                    <div>
                        <div><img src={JSLogo} alt="javascript"/><span>JavaScript / 10 years</span></div>   
                        <div><img src={ReactLogo} alt="react"/><span>React / 5 years</span></div>   
                        <div><img src={GatsbyLogo} alt="gatsby"/><span>Gatsby / 3 years</span></div>    
                        <div><img src={NextLogo} alt="next"/><span>Next.JS / 3 years</span></div>   
                    </div>
                </div>
                <div>
                    <Link to="/contact">Contact Us!</Link>
                </div>
            </div>
        </>  
    )
}

export default Index
// blog.js(#3-j14)

import * as React from "react"
import { graphql, Link } from "gatsby"
 
const Blog = (props) => {
    return (
        <>
          <div>
            <div>
            <h1>Blog</h1>
            <p>弊社サービスやお客様の声などを紹介します。</p>
                 {props.data.allMarkdownRemark.edges.map((singleBlog, index) => (
                        <div key={index}>                            
                            <div>
                                <h3>{singleBlog.node.frontmatter.title}</h3>
                                <p>{singleBlog.node.frontmatter.excerpt}</p>
                                <p>{singleBlog.node.frontmatter.date}</p>
                                <Link to={singleBlog.node.fields.slug}>Read More</Link>
                            </div>
                        </div>
                    )
                )}
            </div>
          </div>
        </>     
    )
}

export default Blog

...
// blog.js(#3-j15)

...

export default Blog

export const query = graphql`
    query BlogQuery {
        allMarkdownRemark(sort: {fields: frontmatter___id, order: DESC}) {
            edges {
                node {
                    frontmatter {
                        date
                        excerpt
                        id
                        // ↓追加
                        image {
                            childImageSharp {
                                gatsbyImageData(
                                    quality: 90, 
                                    formats: AUTO, 
                                    placeholder: BLURRED, 
                                )
                            }
                        }
                        // ↑追加
                        title
                    }
                    fields {
                        slug
                    }
                }
            }
        }
    }
`
// (#3-j16)

gatsbyImageData(
    quality: 90
    formats: [AUTO, WEBP, AVIF],    // 変更
    placeholder: BLURRED, 
)
// blog.js(#3-j17)

import * as React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"   // 追加
 
const Blog = (props) => {
    return (
        <>
          <div>
            <div>
            <h1>Blog</h1>
            <p>弊社サービスやお客様の声などを紹介します。</p>
                 {props.data.allMarkdownRemark.edges.map((singleBlog, index) => (
                        <div key={index}>                            
                            <div>
                                <h3>{singleBlog.node.frontmatter.title}</h3>
                                <p>{singleBlog.node.frontmatter.excerpt}</p>
                                <p>{singleBlog.node.frontmatter.date}</p>
                                <Link to={singleBlog.node.fields.slug}>Read More</Link>
                            </div>
                            <GatsbyImage image={singleBlog.node.frontmatter.image.childImageSharp.gatsbyImageData} alt="card-image" />    // 追加
                        </div>
                    )
                )}
            </div>
          </div>
        </> 
    )
}

export default Blog

...
// blog.js(#3-j18)

import * as React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
 
const Blog = (props) => {
    return (
        <>
          <div>
            <div>
            <h1>Blog</h1>
            <p>弊社サービスやお客様の声などを紹介します。</p>
                 {props.data.allMarkdownRemark.edges.map((singleBlog, index) => (
                        <div key={index}>                            
                            <div>
                                <h3>{singleBlog.node.frontmatter.title}</h3>
                                <p>{singleBlog.node.frontmatter.excerpt}</p>
                                <p>{singleBlog.node.frontmatter.date}</p>
                                <Link to={singleBlog.node.fields.slug}>Read More</Link>
                            </div>
                            <GatsbyImage image={singleBlog.node.frontmatter.image.childImageSharp.gatsbyImageData} alt="card-image" /> 
                        </div>
                    )
                )}
            </div>
          </div>
        </> 
    )
}

export default Blog

export const query = graphql`
    query BlogQuery {
        allMarkdownRemark(sort: {fields: frontmatter___id, order: DESC}) {
            edges {
                node {
                    fields {
                        slug
                    }
                    frontmatter {
                        date
                        excerpt
                        id
                        image {
                            childImageSharp {
                                gatsbyImageData(
                                    quality: 90, 
                                    formats: [AUTO, WEBP, AVIF], 
                                    placeholder: BLURRED, 
                                )
                            }
                        }
                        title
                    }
                }
            }
        }
    }
`
// single-blog.js(#3-j19)

import * as React from "react"
import { graphql } from "gatsby" 
 
const SingleBlog = (props) => {   
    return (
        <>
            <div>

            </div>
            <div>  
                <div>               
                    <h1>{props.data.markdownRemark.frontmatter.title}</h1>
                    <p>{props.data.markdownRemark.frontmatter.date}</p> 
                    <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} /> 
                </div> 
            </div>
        </>                    
    )
}

export default SingleBlog

...
// single-blog.js(#3-j20)

...

export default SingleBlog

export const query = graphql`
    query SingleBlogQuery ($slug: String!) {     
        markdownRemark(fields: { slug: { eq: $slug } }) {   
            frontmatter {
                date
                excerpt
                id
                ↓追加
                image {
                    childImageSharp {
                        gatsbyImageData (
                            formats: AUTO, 
                            quality: 90, 
                            placeholder: BLURRED,
                            width: 1000, 
                        )
                    }
                }
                ↑追加
                title
            }
            html
        }
    }
`
// single-blog.js(#3-j21)

import * as React from "react"
import { graphql } from "gatsby" 
import { GatsbyImage } from "gatsby-plugin-image"       // 追加
 
const SingleBlog = (props) => {   
    return (
        <>
            <div>
                <GatsbyImage image={props.data.markdownRemark.frontmatter.image.childImageSharp.gatsbyImageData} alt="blog-image" />    // 追加
            </div>
            <div>  
                <div>               
                    <h1>{props.data.markdownRemark.frontmatter.title}</h1>

...
// single-blog.js(#3-j22)

import * as React from "react"
import { graphql } from "gatsby" 
import { GatsbyImage } from "gatsby-plugin-image" 
 
const SingleBlog = (props) => {   
    return (
        <>
            <div>
                <GatsbyImage image={props.data.markdownRemark.frontmatter.image.childImageSharp.gatsbyImageData} alt="blog-image" />
            </div>
            <div>  
                <div>               
                    <h1>{props.data.markdownRemark.frontmatter.title}</h1>
                    <p>{props.data.markdownRemark.frontmatter.date}</p> 
                    <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} /> 
                </div> 
            </div>
        </>                     
    )
}

export default SingleBlog

export const query = graphql`
    query SingleBlogQuery($slug: String!) {     
        markdownRemark(fields: { slug: { eq: $slug } }) {   
            frontmatter {
                date
                excerpt
                id
                image {
                    childImageSharp {
                        gatsbyImageData (
                            formats: [AUTO, WEBP, AVIF],
                            quality: 90, 
                            placeholder: BLURRED,
                            width: 1000, 
                        )
                    }
                }
                title
            }
            html
        }
    }
`
// contact.js(#3-j23)

import * as React from "react"

const Contact = () => {
    return (
        <>
            <div>
                <div>
                    <h1>Contact</h1>
                    <p>お気軽にご連絡ください</p>
                    <form>
                        <label htmlFor="name">お名前</label>  
                        <input type="text" name="name" id="name" required/>
                        <label htmlFor="email">メールアドレス</label>
                        <input type="email" name="email" id="email" required/>
                        <label htmlFor="textarea">ご用件</label>
                        <textarea name="message" rows="10" id="textarea" required></textarea>
                        <button type="submit">送信</button>
                    </form> 
                </div>
            </div>
        </>
    )
}

export default Contact
(#3-s2)

src
├── components   
│           ├── footer.js  ←追加
│           └── header.js    ←追加
│
.
.
.
// header.js(#3-j24)

import * as React from "react"
import { Link } from "gatsby"
import { StaticImage } from "gatsby-plugin-image"

const Header = () => {
  return (
     <header>
       <div>
          <div>
            <Link to="/">
                <StaticImage src="../images/logo.png" alt="logo" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} width={50} />
            </Link>
            <ul>
              <li><Link to="/blog">Blog</Link></li>
              <li><Link to="/contact">Contact</Link></li>
            </ul>
          </div>
        </div>
    </header>
  )
}

export default Header
// footer.js(#3-j25)

import * as React from "react"
import { Link } from "gatsby"
import github from "../images/github.svg"
import linkdin from "../images/linkedin.svg"
import twitter from "../images/twitter.svg"
import facebook from "../images/facebook.svg"

const Footer = () => {
    return (
        <footer>
            <div>
                <a href="https://www.google.com/"><img src={github} alt="logo"/></a>
                <a href="https://www.google.com/"><img src={linkdin} alt="logo"/></a>
                <a href="https://www.google.com/"><img src={twitter} alt="logo"/></a>
                <a href="https://www.google.com/"><img src={facebook} alt="logo"/></a>
                <hr/>
                <Link to="/blog">Blog</Link>
                <Link to="/contact">Contact</Link>
                <p>©{new Date().getFullYear()} Jack of All Trades</p>
            </div>
        </footer>
    )
}

export default Footer
(#3-s3)

src
├── components   
│           ├── footer.js        
│           ├── header.js
│           └── layout.js   ←追加
│
.
.
.
// layout.js(#3-j26)

import * as React from "react"
import Header from "./header"
import Footer from "./footer"

const Layout = () => {
  return (
    <>
      <Header />
      <Footer />
    </>
  )
}

export default Layout
// index.js(#3-j27)

import * as React from "react"
import { Link } from "gatsby"  
import { StaticImage } from "gatsby-plugin-image" 
import Layout from "../components/layout"      //追加
import JSLogo from "../images/javascript.svg"  
import ReactLogo from "../images/react.svg"    
import GatsbyLogo from "../images/gatsby.svg"   
import NextLogo from "../images/next.svg"      

const Index = () => {

...
// index.js(#3-j28)

...

const Index = () => {
    return (
        <Layout>   // 置き換え 
            <div>
                <StaticImage src="../images/index-hero.jpg" alt="hero" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} /> 
                <div>
                    <h1>Jack of All Trades</h1>
                    <h3>World's Largest Provider</h3>
                </div>
            </div>
            <div>
                <div>
                    <div>
                        <h2>弊社について</h2>
                        <p>Last Update: 2031/03/14</p>
                        <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
                    </div>
                    <StaticImage src="../images/company.jpg" alt="profile" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} />  
                </div>
                <div>
                    <h2>Service</h2>
                    <div>
                        <div><img src={JSLogo} alt="javascript"/><span>JavaScript / 10 years</span></div>   
                        <div><img src={ReactLogo} alt="react"/><span>React / 5 years</span></div>   
                        <div><img src={GatsbyLogo} alt="gatsby"/><span>Gatsby / 3 years</span></div>    
                        <div><img src={NextLogo} alt="next"/><span>Next.JS / 3 years</span></div>   
                    </div>
                </div>
                <div>
                    <Link to="/contact">Contact Us!</Link>
                </div>
            </div>
        </Layout>   // 置き換え
    )
}

export default Index
// layout.js(#3-j29)

import * as React from "react"
import Header from "./header"
import Footer from "./footer"

const Layout = (props) => {     // 追加
  return (
    <>
      <Header />
      <main>{props.children}</main>   // 追加
      <Footer />
    </>
  )
}

export default Layout
// blog.js(#3-j30)

import * as React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image" 
import Layout from "../components/layout"   // 追加
 
const Blog = (props) => {
    return(
        <Layout>  // 置き換え
            <div>
            <div>
            <h1>Blog</h1>
            <p>弊社サービスやお客様の声などを紹介します。</p>
                {props.data.allMarkdownRemark.edges.map((singleBlog, index) => (
                        <div key={index}>                            
                            <div>
                                <h3>{singleBlog.node.frontmatter.title}</h3>
                                <p>{singleBlog.node.frontmatter.excerpt}</p>
                                <p>{singleBlog.node.frontmatter.date}</p>
                                <Link to={singleBlog.node.fields.slug}>Read More</Link>
                            </div>
                            <GatsbyImage image={singleBlog.node.frontmatter.image.childImageSharp.gatsbyImageData} alt="card-image" />  
                        </div>
                        
                    )
                )}
            </div>
            </div>
        </Layout>  // 置き換え
    )
}

export default Blog

...
// single-blog.js(#3-j31)

import * as React from "react"
import { graphql } from "gatsby" 
import { GatsbyImage } from "gatsby-plugin-image" 
import Layout from "../components/layout"  // 追加
 
const SingleBlog = (props) => {   
    return (
        <Layout>   // 置き換え
            <div>
                <GatsbyImage image={props.data.markdownRemark.frontmatter.image.childImageSharp.gatsbyImageData} alt="blog-image" />
            </div>
            <div>  
                <div>               
                    <h1>{props.data.markdownRemark.frontmatter.title}</h1>
                    <p>{props.data.markdownRemark.frontmatter.date}</p> 
                    <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} /> 
                </div> 
            </div>
        </Layout>  // 置き換え             
    )
}

export default SingleBlog

...
// contact.js(#3-j32)

import * as React from "react"
import Layout from "../components/layout"  // 追加

const Contact = () => {
    return (
        <Layout>  // 置き換え
            <div>
                <div>
                    <h1>Contact</h1>
                    <p>お気軽にご連絡ください</p>
                    <form>
                        <label htmlFor="name">お名前</label>  
                        <input type="text" name="name" id="name" required/>
                        <label htmlFor="email">メールアドレス</label>
                        <input type="email" name="email" id="email" required/>
                        <label htmlFor="textarea">ご用件</label>
                        <textarea name="message" rows="10" id="textarea" required></textarea>
                        <button type="submit">送信</button>
                    </form> 
                </div>
            </div>
        </Layout>   // 置き換え
    )
}

export default Contact
// gatsby-config.js(#3-j33)

...

plugins: [
    `gatsby-plugin-sass`,       // 追加
    `gatsby-transformer-remark`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,

...
// index.js(#3-j34)

import * as React from "react"
import { Link } from "gatsby"  
import { StaticImage } from "gatsby-plugin-image" 
import Layout from "../components/layout"
import JSLogo from "../images/javascript.svg"  
import ReactLogo from "../images/react.svg"    
import GatsbyLogo from "../images/gatsby.svg"   
import NextLogo from "../images/next.svg" 
import * as style from "../styles/index.module.scss"    // 追加

...
// blog.js(#3-j35)

import * as React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import * as style from "../styles/blog.module.scss"    // 追加

...
// single-blog.js(#3-j36)

import * as React from "react"
import { graphql } from "gatsby" 
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import * as style from "../styles/singleBlog.module.scss"    // 追加

...
// contact.js(#3-j37)

import * as React from "react"
import Layout from "../components/layout"
import * as style from "../styles/contact.module.scss"   // 追加

...
// header.js(#3-j38)

import * as React from "react"
import { Link } from "gatsby"
import { StaticImage } from "gatsby-plugin-image"
import * as style from "../styles/common.module.scss"   // 追加

...
// footer.js(#3-j39)

import * as React from "react"
import { Link } from "gatsby"
import github from "../images/github.svg"
import linkdin from "../images/linkedin.svg"
import twitter from "../images/twitter.svg"
import facebook from "../images/facebook.svg"
import * as style from "../styles/common.module.scss"   // 追加

...
// layout.js(#3-j40)

import * as React from "react"
import Header from "./header"
import Footer from "./footer"
import "../styles/all.scss"   // 追加
// index.js(#3-j41)

import * as React from "react"
import { Link } from "gatsby"  
import { StaticImage } from "gatsby-plugin-image" 
import Layout from "../components/layout"
import JSLogo from "../images/javascript.svg"  
import ReactLogo from "../images/react.svg"    
import GatsbyLogo from "../images/gatsby.svg"   
import NextLogo from "../images/next.svg" 
import * as style from "../styles/index.module.scss"  

const Index = () => {
    return (
        <Layout>
            <div className={style.hero}>
                <StaticImage src="../images/index-hero.jpg" alt="hero" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} className={style.heroImg} />
                <div className={style.textContainer}>
                    <h1>Jack of All Trades</h1>
                    <h3>World's Largest Provider</h3>
                </div>
            </div>
            <div className={style.container}>
                <div className={style.company}>
                    <div>
                        <h2>弊社について</h2>
                        <p>Last Update: 2031/03/14</p>
                        <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
                    </div>
                    <StaticImage src="../images/company.jpg" alt="profile" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} />
                </div>
                <div className={style.service}>
                    <h2>Service</h2>
                    <div className={style.serviceContainer}>
                        <div><img src={JSLogo} alt="javascript"/><span>JavaScript / 10 years</span></div>
                        <div><img src={ReactLogo} alt="react"/><span>React / 5 years</span></div>
                        <div><img src={GatsbyLogo} alt="gatsby"/><span>Gatsby / 3 years</span></div>
                        <div><img src={NextLogo} alt="next"/><span>Next.JS / 3 years</span></div>
                    </div>
                </div>
                <div className={style.ctaButton}>
                    <Link to="/contact">Contact Us!</Link>
                </div>
            </div>
        </Layout>
    )
}

export default Index
// blog.js(#3-j42)

import * as React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import * as style from "../styles/blog.module.scss"

const Blog = (props) => {
    return (
        <Layout>
            <div className={style.wrapper}>
                <div className={style.container}>
                    <h1>Blog</h1>
                    <p>弊社サービスやお客様の声などを紹介します。</p>
                    {props.data.allMarkdownRemark.edges.map((singleBlog, index) => (
                            <div className={style.blogCard} key={index}>                            
                                <div className={style.textContainer}>
                                    <h3>{singleBlog.node.frontmatter.title}</h3>
                                    <p>{singleBlog.node.frontmatter.excerpt}</p>
                                    <p>{singleBlog.node.frontmatter.date}</p>
                                    <Link to={singleBlog.node.fields.slug}>Read More</Link>
                                </div>
                                <GatsbyImage image={singleBlog.node.frontmatter.image.childImageSharp.gatsbyImageData} alt="card-image" className={style.cardImg} />
                            </div>
                        )
                    )}
                </div>
            </div>
        </Layout>
    )
}

export default Blog

export const query = graphql`
query BlogQuery {
    allMarkdownRemark(sort: {fields: frontmatter___id, order: DESC}) {
      edges {
        node {
          frontmatter {
            date
            excerpt
            id
            title
            image {
              childImageSharp {
                  gatsbyImageData(
                      placeholder: BLURRED, 
                      formats: [AUTO, WEBP, AVIF], 
                      quality: 90
                  )
              }
            }
          }
          fields {
            slug
          }
        }
      }
    }
  }
`
// single-blog.js(#3-j43)

import * as React from "react"
import { graphql } from "gatsby" 
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import * as style from "../styles/singleBlog.module.scss"
 
const SingleBlog = (props) => {   
    return(
        <Layout>
            <div className={style.hero}>
                <GatsbyImage image={props.data.markdownRemark.frontmatter.image.childImageSharp.gatsbyImageData} alt="blog-image" />
            </div>
            <div className={style.wrapper}>  
                <div className={style.container}>               
                    <h1>{props.data.markdownRemark.frontmatter.title}</h1>
                    <p>{props.data.markdownRemark.frontmatter.date}</p> 
                    <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} /> 
                </div> 
            </div>
        </Layout>                    
    )
}

export default SingleBlog

export const query = graphql`
    query SingleBlogQuery ($slug: String!) {     
        markdownRemark(fields: { slug: { eq: $slug } }) {   
            frontmatter {
                    date
                    excerpt
                    id
                    title
                    image {
                        childImageSharp {
                          gatsbyImageData(            
                                placeholder: BLURRED
                                formats: [AUTO, WEBP, AVIF] 
                                quality: 90
                                width: 1000
                            )
                        }
                    }
            }
            html
        }
    }
`
// contact.js(#3-j44)

import * as React from "react"
import Layout from "../components/layout"
import * as style from "../styles/contact.module.scss"

const Contact = () => {
    return (
        <Layout>
            <div className={style.wrapper}>
                <div className={style.container}>
                    <h1>Contact</h1>
                    <p>お気軽にご連絡ください</p>
                    <form>
                        <label htmlFor="name">お名前</label>  
                        <input type="text" name="name" id="name" required/>
                        <label htmlFor="email">メールアドレス</label>
                        <input type="email" name="email" id="email" required/>
                        <label htmlFor="textarea">ご用件</label>
                        <textarea name="message" rows="10" id="textarea" required></textarea>
                        <button type="submit">送信</button>
                    </form> 
                </div>
            </div>
        </Layout>
    )
}

export default Contact
// header.js(#3-j45)

import * as React from "react"
import { Link } from "gatsby"
import { StaticImage } from "gatsby-plugin-image"
import * as style from "../styles/common.module.scss"

const Header = () => {
  return (
     <header className={style.headerWrapper}>
       <div className={style.container}>
          <div className={style.flexContainer}>
            <Link to="/">
                <StaticImage src="../images/logo.png" alt="logo" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} width={50} />
            </Link>
            <ul>
              <li><Link to="/blog">Blog</Link></li>
              <li><Link to="/contact">Contact</Link></li>
            </ul>
          </div>
        </div>
    </header>
  )
}

export default Header
// footer.js(#3-j46)

import * as React from "react"
import { Link } from "gatsby"
import github from "../images/github.svg"
import linkdin from "../images/linkedin.svg"
import twitter from "../images/twitter.svg"
import facebook from "../images/facebook.svg"
import * as style from "../styles/common.module.scss"

const Footer = () => {
    return (
        <footer className={style.footerWrapper}>
            <div className={style.insideContainer}>
                <a href="https://www.google.com/"><img src={github} alt="logo"/></a>
                <a href="https://www.google.com/"><img src={linkdin} alt="logo"/></a>
                <a href="https://www.google.com/"><img src={twitter} alt="logo"/></a>
                <a href="https://www.google.com/"><img src={facebook} alt="logo"/></a>
                <hr/>
                <Link to="/blog">Blog</Link>
                <Link to="/contact">Contact</Link>
                <p>©{new Date().getFullYear()} Jack of All Trades</p>
            </div>
        </footer>
    )
}

export default Footer
// layout.js(#3-j47)

import * as React from "react"
import Header from "./header"
import Footer from "./footer"
import "../styles/all.scss"

const Layout = (props) => {
  return (
    <>
      <Header />
      <main>{props.children}</main>
      <Footer />
    </>
  )
}

export default Layout

第4章 デプロイ

なし

第5章 ヘッドレスCMS

// gatsby-config.js(#5-j1)

...

  plugins: [
    `gatsby-plugin-sass`,
    `gatsby-transformer-remark`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,
    // ↓追加
    {
      resolve: `gatsby-source-contentful`,
      options: {
        spaceId: ``,
        accessToken: ``,
      },
    },
    // ↑追加
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },

...
// gatsby-config.js(#5-j2)

...

  plugins: [
    `gatsby-plugin-sass`,
    `gatsby-transformer-remark`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,
    {
      resolve: `gatsby-source-contentful`,
      options: {
        spaceId: `xxxxxx6qqsf7`,  // 追加
        accessToken: `xxxxx_4sEXPxxxxxxh51DLxxxxxxoE`,  // 追加
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },

...
// blog.js(#5-j3)

...

export default Blog

export const query = graphql`
// ↓削除
query BlogQuery {
    allMarkdownRemark(sort: {fields: frontmatter___id, order: DESC}) {
      edges {
        node {
          frontmatter {
            date
            excerpt
            id
            title
            image {
              childImageSharp {
                  gatsbyImageData(
                      placeholder: BLURRED, 
                      formats: [AUTO, WEBP, AVIF], 
                      quality: 90
                  )
              }
            }
          }
          fields {
            slug
          }
        }
      }
    }
  }
// ↑削除
`
// blog.js(#5-j4)

export default Blog

export const query = graphql`
// ↓貼り付け
query ContentfulBlogQuery {
  allContentfulBlog {
    edges {
      node {
        title
        slug
        id
        excerpt
        date(formatString: "YYYY-MM-DD")
        image {
          gatsbyImageData(placeholder: BLURRED, quality: 90, formats: AUTO)
        }
      }
    }
  }
}
// ↑貼り付け
`
// blog.js(#5-j5)

...

const Blog = (props) => {
    console.log(props)  // 追加
    return (
        <Layout>
            <div className={style.wrapper}>
                <div className={style.container}>
// blog.js(#5-j6)

import * as React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import * as style from "../styles/blog.module.scss"

const Blog = (props) => {
    console.log(props)
    return (
        <Layout>
            <div className={style.wrapper}>
                <div className={style.container}>
                    <h1>Blog</h1>
                    <p>弊社サービスやお客様の声をお届けします。</p>
                    {props.data.allContentfulBlog.edges.map((singleBlog, index) => (  // 修正
                            <div className={style.blogCard} key={index}>                            
                                <div className={style.textContainer}>
                                    <h3>{singleBlog.node.frontmatter.title}</h3>
                                    <p>{singleBlog.node.frontmatter.excerpt}</p>
                                    <p>{singleBlog.node.frontmatter.date}</p>
                                    <Link to={singleBlog.node.fields.slug}>Read More</Link>
                                </div>
                                <GatsbyImage image={singleBlog.node.frontmatter.image.childImageSharp.gatsbyImageData} alt="card-image" className={style.cardImg} />
                            </div>
                        )
                    )}
                </div>
            </div>
        </Layout>
    )
}

export default Blog

...
// blog.js(#5-j7)

import * as React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import * as style from "../styles/blog.module.scss"

const Blog = (props) => {
    console.log(props)
    return (
        <Layout>
            <div className={style.wrapper}>
                <div className={style.container}>
                    <h1>Blog</h1>
                    <p>弊社サービスやお客様の声をお届けします。</p>
                    {props.data.allContentfulBlog.edges.map((singleBlog, index) => (
                            <div className={style.blogCard} key={index}>                            
                                <div className={style.textContainer}>
                                    <h3>{singleBlog.node.title}</h3>  // 修正
                                    <p>{singleBlog.node.excerpt}</p>  // 修正
                                    <p>{singleBlog.node.date}</p>  // 修正
                                    <Link to={singleBlog.node.slug}>Read More</Link>  // 修正
                                </div>
                                 <GatsbyImage image={singleBlog.node.image.gatsbyImageData} alt="card-image" className={style.cardImg} />   // 修正
                            </div>
                        )
                    )}
                </div>
            </div>
        </Layout>
    )
}

export default Blog
...
// blog.js(#5-j8)

...

export default Blog

export const query = graphql`
query ContentfulBlogQuery {
  allContentfulBlog(sort: {fields: date, order: DESC}) {  // 追加
    edges {

      ...
// blog.js(#5-j9)

import * as React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import * as style from "../styles/blog.module.scss"

const Blog = (props) => {
    console.log(props)  // 削除
    return (
        <Layout>
            <div className={style.wrapper}>
                <div className={style.container}>
                    <h1>Blog</h1>
                    <p>弊社サービスやお客様の声をお届けします。</p>
                    {props.data.allContentfulBlog.edges.map((singleBlog, index) => (
                            <div className={style.blogCard} key={index}>                            
                                <div className={style.textContainer}>
                                    <h3>{singleBlog.node.title}</h3>  
                                    <p>{singleBlog.node.excerpt}</p>  
                                    <p>{singleBlog.node.date}</p>  
                                    <Link to={singleBlog.node.slug}>Read More</Link>  
                                </div>
                                <GatsbyImage image={singleBlog.node.image.gatsbyImageData} alt="card-image" className={style.cardImg} />  
                            </div>
                        )
                    )}
                </div>
            </div>
        </Layout>
    )
}

export default Blog

export const query = graphql`
query ContentfulBlogQuery {
  allContentfulBlog(sort: {fields: date, order: DESC}) {
    edges {
      node {
        title
        slug
        id
        excerpt
        date(formatString: "YYYY-MM-DD")
        image {
          gatsbyImageData(placeholder: BLURRED, quality: 90, formats: AUTO)
        }
      }
    }
  }
}
`
// gatsby-node.js(#5-j10)

const path = require(`path`) 
const { createFilePath } = require(`gatsby-source-filesystem`)

// ↓削除
exports.onCreateNode = ({ node, getNode, actions }) => {
    const { createNodeField } = actions
    if (node.internal.type === `MarkdownRemark`) {
      const slug = createFilePath({ node, getNode })   
      createNodeField({         
        node,                   
        name: `slug`,           
        value: slug,            
      })
    }
}
// ↑削除

exports.createPages = async ({ graphql, actions }) => { 
    const { createPage } = actions 
    const result = await graphql(`              
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `)
    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
        createPage({
            path: node.fields.slug,
            component: path.resolve(`./src/templates/single-blog.js`),
            context: {
                slug: node.fields.slug,
            },
        })
    })
}  
// gatsby-node.js(#5-j11)

const { createFilePath } = require(`gatsby-source-filesystem`)
const path = require(`path`) 

exports.createPages = async ({ graphql, actions }) => { 
    const { createPage } = actions 
    const result = await graphql(`              
        query {
            // ↓変更
            allContentfulBlog {
                edges {
                    node {
                        slug
                    }
                }
            }
            // ↑変更
        }
    `)
    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
        createPage({
            path: node.fields.slug,
            component: path.resolve(`./src/templates/single-blog.js`),
            context: {
                slug: node.fields.slug,
            },
        })
    })
}  
// gatsby-node.js(#5-j12)

const { createFilePath } = require(`gatsby-source-filesystem`)
const path = require(`path`) 

exports.createPages = async ({ graphql, actions }) => { 
    const { createPage } = actions 
    const result = await graphql(`              
        query {
            allContentfulBlog {
                edges {
                    node {
                        slug
                    }
                }
            }
        }
    `)
    result.data.allContentfulBlog.edges.forEach(({ node }) => {  // 修正
        createPage({
            path: node.slug,  // 修正
            component: path.resolve(`./src/templates/single-blog.js`),
            context: {
                slug: node.slug,  // 修正
            },
        })
    })
}  
// single-blog.js(#5-j13)

...

export default SingleBlog

// ↓ 削除
export const query = graphql`
    query SingleBlogQuery ($slug: String!) {     
        markdownRemark(fields: { slug: { eq: $slug } }) {   
            frontmatter {
                    date
                    excerpt
                    id
                    title
                    image {
                        childImageSharp {
                          gatsbyImageData(            
                                placeholder: BLURRED
                                formats: [AUTO, WEBP, AVIF] 
                                quality: 90
                                width: 1000
                            )
                        }
                    }
            }
            html
        }
    }
`
// ↑ 削除
// single-blog.js(#5-j14)

import * as React from "react"
import { graphql } from "gatsby" 
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import * as style from "../styles/singleBlog.module.scss"
 
const SingleBlog = (props) => {   
    return(
        <Layout>
{/*         <div className={style.hero}>
                <GatsbyImage image={props.data.markdownRemark.frontmatter.image.childImageSharp.gatsbyImageData} alt="blog-image" />
            </div>
            <div className={style.wrapper}>  
                <div className={style.container}>               
                    <h1>{props.data.markdownRemark.frontmatter.title}</h1>
                    <p>{props.data.markdownRemark.frontmatter.date}</p> 
                    <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} /> 
                </div> 
            </div>   */}
        </Layout>                      
    )
}

export default SingleBlog
// single-blog.js(#5-j15)

...

export default SingleBlog

export const query = graphql`  // 追加
`                             // 追加

// single-blog.js(#5-j16)

...

export default SingleBlog

export const query = graphql`
    // ↓追加
    query ContentfulSingleBlogQuery {
        contentfulBlog {
            title
            date(formatString: "YYYY-MM-DD")
            image {
                gatsbyImageData(formats: AUTO, placeholder: BLURRED, quality: 90, width: 1000)
            }
            textBody {
                childMarkdownRemark {
                    html
                }
            }
        }
    }
    // ↑追加
`
// single-blog.js(#5-j17)

... 

 
const SingleBlog = (props) => {   
    return(
        <Layout>
            <div className={style.hero}>
                <GatsbyImage image={props.data.contentfulBlog.image.gatsbyImageData} alt="blog-image" />  // 修正
            </div>
            <div className={style.wrapper}>  
                <div className={style.container}>               
                    <h1>{props.data.contentfulBlog.title}</h1> // 修正
                    <p>{props.data.contentfulBlog.date}</p>  // 修正
                    <div dangerouslySetInnerHTML={{ __html: props.data.contentfulBlog.textBody.childMarkdownRemark.html }} /> // 修正 
                </div> 
            </div>
        </Layout>                    
    )
}

export default SingleBlog

...
// single-blog.js(#5-j18)

... 

export default SingleBlog

export const query = graphql`
    query ContentfulSingleBlogQuery ($slug: String!) {   // 追加
        contentfulBlog (slug: { eq: $slug }) {        // 追加
            title
            date(formatString: "YYYY-MM-DD")
            image {
                gatsbyImageData(formats: AUTO, placeholder: BLURRED, quality: 90)
            }
            textBody {
                childMarkdownRemark {
                    html
                }
            }
        }
    }   
`
// index.js(#5-j19)

import * as React from "react"
import { Link, graphql } from "gatsby"      // graphqlを追加
import { StaticImage } from "gatsby-plugin-image" 
import Layout from "../components/layout"
import JSLogo from "../images/javascript.svg"  
import ReactLogo from "../images/react.svg"    
import GatsbyLogo from "../images/gatsby.svg"   
import NextLogo from "../images/next.svg" 
import * as style from "../styles/index.module.scss"  

const Index = () => {
    return (
        <Layout>
            ...
        </Layout>
    )
}

export default Index

// ↓追加
export const query = graphql`
    query IndexQuery {
        contentfulLastupdate {
            lastupdate(formatString: "YYYY-MM-DD")
        }
    }  
`
// ↑追加
// index.js(#5-j20)

...

const Index = (props) => {      // 追加
    return (
        <Layout>
            <div className={style.hero}>
                <StaticImage src="../images/index-hero.jpg" alt="hero" quality={90} placeholder="blurred" formats={["AUTO", "WEBP", "AVIF"]} className={style.heroImg} />
                <div className={style.textContainer}>
                    <h1>Jack of All Trades</h1>
                    <h3>World's Largest Provider</h3>
                </div>
            </div>
            <div className={style.container}>
                <div className={style.company}>
                    <div>
                        <h2>弊社について</h2>
                        <p>Last Update: {props.data.contentfulLastupdate.lastupdate}</p>      // 変更
                        <p>Lorem Ipsum is ...
...          
(#5-s1)

src
.
.
├── pages 
│           ├── 404.js    ←追加
│           ├── blog.js
│           ├── contact.js  
│           └── index.js
.
.
// 404.js(#5-j21)

import * as React from "react"
import Layout from "../components/layout"

const NotFoundPage = () => {
    return(
        <Layout>
            <div style={{textAlign: "center", height: "70vh"}}>
            <h1>404: Not Found</h1>
            <p>ページが見つかりません。</p>
            </div>
        </Layout>
    )
}

export default NotFoundPage
// gatsby-config.js(#5-j22)

module.exports = {
  siteMetadata: {
    defaultTitle: `Jack of All Trades Official Site`,
    defaultDescription: `モダン開発を得意とするJack of All Tradesの公式サイトです。`,
    defaultImage: "src/image/social-card.png",
    siteUrl: "https://jamstack-book-corporate-site.netlify.app",
  },
  plugins: [

...
(#5-s2)

src
.
.
├── components 
│           ├── footer.js    
│           ├── header.js
│           ├── layout.js  
│           └── seo.js    ←追加
.
.
// seo.js(#5-j23)

import * as React from "react"
import { Helmet } from "react-helmet"
import { useLocation } from "@reach/router"
import { useStaticQuery, graphql } from "gatsby"

const SEO = ({ title, description }) => {
    const { pathname } = useLocation()
    const { site } = useStaticQuery(query)
    const {
        defaultTitle,
        defaultDescription,
        defaultImage,
        siteUrl,
    } = site.siteMetadata
    const seo = {
        title: title || defaultTitle,
        description: description || defaultDescription,
        image: defaultImage,
        url: `${siteUrl}${pathname}`,
        canonical: `${siteUrl}${pathname}`
    }
    return (
        <Helmet>
            <html lang="ja" />
            <meta name="viewport" content="initial-scale=1.0, width=device-width" /> 
            <meta charSet="utf-8" />

            <title>{title}</title>
            <meta name="description" content={description} />
            <meta name="image" content={defaultImage} />   
            <link rel="canonical" href={seo.canonical} />

            {seo.title && <meta property="og:title" content={seo.title} />}
            {seo.description && <meta property="og:description" content={seo.description} />}
            {seo.url && <meta property="og:url" content={seo.url} />}
            {seo.image && <meta property="og:image" content={seo.image} />}
        </Helmet>
    )
}

export default SEO

const query = graphql`
  query SEO {
    site {
      siteMetadata {
        defaultTitle
        defaultDescription
        defaultImage
        siteUrl
      }
    }
  }
`
// index.js(#5-j24)

import * as React from "react"
import { Link } from "gatsby"
import { StaticImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import Seo from "../components/seo"     // 追加
import JSLogo from "../images/javascript.svg"
import ReactLogo from "../images/react.svg"
import GatsbyLogo from "../images/gatsby.svg"
import NextLogo from "../images/next.svg"
import * as style from "../styles/index.module.scss"

const Index = () => {
    return (
        <Layout>
            <Seo title="Jack of All Trades" description="Jack of All Tradesの公式サイトです" />     // 追加
            <div className={style.hero}>

...
// blog.js(#5-j25)

import * as React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import Seo from "../components/seo"     // 追加
import * as style from "../styles/blog.module.scss"

const Blog = ({ props }) => {
    return (
        <Layout>
            <Seo title="ブログ" description="これはブログページです" />     // 追加

...
// contact.js(#5-j26)

import * as React from "react"
import Layout from "../components/layout"
import Seo from "../components/seo"     // 追加
import * as style from "../styles/contact.module.scss"

const Contact = () => {
    return (
        <Layout>
            <Seo title="コンタクト" description="これはコンタクトページです" />     // 追加
            <div className={style.wrapper}>

...
// 404.js(#5-j27)

import * as React from "react"
import Layout from "../components/layout"
import Seo from "../components/seo"     // 追加

const NotFoundPage = () => {
    return(
        <Layout>
            <Seo title="ページが見つかりません" description="これは404ページです" />     // 追加
            <div style={{textAlign: "center", height: "70vh"}}>

...
// single-blog.js(#5-j28)

import * as React from "react"
import { graphql } from "gatsby" 
import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import Seo from "../components/seo"     // 追加
import * as style from "../styles/singleBlog.module.scss"
 
const SingleBlog = (props) => {   
    return(
        <Layout>
        <Seo title={props.data.contentfulBlog.title} description={props.data.contentfulBlog.excerpt} />     // 追加
        <div className={style.hero}>
                <GatsbyImage image={props.data.contentfulBlog.image.gatsbyImageData} alt="blog-image" />
            </div>
            <div className={style.wrapper}>  
                <div className={style.container}>               
                    <h1>{props.data.contentfulBlog.title}</h1>
                    <p>{props.data.contentfulBlog.date}</p> 
                    <div dangerouslySetInnerHTML={{ __html: props.data.contentfulBlog.textBody.childMarkdownRemark.html }} /> 
                </div> 
            </div> 
        </Layout>                    
    )
}

export default SingleBlog

export const query = graphql`
    query ContentfulSingleBlogQuery ($slug: String!) {
        contentfulBlog (slug: { eq: $slug }) {
            title
            excerpt                    // 追加
            date(formatString: "YYYY-MM-DD")
            image {
                gatsbyImageData(formats: AUTO, placeholder: BLURRED, quality: 90)
            }
            textBody {
                childMarkdownRemark {
                    html
                }
            }
        }
    }   
`
// gatsby-config.js(#5-j29)

...

    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        // This will impact how browsers show your PWA/website
        // https://css-tricks.com/meta-theme-color-and-trickery/
        // theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/logo.png`, // 変更
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.dev/offline
    // `gatsby-plugin-offline`,
  ],
}