Skip to content
This repository has been archived by the owner on Jun 25, 2024. It is now read-only.

Commit

Permalink
feat: migrate to gatsby
Browse files Browse the repository at this point in the history
/spent 4h
  • Loading branch information
nico-i committed Feb 3, 2024
1 parent 62656f5 commit 2a1aed4
Show file tree
Hide file tree
Showing 18 changed files with 343 additions and 41 deletions.
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["prettier-plugin-tailwindcss"]
}
9 changes: 8 additions & 1 deletion gatsby-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const { defaultLocale, Locale } = i18n;
const locales = Object.values(Locale);
const siteMetadata = {
title: "ssg starter",
description: "A starter for static site generation with Gatsby",
twitterUsername: "@username",
siteUrl: process.env.SITE_URL,
};

Expand Down Expand Up @@ -81,7 +83,12 @@ const analyticsPlugins = [
const config: GatsbyConfig = {
siteMetadata,
graphqlTypegen: true, // generates types for graphql queries
plugins: [...devPlugins, ...i18nPlugins, ...seoPlugins, ...analyticsPlugins],
plugins: [
...devPlugins,
...i18nPlugins,
...seoPlugins,
...analyticsPlugins,
],
};

export default config;
1 change: 1 addition & 0 deletions gatsby-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.onPostBuild = require("./gatsby/onPostBuild");
85 changes: 85 additions & 0 deletions gatsby/onPostBuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const { chromium } = require("playwright");
const path = require("path");
const fs = require("fs");

// TODO: Replace this with a custom template
const exampleTemplate = require("../src/templates/OG/example.js");

/**
* This post build script is used to generate OG images for blog posts and other pages.
* Based off of https://github.com/PostHog/posthog.com/blob/138a67505cc806e12d3500969b96e00b741ce338/gatsby/onPostBuild.js
*/
module.exports = exports.onPostBuild = async ({ graphql }) => {
// TODO: Fetch data from your graphql endpoint
// const { data } = await graphql(`
// query {
// }
// `);

// Create the og-images directory in the output folder if it doesn't exist
const dir = path.resolve(__dirname, "../public/og-images");
if (!fs.existsSync(dir)) fs.mkdirSync(dir);

// TODO: Fetch and save a font to the assets folder
// const relFontPath = `../assets/fonts/example-font.woff`;
// const res = await fetch("url-to-your-font.woff");
// await new Promise((resolve, reject) => {
// // Save the font to the assets folder
// const fileStream = fs.createWriteStream(
// path.resolve(__dirname, relFontPath),
// );
// res.body?.pipe(fileStream);
// res.body?.on("error", (err) => {
// reject(err);
// });
// fileStream.on("finish", function () {
// resolve(void 0);
// });
// });
// const font = fs.readFileSync(path.resolve(__dirname, relFontPath), {
// encoding: "base64",
// });

const browser = await chromium.launch({
args: ["--no-sandbox", "--disable-setuid-sandbox"],
headless: true,
});

const page = await browser.newPage();
await page.setViewportSize({
width: 1200,
height: 630,
});
async function createOG({ html, slug }) {
await page.setContent(html, {
waitUntil: "domcontentloaded",
});

await page.evaluateHandle("document.fonts.ready");

await page.screenshot({
type: "jpeg",
path: `${dir}/${slug.replace(/\//g, "")}.jpeg`,
quality: 75,
});
}

// TODO: Load an image from the assets folder
// const relPathToOGImage = `../assets/img/og/example.jpeg`;
// const image = fs.readFileSync(path.resolve(__dirname, relPathToOGImage), {
// encoding: "base64",
// });

await createOG({
html: exampleTemplate({
title: "Example OG Image",
description: "This is an example blog post.",
image: "",
}),
slug: "/example",
});

// Repeat this process for all pages that need OG images

await browser.close();
};
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
"dev:v": "yarn dev --verbose",
"start": "gatsby develop",
"build": "gatsby build",
"build:local": "dotenvx run --env-file=.env.local -- gatsby build",
"serve": "gatsby serve",
"clean": "gatsby clean",
"test": "vitest",
"test": "yarn test:ci",
"test:watch": "vitest watch",
"test:ci": "vitest run",
"test:coverage": "vitest run --coverage",
"lint": "yarn lint:eslint && yarn lint:prettier && yarn lint:types",
"lint:eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:prettier": "prettier --check .",
"lint:types": "tsc --noEmit",
"prepare": "husky"
"postinstall": "husky"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
Expand All @@ -48,6 +49,7 @@
"gatsby-source-filesystem": "^5.13.1",
"graphql": "^16.8.1",
"i18next": "^23.8.2",
"node-fetch": "^3.3.2",
"postcss": "^8.4.33",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -65,8 +67,10 @@
"@testing-library/jest-dom": "^6.4.1",
"@testing-library/react": "^14.2.1",
"@types/node": "^20.11.5",
"@types/node-fetch": "^2.6.11",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@vitest/coverage-v8": "^1.2.2",
"c8": "^9.1.0",
"eslint": "^8.56.0",
"gatsby-plugin-tsconfig-paths": "^1.0.6",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Link/Link.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Link, LinkProps } from "@components/Link/Link";
import { Link, LinkProps } from "./Link";
import { RenderResult, render } from "@testing-library/react";
import React from "react";
import { beforeEach, describe, expect, it } from "vitest";
Expand Down
55 changes: 55 additions & 0 deletions src/components/Seo/Seo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useSiteMeta } from "@hooks/useSiteMeta";
import { Route } from "@types";
import { useTranslation } from "gatsby-plugin-react-i18next";
import React from "react";

export interface SEOProps {
title: string;
description: string;
route: Route;
slug?: string;
children?: React.ReactNode;
}

