<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>joshuaclanton.dev</title>
  <subtitle></subtitle>
  <link href="https://joshuaclanton.dev/feed.xml" rel="self"/>
  <link href="https://joshuaclanton.dev/"/>
  <updated>2024-05-09T00:00:00+00:00</updated>
  <id>https://joshuaclanton.dev/</id>
  <author>
    <name>Joshua Clanton</name>
    <email>joshua.clanton@gmail.com</email>
  </author>
  
  <entry>
    <title>Introducing recon - a CLI tool to gather context for LLMs</title>
    <link href="https://joshuaclanton.dev/blog/2024-05-09-introducing-recon/"/>
    <updated>2024-05-09T00:00:00+00:00</updated>
    <id>https://joshuaclanton.dev/blog/2024-05-09-introducing-recon/</id>
    <content type="html">&lt;h1&gt;Introducing recon - a CLI tool to gather context for LLMs&lt;/h1&gt;
&lt;p&gt;I use large language models like ChatGPT and Claude Opus a lot. No. More than that. A lot a lot.&lt;/p&gt;
&lt;p&gt;I use them on my coding side projects, on my writing projects, at my day job, as well as for personal things like organizing my D&amp;amp;D campaign.&lt;/p&gt;
&lt;p&gt;But one problem I&#39;m constantly running into is that there are things I&#39;d like to ask of an AI, but either there is a ton of data or it&#39;s spread all over the place. For example, if I&#39;m working on &lt;a href=&quot;https://rpgportrait.app/&quot;&gt;RPG Portrait&lt;/a&gt;, I may want to discuss growth rates and have it help me prioritize my tasks accordingly. But the growth rates are in a Postgres database, the tasks are in GitHub issues, and it probably needs a bit of the app&#39;s source code too.&lt;/p&gt;
&lt;p&gt;That&#39;s where &lt;a href=&quot;https://github.com/joshuacc/recon&quot;&gt;recon&lt;/a&gt; comes in.&lt;/p&gt;
&lt;h2&gt;The basics&lt;/h2&gt;
&lt;p&gt;Recon is a command-line tool that makes it easy to gather context from various sources and feed it to an LLM as a single, coherent prompt. With a simple command, you can recursively gather text from files and directories, fetch additional information from URLs, and pipe it all to your AI assistant of choice.&lt;/p&gt;
&lt;p&gt;Here&#39;s a simple example:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;recon --files ./src/landing-pages &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br&gt;  --urls https://example.com/rules-for-landing-page-copy &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br&gt;  --prompt &lt;span class=&quot;token string&quot;&gt;&quot;Evaluate our landing pages against these rules&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; llm&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Recursively read all files in the &lt;code&gt;./src/landing-pages&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Fetch the text from the URL &lt;code&gt;https://example.com/rules-for-landing-page-copy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Combine all the text (with their respective names) into a single prompt&lt;/li&gt;
&lt;li&gt;Pipe the prompt to the &lt;code&gt;llm&lt;/code&gt; command&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Side note: If you&#39;re not using Simon Willison&#39;s excellent &lt;a href=&quot;https://llm.datasette.io/en/stable/&quot;&gt;llm&lt;/a&gt; CLI tool, you probably should be.&lt;/p&gt;
&lt;p&gt;But even if you&#39;re not using &lt;code&gt;llm&lt;/code&gt;, you can still use recon to gather context and feed it to a model. It supports a &lt;code&gt;--clipboard&lt;/code&gt; command for when you want to paste the prompt into a web-based chat interface.&lt;/p&gt;
&lt;h2&gt;Getting fancy&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;--files&lt;/code&gt; and &lt;code&gt;--urls&lt;/code&gt; flags are handy for ad-hoc types of queries, but recon also supports a configuration file that allows you to define named sources and prompts. This makes it easy to reuse the same sources across multiple queries.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// .recon.config.mjs&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  commands&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// The key is the name of the command&lt;/span&gt;&lt;br&gt;    docs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      gather&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        files&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./docs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        urls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://example.com/some-more-docs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can run the following command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;recon docs &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br&gt;  --prompt &lt;span class=&quot;token string&quot;&gt;&quot;What&#39;s the procedure for multi-stage migrations?&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br&gt;  --clipboard&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Getting fanciest&lt;/h2&gt;
&lt;p&gt;Recon also supports defining your own custom &amp;quot;Recon agents&amp;quot; that can gather text from any source you can imagine. For example, you could write an agent that fetches text from a Google Sheet, a database, or even a proprietary API.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This is a hypothetical recon agent for querying a database&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; myDbAgent &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./myDbAgent&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  agents&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;myDbAgent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  commands&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    growth&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token comment&quot;&gt;// The default prompt for this command&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      prompt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;How are we doing on our growth goals?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      gather&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;// The default set of files to gather information from&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        files&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./docs/business-plan.md&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;// The default set of urls to gather information from&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        urls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://example.com/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;// This tells myDbAgent how to gather its information&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;        myDb&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;          query&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;SELECT COUNT(*) FROM users&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;// Additional notes to pass along with the prompt.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;// This is the built in `notes` agent&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        notes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;REMEMBER: All replies should be in business-speak.&lt;br&gt;                The more synergy, the better.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Future plans&lt;/h2&gt;
&lt;p&gt;I have a lot of ideas for improving recon. Some are obvious, like adding more built-in recon agents. One upcoming feature I&#39;m particularly excited about is letting custom agents register their own CLI flags. This would make the CLI&#39;s ad-hoc querying capabilities even more powerful.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;It&#39;s still early days for Recon, but I use it every day and it&#39;s already saved me a ton of time. I hope that it can do the same for some of you!&lt;/p&gt;
&lt;p&gt;If you&#39;re interested in trying it out or contributing, you can find it on &lt;a href=&quot;https://github.com/joshuacc/recon&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>How to Build an Eleventy Plugin</title>
    <link href="https://joshuaclanton.dev/blog/2020-10-06-how-to-build-an-eleventy-plugin/"/>
    <updated>2020-10-06T00:00:00+00:00</updated>
    <id>https://joshuaclanton.dev/blog/2020-10-06-how-to-build-an-eleventy-plugin/</id>
    <content type="html">&lt;h1&gt;How to Build an Eleventy Plugin&lt;/h1&gt;
