├── static ├── robots.txt └── favicon.ico ├── .prettierignore ├── content ├── assets │ ├── gatsby-icon.png │ └── profile-pic.jpg └── blog │ ├── hello-world │ ├── salty_egg.jpg │ └── index.md │ ├── my-second-post │ └── index.md │ └── new-beginnings │ └── index.md ├── .prettierrc ├── gatsby-browser.js ├── bsconfig.json ├── src ├── templates │ ├── blog-post.js │ └── blogPost.re ├── utils │ ├── typography.js │ └── gatsby.re ├── pages │ ├── 404.js │ └── index.js └── components │ ├── bio.re │ ├── layout.re │ └── seo.re ├── LICENSE ├── .gitignore ├── gatsby-node.js ├── package.json ├── gatsby-config.js └── README.md /static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .cache 2 | package.json 3 | package-lock.json 4 | public 5 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukul-rathi/gatsby-starter-reason-blog/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /content/assets/gatsby-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukul-rathi/gatsby-starter-reason-blog/HEAD/content/assets/gatsby-icon.png -------------------------------------------------------------------------------- /content/assets/profile-pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukul-rathi/gatsby-starter-reason-blog/HEAD/content/assets/profile-pic.jpg -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "singleQuote": false, 5 | "tabWidth": 2, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /content/blog/hello-world/salty_egg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukul-rathi/gatsby-starter-reason-blog/HEAD/content/blog/hello-world/salty_egg.jpg -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | // custom typefaces 2 | import "typeface-montserrat" 3 | import "typeface-merriweather" 4 | 5 | import "prismjs/themes/prism.css" 6 | -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-starter-reason-blog", 3 | "namespace": true, 4 | "reason": { "react-jsx": 3 }, 5 | "bs-dependencies": ["reason-react"], 6 | "sources": [ 7 | { 8 | "dir": "src", 9 | "subdirs": true 10 | } 11 | ], 12 | "package-specs": [ 13 | { 14 | "module": "es6", 15 | "in-source": true 16 | } 17 | ], 18 | "suffix": ".bs.js", 19 | "refmt": 3 20 | } 21 | -------------------------------------------------------------------------------- /content/blog/my-second-post/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: My Second Post! 3 | date: "2015-05-06T23:46:37.121Z" 4 | --- 5 | 6 | Wow! I love blogging so much already. 7 | 8 | Did you know that "despite its name, salted duck eggs can also be made from 9 | chicken eggs, though the taste and texture will be somewhat different, and the 10 | egg yolk will be less rich."? 11 | ([Wikipedia Link](http://en.wikipedia.org/wiki/Salted_duck_egg)) 12 | 13 | Yeah, I didn't either. 14 | -------------------------------------------------------------------------------- /src/templates/blog-post.js: -------------------------------------------------------------------------------- 1 | import { graphql } from "gatsby" 2 | import BlogPost from "./blogPost.bs" 3 | 4 | export default BlogPost 5 | 6 | export const pageQuery = graphql` 7 | query BlogPostBySlug($slug: String!) { 8 | site { 9 | siteMetadata { 10 | title 11 | } 12 | } 13 | markdownRemark(fields: { slug: { eq: $slug } }) { 14 | id 15 | excerpt(pruneLength: 160) 16 | html 17 | frontmatter { 18 | title 19 | date(formatString: "MMMM DD, YYYY") 20 | description 21 | } 22 | } 23 | } 24 | ` 25 | -------------------------------------------------------------------------------- /src/utils/typography.js: -------------------------------------------------------------------------------- 1 | import Typography from "typography" 2 | import Wordpress2016 from "typography-theme-wordpress-2016" 3 | 4 | Wordpress2016.overrideThemeStyles = () => { 5 | return { 6 | "a.gatsby-resp-image-link": { 7 | boxShadow: `none`, 8 | }, 9 | } 10 | } 11 | 12 | delete Wordpress2016.googleFonts 13 | 14 | const typography = new Typography(Wordpress2016) 15 | 16 | // Hot reload typography in development. 17 | if (process.env.NODE_ENV !== `production`) { 18 | typography.injectStyles() 19 | } 20 | 21 | export default typography 22 | export const rhythm = typography.rhythm 23 | export const scale = typography.scale 24 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import {graphql} from 'gatsby' 2 | import React from 'react' 3 | 4 | import Layout from '../components/layout.re' 5 | import SEO from '../components/seo.bs' 6 | 7 | const NotFoundPage = ({data, location}) => { 8 | const siteTitle = data.site.siteMetadata.title 9 | 10 | return ( 11 | 12 | 13 |