export const SEO = ({
title,
description,
route,
slug,
children,
}: Readonly<SEOProps>) => {
const { siteUrl, twitterUsername } = useSiteMeta();
const { i18n } = useTranslation();
const url = new URL(
`${siteUrl}${route ? `/${route}` : ``}${slug ? `/${slug}` : ``}`,
);

const routeAsFileName = (
route?.startsWith("/") ? route.slice(1) : route
)?.replace(/\//g, "-");

const ogImageUrl = new URL(
`${siteUrl}/og-images/${routeAsFileName}${slug || ``}.jpeg`,
);

return (
<>
<title>{title}</title>
<meta name="description" content={description} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:url" content={url.toString()} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImageUrl.toString()} />
<meta name="twitter:creator" content={twitterUsername} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={url.toString()} />
<meta property="og:image" content={ogImageUrl.toString()} />
<meta property="og:site_name" content={title} />
<meta property="og:locale" content={i18n.language} />
<meta property="og:type" content="website" />
{children}
</>
);
};
1 change: 1 addition & 0 deletions src/components/Seo/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Seo";
4 changes: 4 additions & 0 deletions src/gatsby-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1902,24 +1902,28 @@ type SiteSiteMetadata = {
readonly description: Maybe<Scalars['String']>;
readonly siteUrl: Maybe<Scalars['String']>;
readonly title: Maybe<Scalars['String']>;
readonly twitterUsername: Maybe<Scalars['String']>;
};

type SiteSiteMetadataFieldSelector = {
readonly description: InputMaybe<FieldSelectorEnum>;
readonly siteUrl: InputMaybe<FieldSelectorEnum>;
readonly title: InputMaybe<FieldSelectorEnum>;
readonly twitterUsername: InputMaybe<FieldSelectorEnum>;
};

type SiteSiteMetadataFilterInput = {
readonly description: InputMaybe<StringQueryOperatorInput>;
readonly siteUrl: InputMaybe<StringQueryOperatorInput>;
readonly title: InputMaybe<StringQueryOperatorInput>;
readonly twitterUsername: InputMaybe<StringQueryOperatorInput>;
};

type SiteSiteMetadataSortInput = {
readonly description: InputMaybe<SortOrderEnum>;
readonly siteUrl: InputMaybe<SortOrderEnum>;
readonly title: InputMaybe<SortOrderEnum>;
readonly twitterUsername: InputMaybe<SortOrderEnum>;
};

type SiteSortInput = {
Expand Down
2 changes: 2 additions & 0 deletions src/helper/i18n/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* c8 ignore start */
export enum Locale {
EN = "en",
DE = "de",
}
export const defaultLocale = Object.values(Locale)[0];
/* c8 ignore stop */
1 change: 1 addition & 0 deletions src/hooks/useSiteMeta/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./useSiteMeta";
18 changes: 18 additions & 0 deletions src/hooks/useSiteMeta/useSiteMeta.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { graphql, useStaticQuery } from "gatsby";

export const useSiteMeta = () => {
const data = useStaticQuery(graphql`
query {
site {
siteMetadata {
title
description
twitterUsername
siteUrl
}
}
}
`);

return data.site.siteMetadata;
};
14 changes: 9 additions & 5 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Link } from "@components/Link";
import { SEO } from "@components/Seo";
import { Locale } from "@helper/i18n";
import { useSiteMeta } from "@hooks/useSiteMeta";
import { Route } from "@types";
import { graphql, type HeadFC, type PageProps } from "gatsby";
import { useTranslation } from "gatsby-plugin-react-i18next";
import * as React from "react";

const IndexPage: React.FC<PageProps> = () => {
const { t, i18n } = useTranslation();
console.log(i18n);

return (
<main>
<h1 className="text-2xl">{t("hello-world")}</h1>
Expand Down Expand Up @@ -38,9 +41,7 @@ export default IndexPage;

export const query = graphql`
query ($language: String!) {
locales: allLocale(
filter: { language: { eq: $language } }
) {
locales: allLocale(filter: { language: { eq: $language } }) {
edges {
node {
ns
Expand All @@ -52,4 +53,7 @@ export const query = graphql`
}
`;

export const Head: HeadFC = () => <title>Home Page</title>;
export const Head: HeadFC = () => {
const { title, description } = useSiteMeta();
return <SEO title={title} description={description} route={Route.HOME} />;
};
45 changes: 45 additions & 0 deletions src/templates/OG/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Example OG template
module.exports = ({ title, image, font }) => `
<html>
<head>
<meta charset="utf-8" />
<style>
@font-face {
font-family: 'Example Font'; //TODO: Change this to your font
src: url(data:application/x-font-woff;charset=utf-8;base64,${font})
format('woff supports variations'),
url(data:application/x-font-woff;charset=utf-8;base64,${font}) format('woff-variations');
font-style: normal;
font-weight: 400 800;
font-display: swap;
}
body {
// TODO: Add your own styles here
overflow: hidden;
display: flex;
height: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
color: black;
}
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: -1;
}
</style>
</head>
<body>
<h1>${title}</h1>
<img src="${image}" />
</body>
</html>
`;
9 changes: 5 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@
"paths": {
"@types": ["./src/types/index.ts"],
"@helper/*": ["./src/helper/*"],
"@components/*": ["./src/components/*"]
"@hooks/*": ["./src/hooks/*"],
"@components/*": ["./src/components/*"],
},
"esModuleInterop": true,

"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
"skipLibCheck": true,
},
"include": [
"./src/**/*",
"./gatsby-browser.ts",
"./gatsby-config.ts",
"./plugins/**/*"
]
"./plugins/**/*",
],
}
Loading

0 comments on commit 2a1aed4

Please sign in to comment.