&lt;p&gt;Eleventy is an increasingly popular static site generator, thanks to the fact that it is both very customizable, and comes with a good set of defaults to let you get up and running quickly. And, most relevant to us, it also supports a simple plugin system which allows sharing customizations with others.&lt;/p&gt;
&lt;p&gt;The heart of Eleventy&#39;s configurability lies in the optional &lt;code&gt;.eleventy.js&lt;/code&gt; file. It is there that you can override default configuration, add additional filters to your templating language, and change just about anything else. If you don&#39;t already have one, go ahead and create it.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&#39;ll note that we are exporting a function that takes in an &lt;code&gt;eleventyConfig&lt;/code&gt; object, and does nothing with it, leaving all the defaults in place.&lt;/p&gt;
&lt;p&gt;Suppose that as part of our site we want to include each article author&#39;s &lt;a href=&quot;http://gravatar.com/&quot;&gt;Gravatar&lt;/a&gt; image. To do that, we can add a custom filter to our configuration. Let&#39;s add the skeleton of that filter.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;gravatarUrl&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, the way that Gravatar works is by serving up an image named after the MD5 hash of the user&#39;s email address. For example, the image url of &lt;code&gt;foo@bar.com&lt;/code&gt; would be &lt;code&gt;https://www.gravatar.com/avatar/f3ada405ce890b6f8204094deb12d8a8&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We&#39;ll need to transform the author&#39;s email address into an MD5 hash, so let&#39;s install the &lt;code&gt;md5&lt;/code&gt; package with &lt;code&gt;npm install --dev md5&lt;/code&gt;. And then let&#39;s fill out the logic of the filter.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; md5 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;md5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;gravatarUrl&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;md5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://www.gravatar.com/avatar/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you&#39;ll have the &lt;code&gt;gravatarUrl&lt;/code&gt; filter available to use anywhere in your layouts. Here is an example of using it in the Liquid templating language.&lt;/p&gt;
&lt;pre class=&quot;language-liquid&quot;&gt;&lt;code class=&quot;language-liquid&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;img src&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&quot;{{ authorEmail &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; gravatarUrl }}&quot; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&#39;ve got a working filter, but suppose that we&#39;re actually building a bunch of sites with Eleventy, and we&#39;re going to need this functionality again. We could just copy and paste the code everywhere. But we could also turn it into a plugin and publish it to npm so that others can easily use it.&lt;/p&gt;
&lt;p&gt;Let&#39;s make the code change to convert it into a plugin.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; md5 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;md5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;gravatarPlugin&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;gravatarUrl&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;md5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://www.gravatar.com/avatar/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gravatarPlugin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&#39;ll see that instead of directly calling &lt;code&gt;addFilter&lt;/code&gt; in our configuration function, we define a plugin function on line 3. That function takes in the Eleventy config object and does the work of adding the filter. And then on line 11 we add the plugin. At this point all we need to do is extract the function into a separate package.&lt;/p&gt;
&lt;p&gt;So let&#39;s create a new directory for our package. Maybe something like &lt;code&gt;eleventy-plugin-gravatar&lt;/code&gt;. Then we&#39;ll &lt;code&gt;npm init&lt;/code&gt;. Next we create an &lt;code&gt;index.js&lt;/code&gt; file to copy our plugin into. Make sure that we install &lt;code&gt;md5&lt;/code&gt; as a dependency of our plugin package: &lt;code&gt;npm install md5&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We update our new packages &lt;code&gt;index.js&lt;/code&gt; to the following:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; md5 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;md5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;gravatarUrl&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;md5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://www.gravatar.com/avatar/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we update our &lt;code&gt;.eleventy.js&lt;/code&gt; file to the following:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; gravatarPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;eleventy-plugin-gravatar&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gravatarPlugin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test it out locally, in the plugin directory, type &lt;code&gt;npm link&lt;/code&gt;. And then in your Eleventy site&#39;s directory, run &lt;code&gt;npm link eleventy-plugin-gravatar&lt;/code&gt;. Start your Eleventy dev server up and you should see everything working.&lt;/p&gt;
&lt;p&gt;At this point, all you need to do is publish the package to npm. For detailed instructions on that, see the &lt;a href=&quot;https://docs.npmjs.com/creating-and-publishing-unscoped-public-packages&quot;&gt;npm documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Congratulations! If you made it this far, you&#39;ve created your own plugin, made it into it&#39;s own package, and maybe even published it.&lt;/p&gt;
&lt;p&gt;Like this post? &lt;a href=&quot;https://joshuaclanton.dev/feed.xml&quot;&gt;Subscribe to get the next one in your feed reader!&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>A New Site and a New Experiment - Studying in Public</title>
    <link href="https://joshuaclanton.dev/blog/2020-09-29-a-new-site-a-new-experiment-learning-in-public/"/>
    <updated>2020-09-29T00:00:00+00:00</updated>
    <id>https://joshuaclanton.dev/blog/2020-09-29-a-new-site-a-new-experiment-learning-in-public/</id>
    <content type="html">&lt;h1&gt;A New Site and an Experiment with Studying in Public&lt;/h1&gt;
