diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..f82df82 Binary files /dev/null and b/.DS_Store differ diff --git a/LICENSE b/LICENSE index c97fe3d..5027c0d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017–2023 Zach Leatherman @zachleat +Copyright (c) 2017–2024 Zach Leatherman @zachleat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9ba6ffb..6abdbba 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,116 @@ -# My personal site +# eleventy-base-blog v9 -It uses [eleventy-base-blog](https://github.com/11ty/eleventy-base-blog) and it is hosted on [Github Pages](https://pages.github.com/). \ No newline at end of file +A starter repository showing how to build a blog with the [Eleventy](https://www.11ty.dev/) site generator (using the [v3.0 release](https://github.com/11ty/eleventy/releases/tag/v3.0.0)). + +## Getting Started + +* [Want a more generic/detailed getting started guide?](https://www.11ty.dev/docs/getting-started/) + +1. Make a directory and navigate to it: + +``` +mkdir my-blog-name +cd my-blog-name +``` + +2. Clone this Repository + +``` +git clone https://github.com/11ty/eleventy-base-blog.git . +``` + +_Optional:_ Review `eleventy.config.js` and `_data/metadata.js` to configure the site’s options and data. + +3. Install dependencies + +``` +npm install +``` + +4. Run Eleventy + +Generate a production-ready build to the `_site` folder: + +``` +npx @11ty/eleventy +``` + +Or build and host on a local development server: + +``` +npx @11ty/eleventy --serve +``` + +Or you can run [debug mode](https://www.11ty.dev/docs/debugging/) to see all the internals. + +## Features + +- Using [Eleventy v3](https://github.com/11ty/eleventy/releases/tag/v3.0.0) with zero-JavaScript output. + - Content is exclusively pre-rendered (this is a static site). + - Can easily [deploy to a subfolder without changing any content](https://www.11ty.dev/docs/plugins/html-base/) + - All URLs are decoupled from the content’s location on the file system. + - Configure templates via the [Eleventy Data Cascade](https://www.11ty.dev/docs/data-cascade/) +- **Performance focused**: four-hundos Lighthouse score out of the box! + - _0 Cumulative Layout Shift_ + - _0ms Total Blocking Time_ +- Local development live reload provided by [Eleventy Dev Server](https://www.11ty.dev/docs/dev-server/). +- Content-driven [navigation menu](https://www.11ty.dev/docs/plugins/navigation/) +- Fully automated [Image optimization](https://www.11ty.dev/docs/plugins/image/) + - Zero-JavaScript output. + - Support for modern image formats automatically (e.g. AVIF and WebP) + - Processes images on-request during `--serve` for speedy local builds. + - Prefers `` markup if possible (single image format) but switches automatically to `` for multiple image formats. + - Automated `` syntax markup with `srcset` and optional `sizes` + - Includes `width`/`height` attributes to avoid [content layout shift](https://web.dev/cls/). + - Includes `loading="lazy"` for native lazy loading without JavaScript. + - Includes [`decoding="async"`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding) + - Images can be co-located with blog post files. +- Per page CSS bundles [via `eleventy-plugin-bundle`](https://github.com/11ty/eleventy-plugin-bundle). +- Built-in [syntax highlighter](https://www.11ty.dev/docs/plugins/syntaxhighlight/) (zero-JavaScript output). +- Draft content: use `draft: true` to mark any template as a draft. Drafts are **only** included during `--serve`/`--watch` and are excluded from full builds. This is driven by the `addPreprocessor` configuration API in `eleventy.config.js`. Schema validator will show an error if non-boolean value is set in data cascade. +- Blog Posts + - Automated next/previous links + - Accessible deep links to headings +- Generated Pages + - Home, Archive, and About pages. + - [Atom feed included (with easy one-line swap to use RSS or JSON)](https://www.11ty.dev/docs/plugins/rss/) + - `sitemap.xml` + - Zero-maintenance tag pages ([View on the Demo](https://eleventy-base-blog.netlify.app/tags/)) + - Content not found (404) page + +## Demos + +- [Netlify](https://eleventy-base-blog.netlify.app/) +- [Vercel](https://demo-base-blog.11ty.dev/) +- [Cloudflare Pages](https://eleventy-base-blog-d2a.pages.dev/) +- [GitHub Pages](https://11ty.github.io/eleventy-base-blog/) + +## Deploy this to your own site + +Deploy this Eleventy site in just a few clicks on these services: + +- Read more about [Deploying an Eleventy project](https://www.11ty.dev/docs/deployment/) to the web. +- [Deploy this to **Netlify**](https://app.netlify.com/start/deploy?repository=https://github.com/11ty/eleventy-base-blog) +- [Deploy this to **Vercel**](https://vercel.com/import/project?template=11ty%2Feleventy-base-blog) +- Look in `.github/workflows/gh-pages.yml.sample` for information on [Deploying to **GitHub Pages**](https://www.11ty.dev/docs/deployment/#deploy-an-eleventy-project-to-git-hub-pages). +- [Try it out on **Stackblitz**](https://stackblitz.com/github/11ty/eleventy-base-blog) + +### Implementation Notes + +- `content/about/index.md` is an example of a content page. +- `content/blog/` has the blog posts but really they can live in any directory. They need only the `posts` tag to be included in the blog posts [collection](https://www.11ty.dev/docs/collections/). +- Use the `eleventyNavigation` key (via the [Eleventy Navigation plugin](https://www.11ty.dev/docs/plugins/navigation/)) in your front matter to add a template to the top level site navigation. This is in use on `content/index.njk` and `content/about/index.md`. +- Content can be in _any template format_ (blog posts needn’t exclusively be markdown, for example). Configure your project’s supported templates in `eleventy.config.js` -> `templateFormats`. +- The `public` folder in your input directory will be copied to the output folder (via `addPassthroughCopy` in the `eleventy.config.js` file). This means `./public/css/*` will live at `./_site/css/*` after your build completes. +- This project uses three [Eleventy Layouts](https://www.11ty.dev/docs/layouts/): + - `_includes/layouts/base.njk`: the top level HTML structure + - `_includes/layouts/home.njk`: the home page template (wrapped into `base.njk`) + - `_includes/layouts/post.njk`: the blog post template (wrapped into `base.njk`) +- `_includes/postslist.njk` is a Nunjucks include and is a reusable component used to display a list of all the posts. `content/index.njk` has an example of how to use it. + +#### Content Security Policy + +If your site enforces a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (as public-facing sites should), you have a few choices (pick one): + +1. In `base.njk`, remove `` and uncomment `` +2. Configure the server with the CSP directive `style-src: 'unsafe-inline'` (less secure). diff --git a/_config/filters.js b/_config/filters.js new file mode 100644 index 0000000..773151d --- /dev/null +++ b/_config/filters.js @@ -0,0 +1,53 @@ +import { DateTime } from "luxon"; + +export default function(eleventyConfig) { + eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => { + // Formatting tokens for Luxon: https://moment.github.io/luxon/#/formatting?id=table-of-tokens + return DateTime.fromJSDate(dateObj, { zone: zone || "utc" }).toFormat(format || "dd LLLL yyyy"); + }); + + eleventyConfig.addFilter("htmlDateString", (dateObj) => { + // dateObj input: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string + return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat('yyyy-LL-dd'); + }); + + // Get the first `n` elements of a collection. + eleventyConfig.addFilter("head", (array, n) => { + if(!Array.isArray(array) || array.length === 0) { + return []; + } + if( n < 0 ) { + return array.slice(n); + } + + return array.slice(0, n); + }); + + // Return the smallest number argument + eleventyConfig.addFilter("min", (...numbers) => { + return Math.min.apply(null, numbers); + }); + + // Return the keys used in an object + eleventyConfig.addFilter("getKeys", target => { + return Object.keys(target); + }); + + eleventyConfig.addFilter("filterTagList", function filterTagList(tags) { + return (tags || []).filter(tag => ["all", "posts"].indexOf(tag) === -1); + }); + + eleventyConfig.addFilter("sortAlphabetically", strings => + (strings || []).sort((b, a) => b.localeCompare(a)) + ); + + // Return all the tags used in a collection + eleventyConfig.addFilter("getAllTags", collection => { + let tagSet = new Set(); + for(let item of collection) { + (item.data.tags || []).forEach(tag => tagSet.add(tag)); + } + return Array.from(tagSet); + }); + +}; diff --git a/_data/eleventyDataSchema.js b/_data/eleventyDataSchema.js new file mode 100644 index 0000000..bfef5d5 --- /dev/null +++ b/_data/eleventyDataSchema.js @@ -0,0 +1,17 @@ +import { z } from "zod"; +import { fromZodError } from 'zod-validation-error'; + +// Draft content, validate `draft` front matter +export default function() { + return function(data) { + // Note that drafts may be skipped in a preprocessor (see eleventy.config.js) + // when doing a standard build (not --serve or --watch) + let result = z.object({ + draft: z.boolean().or(z.undefined()), + }).safeParse(data); + + if(result.error) { + throw fromZodError(result.error); + } + } +} diff --git a/_data/metadata.js b/_data/metadata.js index ff4f7e5..7e8b636 100644 --- a/_data/metadata.js +++ b/_data/metadata.js @@ -1,11 +1,11 @@ -module.exports = { - title: "Patrick's Pages", +export default { + title: "Eleventy Base Blog v9", url: "https://example.com/", language: "en", - description: "It's Patrick's website", + description: "I am writing about my experiences as a naval navel-gazer.", author: { - name: "Patrick", - email: "", - url: "https://patrickpatrickpatrick.github.io/about/" + name: "Your Name Here", + email: "youremailaddress@example.com", + url: "https://example.com/about-me/" } } diff --git a/_includes/layouts/atamaca.njk b/_includes/layouts/atamaca.njk index 90fbe08..7d6fc7d 100644 --- a/_includes/layouts/atamaca.njk +++ b/_includes/layouts/atamaca.njk @@ -1,4 +1,3 @@ - @@ -22,12 +21,13 @@ - + +
+ - \ No newline at end of file diff --git a/_includes/layouts/email.njk b/_includes/layouts/email.njk index ee4666f..1bd7cd1 100644 --- a/_includes/layouts/email.njk +++ b/_includes/layouts/email.njk @@ -1,4 +1,3 @@ - diff --git a/banner.html b/banner.html deleted file mode 100644 index f274280..0000000 --- a/banner.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - Document - - - - - - \ No newline at end of file diff --git a/content/.DS_Store b/content/.DS_Store new file mode 100644 index 0000000..6065f4b Binary files /dev/null and b/content/.DS_Store differ diff --git a/content/blog/blog.11tydata.js b/content/blog/blog.11tydata.js index 2d655b1..614f505 100644 --- a/content/blog/blog.11tydata.js +++ b/content/blog/blog.11tydata.js @@ -1,4 +1,4 @@ -module.exports = { +export default { tags: [ "posts" ], diff --git a/content/feed/feed.11tydata.js b/content/feed/feed.11tydata.js deleted file mode 100644 index ed3fec9..0000000 --- a/content/feed/feed.11tydata.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - eleventyExcludeFromCollections: true -} diff --git a/content/feed/feed.njk b/content/feed/feed.njk deleted file mode 100755 index a47a7e8..0000000 --- a/content/feed/feed.njk +++ /dev/null @@ -1,27 +0,0 @@ ---- -# Metadata comes from _data/metadata.js -permalink: /feed/feed.xml ---- - - - {{ metadata.title }} - {{ metadata.description }} - - - {{ collections.posts | getNewestCollectionItemDate | dateToRfc3339 }} - {{ metadata.url }} - - {{ metadata.author.name }} - {{ metadata.author.email }} - - {%- for post in collections.posts | reverse %} - {% set absolutePostUrl %}{{ post.url | htmlBaseUrl(metadata.url) }}{% endset %} - - {{ post.data.title }} - - {{ post.date | dateToRfc3339 }} - {{ absolutePostUrl }} - {{ post.templateContent | transformWithHtmlBase(absolutePostUrl, post.url) }} - - {%- endfor %} - diff --git a/content/feed/json.njk b/content/feed/json.njk deleted file mode 100644 index 3b33b59..0000000 --- a/content/feed/json.njk +++ /dev/null @@ -1,29 +0,0 @@ ---- -# Metadata comes from _data/metadata.js -permalink: /feed/feed.json ---- -{ - "version": "https://jsonfeed.org/version/1.1", - "title": "{{ metadata.title }}", - "language": "{{ metadata.language }}", - "home_page_url": "{{ metadata.url | addPathPrefixToFullUrl }}", - "feed_url": "{{ permalink | htmlBaseUrl(metadata.url) }}", - "description": "{{ metadata.description }}", - "author": { - "name": "{{ metadata.author.name }}", - "url": "{{ metadata.author.url }}" - }, - "items": [ - {%- for post in collections.posts | reverse %} - {%- set absolutePostUrl = post.url | htmlBaseUrl(metadata.url) %} - { - "id": "{{ absolutePostUrl }}", - "url": "{{ absolutePostUrl }}", - "title": "{{ post.data.title }}", - "content_html": {% if post.templateContent %}{{ post.templateContent | transformWithHtmlBase(absolutePostUrl, post.url) | dump | safe }}{% else %}""{% endif %}, - "date_published": "{{ post.date | dateToRfc3339 }}" - } - {% if not loop.last %},{% endif %} - {%- endfor %} - ] -} diff --git a/content/works/works.11tydata.js b/content/works/works.11tydata.js index dde50e9..4d7b956 100644 --- a/content/works/works.11tydata.js +++ b/content/works/works.11tydata.js @@ -1,3 +1,3 @@ -module.exports = { +export default { "layout": "layouts/post.njk", }; diff --git a/css/index.css b/css/index.css new file mode 100644 index 0000000..c54a39a --- /dev/null +++ b/css/index.css @@ -0,0 +1,298 @@ +/* Defaults */ +:root { + --font-family: -apple-system, system-ui, sans-serif; + --font-family-monospace: Consolas, Menlo, Monaco, Andale Mono WT, Andale Mono, Lucida Console, Lucida Sans Typewriter, DejaVu Sans Mono, Bitstream Vera Sans Mono, Liberation Mono, Nimbus Mono L, Courier New, Courier, monospace; +} + +/* Theme colors */ +:root { + --color-gray-20: #e0e0e0; + --color-gray-50: #C0C0C0; + --color-gray-90: #333; + + --background-color: #fff; + + --text-color: var(--color-gray-90); + --text-color-link: #082840; + --text-color-link-active: #5f2b48; + --text-color-link-visited: #17050F; + + --syntax-tab-size: 2; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-gray-20: #e0e0e0; + --color-gray-50: #C0C0C0; + --color-gray-90: #dad8d8; + + /* --text-color is assigned to --color-gray-_ above */ + --text-color-link: #1493fb; + --text-color-link-active: #6969f7; + --text-color-link-visited: #a6a6f8; + + --background-color: #15202b; + } +} + + +/* Global stylesheet */ +* { + box-sizing: border-box; +} + +@view-transition { + navigation: auto; +} + +html, +body { + padding: 0; + margin: 0 auto; + font-family: var(--font-family); + color: var(--text-color); + background-color: var(--background-color); +} +html { + overflow-y: scroll; +} +body { + max-width: 40em; +} + +/* https://www.a11yproject.com/posts/how-to-hide-content/ */ +.visually-hidden:not(:focus):not(:active) { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +} + +/* Fluid images via https://www.zachleat.com/web/fluid-images/ */ +img{ + max-width: 100%; +} +img[width][height] { + height: auto; +} +img[src$=".svg"] { + width: 100%; + height: auto; + max-width: none; +} +video, +iframe { + width: 100%; + height: auto; +} +iframe { + aspect-ratio: 16/9; +} + +p:last-child { + margin-bottom: 0; +} +p { + line-height: 1.5; +} + +li { + line-height: 1.5; +} + +a[href] { + color: var(--text-color-link); +} +a[href]:visited { + color: var(--text-color-link-visited); +} +a[href]:hover, +a[href]:active { + color: var(--text-color-link-active); +} + +main, +footer { + padding: 1rem; +} +main :first-child { + margin-top: 0; +} + +header { + border-bottom: 1px dashed var(--color-gray-20); +} + +#skip-link { + text-decoration: none; + background: var(--background-color); + color: var(--text-color); + padding: 0.5rem 1rem; + border: 1px solid var(--color-gray-90); + border-radius: 2px; +} + +/* Prevent visually-hidden skip link fom pushing content around when focused */ +#skip-link.visually-hidden:focus { + position: absolute; + top: 1rem; + left: 1rem; + /* Ensure it is positioned on top of everything else when it is shown */ + z-index: 999; +} + +.links-nextprev { + display: flex; + justify-content: space-between; + gap: .5em 1em; + list-style: ""; + border-top: 1px dashed var(--color-gray-20); + padding: 1em 0; +} +.links-nextprev > * { + flex-grow: 1; +} +.links-nextprev-next { + text-align: right; +} + +table { + margin: 1em 0; +} +table td, +table th { + padding-right: 1em; +} + +pre, +code { + font-family: var(--font-family-monospace); +} +pre:not([class*="language-"]) { + margin: .5em 0; + line-height: 1.375; /* 22px /16 */ + -moz-tab-size: var(--syntax-tab-size); + -o-tab-size: var(--syntax-tab-size); + tab-size: var(--syntax-tab-size); + -webkit-hyphens: none; + -ms-hyphens: none; + hyphens: none; + direction: ltr; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + overflow-x: auto; +} +code { + word-break: break-all; +} + +/* Header */ +header { + display: flex; + gap: 1em; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + padding: 1em; +} +.home-link { + flex-grow: 1; + font-size: 1em; /* 16px /16 */ + font-weight: 700; +} +.home-link:link:not(:hover) { + text-decoration: none; +} + +/* Nav */ +.nav { + display: flex; + gap: .5em 1em; + padding: 0; + margin: 0; + list-style: none; +} +.nav-item { + display: inline-block; +} +.nav-item a[href]:not(:hover) { + text-decoration: none; +} +.nav a[href][aria-current="page"] { + text-decoration: underline; +} + +/* Posts list */ +.postlist { + counter-reset: start-from var(--postlist-index); + list-style: none; + padding: 0; + padding-left: 1.5rem; +} +.postlist-item { + display: flex; + flex-wrap: wrap; + align-items: baseline; + counter-increment: start-from -1; + margin-bottom: 1em; +} +.postlist-item:before { + display: inline-block; + pointer-events: none; + content: "" counter(start-from, decimal-leading-zero) ". "; + line-height: 100%; + text-align: right; + margin-left: -1.5rem; +} +.postlist-date, +.postlist-item:before { + font-size: 0.8125em; /* 13px /16 */ + color: var(--color-gray-90); +} +.postlist-date { + word-spacing: -0.5px; +} +.postlist-link { + font-size: 1.1875em; /* 19px /16 */ + font-weight: 700; + flex-basis: calc(100% - 1.5rem); + padding-left: .25em; + padding-right: .5em; + text-underline-position: from-font; + text-underline-offset: 0; + text-decoration-thickness: 1px; +} +.postlist-item-active .postlist-link { + font-weight: bold; +} + +/* Tags */ +.post-tag { + display: inline-flex; + align-items: center; + justify-content: center; + text-transform: capitalize; + font-style: italic; +} +.postlist-item > .post-tag { + align-self: center; +} + +/* Tags list */ +.post-metadata { + display: inline-flex; + flex-wrap: wrap; + gap: .5em; + list-style: none; + padding: 0; + margin: 0; +} +.post-metadata time { + margin-right: 1em; +} + diff --git a/css/message-box.css b/css/message-box.css new file mode 100644 index 0000000..a6e142e --- /dev/null +++ b/css/message-box.css @@ -0,0 +1,18 @@ +/* Message Box */ +.message-box { + --color-message-box: #ffc; + + display: block; + background-color: var(--color-message-box); + color: var(--color-gray-90); + padding: 1em 0.625em; /* 16px 10px /16 */ +} +.message-box ol { + margin-top: 0; +} + +@media (prefers-color-scheme: dark) { + .message-box { + --color-message-box: #082840; + } +} diff --git a/css/prism-diff.css b/css/prism-diff.css new file mode 100644 index 0000000..9f7e676 --- /dev/null +++ b/css/prism-diff.css @@ -0,0 +1,45 @@ +/* + * New diff- syntax + */ + +pre[class*="language-diff-"] { + --eleventy-code-padding: 1.25em; + padding-left: var(--eleventy-code-padding); + padding-right: var(--eleventy-code-padding); +} +.token.deleted { + background-color: hsl(0, 51%, 37%); + color: inherit; +} +.token.inserted { + background-color: hsl(126, 31%, 39%); + color: inherit; +} + +/* Make the + and - characters unselectable for copy/paste */ +.token.prefix.unchanged, +.token.prefix.inserted, +.token.prefix.deleted { + -webkit-user-select: none; + user-select: none; + display: inline-flex; + align-items: center; + justify-content: center; + padding-top: 2px; + padding-bottom: 2px; +} +.token.prefix.inserted, +.token.prefix.deleted { + width: var(--eleventy-code-padding); + background-color: rgba(0,0,0,.2); +} + +/* Optional: full-width background color */ +.token.inserted:not(.prefix), +.token.deleted:not(.prefix) { + display: block; + margin-left: calc(-1 * var(--eleventy-code-padding)); + margin-right: calc(-1 * var(--eleventy-code-padding)); + text-decoration: none; /* override del, ins, mark defaults */ + color: inherit; /* override del, ins, mark defaults */ +} diff --git a/eleventy.config.drafts.js b/eleventy.config.drafts.js deleted file mode 100644 index 8eb92dc..0000000 --- a/eleventy.config.drafts.js +++ /dev/null @@ -1,50 +0,0 @@ -function eleventyComputedPermalink() { - // When using `addGlobalData` and you *want* to return a function, you must nest functions like this. - // `addGlobalData` acts like a global data file and runs the top level function it receives. - return (data) => { - // Always skip during non-watch/serve builds - if(data.draft && !process.env.BUILD_DRAFTS) { - return false; - } - - return data.permalink; - } -}; - -function eleventyComputedExcludeFromCollections() { - // When using `addGlobalData` and you *want* to return a function, you must nest functions like this. - // `addGlobalData` acts like a global data file and runs the top level function it receives. - return (data) => { - // Always exclude from non-watch/serve builds - if(data.draft && !process.env.BUILD_DRAFTS) { - return true; - } - - return data.eleventyExcludeFromCollections; - } -}; - -module.exports.eleventyComputedPermalink = eleventyComputedPermalink; -module.exports.eleventyComputedExcludeFromCollections = eleventyComputedExcludeFromCollections; - -module.exports = eleventyConfig => { - eleventyConfig.addGlobalData("eleventyComputed.permalink", eleventyComputedPermalink); - eleventyConfig.addGlobalData("eleventyComputed.eleventyExcludeFromCollections", eleventyComputedExcludeFromCollections); - - let logged = false; - eleventyConfig.on("eleventy.before", ({runMode}) => { - let text = "Excluding"; - // Only show drafts in serve/watch modes - if(runMode === "serve" || runMode === "watch") { - process.env.BUILD_DRAFTS = true; - text = "Including"; - } - - // Only log once. - if(!logged) { - console.log( `[11ty/eleventy-base-blog] ${text} drafts.` ); - } - - logged = true; - }); -} diff --git a/eleventy.config.images.js b/eleventy.config.images.js deleted file mode 100644 index 32a0269..0000000 --- a/eleventy.config.images.js +++ /dev/null @@ -1,34 +0,0 @@ -const path = require("path"); -const eleventyImage = require("@11ty/eleventy-img"); - -module.exports = eleventyConfig => { - function relativeToInputPath(inputPath, relativeFilePath) { - let split = inputPath.split("/"); - split.pop(); - - return path.resolve(split.join(path.sep), relativeFilePath); - } - - // Eleventy Image shortcode - // https://www.11ty.dev/docs/plugins/image/ - eleventyConfig.addAsyncShortcode("image", async function imageShortcode(src, alt, widths, sizes) { - // Full list of formats here: https://www.11ty.dev/docs/plugins/image/#output-formats - // Warning: Avif can be resource-intensive so take care! - let formats = ["avif", "webp", "auto"]; - let file = relativeToInputPath(this.page.inputPath, src); - let metadata = await eleventyImage(file, { - widths: widths || ["auto"], - formats, - outputDir: path.join(eleventyConfig.dir.output, "img"), // Advanced usage note: `eleventyConfig.dir` works here because we’re using addPlugin. - }); - - // TODO loading=eager and fetchpriority=high - let imageAttributes = { - alt, - sizes, - loading: "lazy", - decoding: "async", - }; - return eleventyImage.generateHTML(metadata, imageAttributes); - }); -}; diff --git a/eleventy.config.js b/eleventy.config.js index dd1c1bc..aa9685f 100644 --- a/eleventy.config.js +++ b/eleventy.config.js @@ -1,106 +1,116 @@ -const { DateTime } = require("luxon"); -const markdownItAnchor = require("markdown-it-anchor"); -const footnote_plugin = require("markdown-it-footnote"); - -const pluginRss = require("@11ty/eleventy-plugin-rss"); -const pluginSyntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight"); -const pluginBundle = require("@11ty/eleventy-plugin-bundle"); -const pluginNavigation = require("@11ty/eleventy-navigation"); -const { EleventyHtmlBasePlugin } = require("@11ty/eleventy"); - -const pluginDrafts = require("./eleventy.config.drafts.js"); -const pluginImages = require("./eleventy.config.images.js"); +import { IdAttributePlugin, InputPathToUrlTransformPlugin, HtmlBasePlugin } from "@11ty/eleventy"; +import { feedPlugin } from "@11ty/eleventy-plugin-rss"; +import pluginSyntaxHighlight from "@11ty/eleventy-plugin-syntaxhighlight"; +import pluginNavigation from "@11ty/eleventy-navigation"; +import { eleventyImageTransformPlugin } from "@11ty/eleventy-img"; + +import pluginFilters from "./_config/filters.js"; + +/** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */ +export default async function(eleventyConfig) { + // Drafts, see also _data/eleventyDataSchema.js + eleventyConfig.addPreprocessor("drafts", "*", (data, content) => { + if (data.draft) { + data.title = `${data.title} (draft)`; + } -const { Client } = require("@notionhq/client"); -const { NotionToMarkdown } = require("notion-to-md"); -const fs = require('fs'); + if(data.draft && process.env.ELEVENTY_RUN_MODE === "build") { + return false; + } + }); -module.exports = function(eleventyConfig) { // Copy the contents of the `public` folder to the output folder // For example, `./public/css/` ends up in `_site/css/` - eleventyConfig.addPassthroughCopy({ - "./public/": "/", - "./node_modules/prismjs/themes/prism-okaidia.css": "/css/prism-okaidia.css" - }); + eleventyConfig + .addPassthroughCopy({ + "./public/": "/" + }) // Run Eleventy when these files change: // https://www.11ty.dev/docs/watch-serve/#add-your-own-watch-targets - // Watch content images for the image pipeline. - eleventyConfig.addWatchTarget("content/**/*.{svg,webp,png,jpeg}"); - - eleventyConfig.addPassthroughCopy("index.js"); + // Watch CSS files + eleventyConfig.addWatchTarget("css/**/*.css"); + // Watch images for the image pipeline. + eleventyConfig.addWatchTarget("content/**/*.{svg,webp,png,jpg,jpeg,gif}"); + + // Per-page bundles, see https://github.com/11ty/eleventy-plugin-bundle + // Bundle