Not Found

14 |

You just hit a route that doesn't exist... the sadness.

15 |
16 | ) 17 | } 18 | 19 | export default NotFoundPage 20 | 21 | export const pageQuery = graphql` 22 | query { 23 | site { 24 | siteMetadata { 25 | title 26 | } 27 | } 28 | } 29 | ` 30 | -------------------------------------------------------------------------------- /content/blog/hello-world/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hello World 3 | date: "2015-05-01T22:12:03.284Z" 4 | description: "Hello World" 5 | --- 6 | 7 | This is my first post on my new fake blog! How exciting! 8 | 9 | I'm sure I'll write a lot more interesting things in the future. 10 | 11 | Oh, and here's a great quote from this Wikipedia on 12 | [salted duck eggs](http://en.wikipedia.org/wiki/Salted_duck_egg). 13 | 14 | > A salted duck egg is a Chinese preserved food product made by soaking duck 15 | > eggs in brine, or packing each egg in damp, salted charcoal. In Asian 16 | > supermarkets, these eggs are sometimes sold covered in a thick layer of salted 17 | > charcoal paste. The eggs may also be sold with the salted paste removed, 18 | > wrapped in plastic, and vacuum packed. From the salt curing process, the 19 | > salted duck eggs have a briny aroma, a gelatin-like egg white and a 20 | > firm-textured, round yolk that is bright orange-red in color. 21 | 22 | ![Chinese Salty Egg](./salty_egg.jpg) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Gatsbyjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variable files 55 | .env* 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # Reason autogenerated files 72 | *.bs.js 73 | .merlin 74 | lib/ -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require(`path`) 2 | const { createFilePath } = require(`gatsby-source-filesystem`) 3 | 4 | exports.createPages = async ({ graphql, actions }) => { 5 | const { createPage } = actions 6 | 7 | const blogPost = path.resolve(`./src/templates/blog-post.js`) 8 | const result = await graphql( 9 | ` 10 | { 11 | allMarkdownRemark( 12 | sort: { fields: [frontmatter___date], order: DESC } 13 | limit: 1000 14 | ) { 15 | edges { 16 | node { 17 | fields { 18 | slug 19 | } 20 | frontmatter { 21 | title 22 | } 23 | } 24 | } 25 | } 26 | } 27 | ` 28 | ) 29 | 30 | if (result.errors) { 31 | throw result.errors 32 | } 33 | 34 | // Create blog posts pages. 35 | const posts = result.data.allMarkdownRemark.edges 36 | 37 | posts.forEach((post, index) => { 38 | const previous = index === posts.length - 1 ? null : posts[index + 1].node 39 | const next = index === 0 ? null : posts[index - 1].node 40 | 41 | createPage({ 42 | path: post.node.fields.slug, 43 | component: blogPost, 44 | context: { 45 | slug: post.node.fields.slug, 46 | previous, 47 | next, 48 | }, 49 | }) 50 | }) 51 | } 52 | 53 | exports.onCreateNode = ({ node, actions, getNode }) => { 54 | const { createNodeField } = actions 55 | 56 | if (node.internal.type === `MarkdownRemark`) { 57 | const value = createFilePath({ node, getNode }) 58 | createNodeField({ 59 | name: `slug`, 60 | node, 61 | value, 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/utils/gatsby.re: -------------------------------------------------------------------------------- 1 | module Link = { 2 | [@bs.module "gatsby-link"] [@react.component] 3 | external make: 4 | ( 5 | ~_to: string, /* "to" is a reserved keyword in Reason, BuckleScript compiles _to -> to in raw JS (quirky I know!) */ 6 | ~rel: option(string)=?, 7 | ~className: option(string)=?, 8 | ~style: option(ReactDOMRe.Style.t)=?, 9 | ~activeStyle: option(ReactDOMRe.Style.t)=?, 10 | ~activeClassName: option(string)=?, 11 | ~children: option(React.element)=? 12 | ) => 13 | React.element = 14 | "default"; 15 | }; 16 | 17 | module Helmet = { 18 | type metaT = 19 | option( 20 | array({ 21 | . 22 | "name": string, 23 | "content": string, 24 | }), 25 | ); 26 | [@bs.module "react-helmet"] [@react.component] 27 | external make: 28 | ( 29 | ~title: option(string)=?, 30 | ~titleTemplate: option(string)=?, 31 | ~htmlAttributes: option('a)=?, 32 | ~meta: metaT=?, 33 | ~children: option(React.element)=? 34 | ) => 35 | React.element = 36 | "default"; 37 | }; 38 | 39 | module Image = { 40 | [@bs.module "gatsby-image"] [@react.component] 41 | external make: 42 | ( 43 | ~resolutions: option('any)=?, 44 | ~sizes: option('any)=?, 45 | ~fixed: option('any)=?, 46 | ~fluid: option('any)=?, 47 | ~title: option(string)=?, 48 | ~alt: option(string)=?, 49 | ~className: option(string)=?, 50 | ~critical: option(bool)=?, 51 | ~style: option(ReactDOMRe.Style.t)=?, 52 | ~imgStyle: option(ReactDOMRe.Style.t)=?, 53 | ~placeholderStyle: option(ReactDOMRe.Style.t)=?, 54 | ~placeholderClassName: option(string)=? 55 | ) => 56 | React.element = 57 | "default"; 58 | }; 59 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import {graphql, Link} from 'gatsby' 2 | import React from 'react' 3 | 4 | import Bio from '../components/bio.bs' 5 | import Layout from '../components/layout.re' 6 | import SEO from '../components/seo.bs' 7 | import {rhythm} from '../utils/typography' 8 | 9 | const BlogIndex = ({data, location}) => { 10 | const siteTitle = data.site.siteMetadata.title 11 | const posts = data.allMarkdownRemark.edges 12 | 13 | return ( 14 | 15 | 16 | 17 | {posts.map(({ node }) => { 18 | const title = node.frontmatter.title || node.fields.slug 19 | return ( 20 |
21 |
22 |

27 | 28 | {title} 29 | 30 |

31 | {node.frontmatter.date} 32 |
33 |
34 |

39 |

40 |
41 | ) 42 | })} 43 |
44 | ) 45 | } 46 | 47 | export default BlogIndex 48 | 49 | export const pageQuery = graphql` 50 | query { 51 | site { 52 | siteMetadata { 53 | title 54 | } 55 | } 56 | allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) { 57 | edges { 58 | node { 59 | excerpt 60 | fields { 61 | slug 62 | } 63 | frontmatter { 64 | date(formatString: "MMMM DD, YYYY") 65 | title 66 | description 67 | } 68 | } 69 | } 70 | } 71 | } 72 | ` 73 | -------------------------------------------------------------------------------- /src/components/bio.re: -------------------------------------------------------------------------------- 1 | /** 2 | * Bio component that queries for data 3 | * with Gatsby's useStaticQuery component 4 | * 5 | * See: https://www.gatsbyjs.org/docs/use-static-query/ 6 | */ 7 | [@bs.module "gatsby"] 8 | external useStaticQuery: string => 'a = "useStaticQuery"; 9 | %bs.raw 10 | {| import {graphql} from "gatsby" |}; 11 | 12 | module Image = Gatsby.Image; 13 | 14 | [@bs.module "../utils/typography.js"] 15 | external rhythm: float => string = "rhythm"; 16 | 17 | [@react.component] 18 | let make = () => { 19 | let data = 20 | useStaticQuery( 21 | [%bs.raw 22 | {| 23 | graphql` 24 | query BioQuery { 25 | avatar: file(absolutePath: { regex: "/profile-pic.jpg/" }) { 26 | childImageSharp { 27 | fixed(width: 50, height: 50) { 28 | ...GatsbyImageSharpFixed 29 | } 30 | } 31 | } 32 | site { 33 | siteMetadata { 34 | author 35 | social { 36 | twitter 37 | } 38 | } 39 | } 40 | } 41 | `|} 42 | ], 43 | ); 44 | let author = data##site##siteMetadata##author; 45 | let social = data##site##siteMetadata##social; 46 |
50 | author 64 |

