diff --git a/src/posts/2023/semi-automated-hashtags-syndicated-posts.md b/src/posts/2023/semi-automated-hashtags-syndicated-posts.md new file mode 100644 index 00000000..7d53732c --- /dev/null +++ b/src/posts/2023/semi-automated-hashtags-syndicated-posts.md @@ -0,0 +1,116 @@ +--- +date: '2023-09-10' +title: 'Semi-automated hashtags for syndicated posts' +draft: false +tags: + - Eleventy + - development +--- +I went out on a limb recently and decided to build a custom collection in Eleventy that aggregates my post tags and link tags (sourced from [Matter](https://getmatter.com)). These tags then get appended to shared post or link titles when they're syndicated from my site. + +The collection I built sits in my `.eleventy.js` file and looks like this: + +```javascript +eleventyConfig.addCollection('tagMap', (collection) => { + const tags = {} + collection.getAll().forEach((item) => { + if (item.data.collections.posts) { + item.data.collections.posts.forEach((post) => { + const url = post.url.includes('http') ? post.url : `https://coryd.dev${post.url}` + const tagString = [...new Set(post.data.tags.map((tag) => tagAliases[tag.toLowerCase()]))] + .join(' ') + .trim() + if (tagString) tags[url] = tagString + }) + } + if (item.data.links) { + item.data.links.forEach((link) => { + const tagString = [...new Set(link.tags.map((tag) => tagAliases[tag.toLowerCase()]))] + .join(' ') + .trim() + if (tagString) tags[link.url] = tagString + }) + } + }) + return tags + }) +``` + +This works by iterating through the posts and global link data available from `collections.getAll()` and then mapping each tag string to the associated url in a `tags` object. The `tags` object is returned and made available as `tagMap`. + +Once the `tagMap` is available it's leveraged in a template that renders JSON feeds for my site — if `tagMap` is passed into the [LiquidJS](https://liquidjs.com/) render tag for the feed it's leveraged to help populate the title. + +**The template:** + +{% raw %} + +```liquid +{%- assign entries = data | normalizeEntries -%} +{ + "version": "https://jsonfeed.org/version/1", + "title": "{{ title }}", + "icon": "https://coryd.dev/static/images/avatar.webp", + "home_page_url": "{{ site.url }}", + "feed_url": "{{ site.url }}{{ permalink }}", + "items": [{% for entry in entries limit: 20 -%} + { + "id": "{{ entry.url | btoa }}", + "title": "{{ entry.title | escape }}", + "url": "{{ entry.url }}", + "content_text": "{{ entry.title | escape }}{% if tagMap %} {{ entry.url | tagLookup: tagMap }}{% endif %} {{ entry.url }}", + "date_published": "{{ entry.date | stringToDate | dateToRfc822 }}" + }{% if not forloop.last %},{% endif %} + {%- endfor %} + ] +} +``` + +{% endraw %} + +**Leveraging the template:** + +{% raw %} + +```liquid +{% render "partials/feeds/json.liquid" + permalink:'/feeds/follow' + title:'Follow • Cory Dransfeldt' + data:follow.posts + updated:follow.posts[0].date_published + site:site + tagMap:collections.tagMap +%} +``` + +{% endraw %} + +**The exceedingly simple `tagLookup` filter:** + +```javascript +tagLookup: (url, tagMap) => tagMap[url], +``` + +The static part of this whole setup is the `tagAliases` object referenced in my custom collection, which is a curated JSON object that maps tags I typically use between my blog and Matter to formatted hashtags to share and, as of writing, looks like this: + +```json +{ + "11ty": "#Eleventy", + "accessibility": "#Accessibility", + "development": "#WebDev", + "eleventy": "#Eleventy", + "email": "#Email", + "fastmail": "#Email", + "gmail": "#Email", + "javascript": "#JavaScript", + "last.fm": "#Music", + "lastfm": "#Music", + "music": "#Music", + "react": "#JavaScript", + "social media": "#Tech", + "spotify": "#Music", + "tech": "#Tech", + "technology": "#Tech" +} +``` + +So, with all of this in place, when my site builds it generates the `tagMap` collection, which is passed into my `json.liquid` feed template, that then looks up the matching tags via `tagLookup` before being syndicated using a GitHub action. The result is the same syndication that was in place before, but with a few additional tags to help with discovery.