Skip to content

Commit d665354

Browse files
Copilotbenkutil
andauthored
feat: add quality tooling and CI workflows (#104)
* Initial plan * feat: add linting tools and configuration Co-authored-by: benkutil <228373+benkutil@users.noreply.github.com> * feat: add GitHub Actions workflows for quality and accessibility testing Co-authored-by: benkutil <228373+benkutil@users.noreply.github.com> * docs: add quality tooling documentation Co-authored-by: benkutil <228373+benkutil@users.noreply.github.com> * fix: add explicit permissions to GitHub Actions workflows Co-authored-by: benkutil <228373+benkutil@users.noreply.github.com> * fix: add package-lock.json to enable npm caching in CI Co-authored-by: benkutil <228373+benkutil@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: benkutil <228373+benkutil@users.noreply.github.com>
1 parent ba2c498 commit d665354

43 files changed

Lines changed: 10169 additions & 833 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eleventy.js

Lines changed: 141 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
require('dotenv').config();
1+
require("dotenv").config();
22
const Webmentions = require("eleventy-plugin-webmentions");
33
const pluginRss = require("@11ty/eleventy-plugin-rss");
44
const Image = require("@11ty/eleventy-img");
@@ -16,161 +16,160 @@ const pluginShortCodes = require("./_config/shortcode.js");
1616
* representing those same attribute-value pairs.
1717
*/
1818
const stringifyAttributes = (attributeMap) => {
19-
return Object.entries(attributeMap)
20-
.map(([attribute, value]) => {
21-
if (typeof value === 'undefined') return '';
22-
return `${attribute}="${value}"`;
23-
})
24-
.join(' ');
19+
return Object.entries(attributeMap)
20+
.map(([attribute, value]) => {
21+
if (typeof value === "undefined") return "";
22+
return `${attribute}="${value}"`;
23+
})
24+
.join(" ");
2525
};
2626

2727
// shortcode for using 11ty's Image plugin
2828
// from https://www.aleksandrhovhannisyan.com/blog/eleventy-image-plugin/
2929
const imageShortcode = async (
30-
src,
31-
alt,
32-
className = undefined,
33-
widths = [400, 800, 1280],
34-
formats = ['webp', 'jpeg'],
35-
sizes = '100vw'
30+
src,
31+
alt,
32+
className = undefined,
33+
widths = [400, 800, 1280],
34+
formats = ["webp", "jpeg"],
35+
sizes = "100vw"
3636
) => {
37-
const imageMetadata = await Image(src, {
38-
widths: [...widths, null],
39-
formats: [...formats, null],
40-
outputDir: '_site/media/images',
41-
urlPath: '/media/images',
42-
});
43-
const sourceHtmlString = Object.values(imageMetadata)
44-
// Map each format to the source HTML markup
45-
.map((images) => {
46-
// The first entry is representative of all the others
47-
// since they each have the same shape
48-
const { sourceType } = images[0];
49-
50-
// Use our util from earlier to make our lives easier
51-
const sourceAttributes = stringifyAttributes({
52-
type: sourceType,
53-
// srcset needs to be a comma-separated attribute
54-
srcset: images.map((image) => image.srcset).join(', '),
55-
sizes,
56-
});
57-
58-
// Return one <source> per format
59-
return `<source ${sourceAttributes}>`;
60-
})
61-
.join('\n');
62-
63-
const getLargestImage = (format) => {
64-
const images = imageMetadata[format];
65-
return images[images.length - 1];
66-
}
67-
68-
const largestUnoptimizedImg = getLargestImage(formats[0]);
69-
const imgAttributes = stringifyAttributes({
70-
src: largestUnoptimizedImg.url,
71-
width: largestUnoptimizedImg.width,
72-
height: largestUnoptimizedImg.height,
73-
alt,
74-
loading: 'lazy',
75-
decoding: 'async',
76-
});
77-
const imgHtmlString = `<img ${imgAttributes}>`;
78-
79-
const pictureAttributes = stringifyAttributes({
80-
class: className,
81-
});
82-
const picture = `<picture ${pictureAttributes}>
37+
const imageMetadata = await Image(src, {
38+
widths: [...widths, null],
39+
formats: [...formats, null],
40+
outputDir: "_site/media/images",
41+
urlPath: "/media/images",
42+
});
43+
const sourceHtmlString = Object.values(imageMetadata)
44+
// Map each format to the source HTML markup
45+
.map((images) => {
46+
// The first entry is representative of all the others
47+
// since they each have the same shape
48+
const { sourceType } = images[0];
49+
50+
// Use our util from earlier to make our lives easier
51+
const sourceAttributes = stringifyAttributes({
52+
type: sourceType,
53+
// srcset needs to be a comma-separated attribute
54+
srcset: images.map((image) => image.srcset).join(", "),
55+
sizes,
56+
});
57+
58+
// Return one <source> per format
59+
return `<source ${sourceAttributes}>`;
60+
})
61+
.join("\n");
62+
63+
const getLargestImage = (format) => {
64+
const images = imageMetadata[format];
65+
return images[images.length - 1];
66+
};
67+
68+
const largestUnoptimizedImg = getLargestImage(formats[0]);
69+
const imgAttributes = stringifyAttributes({
70+
src: largestUnoptimizedImg.url,
71+
width: largestUnoptimizedImg.width,
72+
height: largestUnoptimizedImg.height,
73+
alt,
74+
loading: "lazy",
75+
decoding: "async",
76+
});
77+
const imgHtmlString = `<img ${imgAttributes}>`;
78+
79+
const pictureAttributes = stringifyAttributes({
80+
class: className,
81+
});
82+
const picture = `<picture ${pictureAttributes}>
8383
${sourceHtmlString}
8484
${imgHtmlString}
8585
</picture>`;
8686

87-
return outdent`${picture}`;
87+
return outdent`${picture}`;
8888
};
8989

9090
module.exports = function (eleventyConfig) {
91-
// 11ty plugins
92-
eleventyConfig.addPlugin(pluginRss);
93-
94-
// Only add Webmentions if token is provided
95-
if (process.env.WEBMENTIONS_TOKEN) {
96-
eleventyConfig.addPlugin(Webmentions, {
97-
domain: "benkutil.com",
98-
token: process.env.WEBMENTIONS_TOKEN
99-
});
100-
}
101-
102-
eleventyConfig.addPlugin(pluginNavigation);
103-
eleventyConfig.addPlugin(pluginSyntaxHighlight, {
104-
preAttributes: { tabindex: 0 }
91+
// 11ty plugins
92+
eleventyConfig.addPlugin(pluginRss);
93+
94+
// Only add Webmentions if token is provided
95+
if (process.env.WEBMENTIONS_TOKEN) {
96+
eleventyConfig.addPlugin(Webmentions, {
97+
domain: "benkutil.com",
98+
token: process.env.WEBMENTIONS_TOKEN,
10599
});
106-
eleventyConfig.addPlugin(pluginTOC, {
107-
tags: ['h2', 'h3', 'h4', 'h5'],
108-
ul: true,
109-
flat: false,
110-
wrapper: 'div'
100+
}
101+
102+
eleventyConfig.addPlugin(pluginNavigation);
103+
eleventyConfig.addPlugin(pluginSyntaxHighlight, {
104+
preAttributes: { tabindex: 0 },
105+
});
106+
eleventyConfig.addPlugin(pluginTOC, {
107+
tags: ["h2", "h3", "h4", "h5"],
108+
ul: true,
109+
flat: false,
110+
wrapper: "div",
111+
});
112+
113+
// Add Tufte filters and shortcodes
114+
eleventyConfig.addPlugin(pluginFilters);
115+
eleventyConfig.addPlugin(pluginShortCodes);
116+
117+
// 11ty shortcodes
118+
eleventyConfig.addShortcode("image", imageShortcode);
119+
120+
// Configure markdown-it
121+
const markdownIt = require("markdown-it");
122+
let options = {
123+
html: true,
124+
breaks: true,
125+
linkify: true,
126+
typographer: true,
127+
};
128+
let markdownLib = markdownIt(options).use(markdownItAttrs);
129+
eleventyConfig.setLibrary("md", markdownLib);
130+
131+
eleventyConfig.amendLibrary("md", (mdLib) => {
132+
mdLib.use(markdownItAnchor, {
133+
permalink: markdownItAnchor.permalink.ariaHidden({
134+
placement: "after",
135+
class: "header-anchor",
136+
symbol: "",
137+
ariaHidden: false,
138+
}),
139+
level: [1, 2, 3, 4],
140+
slugify: eleventyConfig.getFilter("slugify"),
111141
});
112-
113-
// Add Tufte filters and shortcodes
114-
eleventyConfig.addPlugin(pluginFilters);
115-
eleventyConfig.addPlugin(pluginShortCodes);
116-
117-
// 11ty shortcodes
118-
eleventyConfig.addShortcode('image', imageShortcode);
119-
120-
// Configure markdown-it
121-
const markdownIt = require("markdown-it");
122-
let options = {
123-
html: true,
124-
breaks: true,
125-
linkify: true,
126-
typographer: true,
127-
};
128-
let markdownLib = markdownIt(options).use(markdownItAttrs);
129-
eleventyConfig.setLibrary("md", markdownLib);
130-
131-
eleventyConfig.amendLibrary("md", mdLib => {
132-
mdLib.use(markdownItAnchor, {
133-
permalink: markdownItAnchor.permalink.ariaHidden({
134-
placement: "after",
135-
class: "header-anchor",
136-
symbol: "",
137-
ariaHidden: false,
138-
}),
139-
level: [1,2,3,4],
140-
slugify: eleventyConfig.getFilter("slugify")
141-
});
142-
});
143-
144-
// Pass through Tufte CSS and fonts
145-
eleventyConfig.addPassthroughCopy("src/css");
146-
eleventyConfig.addPassthroughCopy("src/et-book");
147-
148-
// run these configs in production only
149-
if (process.env.ELEVENTY_ENV === 'production') {
150-
eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
151-
// find html files
152-
if (outputPath && outputPath.endsWith(".html")) {
153-
// configure html-minify
154-
let minified = htmlmin.minify(content, {
155-
useShortDoctype: true,
156-
removeComments: true,
157-
minifyCSS: true,
158-
collapseWhitespace: true
159-
});
160-
return minified;
161-
}
162-
163-
return content;
142+
});
143+
144+
// Pass through Tufte CSS and fonts
145+
eleventyConfig.addPassthroughCopy("src/css");
146+
eleventyConfig.addPassthroughCopy("src/et-book");
147+
148+
// run these configs in production only
149+
if (process.env.ELEVENTY_ENV === "production") {
150+
eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
151+
// find html files
152+
if (outputPath && outputPath.endsWith(".html")) {
153+
// configure html-minify
154+
let minified = htmlmin.minify(content, {
155+
useShortDoctype: true,
156+
removeComments: true,
157+
minifyCSS: true,
158+
collapseWhitespace: true,
164159
});
160+
return minified;
161+
}
165162

166-
}
167-
168-
// Directory changes
169-
return {
170-
dir: {
171-
input: 'src',
172-
includes: '_includes',
173-
data: '_data'
174-
}
175-
}
176-
};
163+
return content;
164+
});
165+
}
166+
167+
// Directory changes
168+
return {
169+
dir: {
170+
input: "src",
171+
includes: "_includes",
172+
data: "_data",
173+
},
174+
};
175+
};
Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,39 @@
11
---
22
name: Bug Report
33
about: Report a problem with the website
4-
title: ''
4+
title: ""
55
labels: bug
6-
assignees: ''
6+
assignees: ""
77
---
88

99
## Context
10-
<!-- What were you trying to do when you encountered this issue? -->
1110

11+
<!-- What were you trying to do when you encountered this issue? -->
1212

1313
## Current Behavior
14-
<!-- What is actually happening? -->
1514

15+
<!-- What is actually happening? -->
1616

1717
## Expected Behavior
18-
<!-- What should happen instead? -->
1918

19+
<!-- What should happen instead? -->
2020

2121
## Steps to Reproduce
22-
1.
23-
2.
24-
3.
22+
23+
1.
24+
2.
25+
3.
2526

2627
## Definition of Done
28+
2729
<!-- How will we know this is fixed? -->
30+
2831
- [ ] Bug is identified and root cause understood
2932
- [ ] Fix is implemented
3033
- [ ] Site builds without errors
3134
- [ ] Changes are tested locally
3235
- [ ] No regressions introduced
3336

3437
## Additional Context
35-
<!-- Browser, screenshots, error messages, etc. -->
3638

39+
<!-- Browser, screenshots, error messages, etc. -->
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
---
22
name: Feature Request
33
about: Suggest an idea or improvement for this website
4-
title: ''
4+
title: ""
55
labels: enhancement
6-
assignees: ''
6+
assignees: ""
77
---
88

99
## Context
10-
<!-- Brief description of what you're trying to accomplish or the problem you're solving -->
1110

11+
<!-- Brief description of what you're trying to accomplish or the problem you're solving -->
1212

1313
## Proposed Solution
14-
<!-- What changes would you like to see? -->
1514

15+
<!-- What changes would you like to see? -->
1616

1717
## Definition of Done
18+
1819
<!-- How will we know this is complete? Check all that apply or add your own -->
20+
1921
- [ ] Feature is implemented
2022
- [ ] Changes are documented (if applicable)
2123
- [ ] Site builds without errors
2224
- [ ] Changes are tested locally
2325
- [ ] Code follows simplicity principles
2426

2527
## Additional Context
26-
<!-- Add any other context, screenshots, or examples -->
2728

29+
<!-- Add any other context, screenshots, or examples -->

0 commit comments

Comments
 (0)