65 | {React.string("Written by ")} 66 | author 67 | {React.string(" who ")} 68 | {React.string("blogs")} 69 | {React.string(" about the things he's 70 | making. ")} 71 | 72 | {React.string("You should follow him on Twitter")} 73 | 74 |

75 |
; 76 | }; 77 | 78 | React.setDisplayName(make, "Bio"); 79 | 80 | let default = make; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-starter-reason-blog", 3 | "description": "A starter for a blog powered by Gatsby, ReasonML and Markdown", 4 | "version": "0.1.0", 5 | "author": "Mukul Rathi https://gatsby.dev/unit-testing\" && exit 1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/components/layout.re: -------------------------------------------------------------------------------- 1 | module Link = Gatsby.Link; 2 | 3 | [@bs.module "../utils/typography.js"] 4 | external rhythm: float => string = "rhythm"; 5 | 6 | type scaleReturnType = { 7 | fontSize: string, 8 | lineHeight: string, 9 | }; 10 | 11 | [@bs.module "../utils/typography.js"] 12 | external scale: float => scaleReturnType = "scale"; 13 | 14 | [@bs.val] external pathPrefix: string = "__PATH_PREFIX__"; 15 | 16 | type locationType = {pathname: string}; 17 | 18 | [@react.component] 19 | let make = (~location: locationType, ~title, ~children) => { 20 | let rootPath = pathPrefix ++ "/"; 21 | let header = 22 | if (location.pathname === rootPath) { 23 |

