Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
7ea10ea
manually update deps
karianna Dec 17, 2020
5513f9b
update sharp
karianna Dec 17, 2020
a8c8b77
add intellij IDE config to ignore
karianna Dec 17, 2020
9932c60
merge in from upstream
karianna Nov 28, 2022
43eeb1f
correct package-lock
karianna Nov 28, 2022
0a6482b
merge conflicts
karianna Jul 11, 2023
cbb9392
Add VS Code config to git ignore
karianna Apr 8, 2025
2ec1ffd
Merge branch 'master' of github.com:adoptopenjdk/blog
karianna Apr 8, 2025
7833c31
Fix conflict
karianna Jul 17, 2025
5f501b5
Bump on-headers and compression (#1177)
dependabot[bot] Jul 18, 2025
1a02542
Bump multer from 2.0.1 to 2.0.2 (#1178)
dependabot[bot] Jul 18, 2025
fe501a6
Bump form-data from 4.0.1 to 4.0.4 (#1179)
dependabot[bot] Jul 22, 2025
7aaf552
Bump @fortawesome/react-fontawesome from 0.2.2 to 0.2.3 (#1182)
dependabot[bot] Jul 23, 2025
cf5249f
Bump @fortawesome/fontawesome-svg-core from 6.7.2 to 7.0.0 (#1183)
dependabot[bot] Jul 23, 2025
15faa80
Bump @fortawesome/free-brands-svg-icons from 6.7.2 to 7.0.0 (#1181)
dependabot[bot] Jul 23, 2025
e38260d
Bump @fortawesome/free-solid-svg-icons from 6.7.2 to 7.0.0 (#1180)
dependabot[bot] Jul 23, 2025
18eb969
Bump tmp and gatsby (#1187)
dependabot[bot] Aug 6, 2025
2408d46
Fix fetch path
karianna Aug 8, 2025
91c5957
More updates + some copilot instructions
karianna Aug 8, 2025
9378a48
Update TS Config
karianna Aug 8, 2025
d285d06
Update and prep for React 19
karianna Aug 10, 2025
c788cd9
whitespace changes
karianna Aug 10, 2025
960c849
chore(eslint): add flat ESLint config to enable JSX parsing
karianna Sep 14, 2025
d9f153b
chore(gatsby): add JS gatsby-node for runtime (compiled from TS)
karianna Sep 14, 2025
f932358
Bump actions/checkout from 4 to 5 (#1189)
dependabot[bot] Aug 11, 2025
8fac93d
Bump @fortawesome/react-fontawesome from 0.2.3 to 0.2.5 (#1191)
dependabot[bot] Aug 20, 2025
6a640cb
Bump actions/upload-pages-artifact from 3 to 4 (#1193)
dependabot[bot] Aug 23, 2025
c73d788
Bump @fortawesome/react-fontawesome from 0.2.5 to 3.0.0 (#1192)
dependabot[bot] Aug 24, 2025
a69618a
Bump @fortawesome/react-fontawesome from 3.0.0 to 3.0.1 (#1195)
dependabot[bot] Aug 27, 2025
1bc627c
Bump gatsby-remark-smartypants from 6.14.0 to 6.15.0 (#1196)
dependabot[bot] Aug 28, 2025
68ad6de
Bump babel-preset-gatsby from 3.14.0 to 3.15.0 (#1198)
dependabot[bot] Aug 28, 2025
f249680
Bump gatsby-remark-prismjs from 7.14.0 to 7.15.0 (#1199)
dependabot[bot] Aug 28, 2025
666ea85
Bump gatsby from 5.14.6 to 5.15.0 (#1201)
dependabot[bot] Aug 28, 2025
a5ce46f
Bump gatsby-transformer-sharp from 5.14.0 to 5.15.0 (#1203)
dependabot[bot] Aug 28, 2025
98a5ff5
Bump gatsby-plugin-image from 3.14.0 to 3.15.0 (#1200)
dependabot[bot] Aug 28, 2025
30fae48
Bump gatsby-plugin-sharp from 5.14.0 to 5.15.0 (#1206)
dependabot[bot] Aug 28, 2025
97de548
Bump gatsby-remark-copy-linked-files from 6.14.0 to 6.15.0 (#1204)
dependabot[bot] Aug 28, 2025
14279a7
Bump gatsby-remark-images from 7.14.0 to 7.15.0 (#1207)
dependabot[bot] Aug 28, 2025
32efb9b
Bump gatsby-plugin-manifest from 5.14.0 to 5.15.0 (#1202)
dependabot[bot] Aug 28, 2025
d296453
Bump gatsby-plugin-offline from 6.14.0 to 6.15.0 (#1197)
dependabot[bot] Aug 28, 2025
f6eeb26
Bump gatsby-plugin-typography from 5.14.0 to 5.15.0 (#1205)
dependabot[bot] Aug 28, 2025
dee4547
Bump gatsby-plugin-feed from 5.14.0 to 5.15.0 (#1208)
dependabot[bot] Aug 28, 2025
6e9d960
Bump gatsby-remark-responsive-iframe from 6.14.0 to 6.15.0 (#1209)
dependabot[bot] Aug 29, 2025
20a214b
Bump gatsby-plugin-mdx from 5.14.1 to 5.15.0 (#1211)
dependabot[bot] Aug 29, 2025
a6020dc
Bump gatsby-plugin-google-gtag from 5.14.0 to 5.15.0 (#1210)
dependabot[bot] Aug 29, 2025
ed7f51a
Bump gatsby-source-filesystem from 5.14.0 to 5.15.0 (#1212)
dependabot[bot] Aug 29, 2025
96cf2d0
Bump @fortawesome/react-fontawesome from 3.0.1 to 3.0.2 (#1213)
dependabot[bot] Sep 2, 2025
21ff32b
Bump @fortawesome/free-brands-svg-icons from 7.0.0 to 7.0.1 (#1215)
dependabot[bot] Sep 4, 2025
9a7c934
Bump actions/setup-node from 4 to 5 (#1218)
dependabot[bot] Sep 7, 2025
226eb0f
Bump axios from 1.8.2 to 1.12.1 (#1220)
dependabot[bot] Sep 13, 2025
75ba211
Bump @fortawesome/fontawesome-svg-core from 7.0.0 to 7.0.1 (#1216)
dependabot[bot] Sep 13, 2025
25dde9e
Bump @fortawesome/free-solid-svg-icons from 7.0.0 to 7.0.1 (#1217)
dependabot[bot] Sep 13, 2025
b7709f7
docs: move copilot instructions to AGENTS.md
karianna Sep 15, 2025
67bf0b6
Fix typo in README and add formatting to SECURITY.md
karianna Oct 12, 2025
e71fa2d
Merge branch 'master' into update_deps_17_jul_2025
karianna Oct 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
## AGENTS

Purpose: Help AI coding agents work productively in this Gatsby (v5) + MDX (v2) blog.

Big picture
- Static site built with Gatsby 5.x, content in `content/blog/**/index.md` (MDX). Images live next to posts.
- Programmatic pages are created in `gatsby-node.ts` using GraphQL over MDX nodes; custom fields `fields.slug` and `fields.postPath` are added in `onCreateNode` and used across templates.
- Main templates live in `src/templates/` (blog-post, blog-page, tag-page, author-page). Shared UI in `src/components/`.
- Site config and plugin wiring in `gatsby-config.js`. Two filesystem sources (`content/blog`, `content/assets`). MDX is configured with remark plugins (images, prism, etc.).
# AGENTS

Purpose: Help AI coding agents work productively in this Gatsby (v5) + MDX (v2) blog.

Big picture

- Static site built with Gatsby 5.x, content in `content/blog/**/index.md` (MDX). Images live next to posts.

- Programmatic pages are created in `gatsby-node.ts` using GraphQL over MDX nodes; custom fields `fields.slug` and `fields.postPath` are added in `onCreateNode` and used across templates.

- Main templates live in `src/templates/` (blog-post, blog-page, tag-page, author-page). Shared UI in `src/components/`.

- Site config and plugin wiring in `gatsby-config.js`. Two filesystem sources (`content/blog`, `content/assets`). MDX is configured with remark plugins (images, prism, etc.).
# AGENTS

Purpose: Help AI coding agents work productively in this Gatsby (v5) + MDX (v2) blog.

Big picture

- Static site built with Gatsby 5.x, content in `content/blog/**/index.md` (MDX). Images live next to posts.

- Programmatic pages are created in `gatsby-node.ts` using GraphQL over MDX nodes; custom fields `fields.slug` and `fields.postPath` are added in `onCreateNode` and used across templates.

- Main templates live in `src/templates/` (blog-post, blog-page, tag-page, author-page). Shared UI in `src/components/`.

- Site config and plugin wiring in `gatsby-config.js`. Two filesystem sources (`content/blog`, `content/assets`). MDX is configured with remark plugins (images, prism, etc.).

Key workflows

- Install deps: `npm ci` (use Node 18+).

- Dev server: `npm run develop` (hot-reload preview while editing content/components).

- Build: `npm run build` then preview: `npm run serve` (site at <http://localhost:9000>).

- Test script: `npm run test` runs lint then build. Lint uses ESLint 8 with `.eslintrc` (flat config is NOT used here).

Project-specific conventions

- Posts are MDX files at `content/blog/<slug>/index.md` with frontmatter: `title`, `date` (ISO8601), `author`, optional `featuredImage`, `description` for home excerpts. See README for examples.

- Author metadata in `content/authors.json`; author images resolved to `content/assets/authors/<key>.jpg`. If missing, `gatsby-node.ts` downloads GitHub avatar at build time using global `fetch` and writes to that path.

- GraphQL usage relies on custom `fields` on `Mdx` nodes (set in `gatsby-node.ts`). Templates query `fields { slug, postPath }` and `frontmatter { ... }`. When creating new templates/queries, ensure those fields exist or add similar fields in `onCreateNode`.

- Pagination: `gatsby-node.ts` creates `/page/1..N`; index shows up to 10 posts, template queries use `$skip`/`$limit`.

- RSS: only generated in production (`npm run build && npm run serve`).

Important files

- `gatsby-node.ts`: page creation, tag pages, author pages, MDX fields. Uses Node 18 global `fetch` and `fs.promises.writeFile(new Uint8Array(...))` when downloading author avatars.

- `gatsby-config.js`: plugins & site metadata; MDX/remark configuration; Google Analytics; feed; typography.

- `src/templates/*.js`: GraphQL queries + rendering logic per page type.

- `src/components/*`: Layout, SEO, ArticlePreview, Byline, Tags, Comments, etc.

- `content/`: blog posts and assets; authors.json.

Patterns to follow

- When adding GraphQL fields to `Mdx`, define them in `onCreateNode` (slug, postPath). All templates assume these fields are present.

- Use `internal.contentFilePath` when creating pages for MDX (`?__contentFilePath=`) to enable MDX rendering.

- For author pages, ensure each author key in `authors.json` maps to a `.jpg` in `content/assets/authors/` or the avatar fetch will run at build time.

- Keep queries in templates in sync with the schema defined by the plugins and the fields created in `gatsby-node.ts`.

Common pitfalls

- If you see GraphQL errors like “Cannot query field 'fields' on type 'Mdx'”, check `onCreateNode` in `gatsby-node.ts` is creating those fields and that Gatsby is picking up the TS node file (it is, via `gatsby-node.ts`). Run `npm run clean` before `npm run build` after changes.

- Do not upgrade React to v19 or MDX to v3 without coordinating upgrades to Gatsby v6+ and `gatsby-plugin-mdx` v6+. This repo is pinned to Gatsby 5.x with MDX v2.

- ESLint 8 with `.eslintrc` is the current setup; ESLint 9 flat config caused parsing issues. Update only if necessary, and migrate carefully.

Examples

- Creating a new blog post: add `content/blog/hello-world/index.md` with frontmatter, optional images; run `npm run develop`, verify at `/` and `/page/2`.

- Linking to posts: use `node.fields.postPath` provided by `gatsby-node.ts` when building lists (see `src/templates/blog-page.js`).

Local commands (copy/paste)

- Install: `npm ci`

- Dev: `npm run develop`

- Clean + build: `npm run clean && npm run build`

- Preview build: `npm run serve` (<http://localhost:9000>)

Maintainer notes for agents

- Prefer minimal, surgical changes. Avoid upgrading major versions unless explicitly requested.

- When changing GraphQL, run a clean build to regenerate `.cache/schema.gql` and catch schema issues early.

Azure note

- If asked to generate Azure code or commands, follow the Azure best-practices tool guidance available in your environment.

Contribution flow

- Create feature branches from `master` (or the working update branch if collaborating).

- Validate locally before PR:

- `npm ci`

- `npm run test` (runs lint + build)

- Optional: `npm run serve` and verify key pages (/, /page/2, a post, /tags/{tag}, /author/{key}).

- Keep changes minimal; avoid dependency major bumps unless the PR is dedicated to that effort.

- In PRs, call out changes to GraphQL queries/templates and any content structure updates.
Maintainer notes for agents

- Prefer minimal, surgical changes. Avoid upgrading major versions unless explicitly requested.

- When changing GraphQL, run a clean build to regenerate `.cache/schema.gql` and catch schema issues early.

Azure note

- If asked to generate Azure code or commands, follow the Azure best-practices tool guidance available in your environment.

Contribution flow

- Create feature branches from `master` (or the working update branch if collaborating).

- Validate locally before PR:

- `npm ci`

- `npm run test` (runs lint + build)

- Optional: `npm run serve` and verify key pages (/, /page/2, a post, /tags/<tag>, /author/<key>).

- Keep changes minimal; avoid dependency major bumps unless the PR is dedicated to that effort.

- In PRs, call out changes to GraphQL queries/templates and any content structure updates.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Our CSS will take care of rendering it correctly by looking for `img + em`.

Example:

![Photo depiciting a drop of water](./clean-drop-of-water-liquid.jpg)
![Photo depicting a drop of water](./clean-drop-of-water-liquid.jpg)
*AQA v1.0 is a first drop in an on-going series of improvements.*

### Quotes
Expand Down
2 changes: 2 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Security Policy

## Reporting a Vulnerability

Please report vulnerabilities to [[email protected]]([email protected]).
A member of the security team will respond within 48 hours with details on how to proceed including whether or not the vulnerability was accepted, declined or requires further investigation.
26 changes: 26 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import babelParser from '@babel/eslint-parser';
import reactPlugin from 'eslint-plugin-react';

export default [
{
ignores: ['**/public/**', '**/node_modules/**'],
},
{
files: ['**/*.js', '**/*.jsx'],
languageOptions: {
parser: babelParser,
parserOptions: {
requireConfigFile: false,
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: { jsx: true },
},
globals: { window: 'readonly', document: 'readonly' },
},
plugins: { react: reactPlugin },
rules: {
// basic recommended ruleset
},
},
];

144 changes: 144 additions & 0 deletions gatsby-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
const path = require('path');
const fs = require('fs');
const { createFilePath } = require('gatsby-source-filesystem');

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

const authorJson = require('./content/authors.json');
const authorPage = path.resolve('./src/templates/author-page.js');

for (let author of Object.keys(authorJson)) {
try {
await fs.promises.access(`content/assets/authors/${author}.jpg`);
} catch (error) {
const githubUsername = authorJson[author].github;
const response = await fetch(`https://github.com/${githubUsername}.png?size=250`);
if (!response.ok) {
throw new Error(`Unexpected response: ${response.statusText}`);
}
const arrayBuf = await response.arrayBuffer();
const uint8 = new Uint8Array(arrayBuf);
await fs.promises.writeFile(`content/assets/authors/${author}.jpg`, uint8);
}

createPage({
path: `/author/${author}`,
component: authorPage,
context: {
author: author,
limit: 10,
},
});
}

const tagTemplate = path.resolve('./src/templates/tag-page.js');
const blogPost = path.resolve('./src/templates/blog-post.js');
const result = await graphql(`
{
allMdx(sort: {frontmatter: {date: DESC}}) {
edges {
node {
fields {
slug
postPath
}
frontmatter {
title
tags
}
internal {
contentFilePath
}
}
}
}
tagsGroup: allMdx(limit: 2000) {
group(field: {frontmatter: {tags: SELECT}}) {
fieldValue
}
}
}
`);

if (result.errors) {
throw result.errors;
}

if (!result.data) {
throw new Error('Error retrieving blog posts');
}

const posts = result.data.allMdx.edges;

posts.forEach((post, index) => {
const previous = index === posts.length - 1 ? null : posts[index + 1].node;
const next = index === 0 ? null : posts[index - 1].node;

createPage({
path: `${post.node.fields.postPath}`,
component: `${blogPost}?__contentFilePath=${post.node.internal.contentFilePath}`,
context: {
slug: post.node.fields.slug,
postPath: post.node.fields.postPath,
previous,
next,
},
});
});

const tags = result.data.tagsGroup.group;

tags.forEach((tag) => {
createPage({
path: `/tags/${tag.fieldValue}/`,
component: tagTemplate,
context: {
tag: tag.fieldValue,
},
});
});

const postsPerPage = 10;
const numPages = Math.ceil(posts.length / postsPerPage);
Array.from({ length: numPages }).forEach((_, index) => {
const currentPageNumber = index + 1;
const previousPageNumber = currentPageNumber === 1 ? null : currentPageNumber - 1;
const nextPageNumber = currentPageNumber === numPages ? null : currentPageNumber + 1;

createPage({
path: `/page/${index + 1}`,
component: path.resolve('./src/templates/blog-page.js'),
context: {
limit: postsPerPage,
skip: index * postsPerPage,
numPages,
currentPageNumber,
previousPageNumber,
nextPageNumber,
},
});
});
};

exports.onCreateNode = async ({ node, actions, getNode }) => {
const { createNodeField } = actions;

if (node.internal.type === 'Mdx') {
const slug = createFilePath({ node, getNode });
const date = new Date(node.frontmatter.date);
const year = date.getFullYear();
const zeroPaddedMonth = `${date.getMonth() + 1}`.padStart(2, '0');

createNodeField({
name: 'slug',
node,
value: slug,
});
createNodeField({
name: 'postPath',
node,
value: `/${year}/${zeroPaddedMonth}${slug}`,
});
}
};