&lt;p&gt;It has been quite a while since I hand-crafted a site for myself. The last time was when I built &lt;a href=&quot;https://adripofjavascript.com/&quot;&gt;A Drip of JavaScript&lt;/a&gt; way back in 2015. I&#39;ve been doing front-end coding non-stop since then, but exclusively for my day job.&lt;/p&gt;
&lt;p&gt;My goals for the site were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easy authoring&lt;/li&gt;
&lt;li&gt;Easy publishing&lt;/li&gt;
&lt;li&gt;A sophisticated visual style that uses typography to good effect&lt;/li&gt;
&lt;li&gt;To make it easier to study in public - More on that later.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Authoring&lt;/h2&gt;
&lt;p&gt;For authoring, I chose &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt;. I had heard good things about how it was easy to use and also easy to customize. I found these to be mostly true. The one thing that tripped me up was figuring out which templating language to use. Because Eleventy supports several templating languages out of the box, it took me some time to settle on one which I found intuitive (&lt;a href=&quot;https://mozilla.github.io/nunjucks/&quot;&gt;Nunjucks&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Once I was past that, the docs and the occasional Google search were more than sufficient for me to work through everything else quickly. Caveat: Someone new to front-end development could easily get lost in all the options.&lt;/p&gt;
&lt;h2&gt;Publishing&lt;/h2&gt;
&lt;p&gt;For publishing, I went with &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt;. Their reputation for ease of use was great. And it wasn&#39;t an exaggeration. After creating an account and connecting it with my domain name and GitHub repository, Netlify automatically detected that I was using Eleventy and suggest default publishing options, which I accepted. About one minute later, the first version of the site was live. ffffadflajadflkajf&lt;/p&gt;
&lt;h2&gt;Design&lt;/h2&gt;
&lt;p&gt;I used &lt;a href=&quot;https://edwardtufte.github.io/tufte-css/&quot;&gt;Tufte CSS&lt;/a&gt; as the basis for the visual design, but customized it pretty extensively.&lt;/p&gt;
&lt;p&gt;The most obvious customization is in the typography. The  font size is substantially smaller to provide better information density. And rather than using Tufte CSS&#39;s very traditional serif fonts, I opted to use Mozilla&#39;s Open Source &lt;a href=&quot;https://github.com/mozilla/Fira&quot;&gt;Fira Sans&lt;/a&gt;, augmented by &lt;a href=&quot;https://github.com/tonsky/FiraCode&quot;&gt;Fira Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The result, I hope, is a design that balances appearing both very traditonal and very modern.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Beauty of style and harmony and grace and good rhythm depend on simplicity&lt;/p&gt;
&lt;footer&gt;Plato &amp;mdash; &lt;i&gt;Republic&lt;/i&gt;, Book 3&lt;/footer&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Studying in Public&lt;/h2&gt;
&lt;p&gt;My final goal was to make it easier for me to share the results of my studies. I&#39;m constantly reading and studying, but haven&#39;t done much sharing in a while.&lt;/p&gt;
&lt;p&gt;I have done it in the past with blog posts and the Drip of JavaScript newsletter. But it takes a huge amount of effort that to convert the raw materials of my studies into something polished enough to present as a cohesive article.&lt;/p&gt;
&lt;p&gt;So instead, I&#39;m doing something new. I&#39;m taking the things I&#39;m learning and writing them down in a &lt;a href=&quot;https://foambubble.github.io/foam/&quot;&gt;Foam&lt;/a&gt; notebook. &lt;a href=&quot;https://joshuaclanton.dev/notebook/intro&quot;&gt;My notes&lt;/a&gt; are rough, unfinished, and probably wrong occasionally. But I can publish them without thinking too hard. And I suspect that&#39;s the most important thing.&lt;/p&gt;
&lt;p&gt;I hope that others find it helpful, but I also know that I&#39;m going to enjoy having an easy place to find my notes on the go.&lt;/p&gt;
&lt;p&gt;Here&#39;s to a new site and to trying new things!&lt;/p&gt;
</content>
  </entry>
</feed>