33 | 43 | {React.string(title)} 44 | 45 |

; 46 | } else { 47 |

55 | 65 | {React.string(title)} 66 | 67 |

; 68 | }; 69 |
79 |
header
80 |
children
81 | 85 |
; 86 | }; 87 | 88 | React.setDisplayName(make, "Layout"); 89 | 90 | let default = make; -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | title: `Gatsby Starter Reason Blog`, 4 | author: `Mukul Rathi`, 5 | description: `A starter blog demonstrating what Reason and Gatsby can do.`, 6 | siteUrl: `https://gatsby-starter-blog-demo.netlify.com/`, 7 | social: { 8 | twitter: `mukulrathi_`, 9 | }, 10 | }, 11 | plugins: [ 12 | { 13 | resolve: "gatsby-plugin-reason", 14 | options: { 15 | derivePathFromComponentName: true, 16 | }, 17 | }, 18 | { 19 | resolve: `gatsby-source-filesystem`, 20 | options: { 21 | path: `${__dirname}/content/blog`, 22 | name: `blog`, 23 | }, 24 | }, 25 | { 26 | resolve: `gatsby-source-filesystem`, 27 | options: { 28 | path: `${__dirname}/content/assets`, 29 | name: `assets`, 30 | }, 31 | }, 32 | { 33 | resolve: `gatsby-transformer-remark`, 34 | options: { 35 | plugins: [ 36 | { 37 | resolve: `gatsby-remark-images`, 38 | options: { 39 | maxWidth: 590, 40 | }, 41 | }, 42 | { 43 | resolve: `gatsby-remark-responsive-iframe`, 44 | options: { 45 | wrapperStyle: `margin-bottom: 1.0725rem`, 46 | }, 47 | }, 48 | `gatsby-remark-prismjs`, 49 | `gatsby-remark-copy-linked-files`, 50 | `gatsby-remark-smartypants`, 51 | ], 52 | }, 53 | }, 54 | `gatsby-transformer-sharp`, 55 | `gatsby-plugin-sharp`, 56 | { 57 | resolve: `gatsby-plugin-google-analytics`, 58 | options: { 59 | // trackingId: `ADD YOUR TRACKING ID HERE`, 60 | }, 61 | }, 62 | `gatsby-plugin-feed`, 63 | { 64 | resolve: `gatsby-plugin-manifest`, 65 | options: { 66 | name: `Gatsby Starter ReasonML Blog`, 67 | short_name: `GatsbyJS`, 68 | start_url: `/`, 69 | background_color: `#ffffff`, 70 | theme_color: `#663399`, 71 | display: `minimal-ui`, 72 | icon: `content/assets/gatsby-icon.png`, 73 | }, 74 | }, 75 | `gatsby-plugin-react-helmet`, 76 | { 77 | resolve: `gatsby-plugin-typography`, 78 | options: { 79 | pathToConfigModule: `src/utils/typography`, 80 | }, 81 | }, 82 | // this (optional) plugin enables Progressive Web App + Offline 83 | // functionality 84 | // To learn more, visit: https://gatsby.dev/offline 85 | // `gatsby-plugin-offline`, 86 | ], 87 | } 88 | -------------------------------------------------------------------------------- /src/components/seo.re: -------------------------------------------------------------------------------- 1 | /** 2 | * SEO component that queries for data with 3 | * Gatsby's useStaticQuery React hook 4 | * 5 | * See: https://www.gatsbyjs.org/docs/use-static-query/ 6 | */ 7 | module Helmet = Gatsby.Helmet; 8 | 9 | [@bs.module "gatsby"] external useStaticQuery: string => 'a = "useStaticQuery"; 10 | 11 | /* raw import used because no Reason support for Gatsby graphql queries */ 12 | %bs.raw 13 | {| import {graphql} from "gatsby" |}; 14 | 15 | /* We can define the types of fields of JS objects, and thus use them as records in ReasonML */ 16 | type siteMetadataType = { 17 | title: string, 18 | description: string, 19 | author: string, 20 | }; 21 | type siteType = {siteMetadata: siteMetadataType}; 22 | type queryResType = {site: siteType}; 23 | 24 | [@react.component] 25 | let make = (~description, ~lang="en", ~meta=[], ~title) => { 26 | let data: queryResType = 27 | useStaticQuery( 28 | [%bs.raw 29 | {| 30 | graphql` 31 | query { 32 | site { 33 | siteMetadata { 34 | title 35 | description 36 | author 37 | } 38 | } 39 | } 40 | ` 41 | |} 42 | ], 43 | ); 44 | let siteMetadata = data.site.siteMetadata; 45 | let metaDescription = 46 | switch (description) { 47 | | Some(descriptionVal) => descriptionVal 48 | | None => siteMetadata.description 49 | }; 50 | let titleTemplate = "%s | " ++ siteMetadata.title; 51 | let htmlAttributes = {"lang": lang}; 52 | 53 | /* Example of embedding raw JS into our Reason code. */ 54 | let metaTagsFun = [%bs.raw 55 | {| (siteMetadata, title, metaDescription, meta) => 56 | [ 57 | { 58 | name: `description`, 59 | content: metaDescription, 60 | }, 61 | { 62 | property: `og:title`, 63 | content: title, 64 | }, 65 | { 66 | property: `og:description`, 67 | content: metaDescription, 68 | }, 69 | { 70 | property: `og:type`, 71 | content: `website`, 72 | }, 73 | { 74 | name: `twitter:card`, 75 | content: `summary`, 76 | }, 77 | { 78 | name: `twitter:creator`, 79 | content: siteMetadata.author, 80 | }, 81 | { 82 | name: `twitter:title`, 83 | content: title, 84 | }, 85 | { 86 | name: `twitter:description`, 87 | content: metaDescription, 88 | }, 89 | ].concat(meta) 90 | |} 91 | ]; 92 | ; 98 | }; 99 | 100 | React.setDisplayName(make, "SEO"); 101 | 102 | let default = make; -------------------------------------------------------------------------------- /src/templates/blogPost.re: -------------------------------------------------------------------------------- 1 | module Link = Gatsby.Link; 2 | module SEO = Seo; 3 | 4 | [@bs.module "../utils/typography.js"] 5 | external rhythm: float => string = "rhythm"; 6 | [@bs.module "../utils/typography.js"] 7 | external scale: 8 | float => 9 | { 10 | . 11 | "fontSize": string, 12 | "lineHeight": string, 13 | } = 14 | "scale"; 15 | 16 | [@react.component] 17 | let make = (~data, ~pageContext, ~location) => { 18 | let post = data##markdownRemark; 19 | let siteTitle = data##site##siteMetadata##title; 20 | let previous = pageContext##previous; 21 | let next = pageContext##next; 22 | let description = 23 | switch (post##frontmatter##description) { 24 | | Some(postDescription) => postDescription 25 | | None => post##excerpt 26 | }; 27 | let postTitle = post##frontmatter##title; 28 | 29 | 30 |
31 |
32 |

40 | {React.string(postTitle)} 41 |

42 |

52 | {post##frontmatter##date} 53 |

54 |
55 |
56 |
57 |
58 |
59 | 96 |
; 97 | }; 98 | 99 | React.setDisplayName(make, "BlogPostTemplate"); 100 | 101 | let default = make; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gatsby Starter Reason Blog 2 | 3 | A starter that uses ReasonML with Gatsby. We're using the [latest v3 Reason JSX syntax](https://reasonml.github.io/reason-react/docs/en/jsx), there are also a [couple](https://github.com/ReasonVienna/reason-conf) of [other](https://github.com/jtberglund/gatsby-starter-reason) Gatsby sites using the old syntax. I would recommend you stick with the cleaner v3 syntax. 4 | 5 | Happy coding! I've written a [blog post explaining this repo in more depth](https://mukulrathi.netlify.app/gatsby-reasonml-tutorial/). 6 | 7 | Feel free to submit issues if you feel this can be improved! 8 | 9 | ## Getting started 10 | 11 | This is a Gatsby starter repo so install it using the Gatsby CLI by running 12 | 13 | `gatsby new your-gatsby-blog https://github.com/mukul-rathi/gatsby-starter-reason-blog/` 14 | Then to set up the repo: 15 | 16 | ```bash 17 | cd your-gatsby-blog 18 | npm install 19 | gatsby develop 20 | ``` 21 | 22 | Your site is now running at `https://localhost:8000/`! Start making changes to the source code and see the site update live! 23 | 24 | To learn more about Gatsby head to the [Gatsby website](https://www.gatsbyjs.org/). 25 | 26 | The structure of this repository is based off the [Gatsby starter blog](https://github.com/gatsbyjs/gatsby-starter-blog) - check that README out if you want more information about the top level structure of a typical Gatsby project. 27 | 28 | To build a production build of the website, run `npm run build`. This first compiles your Reason files by running `bsb -make-world`, and then creates your production build by running `gatsby build`. (You can see the `npm run build` script in the `package.json` file). 29 | 30 | ## Tips 31 | 32 | JS interop with Reason can be a little messy at times. [This article](https://reasonml.github.io/docs/en/converting-from-js) helps you convert from JS. If `localhost:8000` displays uncaught TypeError messages when running `gatsby develop` but your Reason code compiles, check your JS-Reason bindings (imports/exports). :) 33 | 34 | `src/utils/gatsby.re` contains type wrappers for some Gatsby React components like `` and ``. 35 | 36 | ## GraphQL workarounds for Reason Files 37 | 38 | Gatsby's GraphQL parser only works with JS files. So if you have a StaticQuery in your component, you need to write raw JS (graphql templated string), and wrap it in `[%bs.raw]`. 39 | 40 | ```reason 41 | useStaticQuery([%bs.raw] {| 42 | graphql` 43 | ... 44 | ` 45 | |}]); 46 | ``` 47 | 48 | Gatsby also preprocesses GraphQL queries **before** BuckleScript compilation, and ignores queries in Reason files. To get around this, we pre-compile Reason files (see `yarn build` in the `package.json`) and then to use the component in a JS React component, import the BuckleScript compiled file (a hack, I know). 49 | 50 | ```diff 51 | - import Foo from "./foo.re" 52 | + import Foo from "./foo.bs" 53 | ``` 54 | 55 | This also means we can't use page queries, so the only workaround is to wrap the ReasonReact component in a JS React Component. E.g. see `src/templates/blog-post.js`: 56 | 57 | ```js 58 | import { graphql } from "gatsby" 59 | import BlogPost from "./blogPost.bs" 60 | 61 | export default BlogPost 62 | 63 | export const pageQuery = graphql` 64 | ... 65 | ` 66 | ``` 67 | 68 | And yes, another quirk of the Gatsby GraphQL parser means you can't just write `export {default} from "./blogPost.bs"` you need to import the component and then export it again. 69 | -------------------------------------------------------------------------------- /content/blog/new-beginnings/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: New Beginnings 3 | date: "2015-05-28T22:40:32.169Z" 4 | description: This is a custom description for SEO and Open Graph purposes, rather than the default generated excerpt. Simply add a description field to the frontmatter. 5 | --- 6 | 7 | Far far away, behind the word mountains, far from the countries Vokalia and 8 | Consonantia, there live the blind texts. Separated they live in Bookmarksgrove 9 | right at the coast of the Semantics, a large language ocean. A small river named 10 | Duden flows by their place and supplies it with the necessary regelialia. 11 | 12 | ## On deer horse aboard tritely yikes and much 13 | 14 | The Big Oxmox advised her not to do so, because there were thousands of bad 15 | Commas, wild Question Marks and devious Semikoli, but the Little Blind Text 16 | didn’t listen. She packed her seven versalia, put her initial into the belt and 17 | made herself on the way. 18 | 19 | - This however showed weasel 20 | - Well uncritical so misled 21 | - this is very interesting 22 | - Goodness much until that fluid owl 23 | 24 | When she reached the first hills of the **Italic Mountains**, she had a last 25 | view back on the skyline of her hometown _Bookmarksgrove_, the headline of 26 | [Alphabet Village](http://google.com) and the subline of her own road, the Line 27 | Lane. Pityful a rhetoric question ran over her cheek, then she continued her 28 | way. On her way she met a copy. 29 | 30 | ### Overlaid the jeepers uselessly much excluding 31 | 32 | But nothing the copy said could convince her and so it didn’t take long until a 33 | few insidious Copy Writers ambushed her, made her drunk with 34 | [Longe and Parole](http://google.com) and dragged her into their agency, where 35 | they abused her for their projects again and again. And if she hasn’t been 36 | rewritten, then they are still using her. 37 | 38 | > Far far away, behind the word mountains, far from the countries Vokalia and 39 | > Consonantia, there live the blind texts. Separated they live in Bookmarksgrove 40 | > right at the coast of the Semantics, a large language ocean. 41 | 42 | It is a paradisematic country, in which roasted parts of sentences fly into your 43 | mouth. Even the all-powerful Pointing has no control about the blind texts it is 44 | an almost unorthographic life One day however a small line of blind text by the 45 | name of Lorem Ipsum decided to leave for the far World of Grammar. 46 | 47 | ### According a funnily until pre-set or arrogant well cheerful 48 | 49 | The Big Oxmox advised her not to do so, because there were thousands of bad 50 | Commas, wild Question Marks and devious Semikoli, but the Little Blind Text 51 | didn’t listen. She packed her seven versalia, put her initial into the belt and 52 | made herself on the way. 53 | 54 | 1. So baboon this 55 | 2. Mounted militant weasel gregariously admonishingly straightly hey 56 | 3. Dear foresaw hungry and much some overhung 57 | 4. Rash opossum less because less some amid besides yikes jeepers frenetic 58 | impassive fruitlessly shut 59 | 60 | When she reached the first hills of the Italic Mountains, she had a last view 61 | back on the skyline of her hometown Bookmarksgrove, the headline of Alphabet 62 | Village and the subline of her own road, the Line Lane. Pityful a rhetoric 63 | question ran over her cheek, then she continued her way. On her way she met a 64 | copy. 65 | 66 | > The copy warned the Little Blind Text, that where it came from it would have 67 | > been rewritten a thousand times and everything that was left from its origin 68 | > would be the word "and" and the Little Blind Text should turn around and 69 | > return to its own, safe country. 70 | 71 | But nothing the copy said could convince her and so it didn’t take long until a 72 | few insidious Copy Writers ambushed her, made her drunk with Longe and Parole 73 | and dragged her into their agency, where they abused her for their projects 74 | again and again. And if she hasn’t been rewritten, then they are still using 75 | her. Far far away, behind the word mountains, far from the countries Vokalia and 76 | Consonantia, there live the blind texts. 77 | 78 | #### Silent delightfully including because before one up barring chameleon 79 | 80 | Separated they live in Bookmarksgrove right at the coast of the Semantics, a 81 | large language ocean. A small river named Duden flows by their place and 82 | supplies it with the necessary regelialia. It is a paradisematic country, in 83 | which roasted parts of sentences fly into your mouth. 84 | 85 | Even the all-powerful Pointing has no control about the blind texts it is an 86 | almost unorthographic life One day however a small line of blind text by the 87 | name of Lorem Ipsum decided to leave for the far World of Grammar. The Big Oxmox 88 | advised her not to do so, because there were thousands of bad Commas, wild 89 | Question Marks and devious Semikoli, but the Little Blind Text didn’t listen. 90 | 91 | ##### Wherever far wow thus a squirrel raccoon jeez jaguar this from along 92 | 93 | She packed her seven versalia, put her initial into the belt and made herself on 94 | the way. When she reached the first hills of the Italic Mountains, she had a 95 | last view back on the skyline of her hometown Bookmarksgrove, the headline of 96 | Alphabet Village and the subline of her own road, the Line Lane. Pityful a 97 | rhetoric question ran over her cheek, then she continued her way. On her way she 98 | met a copy. 99 | 100 | ###### Slapped cozy a that lightheartedly and far 101 | 102 | The copy warned the Little Blind Text, that where it came from it would have 103 | been rewritten a thousand times and everything that was left from its origin 104 | would be the word "and" and the Little Blind Text should turn around and return 105 | to its own, safe country. But nothing the copy said could convince her and so it 106 | didn’t take long until a few insidious Copy Writers ambushed her, made her drunk 107 | with Longe and Parole and dragged her into their agency, where they abused her 108 | for their projects again and again. 109 | --------------------------------------------------------------------------------