<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://seanmcp.com/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>seanmcp.com</title>
    <link>https://seanmcp.com/</link>
    <atom:link href="https://seanmcp.com/rss.xml" rel="self" type="application/rss+xml" />
    <description>The online home of Sean McPherson, an unremarkable man from Pittsburgh, PA</description>
    <language>en</language>
    <item>
      <title>Defaults 2026</title>
      <link>https://seanmcp.com/articles/defaults-2026/</link>
      <description>&lt;ul&gt;
&lt;li&gt;Mail Client
&lt;ul&gt;
&lt;li&gt;Gmail app and Zoho app&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Mail Server
&lt;ul&gt;
&lt;li&gt;Gmail for personal email&lt;/li&gt;
&lt;li&gt;Zoho for professional email: I&#39;ve tried other providers for &lt;code&gt;@seanmcp.com&lt;/code&gt;
but keep coming back to Zoho. It&#39;s cheap, customizable, and provides all the
features that I want.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Notes
&lt;ul&gt;
&lt;li&gt;A private repo with shell scripts. Mobile notes are written in Google Keep
and manually backed up.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;To-Do
&lt;ul&gt;
&lt;li&gt;Todoist: I don&#39;t use most of the features, but I love the keyboard shortcut
on Mac to quickly add a TODO.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Phone Photo Shooting:
&lt;ul&gt;
&lt;li&gt;Default Android app&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Photo Management
&lt;ul&gt;
&lt;li&gt;Google Photos (though I&#39;m sad they removed the free tier for photo storage)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Calendar
&lt;ul&gt;
&lt;li&gt;Google Calendar: My wife and I have personal calendars and then a shared
family calendar.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cloud File Storage
&lt;ul&gt;
&lt;li&gt;Google Drive and Git&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;RSS
&lt;ul&gt;
&lt;li&gt;Feedbin: I could probably get by on the free tier of another service, but
Feedbin is so nice and simple. I don&#39;t have to think about it when reading,
and I&#39;m not being upsold at every opportunity.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Contacts
&lt;ul&gt;
&lt;li&gt;Google Contacts: It recently added a new feature to add birthdays to the
calendar and give you a notification ahead of time. I almost built an app
that would do the same, and I&#39;m glad it&#39;s now a feature.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Browser
&lt;ul&gt;
&lt;li&gt;Microsoft Edge: Chromium-based with vertical tabs and the ability to remove
most features.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Chat
&lt;ul&gt;
&lt;li&gt;Text or WhatsApp&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Bookmarks
&lt;ul&gt;
&lt;li&gt;A private repo with shell scripts. I migrated from Raindrop last Fall, and
it has been smooth enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Read It Later:
&lt;ul&gt;
&lt;li&gt;I leave things unread in Feedbin&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Word Processing
&lt;ul&gt;
&lt;li&gt;Google Docs and VS Code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Spreadsheets
&lt;ul&gt;
&lt;li&gt;Google Sheets&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Presentations
&lt;ul&gt;
&lt;li&gt;Google Slides: I&#39;ve tried code-based solutions, but it generally leads to
more tinkering and less preparation. I use all the default layouts and then
tweak the design once I&#39;m finished.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Shopping Lists
&lt;ul&gt;
&lt;li&gt;Google Keep: A note for groceries and another for shopping that are shared
with my wife&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Meal Planning
&lt;ul&gt;
&lt;li&gt;Google Keep (though this is mostly handled by my wife)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Budgeting and Personal Finance
&lt;ul&gt;
&lt;li&gt;A Google Sheet that is updated 1-2 times per year.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;News
&lt;ul&gt;
&lt;li&gt;National: AllSides, the Atlantic&lt;/li&gt;
&lt;li&gt;Local: Pittsburgh Post-Gazette, PublicSource, and Spotlight PA&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Music
&lt;ul&gt;
&lt;li&gt;YouTube Music: It&#39;s included with a YouTube Premium subscription and has
access to the all the music I want.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Podcasts
&lt;ul&gt;
&lt;li&gt;Pocket Casts: I like that it is focused on being a podcast player. I&#39;ve
tried other all-in-one options and keep going back to Pocket Casts. I&#39;ve
listened to 94 days and 22 hours since 2016.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Password Management
&lt;ul&gt;
&lt;li&gt;Bitwarden&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Code
&lt;ul&gt;
&lt;li&gt;VS Code: I&#39;ve tried Zed and Cursor as potential replacements, but neither of
them provide the right features with the right form.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Terminal
&lt;ul&gt;
&lt;li&gt;Terminal (Ubuntu): Not bad enough to look for alternatives&lt;/li&gt;
&lt;li&gt;iTerm2 (Mac): I like that it isn&#39;t trying to be anything other than a
terminal. I used Warp for a while but it became clear that their product
roadmap was not in the terminal space.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Search
&lt;ul&gt;
&lt;li&gt;DuckDuckGo: I&#39;m skeptical of AI summaries, and DDG is the only provider I&#39;ve
found that allows you to disable them. That said: the results aren&#39;t great.
If you have an alternative, please let me know.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Wed, 28 Jan 2026 13:48:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/defaults-2026/</guid>
    </item>
    <item>
      <title>AI use case: Asking questions of large files</title>
      <link>https://seanmcp.com/articles/ai-use-case-asking-questions-of-large-files/</link>
      <description>&lt;p&gt;Khan Academy
&lt;a href=&quot;https://blog.khanacademy.org/genqlient-a-truly-type-safe-go-graphql-client/&quot;&gt;uses GraphQL&lt;/a&gt;
to handle all request to query and mutate data. Each service in the system
contributes to the GraphQL schema, and everything is ultimately combined into a
single file. If a developer wants to know what our API can do, the answer is in
that file.&lt;/p&gt;
&lt;p&gt;The problem is that &lt;strong&gt;it&#39;s over 80,000 lines long&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Most of the time you can get by searching the file for keywords. If you&#39;re
interested in the user or classrooms or a specific feature, you can find
breadcrumbs that lead you to the answer. But sometimes you&#39;re dealing with
unfamiliar terms or features old enough to have been renamed over the past
decade. File search alone won&#39;t always get the job done.&lt;/p&gt;
&lt;p&gt;Yesterday I was in this situation and turned to Copilot in VS Code. I switched
it to &amp;quot;Ask&amp;quot; mode and entered my query: &amp;quot;How can I determine if a user is in a
given district?&amp;quot; The model interpreted by question, read the file in chunks, and
then returned an answer.&lt;/p&gt;
&lt;p&gt;Of the options provided, only one included the exact terms that I had in my
query. After inspecting the response, I found that the best option used an old
term that we don&#39;t use anymore and I&#39;ve never heard. I would not have found that
by searching the file by myself. Uncovering the answer was only possible with an
LLM.&lt;/p&gt;
&lt;p&gt;I&#39;m generally skeptical of using AI to write production code that matters. But
as a search tool, I&#39;m increasingly convinced that they are a powerful addition
to a developer&#39;s toolbox. When file search can&#39;t surface the answer, the ability
to ask questions of large files with an LLM is a valuable resource.&lt;/p&gt;
</description>
      <pubDate>Wed, 07 Jan 2026 12:50:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/ai-use-case-asking-questions-of-large-files/</guid>
    </item>
    <item>
      <title>Install curl with apt (and not snap)</title>
      <link>https://seanmcp.com/articles/install-curl-with-apt-and-not-snap/</link>
      <description>&lt;p&gt;After switching my desktop to Linux from Windows&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/install-curl-with-apt-and-not-snap/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, I ran into an issue installing Node. The official directions recommend using &lt;code&gt;nvm&lt;/code&gt;, but the &lt;code&gt;nvm install 24&lt;/code&gt; command was failing with a lack of permissions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Downloading and installing node v24.6.0...
Downloading https://nodejs.org/dist/v24.6.0/node-v24.6.0-linux-x64.tar.xz...
Warning: Failed to open the file /home/seanmcp/.nvm/.cache/bin/node-v24.6.0-linux-x64/node-v24.6.0-linux-x64.tar.xz: Permission denied
curl: (23) client returned ERROR on write of 1360 bytes

download from https://nodejs.org/dist/v24.6.0/node-v24.6.0-linux-x64.tar.xz failed
grep: /home/seanmcp/.nvm/.cache/bin/node-v24.6.0-linux-x64/node-v24.6.0-linux-x64.tar.xz: No such file or directory
Provided file to checksum does not exist.
Binary download failed, trying source.
Downloading https://nodejs.org/dist/v24.6.0/node-v24.6.0.tar.xz...

Warning: Failed to open the file /home/seanmcp/.nvm/.cache/src/node-v24.6.0/node-v24.6.0.tar.xz: Permission denied
curl: (23) client returned ERROR on write of 1360 bytes

download from https://nodejs.org/dist/v24.6.0/node-v24.6.0.tar.xz failed
grep: /home/seanmcp/.nvm/.cache/src/node-v24.6.0/node-v24.6.0.tar.xz: No such file or directory
Provided file to checksum does not exist.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The permissions in the &lt;code&gt;.nvm/.cache/&lt;/code&gt; directory looked fine, &lt;a href=&quot;https://xkcd.com/149/&quot;&gt;&lt;code&gt;sudo&lt;/code&gt; didn&#39;t work&lt;/a&gt;, and Claude Sonnet 4 wasn&#39;t helpful. I went searching online and found &lt;a href=&quot;https://askubuntu.com/a/1489138&quot;&gt;a question and answer from three years ago&lt;/a&gt; that recommended:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Uninstalling &lt;code&gt;curl&lt;/code&gt; with snap: &lt;code&gt;sudo snap remove curl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Reinstalling &lt;code&gt;curl&lt;/code&gt; with apt: &lt;code&gt;sudo apt install curl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Running the nvm/Node install commands again&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Doing that fixed the issue for me.&lt;/p&gt;
&lt;p&gt;I would have never considered &lt;code&gt;curl&lt;/code&gt; as possible source of my issues. So I&#39;m thankful that questions/answers like that exist on the internet.&lt;/p&gt;
&lt;p&gt;I think I need to learn about the differences between apt and snap, and why you would choose one versus the other. If you have a good resources for that, please let me know.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;My build was incompatible and Windows 11, and I didn&#39;t want to upgrade to use an operating system that I am ambivalent about. &lt;a href=&quot;https://seanmcp.com/articles/install-curl-with-apt-and-not-snap/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Mon, 25 Aug 2025 11:50:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/install-curl-with-apt-and-not-snap/</guid>
    </item>
    <item>
      <title>Four perspectives on AI</title>
      <link>https://seanmcp.com/articles/four-perspectives-on-ai/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://frontendatscale.com/issues/46&quot;&gt;Maxi Ferreira writes in &lt;em&gt;Fear and Curiosity and AI&lt;/em&gt;&lt;/a&gt;
about the two stages of AI adoption among developers:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A Stage 1 reaction looks something like this: “Ha! Look at this dumb mistake
the AI made. It thought I was using Tailwind when I’m actually using Style
Components. More like Artificial Unintelligence, am I right? Good luck taking
my job, bots.”&lt;/p&gt;
&lt;p&gt;A Stage 2 reaction looks more like this: “Hmm, I wonder why the AI got
confused there. Maybe some of my classes in this component look like Tailwind
classes. How can I prevent this from happening in the future? Let me add a
Cursor Rule to tell it I’m actually using Styled Components in this project.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The naming suggestions a progression. Developers start at the defensive/fearful
Stage 1 but are able to progress to Stage 2 with a &amp;quot;curiosity mindset&amp;quot;.&lt;/p&gt;
&lt;p&gt;I agree with Ferreira about the two reactions. I feel like I&#39;ve heard them both
near verbatim while talking about AI with colleagues. And I agree that curiosity
and a growth mindset are invaluable in our field and life broadly. But the
framing of &amp;quot;Stage 1/Stage 2&amp;quot; and the motivation behind them is uncharitable (at
best) and unhelpful (at worst).&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/four-perspectives-on-ai/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The difference between the two reactions depends not on time and mindset but on
an individual&#39;s belief of AI&#39;s transformative potential and positive impact.&lt;/p&gt;
&lt;img src=&quot;https://seanmcp.com/img/beliefs-about-ai.png&quot; alt=&quot;A two-axis graph labeled &#39;Beliefs about AI&#39; with the x-axis of &#39;Transformative&#39; and the y-axis of &#39;Positive&#39;. The field is divided into four quadrants: &#39;Skeptical&#39; in the bottom-left, &#39;Wary&#39; in the bottom-right, &#39;Pragmatist&#39; in the top-left, and &#39;Optimist&#39; in the top-right.&quot;&gt;
&lt;p&gt;The &lt;strong&gt;Skeptical&lt;/strong&gt; see AI&#39;s capabilities and limitations. They question the
imminent advent of AGI and whether future frontier models will have a
significant impact on the world in the long-term. They don&#39;t see the
technological developments of AI as a net positive. They&#39;ll use phrases like
&amp;quot;over-hyped&amp;quot; and &amp;quot;flash in the pan&amp;quot;. They are unlikely to experiment with AI
tools. When they see the output of any-given model, they tend to have Reaction 1
because criticism illustrates their beliefs.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Wary&lt;/strong&gt; also see AI&#39;s capabilities and limitations but believe that
developments in the field will result in dramatic changes to our work and lives.
They are concerned that the advancement of this technology will come at severe
cost to other aspects of our world. They&#39;ll have a &amp;quot;p(doom)&amp;quot; number, raise
concerns about safety, and cite environmental or societal detriments of AI. They
may have Reaction 1 but are more likely to avoid AI entirely and encourage
others to do the same.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Pragmatic&lt;/strong&gt; aren&#39;t swayed by arguments for or projections of AGI. They see
AI as a tool to help them with tasks today. They&#39;ll experiment with prompts and
models but will quickly move on when it isn&#39;t working. They&#39;ll mention
&amp;quot;usefulness&amp;quot;, &amp;quot;utility&amp;quot;, and &amp;quot;productivity&amp;quot;. They spend little time thinking
about AI beyond a specific problem and solution. They may have Reaction 2,
though there will be a reasonable time box for how long they explore.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Optimistic&lt;/strong&gt; see the potential in AI&#39;s capabilities and trust that the
limitations will eventually be overcome. They believe that future models will
radically transform our world for the better citing problems to be solved in
medicine and the environment. They&#39;ll encourage others to learn how to use AI so
that they &amp;quot;don&#39;t get left behind&amp;quot;. They tend to have Reaction 2 and will push
the boundaries of what is capable with the current state of AI.&lt;/p&gt;
&lt;p&gt;I&#39;ve grouped these four perspectives, but everything here is a spectrum.
Depending on the context or day, you might find yourself at any point on the
graph. And I&#39;ve attempted to describe each perspectively generously, because I
don&#39;t believe that any are inherently good or bad. I find myself switching
between perspectives throughout the day as I implement features, use tools, and
read articles. A good team is probably made of members from all perspectives.&lt;/p&gt;
&lt;p&gt;Which perspective resonates with you today? Do you also find yourself moving
around the graph?
&lt;a href=&quot;mailto:sean@seanmcp.com?subject=RE:%20Four%20perspectives%20on%20AI&quot;&gt;I would love to hear your thoughts&lt;/a&gt;,
as I&#39;m sure mine will continue to develop over time.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Despite my disagreement with Ferreira, I still
recommend &lt;a href=&quot;https://frontendatscale.com/&quot;&gt;his newsletter&lt;/a&gt;. It has helpful
insights and interesting links that I&#39;m happy to see in my inbox. &lt;a href=&quot;https://seanmcp.com/articles/four-perspectives-on-ai/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Wed, 20 Aug 2025 10:24:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/four-perspectives-on-ai/</guid>
    </item>
    <item>
      <title>Axe DevTools doesn&#39;t flag multiple h1 elements on a page</title>
      <link>https://seanmcp.com/articles/axe-devtools-doesnt-flag-multiple-h1-elements-on-a-page/</link>
      <description>&lt;p&gt;The
&lt;a href=&quot;https://www.deque.com/get-started-axe-devtools-browser-extension/&quot;&gt;axe DevTools extension&lt;/a&gt;
is valuable for testing the quality of your work. I use it multiple times per
day when working on frontend tasks. So I was surprised today when I learned that
axe does not consider multiple &lt;code&gt;h1&lt;/code&gt; elements on a page an issue.&lt;/p&gt;
&lt;p&gt;It turns out that this is intentional. There is no rule stating that pages
cannot have more than one &lt;code&gt;h1&lt;/code&gt;–even if it is widely considered best
practice&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/axe-devtools-doesnt-flag-multiple-h1-elements-on-a-page/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.
&lt;a href=&quot;https://www.a11yproject.com/posts/how-to-accessible-heading-structure/#one-h1&quot;&gt;The A11y Project has a nice write up on the topic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Even though axe doesn&#39;t consider it an issue, I still want to avoid rendering
more than one &lt;code&gt;h1&lt;/code&gt; on a page.
&lt;a href=&quot;https://gist.github.com/SeanMcP/ed0ad9685b0a7aa78b457f4301fb37de&quot;&gt;I previously wrote a little script to print all of a webpage&#39;s headings to the console&lt;/a&gt;,
and I wanted to make that as easy to use as axe. Thankfully we can do that with
&lt;a href=&quot;https://developer.chrome.com/docs/devtools/javascript/snippets&quot;&gt;Chromium&#39;s snippets API&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/axe-devtools-doesnt-flag-multiple-h1-elements-on-a-page/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the browser devtools&lt;/li&gt;
&lt;li&gt;Select the &amp;quot;Sources&amp;quot; panel&lt;/li&gt;
&lt;li&gt;In the left-hand panel, select the &amp;quot;Snippets&amp;quot; tab (it might be hidden in the
caret menu)&lt;/li&gt;
&lt;li&gt;Select &amp;quot;New snippet&amp;quot;&lt;/li&gt;
&lt;li&gt;Name it something like &amp;quot;Print headings&amp;quot;&lt;/li&gt;
&lt;li&gt;Paste the code from my gist:&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;document
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;h1,h2,h3,h4,h5,h6,[role=heading][aria-level]&quot;&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 function&quot;&gt;forEach&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;node&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 class=&quot;token keyword&quot;&gt;let&lt;/span&gt; level &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&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 keyword&quot;&gt;let&lt;/span&gt; prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&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 interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;prefix&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;level&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent&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 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;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Save the snippet&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can run snippets from the Sources panel, but the faster way is to use the
keyboard shortcut:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the browser devtools, press &lt;kbd&gt;ctrl/cmd&lt;/kbd&gt;+&lt;kbd&gt;o&lt;/kbd&gt; to open the
command menu&lt;/li&gt;
&lt;li&gt;Type &amp;quot;!&amp;quot; to bring up the list of snippets&lt;/li&gt;
&lt;li&gt;Select your snippet from the list&lt;/li&gt;
&lt;li&gt;Press &lt;kbd&gt;enter/return&lt;/kbd&gt; to run it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The snippet will execute and print all of the headings on the page to the
console.&lt;/p&gt;
&lt;p&gt;You could do more in your version of the snippet like validating the existence
of a single &lt;code&gt;h1&lt;/code&gt; or the heading order. For me, having it print the headings is
good enough to spot check my work.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Axe&#39;s has a &amp;quot;Best Practices&amp;quot; option, but that doesn&#39;t flag it
either. &lt;a href=&quot;https://seanmcp.com/articles/axe-devtools-doesnt-flag-multiple-h1-elements-on-a-page/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I
couldn&#39;t find an equivalent for Firefox (and didn&#39;t check Safari). If I&#39;ve
missed it, let me know and I&#39;ll update this article to include information for
other browsers. &lt;a href=&quot;https://seanmcp.com/articles/axe-devtools-doesnt-flag-multiple-h1-elements-on-a-page/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Wed, 16 Jul 2025 18:40:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/axe-devtools-doesnt-flag-multiple-h1-elements-on-a-page/</guid>
    </item>
    <item>
      <title>Writing code was never the bottleneck in software engineering</title>
      <link>https://seanmcp.com/articles/writing-code-was-never-the-bottleneck-in-software-engineering/</link>
      <description>&lt;p&gt;&lt;em&gt;Writing Code Was Never the Bottleneck&lt;/em&gt; by Pedro Tavares makes an important
observation about using LLMs when writing software. The problem as stated by the
solution is that writing code is primarily the bottleneck in development. &amp;quot;If
only we could write code faster, then we&#39;d be able to ship more features.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ordep.dev/posts/writing-code-was-never-the-bottleneck&quot;&gt;Tavares challenges that presupposition&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The actual bottlenecks were, and still are, code reviews, knowledge transfer
through mentoring and pairing, testing, debugging, and the human overhead of
coordination and communication. All of this wrapped inside the labyrinth of
tickets, planning meetings, and agile rituals.&lt;br&gt;&lt;br&gt;These processes, meant
to drive quality, often slow us down more than the act of writing code itself
because they require thought, shared understanding, and sound
judgment.&lt;br&gt;&lt;br&gt;...&lt;br&gt;&lt;br&gt; The marginal cost of adding new software is
approaching zero, especially with LLMs. But what is the price of
understanding, testing, and trusting that code? Higher than ever.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When engineering software for an organization, writing code is rarely the hard
part. It&#39;s everything that precedes and follows writing code that slows you
down: gathering requirements, exploring existing patterns, reviewing pull
requests, testing solutions, sharing knowledge. LLMs can supercharge the
generation of code, but they make the rest of the process slower.&lt;/p&gt;
&lt;p&gt;Judicious use can result in modest productivity improvements. But stray too far
and suddenly you&#39;re spending more time to assure quality or waving it away with
&amp;quot;vibes&amp;quot;.&lt;/p&gt;
&lt;p&gt;Tavares&#39; observation also explains why using LLMs in greenfield or prototype
environments feels so different. In those situations, writing code &lt;em&gt;is&lt;/em&gt; the
bottleneck and generating code with an LLM blows it wide open. The tech debt
created by using LLM code takes time to be noticed and may never come up (&lt;em&gt;e.g.&lt;/em&gt;
sacrificial prototypes).&lt;/p&gt;
&lt;p&gt;But in a software-development environment, the equation changes. Requirements,
understanding, reviews, and testing take priority. In that world, generating
huge swaths of code with LLMs only exacerbates the true bottlenecks of software
engineering. The craft becomes learning how to use them well without creating
more problems for your team down the road.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Hat tip to &lt;a href=&quot;https://cassidoo.co/newsletter/&quot;&gt;Rendezvous with Cassidoo&lt;/a&gt; for
sharing the article.&lt;/p&gt;
</description>
      <pubDate>Wed, 09 Jul 2025 11:05:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/writing-code-was-never-the-bottleneck-in-software-engineering/</guid>
    </item>
    <item>
      <title>Colocate functionally-related code</title>
      <link>https://seanmcp.com/articles/colocate-functionally-related-code/</link>
      <description>&lt;p&gt;I read &lt;a href=&quot;https://kentcdodds.com/blog/colocation&quot;&gt;&lt;em&gt;Colocation&lt;/em&gt; by Kent C. Dodds&lt;/a&gt;
back in 2019 before I had an opinion on the topic. Six years later, I&#39;ve come to
a similar conclusion. Generally speaking, we should keep related code as close
together as possible.&lt;/p&gt;
&lt;p&gt;Common related code in my work are components, styles, tests, and stories. My
preference is to keep all of these files together in the same directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;components/
    ├── my-component.tsx
    ├── my-component.css
    ├── my-component.test.tsx
    └── my-component.stories.tsx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A change to one will often require a change to one or more others. By keeping
the code together in your file system, you both remind yourself of that fact and
make it easier to find the other files that need to change.&lt;/p&gt;
&lt;p&gt;The major alternative pattern I see are dedicated directories for each
&amp;quot;concern&amp;quot;: &lt;code&gt;__tests__&lt;/code&gt;, &lt;code&gt;__stories__&lt;/code&gt;, &lt;em&gt;etc&lt;/em&gt;. By one definition of related, this
is a method of colocating related code. Everything in those directories is
related to the same domain and has some things in common.&lt;/p&gt;
&lt;p&gt;But rarely do I need to update &amp;quot;all test files.&amp;quot; And when I do the filename
pattern is sufficient to find them. In contrast, every day I need to update
functionally-related code. So I want my organization system to prioritize the
work that occurs most frequently.&lt;/p&gt;
&lt;p&gt;Colocate all of your functionally-related code.&lt;/p&gt;
</description>
      <pubDate>Mon, 30 Jun 2025 18:30:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/colocate-functionally-related-code/</guid>
    </item>
    <item>
      <title>Maccy is a great clipboard manager for Mac</title>
      <link>https://seanmcp.com/articles/maccy-is-a-great-clipboard-manager-for-mac/</link>
      <description>&lt;p&gt;I don&#39;t where I saw a recommendation for &lt;a href=&quot;https://maccy.app/&quot;&gt;Maccy&lt;/a&gt;, but I owe
them a big thank you. It&#39;s a simple clipboard manager for Mac that takes the
headache out of copying and pasting.&lt;/p&gt;
&lt;p&gt;Once it&#39;s installed, use copy and paste like normal. But when you want to dig
into your clipboard history, you can open Maccy with the keyboard shortcut. Mine
is:&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;
&lt;kbd&gt;cmd&lt;/kbd&gt; + &lt;kbd&gt;shift&lt;/kbd&gt; + &lt;kbd&gt;c&lt;/kbd&gt;
&lt;/p&gt;
&lt;p&gt;A pop up shows you a searchable list of items that you&#39;ve copied in
reverse-chronological order. Selecting an item adds it back to your clipboard.&lt;/p&gt;
&lt;p&gt;Maccy also remembers images, which is useful when taking screenshots of your
work. Snap a few pictures and then retrieve them from Maccy when filling out
your pull request or Slack message. It&#39;s handy when managing multiple git
branches too. I can quickly retrieve a branch from memory when pulling, merging,
or rebasing.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://9to5mac.com/2025/06/10/macos-26-spotlight-gets-actions-clipboard-manager-custom-shortcuts/&quot;&gt;It looks like macOS 26 will add clipboard history to Spotlight&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/maccy-is-a-great-clipboard-manager-for-mac/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, but we&#39;ll see if the implementation works as well as Maccy.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Hopefully
it doesn&#39;t become
&lt;a href=&quot;https://en.wikipedia.org/wiki/Sherlock_(software)#Sherlocked_as_a_term&quot;&gt;another case of &lt;em&gt;sherlocking&lt;/em&gt;&lt;/a&gt;
from Cuppertino. &lt;a href=&quot;https://seanmcp.com/articles/maccy-is-a-great-clipboard-manager-for-mac/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Fri, 27 Jun 2025 13:02:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/maccy-is-a-great-clipboard-manager-for-mac/</guid>
    </item>
    <item>
      <title>My on-again-off-again relationship with AI assistants</title>
      <link>https://seanmcp.com/articles/my-on-again-off-again-relationship-with-ai-assistants/</link>
      <description>&lt;p&gt;For the past few years, I have been paying on and off for ChatGPT and Claude. I
will read an interesting story about how someone transformed their work with AI,
then sign up for a premium plan for one month. I spend a few days actively
engaging with the AI and then quickly fall off. I go weeks without chatting with
the assistant and then cancel before the month is up.&lt;/p&gt;
&lt;p&gt;There is a lot of smoke in the work-productivity AI space. I believe there is
(probably) fire there somewhere. But I haven&#39;t been able to find it.&lt;/p&gt;
&lt;p&gt;Beyond one-off questions that require more nuance than a search on StackOverflow
or quick chats about naming things&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/my-on-again-off-again-relationship-with-ai-assistants/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, I struggle to find
many use cases for an AI assistant. I don&#39;t know if that is a result of how I
think and work or a limitation of the product. But it&#39;s not working for me.&lt;/p&gt;
&lt;p&gt;GitHub Copilot is a little different. It continues to churn in the background
while I code, and I frequently utilize its autocompletions. Sometimes the
refactors are usable, but others they are way off. I have been experimenting
with the more &amp;quot;agentic&amp;quot; features, but I&#39;m finding that reviewing the generated
code takes a comparable amount of time to writing it myself. And writing code is
more fun than reviewing.&lt;/p&gt;
&lt;p&gt;I find AI assistants useful, just less so than other folks online. I&#39;m glad to
have them as an option&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/my-on-again-off-again-relationship-with-ai-assistants/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; but am still on the lookout
for a reason to pay $20/month for a premium plan. If that all resonants and you
have some suggestions, please reach out. I can be convinced!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;A famously hard problem &lt;a href=&quot;https://seanmcp.com/articles/my-on-again-off-again-relationship-with-ai-assistants/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I am especially thankful for the free access to GitHub
Copilot that I am granted every month. I don&#39;t know what I did to qualify for
this program and at this point am afraid to ask! &lt;a href=&quot;https://seanmcp.com/articles/my-on-again-off-again-relationship-with-ai-assistants/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Mon, 31 Mar 2025 10:18:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/my-on-again-off-again-relationship-with-ai-assistants/</guid>
    </item>
    <item>
      <title>Introducing Speed Catan</title>
      <link>https://seanmcp.com/articles/introducing-speed-catan/</link>
      <description>&lt;p&gt;For the record: I like &lt;em&gt;Settlers of Catan&lt;/em&gt;. But as I age, I have less interest
in games that can be unbalanced or take a long time. A decade ago my friends and
I came up with &lt;a href=&quot;https://seanmcp.com/board-games/sanders-of-catan&quot;&gt;&lt;em&gt;Sanders of Catan&lt;/em&gt;&lt;/a&gt; to improve
balance and increase speed, and it is still my favorite way to play &lt;em&gt;Catan&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But then I had kids and needed to increase speed while also decreasing the
amount of time spent setting up and cleaning up the game.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/introducing-speed-catan/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; So we came up with
&lt;a href=&quot;https://seanmcp.com/board-games/speed-catan&quot;&gt;&lt;em&gt;Speed Catan&lt;/em&gt;, a faster and simpler way to play Catan&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main change is that instead of setting out the circular number tokens (or
chits) and using them to determine what resources to draw, you set the resource
and development cards in an ordered list on the table. Whenever you roll the
dice, you draw the resource based on it&#39;s index in the list.&lt;/p&gt;
&lt;p&gt;It also introduces a time-based win scenario where the player with the most
victory points after &lt;em&gt;x&lt;/em&gt; minutes wins (I recommend 15 minutes). And finally it
removes the robber because who wants to play with crying kids? You can
&lt;a href=&quot;https://seanmcp.com/board-games/speed-catan&quot;&gt;read more and see examples here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With these changes we are able to complete a full game with young children and
no conflict!&lt;/p&gt;
&lt;p&gt;If you have an old version of &lt;em&gt;Catan&lt;/em&gt; on the shelf, I recommend dusting it off
and playing some &lt;em&gt;Speed Catan&lt;/em&gt; with the kids or adults in your life. Then let me
know what you think.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;It is common in our
house to spend a long time setting up a game only to have the kid lose interest
half-way through. &lt;a href=&quot;https://seanmcp.com/articles/introducing-speed-catan/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Sat, 18 Jan 2025 18:30:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/introducing-speed-catan/</guid>
    </item>
    <item>
      <title>Zed still isn&#39;t ready</title>
      <link>https://seanmcp.com/articles/zed-still-isnt-ready/</link>
      <description>&lt;p&gt;VS Code started acting up after a recent update, so I took the opportunity to
&lt;a href=&quot;https://seanmcp.com/articles/zed-is-a-rocket-powered-skateboard&quot;&gt;try Zed again&lt;/a&gt;. It has improved
since my last attempt and was a perfectly workable editor for small stuff.&lt;/p&gt;
&lt;p&gt;The cracks started to show when I used it with a large codebase. The search was
incredibly slow—much slower than VS Code. And since it doesn&#39;t preserve filter
settings between queries, every subsequent search was slow too.&lt;/p&gt;
&lt;p&gt;I liked some of the code actions like autofix imports, but I never felt
confident that Zed would select the right file. At work we have some duplicate
file names and exports, so it is important to be able to choose which &lt;code&gt;View&lt;/code&gt;
component to import.&lt;/p&gt;
&lt;p&gt;The biggest issue I encountered was around memory and possible leaks. Zed (for
some reason) supports looking up all references for primatives like &lt;code&gt;false&lt;/code&gt;,
which would crash my M2 Mac by scouring the whole codebase for every instance of
&lt;code&gt;false&lt;/code&gt;. Today it crashed when I searched for a term with too many matches.&lt;/p&gt;
&lt;p&gt;I couldn&#39;t get AI completions working correctly (despite installing and
registering with GitHub Copilot) nor the unit test runner.&lt;/p&gt;
&lt;p&gt;Zed is promising, and I think they nail a lot of the intangibles that make a
product desirable. But it still isn&#39;t ready for full-time development work.&lt;/p&gt;
</description>
      <pubDate>Thu, 16 Jan 2025 24:30:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/zed-still-isnt-ready/</guid>
    </item>
    <item>
      <title>Rewriting in Eleventy (again)</title>
      <link>https://seanmcp.com/articles/rewriting-in-eleventy-again/</link>
      <description>&lt;p&gt;After
&lt;a href=&quot;https://seanmcp.com/articles/astro-and-release-velocity&quot;&gt;growing disillusioned with Astro in 2024&lt;/a&gt;,
I rewrote this site in Eleventy.
&lt;a href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy&quot;&gt;Again&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I want a library with features that are laser-focused on my project&#39;s needs.
Astro is an awesome framework for building complex content sites, but I no
longer believe that it is the right tool for a static site like this.&lt;/p&gt;
&lt;p&gt;Eleventy is focused on crawling through a directory and converting markdown
files into a HTML. And it has stayed the course over many years of regular
updates from its maintainer.&lt;/p&gt;
&lt;p&gt;That gives me the freedom to reorganize my site based on what works for me (and
not what the collections API wants). As a result, some pages have moved and
others disappeared.
&lt;a href=&quot;https://www.w3.org/Provider/Style/URI&quot;&gt;That used to bother me&lt;/a&gt;, but I can&#39;t be
on the hook for preserving all content forever. The grass withers and the
flowers fade.&lt;/p&gt;
&lt;p&gt;Last time I built this site with Eleventy,
&lt;a href=&quot;https://seanmcp.com/articles/rebuilding-with-astro&quot;&gt;I got bogged down&lt;/a&gt; with tons of filters,
shortcodes, and partials that made working on pages difficult. This time around,
I wanted to take care to avoid unnecessary complexity.&lt;/p&gt;
&lt;p&gt;For templating, I tried WebC but it felt too niche. I wanted to choose boring
technology for this site, and the uncommon errors with limited online resources
were not going to be helpful. I opted for Liquid because it has support for all
of Eleventy&#39;s internal features and the best documentation/support resources.&lt;/p&gt;
&lt;p&gt;This time I have five filters: two for debugging, one for formatting dates, and
two for handling RSS-ONLY and DRAFT articles. I have two partials: one for a
list of articles, and one for a list item. These are used in the
&lt;a href=&quot;https://seanmcp.com/articles&quot;&gt;articles&lt;/a&gt;, &lt;a href=&quot;https://seanmcp.com/&quot;&gt;home&lt;/a&gt;, and &lt;a href=&quot;https://seanmcp.com/search&quot;&gt;search&lt;/a&gt; pages to make sure that
everything renders consistently. I have no shortcodes.&lt;/p&gt;
&lt;p&gt;A few articles had interactive components that were written in Svelte or used
Astro components. I rewrote those with web components, which I should have done
in the first place. It feels right to mix custom elements in markdown with
script tags to load the JavaScript.&lt;/p&gt;
&lt;p&gt;The biggest tradeoff is the developer experience. Astro invests heavily in that
area. and it shows. Eleventy errors can be confusing and the documentation hard
to parse. And I miss the inline documentation in my editor that comes from a
fully-typed library&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/rewriting-in-eleventy-again/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. I think those tradeoffs are worth the simplicity of
Eleventy.&lt;/p&gt;
&lt;p&gt;Going forward, I want to think less about maintaining this site. It exists as a
collection of resources for myself and others. And when I have something new
that I want to write or add, I have a place for that.&lt;/p&gt;
&lt;p&gt;Here&#39;s to a few happy years before another rewrite.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;JSDoc comment syntax can help a lot, but I struggle to get
it working correctly &lt;a href=&quot;https://seanmcp.com/articles/rewriting-in-eleventy-again/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Tue, 14 Jan 2025 10:50:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/rewriting-in-eleventy-again/</guid>
    </item>
    <item>
      <title>Sorry if I broke your feed</title>
      <link>https://seanmcp.com/articles/sorry-if-i-broke-your-feed/</link>
      <description>&lt;p&gt;I&#39;m in the process of completing a
&lt;a href=&quot;https://seanmcp.com/articles/rewriting-in-eleventy-again&quot;&gt;rewrite of this site in Eleventy&lt;/a&gt;, and
have moved a bunch of content around. Last year I made the foolish decision to
make everything an article and therefore part of the RSS feed. That was a
mistake and so a lot of articles have been removed.&lt;/p&gt;
&lt;p&gt;Feedbin at least is able to reconcile the changes without too many issues, but I
apologize in advance if your RSS-reader-of-choice does not handle the changes
well.&lt;/p&gt;
</description>
      <pubDate>Tue, 14 Jan 2025 10:40:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/sorry-if-i-broke-your-feed/</guid>
    </item>
    <item>
      <title>Mc-Pher-son</title>
      <link>https://seanmcp.com/articles/mc-pher-son/</link>
      <description>&lt;p&gt;This morning my son asked me &amp;quot;If our last name is McPherson, why do people
always call us McPhearson?&amp;quot;&lt;/p&gt;
&lt;p&gt;&amp;quot;I don&#39;t know,&amp;quot; I replied. Besides the &amp;quot;Mc&amp;quot; there are no tricks; the
pronunciation follows the standard English reading rules.&lt;/p&gt;
&lt;img alt=&quot;Meme: Phoebe teaches Joey how to pronounce &#39;er&#39;, &#39;her&#39;, and &#39;pher&#39; and then asks him how to pronounce &#39;McPherson&#39;. He confidently responds with the incorrect &#39;McPhearson&#39;&quot; src=&quot;https://seanmcp.com/img/phoebe-teaches-joey-mcpherson.jpg&quot; style=&quot;width:min(100%, 600px)&quot;&gt;
&lt;p&gt;I know that text on a page is not the best medium for a pronunciation guide (at
least until we all learn
&lt;a href=&quot;https://en.wikipedia.org/wiki/International_Phonetic_Alphabet&quot;&gt;IPA&lt;/a&gt;), so here&#39;s
a read aloud:&lt;/p&gt;
&lt;div style=&quot;text-align: center&quot;&gt;
    &lt;audio controls=&quot;&quot; controlslist=&quot;nodownload&quot; src=&quot;https://seanmcp.com/assets/reading-mcpherson-meme.mp3&quot;&gt;&lt;/audio&gt;
&lt;/div&gt;
&lt;p&gt;Related: &lt;a href=&quot;https://seanmcp.com/articles/how-to-pronounce-my-name&quot;&gt;&lt;em&gt;How to pronounce my name&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Tue, 26 Nov 2024 13:43:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/mc-pher-son/</guid>
    </item>
    <item>
      <title>Balance bikes are just better</title>
      <link>https://seanmcp.com/articles/balance-bikes-are-just-better/</link>
      <description>&lt;p&gt;I learned to ride a bike with training wheels. So when it was time for our
oldest son to learn, we tried training wheels. A year passed with no noticeable
progress. Maybe he just wasn&#39;t a bike rider?&lt;/p&gt;
&lt;p&gt;Our friends suggested a balance bike instead. After two weeks of practice, he
was riding a normal bike independently. It was like magic.&lt;/p&gt;
&lt;p&gt;Not wanting to be left behind, our four-year-old wanted to learn how to ride
too. So we got a balance bike, he practiced for two weeks, and then he learned
to ride a bike too.&lt;/p&gt;
&lt;p&gt;Training wheels help you practice pedaling and steering, which most kids have
mastered on a tricycle or big-wheel. Training wheels don&#39;t help with the most
important skill for riding a bike: balancing. By focusing on that, balance bikes
help kids learn to ride faster than with training wheels.&lt;/p&gt;
&lt;p&gt;The are a bunch of brands that sell balance bikes, and since kids only need them
for a short time there is a healthy second-hand market for them. We got ours for
free from a Buy Nothing group.&lt;/p&gt;
&lt;p&gt;But if that isn&#39;t available, you can convert a normal bike into a balance bike
by removing the pedals and lowering the seat. Once the kids has learned how to
balance as they roll, add the pedals, raise the seat, and watch them go.&lt;/p&gt;
&lt;p&gt;For learning how to ride, balance bikes are just better.&lt;/p&gt;
</description>
      <pubDate>Sat, 23 Nov 2024 18:28:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/balance-bikes-are-just-better/</guid>
    </item>
    <item>
      <title>Deleting my GPTs</title>
      <link>https://seanmcp.com/articles/deleting-my-gpts/</link>
      <description>&lt;p&gt;Today I deleted all of my public and private
&lt;a href=&quot;https://openai.com/index/introducing-gpts/&quot;&gt;GPTs&lt;/a&gt;. I thought I would feel sad:
they were the feature that convinced me to start paying for OpenAI last year.
But instead it feels like an anticlimactic end to an experiment.&lt;/p&gt;
&lt;p&gt;When custom GPTs were initially launched, I was interested in an official
playground for prompt engineering with the potential of a marketplace. I spun up
a few and even &lt;a href=&quot;https://seanmcp.com/tags/gpts&quot;&gt;wrote some articles about it&lt;/a&gt;. But the feature
fizzled and the market hasn&#39;t panned out.&lt;/p&gt;
&lt;p&gt;Beyond a questionably-accurate conversation count, OpenAI doesn&#39;t provide even
basic usage data. Critical measures like recent sessions, average conversation
length, and bounce rate are all missing. Maybe you could overlook that if your
GPTs made a few bucks a month, but there is no market and no clear road to one.&lt;/p&gt;
&lt;p&gt;That was enough to convince me, but paying for the privilege of keeping these
GPTs online wore me down too. I don&#39;t like being tied down with subscription
services, and GPTs were the only thing keeping me in the OpenAI ecosystem. So I
deleted them.&lt;/p&gt;
&lt;p&gt;Without the GPTs, I&#39;m free to switch to Claude (which all my cool
prompt-engineer friends say is better) or nothing at all. $20 a month goes
pretty far at a food bank or international charity.&lt;/p&gt;
&lt;p&gt;If you were a faithful user of my GPTs, then I apologize for the change. But
dozens of users are not enough to justify the expense or the lock-in. Send me an
email, and I will share everything you need to make your own.&lt;/p&gt;
</description>
      <pubDate>Tue, 12 Nov 2024 24:38:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/deleting-my-gpts/</guid>
    </item>
    <item>
      <title>Smile and wave</title>
      <link>https://seanmcp.com/articles/smile-and-wave/</link>
      <description>&lt;p&gt;I live on a neighborhood street that runs parallel to a larger road connecting
multiple communities. As a result, our street gets extra traffic from folks who
try to cut a corner and shorten their commute. Some of these drivers slow down
on their way through the neighborhood, but others do not.&lt;/p&gt;
&lt;p&gt;I have two young children who like to play outside in the yard and ride their
bikes on the street. We have worked with them to recognize when a car is
approaching and what to do. But they&#39;re kids. Sometimes they don&#39;t hear a car
coming and sometimes they forget what to do. So speeding cars make me nervous.&lt;/p&gt;
&lt;p&gt;Most people try to discourage speeding cars through shame. They frown and shake
their head or gesture with their arms. Maybe they mouth the words &amp;quot;Slow down!&amp;quot; I
don&#39;t know if this is effective; shame seems to be a pretty poor motivator in my
society. But I understand the instinct. A speeding car is dangerous and
negatively impacts me, my family, and my neighbors. It is legitimate to want to
communicate that.&lt;/p&gt;
&lt;p&gt;However, there is a different danger to the shame approach: it destroys the
relationship&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/smile-and-wave/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; between you and the driver. It turns you into a judge looking
down on and condemning the defendant. And because you have seized this seat of
authority, and the driver might reasonably rebel against it. They&#39;ll get mad.
Maybe they&#39;ll drive faster. You will both leave the interaction filling angry
and self-righteous.&lt;/p&gt;
&lt;p&gt;I don&#39;t like what shaming does to me or the driver. Is there a way to discourage
speeding that doesn&#39;t destroy relationships?&lt;/p&gt;
&lt;p&gt;I think there is, and it&#39;s simple: instead of shaming just smile and wave. I do
this to every driver I see on my street. Drivers in cars, trucks, vans, work
vehicles, delivery trucks, and everything in between get a smile and a
wave—whether they are speeding or not. I think this is better for three reasons.&lt;/p&gt;
&lt;p&gt;First, it&#39;s good for me. Frequently in life we are provided with opportunities
to elevate ourselves above others. To judge. To condemn. By choosing to to smile
and wave, I am consciously defying that urge and replacing it with kindness. It
helps put me in the right head space for this and future reactions.&lt;/p&gt;
&lt;p&gt;Second, it&#39;s good for the driver. No one wants to have their actions judged,
especially by a random person on the street. Smiling and waving humanizes
pedestrians without introducing a power dynamic. Your smile will linger on their
mind for a long time, shading future thoughts and actions.&lt;/p&gt;
&lt;p&gt;Third, it works. The drivers of speeding cars slow down when they see people
smile and wave. This is especially true when our kids do it. The natural
reaction for the driver is to slow down, and then return the smile and wave. The
next time they drive down your street they will remember &amp;quot;there are friendly
people who live here.&amp;quot;&lt;/p&gt;
&lt;p&gt;Smiling and waving is a radically egalitarian response to the problem of
speeding, but I think it&#39;s the right solution. It helps you, helps your
neighbor, and results in a safer and healthier environment for everyone.&lt;/p&gt;
&lt;p&gt;So next time you see a speeding driver, try smiling and waving. I think you&#39;ll
be surprised. And look for other opportunities to extend kindness and grace to
those who may be misbehaving. It just might be the right solution then too.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I&#39;m using relationship in the general sense of a connection between two
people. I do not know most of the speeding drivers personally. &lt;a href=&quot;https://seanmcp.com/articles/smile-and-wave/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Fri, 18 Oct 2024 17:54:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/smile-and-wave/</guid>
    </item>
    <item>
      <title>Pittsburgh TechFest 2024</title>
      <link>https://seanmcp.com/articles/pittsburgh-techfest-2024/</link>
      <description>&lt;p&gt;Yesterday I went to
&lt;a href=&quot;https://www.pghtech.org/events/2024TechFest&quot;&gt;Pittsburgh TechFest 2024&lt;/a&gt; at the
Commonwealth Charter Academy building on the Waterfront. The venue was nice, the
food was good, and both keynotes were interesting—though I definitely did not
know enough about SNL to fully appreciate the closing.&lt;/p&gt;
&lt;p&gt;My favorite talk was by Dominika Blach of the Greater Pittsburgh Community Food
Bank about how they&#39;re using low-code solutions for an organization that needs
technology solutions but can&#39;t afford developers. I knew nothing about low code
but can imagine a lot of organizations with similar constraints who need
professionals who can navigate that world for them.&lt;/p&gt;
&lt;p&gt;I gave a talk title
&lt;em&gt;&lt;a href=&quot;https://seanmcp.com/talks/thinking-outside-the-bots&quot;&gt;Thinking Outside the Bots&lt;/a&gt;: A human-centric
approach to model interactions&lt;/em&gt; and discussed some of my thoughts about how we
build features using AI&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/pittsburgh-techfest-2024/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. I got to share some of the cool things that I&#39;ve
been working on at Khan Academy and felt validated by the responses. The talk
was well received, sparking interesting conversations afterwards and a couple
invitations to give the talk again to other groups.&lt;/p&gt;
&lt;p&gt;For the first time, I enjoyed the networking side of the event more than the
talks. I had the chance to reconnect with some former colleagues at Niche, met
some of the folks from Carnegie Learning, talked with friends, and saw a lot of
familiar faces from last year. I handed out some business cards (vintage 2017)
and made some good connections.&lt;/p&gt;
&lt;p&gt;It&#39;s hard to feel connected to the local tech scene when you work remotely for a
company based 2,200 miles away. So I think it&#39;s important for workers like me to
invest time in events like this. Looking forward to next year!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;tl;dr I think that chatbots are a bad UI for most use cases and that we
should focus our design efforts on user needs and defined outcomes &lt;a href=&quot;https://seanmcp.com/articles/pittsburgh-techfest-2024/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Sat, 28 Sep 2024 10:31:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/pittsburgh-techfest-2024/</guid>
    </item>
    <item>
      <title>Seventy7 Ventures feigns credibility with fake &quot;Best of British Business&quot; award</title>
      <link>https://seanmcp.com/articles/seventy7-ventures-feigns-credibility-with-fake-best-of-british-business-award/</link>
      <description>&lt;p&gt;Seventy7 Ventures, the new majority shareholder of Inverness Caledonian Thistle
FC, &lt;a href=&quot;https://seventy7.ventures/our-story&quot;&gt;state on their website&lt;/a&gt; that they have
&amp;quot;been awarded Best of British Business by The Independent.&amp;quot;&lt;/p&gt;
&lt;p&gt;This claim has been repeated
&lt;a href=&quot;https://www.pressandjournal.co.uk/fp/sport/football/inverness-caledonian-thistle/6544647/caley-thistle-podcaster-gives-insight-into-new-major-shareholder/&quot;&gt;by other outlets&lt;/a&gt;
and
&lt;a href=&quot;https://www.reddit.com/r/ScottishFootball/comments/1ecmrq8/comment/lf3k299/&quot;&gt;Scottish soccer fans&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A biography of Seventy7 Ventures Director Ketan Makwana includes
&lt;a href=&quot;https://www.businessbookawards.co.uk/ketan-makwana-2023/&quot;&gt;a similar and more specific claim&lt;/a&gt;:
“Seventy7 Ventures was awarded the Best of British Business 2023 by The
Independent for its impact on British Businesses.”&lt;/p&gt;
&lt;p&gt;Jimmy Nguyen, Executive Board member of Seventy7 Ventures,
&lt;a href=&quot;https://x.com/BiznessReporter/status/1657003471395074049&quot;&gt;stated&lt;/a&gt; that
“Seventy7 Ventures awarded Best of British Business 2023 by @BiznessReporter for
The @Independent”.&lt;/p&gt;
&lt;p&gt;However, the Independent does not administer the &amp;quot;Best of British Business&amp;quot;
awards. It is,
&lt;a href=&quot;https://www.independent.co.uk/news/business/business-reporter/best-of-british-businesses-diversity-b1896766.html&quot;&gt;according to the Independent&lt;/a&gt;,
a “campaign” run by the Business Reporter. A marketing campaign.&lt;/p&gt;
&lt;p&gt;Business Reporter creates
“&lt;a href=&quot;http://mediakits.business-reporter.co.uk/media-kit/&quot;&gt;high-quality video, imagery and articles&lt;/a&gt;”
to showcase their client’s business, &lt;em&gt;i.e.&lt;/em&gt; sponsored content. Business Reporter
has contracts with larger and more reputable organizations that allow them to
publish the sponsored content on those platforms. Their two biggest contracts
are with Bloomberg and the Independent.&lt;/p&gt;
&lt;p&gt;Seventy7 Ventures paid Business Reporter to create
&lt;a href=&quot;https://www.business-reporter.co.uk/management/the-totally-digital-world-is-nearly-here--and-creative-industries-need-to-get-on-board-if-theyre-to-grow-and-prosper&quot;&gt;sponsored content marketing their business&lt;/a&gt;.
That article is clearly marked “Sponsored by Seventy7 Ventures” and tagged “Best
of Business”.&lt;/p&gt;
&lt;p&gt;The sponsored article was then
&lt;a href=&quot;https://www.independent.co.uk/news/business/business-reporter/gaming-vr-xr-digitisation-technology-b2345679.html&quot;&gt;cross posted to the Independent&lt;/a&gt;
with the “sponsored” indicator replaced with an opaque notice: “Seventy7
Ventures is a Business Reporter client&amp;quot;. That cross post is dated May 31, 2023,
the same year that Seventy7 Ventures claims to have received the “award”.&lt;/p&gt;
&lt;p&gt;The “Best of British Business awards” is a marketing campaign for companies who
pay Business Reported for sponsored content. The content, as part of the paid
package with Business Reporter, is cross posted to the Independent. But stating
that the “award” is “by the Independent” or “for [Seventy7 Ventures’] impact on
British Business” is false.&lt;/p&gt;
&lt;p&gt;By linking the “award” to the Independent, Seventy7 Ventures is either
unintentionally misrepresenting the truth or deliberately deceiving the public
to give their business credibility.&lt;/p&gt;
&lt;p&gt;Seventy7 Ventures and Business Reporter were reached for comment but did not
respond.&lt;/p&gt;
</description>
      <pubDate>Sat, 03 Aug 2024 18:08:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/seventy7-ventures-feigns-credibility-with-fake-best-of-british-business-award/</guid>
    </item>
    <item>
      <title>Big changes ahead</title>
      <link>https://seanmcp.com/articles/big-changes-ahead/</link>
      <description>&lt;p&gt;Hey RSS Club! Long time no see. I&#39;ve gotten out of the habit of writing to you
this year, but I wanted to give a heads up about upcoming changes to the site.&lt;/p&gt;
&lt;p&gt;I&#39;m going to be merging gardens and notes into articles to create a single
content type. As a result, there will be a bunch of new entries into the RSS
feed that may affect you, dear reader. I apologize for any inconvenience, but I
think this is the right move for the health of this site going forward.&lt;/p&gt;
&lt;p&gt;I have really enjoyed creating gardens this year with the explicit permission to
edit/change content as the page grew. So while the &amp;quot;Garden&amp;quot; content type is
going away, the ethos is carrying on into articles. Rather than a resource that
is set in stone when published, I&#39;m going to treat articles as living documents
that will change over time.&lt;/p&gt;
&lt;p&gt;To summarize the changes: Soon everything will be an article, and articles will
be more like gardens.&lt;/p&gt;
</description>
      <pubDate>Thu, 25 Jul 2024 10:46:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/big-changes-ahead/</guid>
    </item>
    <item>
      <title>Bypass &quot;Error: There are no prompts&quot; in promptfoo</title>
      <link>https://seanmcp.com/articles/bypass-error-there-are-no-prompts-in-promptfoo/</link>
      <description>&lt;p&gt;While evaluating different language models with
&lt;a href=&quot;https://promptfoo.com/&quot;&gt;promptfoo&lt;/a&gt;, I started running into the following error
message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/Users/seanmcp/project-dir/node_modules/.pnpm/promptfoo@0.67.0_@aws-sdk+client-bedrock-runtime@3.606.0_@azure+identity@4.3.0_@azure+openai-_c7fwzdecwpaax3yh5ny536i2qu/node_modules/promptfoo/dist/src/prompts/index.js:136
            throw new Error(`There are no prompts in ${JSON.stringify(prompt.raw)}`);
                  ^

Error: There are no prompts in &amp;quot;TEXT_OF_THE_PROMPT_THAT_I_TRIED_TO_EVALUATE&amp;quot;
    at readPrompts (/Users/seanmcp/project-dir/node_modules/.pnpm/promptfoo@0.67.0_@aws-sdk+client-bedrock-runtime@3.606.0_@azure+identity@4.3.0_@azure+openai-_c7fwzdecwpaax3yh5ny536i2qu/node_modules/promptfoo/dist/src/prompts/index.js:136:19)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async resolveConfigs (/Users/seanmcp/project-dir/node_modules/.pnpm/promptfoo@0.67.0_@aws-sdk+client-bedrock-runtime@3.606.0_@azure+identity@4.3.0_@azure+openai-_c7fwzdecwpaax3yh5ny536i2qu/node_modules/promptfoo/dist/src/main.js:129:27)
    at async runEvaluation (/Users/seanmcp/project-dir/node_modules/.pnpm/promptfoo@0.67.0_@aws-sdk+client-bedrock-runtime@3.606.0_@azure+identity@4.3.0_@azure+openai-_c7fwzdecwpaax3yh5ny536i2qu/node_modules/promptfoo/dist/src/main.js:545:48)
    at async Command.&amp;lt;anonymous&amp;gt; (/Users/seanmcp/project-dir/node_modules/.pnpm/promptfoo@0.67.0_@aws-sdk+client-bedrock-runtime@3.606.0_@azure+identity@4.3.0_@azure+openai-_c7fwzdecwpaax3yh5ny536i2qu/node_modules/promptfoo/dist/src/main.js:704:9)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It didn&#39;t occur for a long time, but then started occurring every time I added a
new prompt to the config. Clearing the cache did not help.&lt;/p&gt;
&lt;p&gt;The only solution I found was to add a space to the end of the prompt text:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;prompts&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 string&quot;&gt;&quot;TEXT_OF_THE_PROMPT_THAT_I_TRIED_TO_EVALUATE &quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This enabled promptfoo to run as expected. Definitely not ideal, but hopefully
this unblocks you if you run into the same issue.&lt;/p&gt;
&lt;p&gt;I will update this article if I find a better solution. If you have one, please
send me an email.&lt;/p&gt;
</description>
      <pubDate>Sun, 30 Jun 2024 01:21:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/bypass-error-there-are-no-prompts-in-promptfoo/</guid>
    </item>
    <item>
      <title>Use state callbacks in React</title>
      <link>https://seanmcp.com/articles/use-state-callbacks-in-react/</link>
      <description>&lt;p&gt;When you want to update state in React, you have two options: pass the next
value, or pass a callback function that returns the next value.&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; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setState&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&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 comment&quot;&gt;// Next value&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;reset&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&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 comment&quot;&gt;// Callback function&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;increment&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setState&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;prev&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; prev &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;To choose correctly, you need to ask if the next state depends on the previous.
If so, choose the callback function. Otherwise, you can pass the next value.&lt;/p&gt;
&lt;p&gt;The reason for this is that React does not update state until the next render.
So if you have multiple references to &lt;code&gt;state&lt;/code&gt; in the callstack, all but the
first will be stale. Here&#39;s a contrived example:&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;// ⚠ Buggy code&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loginSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;username&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 function&quot;&gt;setState&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;...&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; username &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 comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;setState&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;...&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;showLoginModal&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the &lt;code&gt;loginSuccess&lt;/code&gt; function is called, it first sets the &lt;code&gt;username&lt;/code&gt;
property while referencing the current &lt;code&gt;state&lt;/code&gt;. No issues so fare.&lt;/p&gt;
&lt;p&gt;But although &lt;code&gt;setState&lt;/code&gt; has executed, &lt;code&gt;state&lt;/code&gt; will not change until the next
render. So when the second &lt;code&gt;setState&lt;/code&gt; is called, it references the stale &lt;code&gt;state&lt;/code&gt;
value which does not include the updated &lt;code&gt;username&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The final &lt;code&gt;state&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; what we want:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// We wanted this to be updated&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;showLoginModal&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can try to avoid this by avoiding multiple &lt;code&gt;setState&lt;/code&gt; calls in the same
stack. But as your application grows, you will inevitably run into this issue.
And in my experience it&#39;s not an easy bug to track down.&lt;/p&gt;
&lt;p&gt;The better solution is to &lt;strong&gt;use the callback function every time you need to
reference the previous state&lt;/strong&gt;:&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;// ❌ setState(count + 1)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;setState&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;count&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; count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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 comment&quot;&gt;// ❌ setState(items.filter((item) =&gt; item.id !== id))&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;setState&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;items&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; items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&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;item&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; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; id&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 comment&quot;&gt;// ❌ setState(!isEnabled)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;setState&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;isEnabled&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 operator&quot;&gt;!&lt;/span&gt;isEnabled&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 comment&quot;&gt;// ❌ setState({ ...user, username: &quot;seanmcp&quot; })&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;setState&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;user&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 class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;seanmcp&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will ensure that every &lt;code&gt;setState&lt;/code&gt; call references the latest state,
regardless of where it is in the callstack.&lt;/p&gt;
</description>
      <pubDate>Fri, 28 Jun 2024 01:17:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/use-state-callbacks-in-react/</guid>
    </item>
    <item>
      <title>VIVO sit/stand desk</title>
      <link>https://seanmcp.com/articles/vivo-sit-stand-desk/</link>
      <description>&lt;p&gt;In 2021 I purchased a
&lt;a href=&quot;https://vivo-us.com/collections/most-popular-standing-desks/products/desk-kit-b04b?variant=33285162795107&quot;&gt;43&amp;quot; VIVO sit/stand desk&lt;/a&gt;
from Amazon. It has served me well for the last few years, but I have
encountered a few issues and solutions that I want to log here.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;desktop-rocks-back-and-forth&quot; tabindex=&quot;-1&quot;&gt;Desktop rocks back and forth&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/vivo-sit-stand-desk/#desktop-rocks-back-and-forth&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Desktop rocks back and forth”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;When the allen bolts that hold the desktop to the frame are too loose, then the
desktop will rock back and forth with normal use. This is particularly
noticeable when you are leaning on the desk, but also when writing or typing.&lt;/p&gt;
&lt;p&gt;To fix this, I&#39;ve found that tightening the bolts with an allen wrench prevents
the rocking. However, over-tightening results in the paint chipping around the
bolt holes and slight deformations of the metal. Be careful and only tighten as
much as necessary.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;desktop-squeaks-under-pressure&quot; tabindex=&quot;-1&quot;&gt;Desktop squeaks under pressure&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/vivo-sit-stand-desk/#desktop-squeaks-under-pressure&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Desktop squeaks under pressure”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This one was maddening: whenever I applied the slightest pressure on the desktop
it would squeak. Some days it was so bad that every keystroke prompted a small
squeak.&lt;/p&gt;
&lt;p&gt;Loosening the allen bolts that hold the desktop to the frame fixed this issue
but caused the rocking issue mentioned above.&lt;/p&gt;
&lt;p&gt;To fix this, I purchased nylon washers from the hardware store and placed them
in-between the frame and the brackets holding the desktop&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/vivo-sit-stand-desk/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. With those in
place the the allen bolts tightened, the squeaking has stopped.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;modifications&quot; tabindex=&quot;-1&quot;&gt;Modifications&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/vivo-sit-stand-desk/#modifications&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Modifications”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Added an
&lt;a href=&quot;https://www.ikea.com/us/en/p/signum-cable-management-horizontal-silver-color-30200253/&quot;&gt;IKEA Signum cable organizer&lt;/a&gt;
to the bottom of the desktop&lt;/li&gt;
&lt;li&gt;Added a generic clear acrylic headphone hanger to the bottom of the desktop&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Don&#39;t be like me: make sure to clear the desktop before you do this &lt;a href=&quot;https://seanmcp.com/articles/vivo-sit-stand-desk/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Thu, 27 Jun 2024 18:09:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/vivo-sit-stand-desk/</guid>
    </item>
    <item>
      <title>Simplify event delegation with the Element.closest method</title>
      <link>https://seanmcp.com/articles/simplify-event-delegation-with-the-element-closest-method/</link>
      <description>&lt;p&gt;Let&#39;s say you have a list of buttons and want to perform an action when one is
selected. A solution is to add an event listener to each button with a reference
from querying or creating the button.&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;let&lt;/span&gt; buttons &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;button&quot;&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;
buttons&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&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;button&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;
  button&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&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;e&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;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Clicked:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; button&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a small number of buttons, this is fine. But as the number of buttons grows
or if they are added and removed dynamically, then this solution grows
inefficient and fiddly&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/simplify-event-delegation-with-the-element-closest-method/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Another option is to delegate the event handling to a parent element. Since most
events bubble in JavaScript, an event that is fired on the &lt;code&gt;button&lt;/code&gt; child will
trigger the listener callback for the parent.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example of a delegated event listener in a counter where a parent
contains buttons that increment by different amounts:&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;let&lt;/span&gt; parent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;#increment-buttons&quot;&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;
parent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&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;e&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;
  count &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;increment&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I like this pattern because it enables you to add buttons declaratively in HTML
instead of imperatively in JavaScript. That said, the code above is brittle. If
you add additional markup to the buttons, like an &lt;code&gt;svg&lt;/code&gt; icon or a &lt;code&gt;span&lt;/code&gt;, then
the &lt;code&gt;e.target&lt;/code&gt; will refer to that element instead of the &lt;code&gt;button&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To resolve this, we can
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/closest&quot;&gt;use the &lt;code&gt;Element.closest&lt;/code&gt; method&lt;/a&gt;
to walk up the DOM tree to find the nearest element that matches a selector.
&lt;code&gt;Element.closest&lt;/code&gt; checks the element first before moving to the parent, so we
can use it in our event listener to find the button:&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;let&lt;/span&gt; parent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;#increment-buttons&quot;&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;
parent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&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;e&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 class=&quot;token keyword&quot;&gt;let&lt;/span&gt; button &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;button[data-increment]&quot;&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    count &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;increment&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 comment&quot;&gt;// Other rendering logic...&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we are free to add children elements to our buttons without breaking our
event listener.&lt;/p&gt;
&lt;p&gt;Event delegation is a useful pattern to simplify situations like this where you
have many descendant elements that need similar event listeners. But if you
tried to route all of your events through a single parent like the &lt;code&gt;document&lt;/code&gt;,
it would grow inefficient and unwieldy too&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/simplify-event-delegation-with-the-element-closest-method/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Reach for the best tool for the job, and when you choose event delegation:
remember &lt;code&gt;Element.closest&lt;/code&gt;.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;You have more event listeners in memory and need to manage them all
manually. &lt;a href=&quot;https://seanmcp.com/articles/simplify-event-delegation-with-the-element-closest-method/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;You need to call &lt;code&gt;Element.closest&lt;/code&gt; and add conditional logic for every event
interaction. &lt;a href=&quot;https://seanmcp.com/articles/simplify-event-delegation-with-the-element-closest-method/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Wed, 12 Jun 2024 01:31:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/simplify-event-delegation-with-the-element-closest-method/</guid>
    </item>
    <item>
      <title>Align content in Markdown tables</title>
      <link>https://seanmcp.com/articles/align-content-in-markdown-tables/</link>
      <description>&lt;p&gt;You can set the &lt;code&gt;align&lt;/code&gt; attribute on a table column in Markdown by using a colon
in the header row separator.&lt;/p&gt;
&lt;p&gt;A typical table looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token table&quot;&gt;&lt;span class=&quot;token table-header-row&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt; A   &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt; B   &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt; C   &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token table-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 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;span class=&quot;token table-data-rows&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To align the content, add colons to the separator row--&lt;code&gt;:-&lt;/code&gt; for left, &lt;code&gt;:-:&lt;/code&gt; for
center, and &lt;code&gt;-:&lt;/code&gt; for right:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token table&quot;&gt;&lt;span class=&quot;token table-header-row&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt; A    &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt;   B    &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt;     C &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token table-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 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;span class=&quot;token table-data-rows&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; Left &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; Center &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; Right &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That Markdown will render a &lt;code&gt;table&lt;/code&gt; with the &lt;code&gt;align&lt;/code&gt; attribute set on the header
and body cells:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:left&quot;&gt;A&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;B&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;C&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;Left&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;Center&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;Right&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;a href=&quot;https://www.markdownguide.org/extended-syntax/#tables&quot;&gt;Read more about Markdown tables&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Fri, 07 Jun 2024 10:47:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/align-content-in-markdown-tables/</guid>
    </item>
    <item>
      <title>Solo-playing Adventure Games with AI</title>
      <link>https://seanmcp.com/articles/solo-playing-adventure-games-with-ai/</link>
      <description>&lt;p&gt;Adventure Games like Dungeons &amp;amp; Dragons and Cairn are best played in-person with
friends. But when playing with others isn&#39;t an option, solo play is a fun
alternative.&lt;/p&gt;
&lt;p&gt;Here are the best prompts that I have found for solo play based on the
product/model. As with all AI: your experiences will vary.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;chatgpt-4o&quot; tabindex=&quot;-1&quot;&gt;ChatGPT 4o&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/solo-playing-adventure-games-with-ai/#chatgpt-4o&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “ChatGPT 4o”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;You are a game master for Cairn RPG. Run a play by post adventure with me.
Keep everything brief, the goal is to get through an adventure quickly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;+ Creative and engaging story and characters&lt;/li&gt;
&lt;li&gt;+ Generates options for you to choose from&lt;/li&gt;
&lt;li&gt;-/+ Let&#39;s you steer the story&lt;/li&gt;
&lt;li&gt;- Some of the plot points were repetitive&lt;/li&gt;
&lt;li&gt;- Encounters were too easy and NPCs too helpful&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Wed, 29 May 2024 23:09:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/solo-playing-adventure-games-with-ai/</guid>
    </item>
    <item>
      <title>CSS nesting is (almost) ready</title>
      <link>https://seanmcp.com/articles/css-nesting-is-almost-ready/</link>
      <description>&lt;p&gt;I&#39;ve been hearing a lot about CSS nesting, but I figured that it was still years
away from being ready for use even on a site like this.&lt;/p&gt;
&lt;p&gt;But a quick check of &lt;a href=&quot;https://caniuse.com/css-nesting&quot;&gt;Can I Use&lt;/a&gt; shows that it
is ready in all major browsers. Even more if you make sure to use the &lt;code&gt;&amp;amp;&lt;/code&gt;
nesting selector.&lt;/p&gt;
&lt;p&gt;Try it out for yourself:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://seanmcp.com/articles/css-nesting-is-almost-ready&quot; style=&quot;display: none&quot;&gt;View this
interactive on seanmcp.com&lt;/a&gt;&lt;/p&gt;
&lt;style style=&quot;background-color: var(--inline-code-background); color: var(--inline-code-color); display:block; font-family: var(--mono-font); padding: 1em; white-space: preserve;&quot; contenteditable=&quot;&quot;&gt;main {
    &amp; time {
        display: block;
        text-align: center;
    }

    &amp; h1 {
        color: red;
    }

    &amp; p:first-of-type {
        font-style: italic;
    }

    &amp; p {
        &amp; code {
            outline: 2px dotted currentColor;
        }
    }

    &amp; style + p:has(em) {
        border: 1px solid currentColor;
        padding: 1em;
    }

    &amp; p:last-child {
        text-decoration: double underline;
    }
}
&lt;/style&gt;
&lt;p&gt;So should you start using CSS nesting today? Probably not. But it is &lt;em&gt;almost&lt;/em&gt;
ready, and that&#39;s exciting. Variables and nesting are the two features that
would still draw me to a preprocessor like Sass or Less. We&#39;ve had native custom
properties for years, and now we are so close to being able to use nesting.&lt;/p&gt;
&lt;p&gt;Exciting times!&lt;/p&gt;
</description>
      <pubDate>Tue, 28 May 2024 24:50:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/css-nesting-is-almost-ready/</guid>
    </item>
    <item>
      <title>Looking over the fence at Eleventy</title>
      <link>https://seanmcp.com/articles/looking-over-the-fence-at-eleventy/</link>
      <description>&lt;p&gt;First of all: I&#39;m happy with Astro. It&#39;s an exciting project that provides the
tools necessary to build websites in the way I want to build them. The future is
bright for Astro.&lt;/p&gt;
&lt;p&gt;But... I still find myself looking at alternatives.
&lt;a href=&quot;https://seanmcp.com/articles/astro-and-release-velocity/&quot;&gt;I&#39;ve written previously about Astro&#39;s relentless releases&lt;/a&gt;,
and it continues to be a source of stress for me.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/looking-over-the-fence-at-eleventy/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; Every other week it&#39;s a
fresh release and reminder that I am behind. Are there alternatives that provide
me some of the same features without the release cadence?&lt;/p&gt;
&lt;p&gt;Thanks to the recent &amp;quot;International Symposium On Making Web Sites Real Good&amp;quot;
conference and its positive reviews, Eleventy has caught my eye again. Robin
Rendle&#39;s
&lt;a href=&quot;https://buttondown.email/cascade/archive/020-notes-on-the-blog/&quot;&gt;recent experience returning to Eleventy&lt;/a&gt;
was insightful. Once upon a time this site was previously built with Eleventy,
and I liked it too... until I didn&#39;t.&lt;/p&gt;
&lt;p&gt;Eleventy was fast and focused, but the lack of opinions meant that you could
easily architect yourself into a corner. I was so bogged down with Nunjucks
templates, filters, and shortcodes in the end that I gave up and rebuilt in
Astro.&lt;/p&gt;
&lt;p&gt;That said, I still admire what Zach Leatherman is doing with Eleventy. and the
community that he is building. I like the ethos, and that gives me confidence in
where the project will be in 3-5 years. Can I say the same for Astro?&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/looking-over-the-fence-at-eleventy/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m not going to re-write this site. But I think I&#39;m going to
&lt;a href=&quot;https://seanmcp.com/resources/getting-started-with-eleventy/&quot;&gt;reach for Eleventy&lt;/a&gt; the next time
I&#39;m building a simple or long-lived website. If all I need is a way to generate
a bunch of HTML with linked CSS and JavaScript, Eleventy seems like the better
option of the two.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;At the time of writing, I am five minor releases behind and counting. &lt;a href=&quot;https://seanmcp.com/articles/looking-over-the-fence-at-eleventy/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I think the answer is no. They have received venture capital and need to
find some way to make a return on that investment. Since I&#39;m not a paying
customer, I don&#39;t have great confidence that the project will grow in the
direction that meets my needs. &lt;a href=&quot;https://seanmcp.com/articles/looking-over-the-fence-at-eleventy/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Mon, 27 May 2024 18:43:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/looking-over-the-fence-at-eleventy/</guid>
    </item>
    <item>
      <title>ChatGPT 4o isn&#39;t a good researcher yet</title>
      <link>https://seanmcp.com/articles/chatgpt-4o-isnt-a-good-researcher-yet/</link>
      <description>&lt;p&gt;I asked ChatGPT 4o for general recommendations for
&lt;a href=&quot;https://seanmcp.com/articles/astro-and-release-frequency/&quot;&gt;software release frequency&lt;/a&gt;, and it
spouted out some information that seemed generally plausible. I asked it to cite
its sources, and after an internet search it provided three links. I checked all
three, and none of them contained the information that ChatGPT initially
provided.&lt;/p&gt;
&lt;p&gt;I replied with &amp;quot;None of those articles had the guidelines that you cited&amp;quot; and it
responded back with more information and more links. I checked a few of them,
but it was the same as before. They were on the same topic, but the information
that ChatGPT provided was not sourced from those articles.&lt;/p&gt;
&lt;p&gt;I don&#39;t know what I expected, but it&#39;s clear that ChatGPT 4o isn&#39;t a reliable
research assistant yet. It can provide you some common &lt;s&gt;misconceptions&lt;/s&gt;
information, but it isn&#39;t going to point you to a true source consistently.
You&#39;re going to need to research for yourself.&lt;/p&gt;
&lt;p&gt;Use with caution.&lt;/p&gt;
</description>
      <pubDate>Sat, 18 May 2024 01:51:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/chatgpt-4o-isnt-a-good-researcher-yet/</guid>
    </item>
    <item>
      <title>Astro and release velocity</title>
      <link>https://seanmcp.com/articles/astro-and-release-velocity/</link>
      <description>&lt;p&gt;This website is built with &lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt;, and overall I&#39;ve been
happy with my choice. The framework model makes sense to me, and I really enjoy
using their templating language for building UI. If I were to start over today,
I would still choose Astro.&lt;/p&gt;
&lt;p&gt;But I&#39;ve had an unsettled feeling about Astro and this site, and I think I&#39;ve
finally put my finger on it: Astro is moving too quickly. It feels like every
few weeks they&#39;re putting out a new version with new features and new patterns
to build new websites.&lt;/p&gt;
&lt;p&gt;And as exciting as that is (I&#39;m sure the maintainers are thrilled to be able to
ship new things so quickly), it&#39;s overwhelming for me as a user. I feel like I&#39;m
always behind because I am. I updated the Astro dependencies on this site two
months ago, and I&#39;m already four minor versions behind. That&#39;s two minor
versions per month!&lt;/p&gt;
&lt;p&gt;Now full credit should go to the Astro team for working to make updates
relatively painless. I haven&#39;t encountered any particularly difficult breaking
changes, and they have tooling that helps make the process simpler. But a simple
process is still a process, and it&#39;s one that users have to do a lot to keep
their Astro sites up to date.&lt;/p&gt;
&lt;p&gt;I know that no one is forcing me keep my dependencies on latest; that pressure
is internal. But it&#39;s real, and it impacts how I feel about working with Astro.&lt;/p&gt;
&lt;p&gt;Given two identical frameworks, my preference would be for one that updated
&lt;em&gt;regularly&lt;/em&gt; but less frequently. Quarterly or even twice-yearly updates would be
perfect (patches excluded). Enough to keep you thinking about updating
dependencies but not so often that you&#39;re always behind.&lt;/p&gt;
&lt;p&gt;Sadly for me, that isn&#39;t the reality with Astro. Is that enough to drive me to
choose a different framework? No. But release velocity was not something I
considered when choosing Astro. Now with a little more experience it is
something that I will think about in the future.&lt;/p&gt;
</description>
      <pubDate>Sat, 18 May 2024 01:12:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/astro-and-release-velocity/</guid>
    </item>
    <item>
      <title>Render and effect call order in React</title>
      <link>https://seanmcp.com/articles/render-and-effect-call-order-in-react/</link>
      <description>&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;: Let&#39;s say I have a React tree like this:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Parent&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;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Parent render&quot;&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 function&quot;&gt;useEffect&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;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Parent effect&quot;&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 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 class=&quot;token function&quot;&gt;useLayoutEffect&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;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Parent layout effect&quot;&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 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 class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Child&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Child&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;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Child render&quot;&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 function&quot;&gt;useEffect&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;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Child effect&quot;&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 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 class=&quot;token function&quot;&gt;useLayoutEffect&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;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Child layout effect&quot;&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 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 class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&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;

ReactDOM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&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 function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Parent&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;What order would you expect the logs to fire in? If you were like me, you&#39;d
probably have some mental model of how React works and be able to make an
educated guess. But I was wrong.&lt;/p&gt;
&lt;p&gt;Then I asked this question to a handful of seasoned React developers, and they
all got it wrong.&lt;/p&gt;
&lt;p&gt;Because the real answer is unexpected:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Parent render
Child render
Child layout effect
Parent layout effect
Child effect
Parent effect
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Try it yourself&lt;/strong&gt;:
&lt;a href=&quot;https://stackblitz.com/edit/vitejs-vite-7n9w8p?file=src%2Fmain.tsx&quot;&gt;Here&#39;s a StackBlitz in case you don&#39;t believe me.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attempting an explanation&lt;/strong&gt;: When rendering a tree, React calls the components
from the top of the tree downward. So because &lt;code&gt;Parent&lt;/code&gt; is at the top of the
tree, its render log is called before &lt;code&gt;Child&lt;/code&gt;. So far, so good.&lt;/p&gt;
&lt;p&gt;Effects are a different story. React effectively queues effects from the bottom
of the tree upward, so &lt;code&gt;Child&lt;/code&gt;&#39;s effects are called before &lt;code&gt;Parent&lt;/code&gt;&#39;s. Plus
there is the additional layer that &lt;code&gt;useLayoutEffect&lt;/code&gt; is called before
&lt;code&gt;useEffect&lt;/code&gt; (which, to be fair, does make sense but is one more thing to think
about).&lt;/p&gt;
&lt;p&gt;I ran into this issue at work when a child had code running on render that was
occurring before the parent&#39;s &lt;code&gt;useLayoutEffect&lt;/code&gt; ran. There were several layers
of components between the two, and finding the cause was not fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: I don&#39;t expect you or me or anyone else to remember this order.
But if you can remember that the order is unexpected, then that will put you on
the right path.&lt;/p&gt;
</description>
      <pubDate>Sat, 18 May 2024 24:43:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/render-and-effect-call-order-in-react/</guid>
    </item>
    <item>
      <title>Tweaking garden details</title>
      <link>https://seanmcp.com/articles/tweaking-garden-details/</link>
      <description>&lt;p&gt;I like the &lt;em&gt;idea&lt;/em&gt; of digital gardens, but I haven&#39;t been happy with how I
implemented them on my site. This afternoon as I was thinking about it,
inspiration struck and I redesigned how I illustrate a historical record of
tended dates.&lt;/p&gt;
&lt;p&gt;Instead of a verbose list of &amp;quot;Planted on...&amp;quot;, &amp;quot;Tended X times&amp;quot;, and &amp;quot;Last
tended...&amp;quot;, it is now two dates and a visualization representing the number of
intermediary dates. You can see how this looks for gardens in different states
on &lt;a href=&quot;https://seanmcp.com/gardens&quot;&gt;/gardens&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Check it out and let me know what you think.&lt;/p&gt;
</description>
      <pubDate>Fri, 17 May 2024 17:58:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/tweaking-garden-details/</guid>
    </item>
    <item>
      <title>Manage your photo archive</title>
      <link>https://seanmcp.com/articles/manage-your-photo-archive/</link>
      <description>&lt;p&gt;A coworker at Khan Academy shared some advice (apparently secondhand from the
New York Times) for managing your ever-growing photo archive:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Every day open your photos app&lt;/li&gt;
&lt;li&gt;Search for the day&#39;s date&lt;/li&gt;
&lt;li&gt;Review your old photos from that day&lt;/li&gt;
&lt;li&gt;Delete ones you don&#39;t need anymore&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My first thought was &amp;quot;That&#39;s brilliant!&amp;quot; It&#39;s a low lift but once the habit is
established it ensures that you address every day of the year.&lt;/p&gt;
&lt;p&gt;The first day I tried it, I found this photo from 2017 when I was still
teaching:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/old-photo.jpg&quot; alt=&quot;Directions for a writing (?) assignment written on a whiteboard&quot;&gt;&lt;/p&gt;
&lt;p&gt;I have no memory of this day or what I was expecting the students to do, so I
deleted it. One less photo in the archive!&lt;/p&gt;
&lt;p&gt;With strategies like this, we can reduce the amount of junk data that we all
have hanging around on someone else&#39;s server.&lt;/p&gt;
</description>
      <pubDate>Thu, 09 May 2024 10:36:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/manage-your-photo-archive/</guid>
    </item>
    <item>
      <title>Site update May 2024</title>
      <link>https://seanmcp.com/articles/site-update-may-2024/</link>
      <description>&lt;p&gt;I &lt;a href=&quot;https://github.com/SeanMcP/seanmcp.com/pull/73&quot;&gt;just landed a PR&lt;/a&gt; that made
some changes to this site. It started as an experiment to move from Netlify to
Cloudflare, but then turned into Spring cleaning. Here is a summary of all the
changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Removed dark mode&lt;/strong&gt;: I&#39;m sorry if dark mode was your preference (it was mine
too), but I didn&#39;t like the ongoing cost of maintaining multiple themes. Now
it&#39;s just light mode, but that will change in the future.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Removed &lt;code&gt;/contact&lt;/code&gt;&lt;/strong&gt;: No one used it, and my email address is in the footer
of every page.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Removed likes&lt;/strong&gt;: It was an interesting experiment, but a) it wasn&#39;t valuable
to me and b) it locked me in to specific platform features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Removed analytics&lt;/strong&gt;: I hadn&#39;t checked the analytics in months, and even when
I did it never had an impact on my decisions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Removed Netlify&lt;/strong&gt;: This included Netlify CMS and an &lt;code&gt;/admin&lt;/code&gt; route I never
used, Netlify CLI, Netlify edge functions, and a single Netlify form.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replaced &lt;code&gt;/fn/random&lt;/code&gt; with &lt;a href=&quot;https://seanmcp.com/articles/random&quot;&gt;/articles/random&lt;/a&gt;&lt;/strong&gt;: A feature
for random articles is fun, and this iteration handles everything with
client-side JavaScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that the work is done, I don&#39;t actually have plans to move off Netlify. I
didn&#39;t see the performance improvements that others have reported, so inertia
will keep me here for now&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/site-update-may-2024/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. But I like the idea that I could move quickly
if/when the need arises.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/webdev/comments/1b14bty/netlify_just_sent_me_a_104k_bill_for_a_simple/&quot;&gt;Hopefully I don&#39;t live to regret that decision&lt;/a&gt;. &lt;a href=&quot;https://seanmcp.com/articles/site-update-may-2024/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Sat, 04 May 2024 18:29:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/site-update-may-2024/</guid>
    </item>
    <item>
      <title>Measuring and cutting</title>
      <link>https://seanmcp.com/articles/measuring-and-cutting/</link>
      <description>&lt;p&gt;I learned “measure twice, cut once” while working with my dad. He probably
learned it from his dad and him from his dad. It’s good advice, but in an issue
of &lt;a href=&quot;https://frontendatscale.com/issues/16/&quot;&gt;Frontend at Scale&lt;/a&gt; Maxi Ferreira
suggests that it might not apply neatly to software engineering:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[S]oftware is different, and trying to apply this piece of advice to planning,
designing, and writing applications can sometimes do more harm than good.&lt;/p&gt;
&lt;p&gt;Measuring twice (i.e. considering all possible options and coming up with
the &lt;em&gt;perfect&lt;/em&gt; design or architecture before writing a single line of code) is
not the most effective way to build software. That’s the waterfall approach,
and we know it doesn’t work.&lt;/p&gt;
&lt;p&gt;Instead, we can use the flexibility of software to our advantage and flip the
woodworker’s advice on its head. We can measure once, decide quickly, and cut
as many times as needed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When your materials are (close to) free, you don’t need to spend the extra time
up front getting all of measurements perfect. Instead, get ship something and
spend that time iterating.&lt;/p&gt;
&lt;p&gt;He expands on the idea more in the article. Give it a read and let me know what
you think.&lt;/p&gt;
</description>
      <pubDate>Wed, 24 Apr 2024 17:24:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/measuring-and-cutting/</guid>
    </item>
    <item>
      <title>A Few Things #12</title>
      <link>https://seanmcp.com/articles/a-few-things-12/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;machine-by-xkcd&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://xkcd.com/2916/&quot;&gt;Machine&lt;/a&gt; by XKCD&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-12/#machine-by-xkcd&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Machine by XKCD”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Machine&lt;/em&gt; is a collaborative interactive where you help build a machine that
moves colored balls from point A to point B. Once you have completed your task,
you can take a look at the machines that others have built. It&#39;s really neat and
an interesting change of pace for XKCD.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;africa-by-toto-but-its-played-on-instruments-in-majora&#39;s-mask-by-bigfatfrown&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Av5oRf88aso&quot;&gt;Africa by Toto but its played on instruments in Majora&#39;s Mask&lt;/a&gt; by bigfatfrown&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-12/#africa-by-toto-but-its-played-on-instruments-in-majora&#39;s-mask-by-bigfatfrown&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Africa by Toto but its played on instruments in Majora&#39;s Mask by bigfatfrown”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I really like YouTube, but sometimes I get nostalgic for the simpler, amateur
past. This video is only two years old, but it feels like one from an earlier
era of YouTube.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;truncate%3A-a-word-based-strategy-game&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://truncate.town/&quot;&gt;Truncate: A word-based strategy game&lt;/a&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-12/#truncate%3A-a-word-based-strategy-game&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Truncate: A word-based strategy game”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This one took some time to understand, but it&#39;s a fun little combat-style word
game. It&#39;s a challenging and refreshing alternative to more traditional word
games.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;canabalt&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://canabalt.com/&quot;&gt;Canabalt&lt;/a&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-12/#canabalt&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Canabalt”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I have a soft spot for all Flash games, and I have fond memories of playing this
one on &lt;a href=&quot;http://Flashplayer.com&quot;&gt;Flashplayer.com&lt;/a&gt; (RIP). It&#39;s a simple infinite runner game, but the
artistic style and the feel set it apart. See if you can beat 2000 meters.&lt;/p&gt;
</description>
      <pubDate>Sun, 07 Apr 2024 01:05:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-12/</guid>
    </item>
    <item>
      <title>A Few Things #11</title>
      <link>https://seanmcp.com/articles/a-few-things-11/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;why-i-am-a-christian-by-james-j.-choi&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://faculty.som.yale.edu/jameschoi/whychrist/&quot;&gt;Why I am a Christian&lt;/a&gt; by James J. Choi&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-11/#why-i-am-a-christian-by-james-j.-choi&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Why I am a Christian by James J. Choi”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Choi, a professor of Finance at Yale, shares his reasons for being a Christian.
The whole article is worth reading, but I wanted to highlight the sections
surrounding Jesus&#39; death and resurrection:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Christianity is falsifiable&lt;/strong&gt;&lt;br&gt; MANY RELIGIOUS CLAIMS ARE NOT VERIFIABLE
OR FALSIFIABLE&lt;br&gt;Claim: Religious Figure X received a divine
revelation&lt;br&gt;On what basis would you confirm or deny that claim?&lt;br&gt;&lt;br&gt;
CHRISTIANITY IS DIFFERENT&lt;br&gt;It makes a claim that an event happened in space
and time: Jesus Christ died and then was raised from the dead. And it says, if
this didn’t happen, you should dismiss the entire religion.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;There is credible evidence for Jesus’ death and resurrection&lt;/strong&gt;&lt;br&gt;
Non-Christian philosopher of religion Antony Flew: “The evidence for the
resurrection is better than for claimed miracles in any other religion. It’s
outstandingly different in quality and quantity.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;the-world-is-awful.-the-world-is-much-better.-the-world-can-be-much-better.-by-max-roser-(our-world-in-data)&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://ourworldindata.org/much-better-awful-can-be-better&quot;&gt;The world is awful. The world is much better. The world can be much better.&lt;/a&gt; by Max Roser (Our World in Data)&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-11/#the-world-is-awful.-the-world-is-much-better.-the-world-can-be-much-better.-by-max-roser-(our-world-in-data)&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “The world is awful. The world is much better. The world can be much better. by Max Roser (Our World in Data)”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The world is awful. The world is much better. The world can be much better.
All three statements are true at the same time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This article unpacks some of the challenge that we face when trying to
understand our deeply complex world. The reality is that there are terrible
things happening. &lt;em&gt;And&lt;/em&gt; that the world has improved significantly for many
people. &lt;em&gt;And&lt;/em&gt; there is more that we can do to make the world a better place.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;scotland&#39;s-forgotten-rainforest-by-aidin-robbins&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=0x-WUKT5hUo&quot;&gt;Scotland&#39;s Forgotten Rainforest&lt;/a&gt; by Aidin Robbins&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-11/#scotland&#39;s-forgotten-rainforest-by-aidin-robbins&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Scotland&#39;s Forgotten Rainforest by Aidin Robbins”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This video takes an interesting look into the history of forests in Scotland. I
was impressed with the quality of the video and the detailed descriptions with
links to dive further into the topic. More creators should do that.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;music-map&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://music-map.com&quot;&gt;Music Map&lt;/a&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-11/#music-map&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Music Map”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Finding new music is hard. Even when I try to learn into algorithmic
recommendations, I still find myself gravitating to the old bands and sounds
that I&#39;m used to. Websites like Music Map can help by suggesting artists that
are similar to your favorite bands. I&#39;ve used it to discover new music and
re-discover bands that I&#39;d forgotten.&lt;/p&gt;
</description>
      <pubDate>Sun, 31 Mar 2024 24:59:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-11/</guid>
    </item>
    <item>
      <title>A Few Things #10</title>
      <link>https://seanmcp.com/articles/a-few-things-10/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;lake-retba%3A-the-pink-lake-of-senegal&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://www.lakeretba.com/&quot;&gt;Lake Retba: The Pink Lake of Senegal&lt;/a&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-10/#lake-retba%3A-the-pink-lake-of-senegal&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Lake Retba: The Pink Lake of Senegal”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Lake Retba is one of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Pink_lake&quot;&gt;many pink lakes in the world&lt;/a&gt;, that
are colored by bacteria or algae. The lake is a source of salt production for
the surrounding communities in addition to being a tourist attraction.&lt;/p&gt;
&lt;p&gt;More photos: &lt;a href=&quot;https://africageographic.com/stories/pink-lake-retba-senegal/&quot;&gt;https://africageographic.com/stories/pink-lake-retba-senegal/&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;how-farmers-reshaped-a-region-and-solved-drought-by-andrew-millison&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=79VUAFq2rbg&quot;&gt;How Farmers Reshaped a Region and Solved Drought&lt;/a&gt; by Andrew Millison&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-10/#how-farmers-reshaped-a-region-and-solved-drought-by-andrew-millison&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “How Farmers Reshaped a Region and Solved Drought by Andrew Millison”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This is the second video that I&#39;ve shared from Andrew Millison (see
&lt;a href=&quot;https://seanmcp.com/articles/a-few-things-6&quot;&gt;A few things #6&lt;/a&gt;). In this video, he learns about a
community in northern India that used permaculture to transform environment for
the better over the course of 45 years.&lt;/p&gt;
&lt;p&gt;I was struck by the long-term commitment to collective action for the benefit of
all. That&#39;s the stuff that it feels like we can only dream of here in the United
States.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;did-idaho-get-its-name-as-a-result-of-a-hoax%3F-(hoaxes.org)&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://hoaxes.org/weblog/comments/did_idaho_get_its_name_as_a_result_of_a_hoax&quot;&gt;Did Idaho Get Its Name As A Result Of A Hoax?&lt;/a&gt; (&lt;a href=&quot;http://hoaxes.org&quot;&gt;hoaxes.org&lt;/a&gt;)&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-10/#did-idaho-get-its-name-as-a-result-of-a-hoax%3F-(hoaxes.org)&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Did Idaho Get Its Name As A Result Of A Hoax? (hoaxes.org)”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The US state of Idaho may have been named a man making up a Native-American
word. He initially proposed &amp;quot;Idaho&amp;quot; as a name for Colorado which was rejected.
But his
&lt;a href=&quot;https://en.wikipedia.org/wiki/Idaho#Etymology&quot;&gt;made-up word that meant &amp;quot;gem of the mountains&amp;quot;&lt;/a&gt;
was planted in the minds of many, and eventually became the name of a different
state.&lt;/p&gt;
&lt;p&gt;This is, if Idaho even exists at all:
&lt;a href=&quot;http://www.fantasymaps.com/stuff/idaho.html&quot;&gt;http://www.fantasymaps.com/stuff/idaho.html&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Sat, 23 Mar 2024 10:55:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-10/</guid>
    </item>
    <item>
      <title>Create a custom search for a static site</title>
      <link>https://seanmcp.com/articles/create-a-custom-search-for-a-static-site/</link>
      <description>&lt;p&gt;There are out-of-the-box solutions for searching on a static site. I previously
used &lt;a href=&quot;https://pagefind.app/&quot;&gt;Pagefind&lt;/a&gt; for this site which remains a great
option. But if you want fully control of the behavior and appearance of your
search feature, then you might have to build your own.&lt;/p&gt;
&lt;p&gt;After thinking through how I wanted to use a search feature, I came up with
these acceptance criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Query and filter by type, year, and tag&lt;/strong&gt;: I want to be able to view all
Articles from 2023 tagged JavaScript with the text “class”.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fuzzy search&lt;/strong&gt;: Before Pagefind I had a search that used strict string
matching, but the experience was poor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URL-powered&lt;/strong&gt;: I want to use it as a custom search engine in the browser and
be able to link directly to filtered search results.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With those criteria established, I could move on to designing and building the
solution.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;solution&quot; tabindex=&quot;-1&quot;&gt;Solution&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-custom-search-for-a-static-site/#solution&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Solution”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Without going into too many details about
&lt;a href=&quot;https://github.com/SeanMcP/seanmcp.com/blob/bf0792380cc13f6c3b79d30121a78c9c5548f225/src/pages/search.astro&quot;&gt;how I accomplished this in Astro&lt;/a&gt;,
here are the high-level steps I followed to build a custom search:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Gather all of my content into a normalized array:&lt;/strong&gt; As I’ve written
previously,
&lt;a href=&quot;https://www.seanmcp.com/articles/normalizing-data-is-a-good-idea/&quot;&gt;normalizing data is a good idea&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expose the data to client-side JavaScript:&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;Astro has a helpful &lt;code&gt;define:vars&lt;/code&gt; directive for this, but your static-site
generator has some kind of solution for hand off data with a &lt;code&gt;script&lt;/code&gt; tag.&lt;/li&gt;
&lt;li&gt;The important part for me was to avoid network requests for the content
data; I wanted this to be available at build time.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In JS sync HTML with URL:&lt;/strong&gt; Read the search parameters and then update the
in-page form elements with the data. For this solution, the URL is the source
of truth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create a function to search and render:&lt;/strong&gt; I wanted the results to render on
load and whenever there was a change to the search form. By encapsulating
that logic in a function, I was able to call it whenever I wanted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;a href=&quot;https://www.fusejs.io/&quot;&gt;Fuse.js&lt;/a&gt; to search through content:&lt;/strong&gt; This is
the first time I’ve used Fuse and it was awesome. I added it from a CDN with
a script tag, and it worked without any issues.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Render:&lt;/strong&gt; Take the results from Fuse and add some markup to an &lt;code&gt;output&lt;/code&gt;
element.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add event listeners for &lt;code&gt;change&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt;, and &lt;code&gt;submit&lt;/code&gt;:&lt;/strong&gt; These were all
added to the &lt;code&gt;form&lt;/code&gt; element to a) sync URL with form state, and b) render
updated results.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can view the finished product at
&lt;a href=&quot;https://seanmcp.com/search&quot;&gt;seanmcp.com/search&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://seanmcp.com/search/?q=JavaScript&quot;&gt;seanmcp.com/search/?q=JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seanmcp.com/search/?type=note&quot;&gt;seanmcp.com/search/?type=note&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seanmcp.com/search/?tag=A+Few+Things&quot;&gt;seanmcp.com/search/?tag=A+Few+Things&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seanmcp.com/search/?year=2024&quot;&gt;seanmcp.com/search/?year=2024&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seanmcp.com/search/?q=class&amp;amp;type=article&amp;amp;year=2023&amp;amp;tag=JavaScript&quot;&gt;seanmcp.com/search/?q=class&amp;amp;type=article&amp;amp;year=2023&amp;amp;tag=JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;lessons-learned&quot; tabindex=&quot;-1&quot;&gt;Lessons learned&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-custom-search-for-a-static-site/#lessons-learned&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Lessons learned”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Pushing search parameters to the URL without reloading is easier than I
thought. I really like the idea of using the URL as the state for a feature
like this.&lt;/li&gt;
&lt;li&gt;The default &lt;code&gt;select[multiple]&lt;/code&gt; UI is difficult to design around. I ended up
limiting the filters to a single selection because I couldn’t find a layout
that I liked with multiple.&lt;/li&gt;
&lt;li&gt;Fuse.js was a delight, and I look forward to more opportunities to use it.&lt;/li&gt;
&lt;li&gt;I thought that I would need to debounce changes from the form, but in my
testing it handled individual keypresses without any issue.&lt;/li&gt;
&lt;li&gt;Astro will scope styles for you, but you can only reference elements that are
currently in the file. This is a problem when you will be building a UI with
client JavaScript. Thankfully Astro works with &lt;code&gt;template&lt;/code&gt;s, so you can style a
sample response element and style it without resorting to &lt;code&gt;:global()&lt;/code&gt; calls.&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Fri, 22 Mar 2024 19:08:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/create-a-custom-search-for-a-static-site/</guid>
    </item>
    <item>
      <title>Fix missing Astro files on GitHub pages</title>
      <link>https://seanmcp.com/articles/fix-missing-astro-files-on-github-pages/</link>
      <description>&lt;p&gt;I&#39;m in the middle of a long project to migrate my projects from Netlify to
GitHub pages. I was working on the GitHub actions for
&lt;a href=&quot;https://shama.seanmcp.com&quot;&gt;an Astro project&lt;/a&gt; and noticed that most of the CSS
files were missing from the assets directory.&lt;/p&gt;
&lt;p&gt;When I built the project locally, I saw the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_book_.95a15f93.css
_book_.ef58a27b.css
_chapter_.02b05af9.css
index.3e3ecbbf.css
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;index.3e3ecbbf.css&lt;/code&gt; file was the only CSS one that was successfully
deployed. That led me to think that there was something about the underscore
prefix that was causing the other files to be ignored.&lt;/p&gt;
&lt;p&gt;GitHub Pages uses Jekyll behind the scenes, even if your site doesn&#39;t use it.
And
&lt;a href=&quot;https://github.com/jekyll/jekyll/issues/55&quot;&gt;Jekyll ignores files and directories that start with an underscore&lt;/a&gt;.
So the solution is to tell GitHub to skip Jekyll processing of your build
directory with a &lt;code&gt;.nojekyll&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;In the GitHub Action for the Astro project, I added the following line to the
build step:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;touch dist/.nojekyll&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that was added, all of the generated CSS files were successfully deployed
to GitHub Pages.&lt;/p&gt;
&lt;p&gt;There is also an
&lt;a href=&quot;https://github.com/withastro/action&quot;&gt;official action for Astro projects&lt;/a&gt; that
probably handles that for you.&lt;/p&gt;
</description>
      <pubDate>Wed, 20 Mar 2024 01:12:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-missing-astro-files-on-github-pages/</guid>
    </item>
    <item>
      <title>Create a useIsOnline React hook</title>
      <link>https://seanmcp.com/articles/create-a-use-is-online-react-hook/</link>
      <description>&lt;p&gt;There are two browser features that enable you to detect an internet connection
with JavaScript:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Navigator interface&#39;s &lt;code&gt;onLine&lt;/code&gt; property for the current state&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;online&lt;/code&gt; and &lt;code&gt;offline&lt;/code&gt; events for state changes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By combining these, we can create a custom React hook that tells us if the
browser is currently online.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Check if window is available&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; maybeWindow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; window &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;undefined&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useIsOnline&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 keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;isOnline&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setIsOnline&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Read onLine value from navigator if available, otherwise fallback to true&lt;/span&gt;
    maybeWindow&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onLine &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 function&quot;&gt;useEffect&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;span class=&quot;token comment&quot;&gt;// Add event listeners on mount&lt;/span&gt;
    maybeWindow&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;online&quot;&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 function&quot;&gt;setIsOnline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&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;
    maybeWindow&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;offline&quot;&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 function&quot;&gt;setIsOnline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&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 comment&quot;&gt;// Remove event listeners on unmount&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&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;
      maybeWindow&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;online&quot;&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 function&quot;&gt;setIsOnline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&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;
      maybeWindow&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;offline&quot;&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 function&quot;&gt;setIsOnline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&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 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 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 comment&quot;&gt;// Return the online status&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isOnline&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;We can use this hook to display a banner when the user is running our app and
then goes offline:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&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 keyword&quot;&gt;const&lt;/span&gt; isOnline &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useIsOnline&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;isOnline &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;alert&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
                    You are offline. Changes will be synced when you reconnect.
                &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&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 plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    )
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In newer versions of React you can
&lt;a href=&quot;https://github.com/uiwjs/react-use-online&quot;&gt;use a hook like &lt;code&gt;useOnline&lt;/code&gt;&lt;/a&gt; which
depends on
&lt;a href=&quot;https://react.dev/reference/react/useSyncExternalStore&quot;&gt;&lt;code&gt;useSyncExternalStore&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Tue, 19 Mar 2024 19:35:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/create-a-use-is-online-react-hook/</guid>
    </item>
    <item>
      <title>Fix static file endpoints after Astro v4 upgrade</title>
      <link>https://seanmcp.com/articles/fix-static-file-endpoints-after-astro-v4-upgrade/</link>
      <description>&lt;p&gt;Yesterday I
&lt;a href=&quot;https://seanmcp.com/articles/upgrading-from-astro-v2-to-v4&quot;&gt;upgraded to Astro v4 without any issues&lt;/a&gt;,
but this morning I noticed that my RSS feed was broken.&lt;/p&gt;
&lt;p&gt;I have two static file endpoints that generate a file at build time, one for RSS
and the other for a webfinger. Both of these had two issues highlighted after
the upgrade:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The files did not export a &lt;code&gt;GET&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;The files did not return a &lt;code&gt;Response&lt;/code&gt; or &lt;code&gt;Promise&amp;lt;Response&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first issue was resolved by capitalizing the previously-valid &lt;code&gt;get&lt;/code&gt; function
to &lt;code&gt;GET&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second issue required an update to the &lt;code&gt;@astrojs/rss&lt;/code&gt; dependency which now
returns a &lt;code&gt;Promise&amp;lt;Response&amp;gt;&lt;/code&gt;. The webfinger endpoint is custom, but all I
needed to do was wrap my return value with a &lt;code&gt;Response&lt;/code&gt; object:&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;// Before&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&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 comment&quot;&gt;// After&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;The webfinger issue is easy to miss (and nonconsequential), but I should have
verified that the RSS feed was working before shipping the upgrade. Lessons
learned!&lt;/p&gt;
</description>
      <pubDate>Tue, 19 Mar 2024 10:03:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-static-file-endpoints-after-astro-v4-upgrade/</guid>
    </item>
    <item>
      <title>Upgrading from Astro v2 to v4</title>
      <link>https://seanmcp.com/articles/upgrading-from-astro-v2-to-v4/</link>
      <description>&lt;call-out type=&quot;info&quot;&gt;
&lt;p&gt;&lt;strong&gt;Update March 19, 2024&lt;/strong&gt;: I missed some
&lt;a href=&quot;https://seanmcp.com/articles/fix-static-file-endpoints-after-astro-v4-upgrade&quot;&gt;issues with static file endpoints&lt;/a&gt;
that I needed to fix after the upgrade.&lt;/p&gt;
&lt;/call-out&gt;
&lt;p&gt;In a &lt;a href=&quot;https://shoptalkshow.com/607/&quot;&gt;recent episode of the ShopTalk podcast&lt;/a&gt;,
Chris Coyier and Dave Rupert interviewed Fred K. Schott about
&lt;a href=&quot;https://astro.build/blog/astro-db/&quot;&gt;new features in Astro&lt;/a&gt;. That reminded me
that I need to upgrade this site from &lt;code&gt;2.6.1&lt;/code&gt; to &lt;code&gt;4.5.6&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is a log of the process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;pnpm install astro@latest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Peer dependency issues
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;pnpm install typescript@latest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pnpm install @astrojs/mdx@latest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pnpm install @astrojs/svelte@latest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pnpm install svelte@latest&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Tried &lt;code&gt;pnpm run build&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Build error on articles: &lt;code&gt;localStorage is not defined&lt;/code&gt;
&lt;ol&gt;
&lt;li&gt;Found a reference to &lt;code&gt;localStorage&lt;/code&gt; in a Svelte component in the last page
generated by Astro&lt;/li&gt;
&lt;li&gt;Remove all direct references to &lt;code&gt;localStorage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;That fixed it&lt;/li&gt;
&lt;li&gt;Read
&lt;a href=&quot;https://docs.astro.build/en/reference/directives-reference/#clientonly&quot;&gt;&lt;code&gt;client:only&lt;/code&gt; documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Realized that is what I wanted&lt;/li&gt;
&lt;li&gt;Reverted changes to Svelte component&lt;/li&gt;
&lt;li&gt;Added &lt;code&gt;client:only&lt;/code&gt; to MDX files that referenced the component&lt;/li&gt;
&lt;li&gt;That fixed it&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Tried &lt;code&gt;pnpm run build&lt;/code&gt; again&lt;/li&gt;
&lt;li&gt;It worked&lt;/li&gt;
&lt;li&gt;Netlify build failed:&lt;pre&gt;&lt;code&gt;9:32:21 PM: Node.js v18.12.1 is not supported by Astro!
9:32:21 PM: Please upgrade Node.js to a supported version: &amp;quot;&amp;gt;=18.14.1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Updated &lt;code&gt;NODE_VERSION&lt;/code&gt; environment variable to LTS &lt;code&gt;20.11.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;That fixed it&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Done&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I get a little nervous reading about the complexity of Astro--especially because
I&#39;m using it to build this simple site. But in my experience, all of the
upgrades have gone smoothly. (Edit: they even have
&lt;a href=&quot;https://docs.astro.build/en/guides/upgrade-to/v4/#upgrade-astro&quot;&gt;an upgrade tool&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The Astro team deserves a lot of credit for engineering a tool that is
feature-rich while managing the complexity for the developer. I hope they can
continue that as they grow.&lt;/p&gt;
</description>
      <pubDate>Tue, 19 Mar 2024 01:44:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/upgrading-from-astro-v2-to-v4/</guid>
    </item>
    <item>
      <title>A Few Things #9</title>
      <link>https://seanmcp.com/articles/a-few-things-9/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;when-can-you-hear-yourself-think%3F-by-greg-gilbert&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://www.ggnotes.com/when-can-you-hear-yourself-think/&quot;&gt;When can you hear yourself think?&lt;/a&gt; by Greg Gilbert&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-9/#when-can-you-hear-yourself-think%3F-by-greg-gilbert&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “When can you hear yourself think? by Greg Gilbert”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Gilbert reflects on the modern (maybe?) impulse to continuously consume media.
News, audiobooks, podcasts, music: &amp;quot;we fool ourselves into thinking we are doing
something meaningful and productive.&amp;quot; But the reality more often is cluttered
minds and unorganized thoughts.&lt;/p&gt;
&lt;p&gt;The antidote is quite time in our own mind. &amp;quot;We need to hear ourselves think on
a regular basis. The easiest way to do that is to unplug from
&#39;knowledgetainement&#39; when we perform mundane tasks like emptying the
dishwasher.&amp;quot;&lt;/p&gt;
&lt;p&gt;I think that washing the dishes can be a great time to catch up on podcasts or
YouTube videos. But too frequently I feel myself reaching for media just to fill
the void. Gilbert&#39;s article gives me something to thinking about.&lt;/p&gt;
&lt;p&gt;h/t &lt;a href=&quot;https://evantravers.com/&quot;&gt;Evan Travers&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;making-a-medieval-book-by-four-keys-book-arts&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=lQ50XdqfG00&quot;&gt;Making A Medieval Book&lt;/a&gt; by Four Keys Book Arts&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-9/#making-a-medieval-book-by-four-keys-book-arts&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Making A Medieval Book by Four Keys Book Arts”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This video compresses 60 hours of expert bookmaking into 24 minutes. I haven&#39;t
thought much about how books are made before, but I really appreciated the care
and attention to detail. Making books is an art.&lt;/p&gt;
&lt;p&gt;For a more accessible look at bookmaking,
&lt;a href=&quot;https://www.youtube.com/watch?v=OzsBxpPtvoM&quot;&gt;checkout Adam Savage on Tested&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;semantic-satiation-(wikipedia)&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Semantic_satiation&quot;&gt;Semantic Satiation&lt;/a&gt; (Wikipedia)&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-9/#semantic-satiation-(wikipedia)&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Semantic Satiation (Wikipedia)”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This week I wrote about the term &amp;quot;blog&amp;quot;, and it didn&#39;t take long before the word
felt weird in the mouth and sounded strange to my eat. That phenomenon has a
name: semantic satiation.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;matthew-wolff-design&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://matthewwolff.com/&quot;&gt;Matthew Wolff Design&lt;/a&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-9/#matthew-wolff-design&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Matthew Wolff Design”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I really like &lt;a href=&quot;https://seanmcp.com/soccer&quot;&gt;soccer&lt;/a&gt;, and I really like logo design. I was pleasantly
surprised to learn that almost all of my favorite American soccer logos were
designed by the same guy: Matthew Wolf.&lt;/p&gt;
</description>
      <pubDate>Sun, 17 Mar 2024 24:10:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-9/</guid>
    </item>
    <item>
      <title>On articles and blogs</title>
      <link>https://seanmcp.com/articles/on-articles-and-blogs/</link>
      <description>&lt;p&gt;My first memory of the word “blog” was my four-years-older brother complaining
that he didn’t like the term. “It’s a web log,” he insisted, “not a buh-lawg!” I
don’t know why he felt so passionately, but as a dutiful little brother I
adopted his position without question.&lt;/p&gt;
&lt;p&gt;So from my earliest days online I have avoided the word blog. Occasionally I
used “web log” or just “log”, but even when I was writing on Blogger I didn’t
refer to my site or my writing as a “blog”.&lt;/p&gt;
&lt;p&gt;After college, I spent some time as an amateur soccer journalist covering the
lower leagues in my area. I have fond memories of interviewing players and staff
and writing about their experiences (though most of what I wrote were
less-memorable match reports).&lt;/p&gt;
&lt;p&gt;In journalism, you write the news. You write articles. The word feels more
dignified. An article is professional. An article is something worth
writing—worth reading.&lt;/p&gt;
&lt;p&gt;When I begin writing as a software engineer, I continued with that term. That’s
what I used on this site, both as a label and URL.&lt;/p&gt;
&lt;p&gt;But as time passes, I’m starting to reevaluate my position. I don’t actually
have a problem with the word blog (though my brother still might!). And calling
what I write here “articles” is a stretch. “Blog” is the common term, and a blog
is what I have.&lt;/p&gt;
&lt;p&gt;I’m not going to change my site based on this realization. After all,
&lt;a href=&quot;https://www.w3.org/Provider/Style/URI&quot;&gt;cool URLs don’t change&lt;/a&gt;. But I am going
to describe this site differently. Instead of telling people “I write articles
on my website”, I’m going to try “I write posts on my blog.”&lt;/p&gt;
&lt;p&gt;It&#39;s more accurate and more helpful, so I&#39;m going with it (sorry bro!).&lt;/p&gt;
&lt;p&gt;Thanks for reading my blog!&lt;/p&gt;
</description>
      <pubDate>Sat, 09 Mar 2024 19:07:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/on-articles-and-blogs/</guid>
    </item>
    <item>
      <title>A Few Things #8</title>
      <link>https://seanmcp.com/articles/a-few-things-8/</link>
      <description>&lt;p&gt;I&#39;m trying something different with the formatting this week.
&lt;a href=&quot;mailto:sean@seanmcp.com&quot;&gt;Let me know&lt;/a&gt; what you think.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;leaf-letters%3A-fan-email-for-melbourne&#39;s-trees-pours-in-from-around-the-world-by-monica-tan-(the-guardian)&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://www.theguardian.com/australia-news/2015/jul/15/leaf-letters-fan-mail-melbourne-trees-pours-in-around-the-world&quot;&gt;Leaf letters: fan email for Melbourne&#39;s trees pours in from around the world&lt;/a&gt; by Monica Tan (The Guardian)&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-8/#leaf-letters%3A-fan-email-for-melbourne&#39;s-trees-pours-in-from-around-the-world-by-monica-tan-(the-guardian)&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Leaf letters: fan email for Melbourne&#39;s trees pours in from around the world by Monica Tan (The Guardian)”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Melbourne Australia registered email address for trees so that people could
report damages. Instead people sent kind messages:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello, dear Tree. I read about this wonderful project and suppose to write you
from another side of Earth - Russia. I hope you have a good care and don’t
sick. One day we will meet, may be.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I hope the same for you, dear reader.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;where-did-all-the-roaches-go%3F-by-radio-atlantic-via-search-engine&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://pjvogt.substack.com/p/where-did-all-the-roaches-go&quot;&gt;Where did all the roaches go?&lt;/a&gt; by Radio Atlantic via Search Engine&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-8/#where-did-all-the-roaches-go%3F-by-radio-atlantic-via-search-engine&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Where did all the roaches go? by Radio Atlantic via Search Engine”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I wouldn&#39;t have thought that I would be recommending media about cockroaches,
but that&#39;s long-form narrative podcasting for you! Although it is billed as the
third part of a series on animals from the Search Engine podcast (which I
recommend), it&#39;s really an episode from Radio Atlantic (which I hadn&#39;t heard
of).&lt;/p&gt;
&lt;p&gt;I&#39;m too young to remember the cockroach days, but I remember lots of references
from my youth that didn&#39;t match reality. Pixar&#39;s Wall-E is a good example of the
lasting legacy of roaches in popular media.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;one-year-to-make-a-game---or-be-a-janitor-forever---devlog-0-by-wyrmyr-games&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Dh9mhtEFUtg&quot;&gt;One Year to Make a Game - Or Be a Janitor Forever - Devlog 0&lt;/a&gt; by Wyrmyr Games&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-few-things-8/#one-year-to-make-a-game---or-be-a-janitor-forever---devlog-0-by-wyrmyr-games&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “One Year to Make a Game - Or Be a Janitor Forever - Devlog 0 by Wyrmyr Games”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I&#39;m a fan of the &amp;quot;Game Dev Log&amp;quot; genre on YouTube, and subscribe to a few
different channels. I don&#39;t have a passion for game development, but I
appreciate documenting the building process.&lt;/p&gt;
&lt;p&gt;This particular video is a small creator who is trying to get into game
development while also working full-time as a cleaner. His fish-feeding
simulator idea is intriguing, and I hope that he is able to meet his goals.&lt;/p&gt;
</description>
      <pubDate>Sat, 09 Mar 2024 10:40:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-8/</guid>
    </item>
    <item>
      <title>Why Microsoft Edge?</title>
      <link>https://seanmcp.com/articles/why-microsoft-edge/</link>
      <description>&lt;p&gt;I like trying out new browsers and try to avoid being tethered to one for too
long. But ever since its switch to Chromium, I find myself coming back to
Microsoft Edge.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Powered by Chromium&lt;/strong&gt;: I have issues with recent releases of Chrome, but
now that Edge is powered by the same underlying technology I don&#39;t need to
choose.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Size conscientious&lt;/strong&gt;: Screen real estate is precious, and Edge is the only
cross-platform browser that cares about conserving it. The combination of
vertical tabs and minimal UI makes it the clear winner for smaller screens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customizable UI&lt;/strong&gt;: Chrome has started adding buttons to the toolbar that
you can&#39;t remove. Edge allows you to decide what icons make sense for you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unobtrusive AI&lt;/strong&gt;: I don&#39;t use Copilot features often, but it&#39;s there when I
need it and out-of-the-way when I don&#39;t.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DevTools improvements&lt;/strong&gt;: I like the vertical activity bar and the built-in
accessibility checks.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you haven&#39;t tried the new Edge, I recommend giving it a look.&lt;/p&gt;
</description>
      <pubDate>Wed, 06 Mar 2024 17:59:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/why-microsoft-edge/</guid>
    </item>
    <item>
      <title>Switching from Ubuntu to Linux Mint</title>
      <link>https://seanmcp.com/articles/switching-from-ubuntu-to-linux-mint/</link>
      <description>&lt;p&gt;Ubuntu was running slow on my old personal laptop. Previously I had turned to
Lubuntu, but I read online that Linux Mint has slighly better performance on
low-powered devices. So with a free morning I decided to give that a go.&lt;/p&gt;
&lt;p&gt;Setup steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Customized the colors and wallpaper&lt;/li&gt;
&lt;li&gt;Switched trackpad scroll from &amp;quot;Reversed&amp;quot; to normal (&lt;em&gt;i.e.&lt;/em&gt; down goes down)&lt;/li&gt;
&lt;li&gt;Installed some key programs with Software Manager&lt;/li&gt;
&lt;li&gt;Removed some bundled software that I won&#39;t need&lt;/li&gt;
&lt;li&gt;Set up a custom search engine in the browser&lt;/li&gt;
&lt;li&gt;Installed git with &lt;code&gt;apt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Installed Node with &lt;code&gt;nvm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set up SSH for GitHub&lt;/li&gt;
&lt;li&gt;Cloned this repo and started writing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First impressions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It seems to run faster than Ubuntu&lt;/li&gt;
&lt;li&gt;It generally looks good (though I&#39;m not sold on the &amp;quot;LM&amp;quot; icon)&lt;/li&gt;
&lt;li&gt;The window management is nice&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I first installed VS Code with Software Manager, but 1) the terminal was not
loading the correct shell, and 2) the icon was non standard. I downloaded the
&lt;code&gt;.deb&lt;/code&gt; directly from VS Code and installed to fix those problems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How can I get an emoji picker?&lt;/li&gt;
&lt;li&gt;How do I configure the panel clock to use 12-hour time?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notes on improving the process for me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I haven&#39;t setup settings sync for VS Code, and getting everything running from
scratch isn&#39;t fun. Figure out how to do that for the future.&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Wed, 06 Mar 2024 17:24:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/switching-from-ubuntu-to-linux-mint/</guid>
    </item>
    <item>
      <title>A Few Things #7</title>
      <link>https://seanmcp.com/articles/a-few-things-7/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://seanmcp.com/articles/a-few-things-6&quot;&gt;In #6&lt;/a&gt; I shared &lt;a href=&quot;https://triviapig.com/&quot;&gt;Trivia Pig&lt;/a&gt;,
the Wordle-inspired trivia game. After playing it for a week I can confidently
say that I am not good at trivia. But my wife and I have been playing every
night before bed, and it&#39;s been fun to share our successes (but mostly failures)
together.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://explorer.opensyllabus.org/&quot;&gt;Open Syllabus: Explorer&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;Recently
I was looking to find a popular textbook for a given topic. That search led
me to Open Syllabus, a database of over 7 million college syllabi from across
the world. You can find the most commonly assigned for a given area of study.
Pretty niche, but it&#39;s great to have in your pocket for later.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=uw9SMpywYHY&quot;&gt;&lt;em&gt;Best Trash Bag? Let’s Find Out!&lt;/em&gt;&lt;/a&gt;&lt;/strong&gt;
by Project Farm&lt;br&gt;This YouTube channel has videos of comprehensive tests
for competing consumer products. In this video they test trash bags to see
which are the best based on their metrics. I love that they do what I think
of doing when I&#39;m at the store but know that I don&#39;t have time for. It&#39;s an
interesting watch, but
&lt;a href=&quot;https://youtu.be/uw9SMpywYHY?&amp;amp;t=1360&quot;&gt;click here for the results&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://shawcat.itch.io/living-for-plants&quot;&gt;&lt;em&gt;Living for Plants&lt;/em&gt;&lt;/a&gt; by
shawcat&lt;/strong&gt;&lt;br&gt;Play as a skeleton caring for their plants. I had plans to
build a very similar game, but this is 10 times better than anything I would
have created!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://obviousplant.com/&quot;&gt;Obvious Plant Store&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;My wife showed me
this store, and we both shared a good laugh. You will either think these are
funny or you won&#39;t. Which I now realize is true of all things but
particularly these.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <pubDate>Sat, 02 Mar 2024 20:40:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-7/</guid>
    </item>
    <item>
      <title>A Few Things #6</title>
      <link>https://seanmcp.com/articles/a-few-things-6/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://css-irl.info/design-patterns-that-encourage-junk-data/&quot;&gt;&lt;em&gt;Design Patterns that Encourage Junk Data&lt;/em&gt;&lt;/a&gt;
by Michelle Barker&lt;/strong&gt;&lt;br&gt;I don&#39;t think the thought has occurred to me before,
but our digital footprints are huge and ever expanding. And all of that data,
from old forum accounts to Google Docs from high school to random photos on
various platforms needs to live somewhere.&lt;/p&gt;
&lt;p&gt;Personally, I &lt;em&gt;like&lt;/em&gt; being able to access all of that. I &lt;em&gt;like&lt;/em&gt; that data
feels permanent. But maybe that&#39;s because I don&#39;t bear the cost of that
availability. And maybe I should think about more and more deeply. Good food
for thought.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=WCli0gyNwL0&quot;&gt;&lt;em&gt;How the UN is Holding Back the Sahara&lt;/em&gt;&lt;/a&gt;&lt;/strong&gt;
by Andrew Millison&lt;br&gt;The UN is working with local communities in the
African Sahel to build a &amp;quot;Great Green Wall&amp;quot; to 1) reclaim low-quality
soil, 2) increase local agriculture, and 3) prevent the southward spread of
the Sahara. For more information, checkout
&lt;a href=&quot;https://www.thegreatgreenwall.org/&quot;&gt;thegreatgreenwall.org&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://digitalcollections.tcd.ie/concern/works/hm50tr726?locale=en&quot;&gt;&lt;em&gt;The Book of Kells&lt;/em&gt; Collection&lt;/a&gt;
at Trinity College Dublin&lt;/strong&gt;&lt;br&gt;After last week&#39;s article about text
illuminations, I wanted to track down the most famous illuminated text: The
Book of Kells. The detail of the illustrations is stunning, and the colors
are beautiful. Take a few minutes and explore the pages of this 1200-year-old
book.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://triviapig.com/&quot;&gt;Trivia Pig&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;A fun Wordle-style game with
daily trivia questions in 8 different categories. I don&#39;t see it being a
runaway success, but it&#39;s good for a quick diversion.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <pubDate>Sat, 24 Feb 2024 20:40:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-6/</guid>
    </item>
    <item>
      <title>Running Ollama without a GPU</title>
      <link>https://seanmcp.com/articles/running-ollama-without-a-gpu/</link>
      <description>&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; You &lt;em&gt;can&lt;/em&gt; run Ollama on an older device, but the response will be slow
and/or low quality.&lt;/p&gt;
&lt;p&gt;I have successfully run Ollama with a new Macbook M2 and a mid-range gaming PC,
but I wanted to experiment using an older computer. My personal laptop is a 2017
Lenovo Yoga with Ubuntu and no graphics card. Here are some specs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU: Intel i5-7200U CPU @ 2.50GHz&lt;/li&gt;
&lt;li&gt;RAM: 4GB&lt;/li&gt;
&lt;li&gt;Memory: 128GB SSD&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Following the setup instructions for Linux, Ollama installed fine but printed
the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WARNING: No NVIDIA GPU detected. Ollama will run in CPU-only mode.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was foreshadowing for everything to follow.&lt;/p&gt;
&lt;p&gt;I decided to run &lt;code&gt;mistrel&lt;/code&gt; and sent the model a prompt by the terminal. The
response was streaming in at about one character every four seconds, and my
computer was obviously struggling with the task. I held on for around three
minutes but then had to quit when the response was less than on sentence.&lt;/p&gt;
&lt;p&gt;That seemed conclusive enough, but I decided to try with a smaller model. I ran
&lt;code&gt;tinyllama&lt;/code&gt; and was able to stream responses at a normal rate. However, the
quality of the responses was terrible. I don&#39;t have experience with this model
on a more powerful machine, so I can&#39;t blame the model. But the responses were
so bad that I wouldn&#39;t use it for anything on this device.&lt;/p&gt;
&lt;p&gt;These results were disappointing, but the reality is that running LLMs is
resource intensive. So if you want to try running LLMs locally with Ollama, make
sure you have a modern CPU or a dedicated GPU.&lt;/p&gt;
</description>
      <pubDate>Sat, 24 Feb 2024 10:48:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/running-ollama-without-a-gpu/</guid>
    </item>
    <item>
      <title>Use Ollama with the official Python library</title>
      <link>https://seanmcp.com/articles/use-ollama-with-the-official-python-library/</link>
      <description>&lt;p&gt;Ollama is a great way to get started with AI by using open-source and publically
available large-language models locally on your computer. I wrote previously
about
&lt;a href=&quot;https://seanmcp.com/articles/get-started-prompt-engineering-with-local-llms&quot;&gt;how to get started with the experimental OpenAI API&lt;/a&gt;,
but
&lt;a href=&quot;https://github.com/ollama/ollama-python&quot;&gt;Ollama has a dedicated Python library&lt;/a&gt;
that is even simpler.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install the library: &lt;code&gt;pip3 install ollama&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new python file: &lt;code&gt;touch completion.py&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to &lt;code&gt;completion.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; ollama

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_completion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prompt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ollama&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chat&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; messages&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 punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;user&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;content&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prompt&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 class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#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 string&quot;&gt;&#39;content&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

prompt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;What is the chief end of man?&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;get_completion&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prompt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mistral&quot;&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;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the file: &lt;code&gt;python3 completion.py&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There is more information available in the
&lt;a href=&quot;https://github.com/ollama/ollama-python?tab=readme-ov-file#api&quot;&gt;library repo on GitHub&lt;/a&gt;,
including examples for streaming responses and a custom client. For even more
documentation on Ollama, check out the
&lt;a href=&quot;https://github.com/ollama/ollama/tree/main/docs&quot;&gt;&lt;code&gt;/docs&lt;/code&gt; directory in the main repo&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Wed, 21 Feb 2024 17:06:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/use-ollama-with-the-official-python-library/</guid>
    </item>
    <item>
      <title>A Few Things #5</title>
      <link>https://seanmcp.com/articles/a-few-things-5/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.frontiersin.org/journals/psychology/articles/10.3389/fpsyg.2023.1219945/full&quot;&gt;&lt;em&gt;Handwriting but not typewriting leads to widespread brain connectivity&lt;/em&gt;&lt;/a&gt;
by Van der Weel &lt;em&gt;et al&lt;/em&gt;.&lt;/strong&gt;&lt;br&gt;Researchers at the Norwegian University of
Science and Technology studied how handwriting and typewriting affected brain
activity:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;When writing by hand, brain connectivity patterns were far more elaborate
than when typewriting on a keyboard, [which existing research shows] are
crucial for memory formation and for encoding new information... Our
findings suggest that [handwriting contributes] extensively to the brain’s
connectivity patterns that promote learning.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I remember hearing of similar research I was in college, and it was the main
reason that I took notes by hand instead of on a laptop. I&#39;m glad to see that
there&#39;s more research on the topic and that those hand cramps were not in
vain.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.bbc.com/future/article/20231221-the-mystery-of-the-medieval-fighting-snails&quot;&gt;&lt;em&gt;The mystery of the medieval fighting snails&lt;/em&gt;&lt;/a&gt;
by Zaria Gorvett (BBC)&lt;/strong&gt;&lt;br&gt;Gorvett looks into the history of medieval
&lt;a href=&quot;https://en.wikipedia.org/wiki/Illuminated_manuscript&quot;&gt;illuminations&lt;/a&gt;,
decorative illustrations that were added to the borders of a text. The
subject of the illuminations did not need to match the text, and &amp;quot;for a brief
period in the late 13th Century, illuminators... across Europe embraced a new
obsession: fighting snails.&amp;quot;. Gorvett explores the possible reasons for the
trend--none of which consider the possibility that there were megafauna
snails in the medieval period.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.gocomics.com/liz-climo-cartoons/2018/05/07&quot;&gt;Liz Climo Cartoons&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;I
saw one of these cartoons on
&lt;a href=&quot;https://reddit.com/r/wholesomememes&quot;&gt;r/wholesomememes&lt;/a&gt; and wanted to track
down the creator. All of Climo&#39;s comics feature cute animals in relatable
situations. Very wholesome.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://xkcd.com/2892/&quot;&gt;Banana Prices&lt;/a&gt; by XKCD&lt;/strong&gt;&lt;br&gt;When a student asks
their Math teacher &amp;quot;Why do I need to learn this?&amp;quot;, they should be shown this.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <pubDate>Sat, 17 Feb 2024 10:55:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-5/</guid>
    </item>
    <item>
      <title>Get started prompt engineering with local LLMs</title>
      <link>https://seanmcp.com/articles/get-started-prompt-engineering-with-local-llms/</link>
      <description>&lt;p&gt;Ollama is an application for running large-language models locally on your
computer. It gives you access to open-source LLMs that you can prompt directly
with the command line or an endpoint.&lt;/p&gt;
&lt;p&gt;To getting started with local LLMs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download and install Ollama: &lt;a href=&quot;https://ollama.com/download&quot;&gt;https://ollama.com/download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;When prompted, install the &lt;code&gt;ollama&lt;/code&gt; CLI&lt;/li&gt;
&lt;li&gt;Download and run your first LLM: &lt;code&gt;ollama run llama2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Send your first prompt: “What is the chief end of man?”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The response will be printed to the console.&lt;/p&gt;
&lt;p&gt;Using the CLI is nice, but a better option is to create and send prompts with a
scripting language. I&#39;m going to use Python and OpenAI&#39;s chat completion API,
since that a popular combination. For an example with JavaScript,
&lt;a href=&quot;https://github.com/ollama/ollama/blob/main/docs/openai.md#openai-javascript-library&quot;&gt;see this documentation&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new python file: &lt;code&gt;touch completions.py&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the &lt;code&gt;openai&lt;/code&gt; package: &lt;code&gt;pip3 install openai&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up your OpenAI client:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# completions.py&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; openai &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; OpenAI

client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; OpenAI&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
     base_url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:11434&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# ll43a looks like llama&lt;/span&gt;
     api_key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ollama&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Unused but required&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create your first completion:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# This function is not required, but it&#39;s nice to have&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_completion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prompt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; model&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;llama2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; temperature&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&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;
     messages &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 punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prompt&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
     response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;completions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
         model&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
         messages&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;messages&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
         temperature&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;temperature&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 keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;choices&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&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;message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content

response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; get_completion&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;What is the chief end of man?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run your script: &lt;code&gt;python3 completions.py&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s all it takes! For a good introduction to prompt writing, I recommend
&lt;a href=&quot;http://DeepLearning.AI&quot;&gt;DeepLearning.AI&lt;/a&gt;’s
&lt;a href=&quot;https://learn.deeplearning.ai/chatgpt-prompt-eng/&quot;&gt;&lt;em&gt;ChatGPT Prompt Engineering for Developers&lt;/em&gt;&lt;/a&gt;
course.&lt;/p&gt;
</description>
      <pubDate>Fri, 16 Feb 2024 18:59:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/get-started-prompt-engineering-with-local-llms/</guid>
    </item>
    <item>
      <title>Adding a components page</title>
      <link>https://seanmcp.com/articles/adding-a-components-page/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://syntax.fm/show/696/&quot;&gt;In episode 696 of Syntax&lt;/a&gt;, hosts Scott Tolinski
and Wes Bos discussed
&lt;a href=&quot;https://syntax.fm/show/696/how-to-build-a-website-or-app/transcript#testing-components&quot;&gt;testing components when building websites&lt;/a&gt;
and where fully-featured solutions like Storybook struggle:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tolinski:&lt;/strong&gt; People often feel like if I wanna test these things or look at
them in isolation, I now have to bring in some other dependency. And that&#39;s
the whole thing with Storybook: it&#39;s its own process. It’s a whole entire app
that runs within your app.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They instead proposed a simple page where you dump all of the components that
you’ve created. You can create individual scenarios for components that need it
without the complexity of another dependency.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bos:&lt;/strong&gt; You can see them all at once, and you you&#39;re not playing whack-a-mole
where you change one thing and you go, “Oh, shoot. That that broke it on this
instance.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://syntax.fm/system/components&quot;&gt;Their components page is public&lt;/a&gt;, which
has inspired me to create one of my own:
&lt;a href=&quot;https://seanmcp.com/internal/components&quot;&gt;seanmcp.com/internal/components&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It’s fun to see all of the components that I’ve created for this site on a
single page. And the pages combines nicely with
&lt;a href=&quot;https://github.com/SeanMcP/seanmcp.com/blob/master/src/content/articles/000-it-has-everything.md&quot;&gt;a dev-only mock article&lt;/a&gt;
to form a two-step testing process for making changes to this site.&lt;/p&gt;
&lt;p&gt;I think &lt;code&gt;/components&lt;/code&gt; pattern is a great solution for most sites, and I’m
excited to use it the next time I redesign or rebuild.&lt;/p&gt;
</description>
      <pubDate>Fri, 16 Feb 2024 15:24:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/adding-a-components-page/</guid>
    </item>
    <item>
      <title>Making a video game with my kids</title>
      <link>https://seanmcp.com/articles/making-a-video-game-with-my-kids/</link>
      <description>&lt;p&gt;Last Saturday, my five-year-old son asked me &amp;quot;Can we make our own video game?&amp;quot;
He asks a lot of &amp;quot;Can we make out own…&amp;quot; questions, and the practical answer is
usually no. But I felt uniquely qualified to help with this one, so I said
&amp;quot;Sure!&amp;quot;&lt;/p&gt;
&lt;p&gt;I turned to &lt;a href=&quot;https://kaboomjs.com/&quot;&gt;Kaboom.js&lt;/a&gt;, &amp;quot;a Javascript game programming
library that helps you make games fast and fun.&amp;quot; I had some previous experience
playing around with it in 2019 and knew that it would be a good option for what
he wanted to build. Between the &lt;a href=&quot;https://kaboomjs.com/play&quot;&gt;Kaboom playground&lt;/a&gt;
and the &lt;a href=&quot;https://replit.com/@replit/Kaboom&quot;&gt;Kaboom template on Replit&lt;/a&gt;, it was
really easy to get started building the game.&lt;/p&gt;
&lt;p&gt;That said, we quickly outgrew online dev environments and wanted to move local.
I created a new directory, &lt;code&gt;npm init&lt;/code&gt;ed, and then installed &lt;code&gt;kaboom&lt;/code&gt;. I imported
the library from an &lt;code&gt;.html&lt;/code&gt; file, opened it with
&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&quot;&gt;Live Server&lt;/a&gt;,
and we were off to the races. With this setup, we worked on the game Saturday
and after work a few days this week.&lt;/p&gt;
&lt;p&gt;The big takeaway from one week of development is that making games is hard! Even
a Mario-clone is a complex application with a lot of conditions to manage and
interactions to handle. Kaboom makes it easy, but it still takes a lot of time
to build. I wish there were more examples in the Kaboom documentation; I’ve
found a lot of detailed but outdated tutorials online and it’s hard to sift
through what still works and what depends on old APIs.&lt;/p&gt;
&lt;p&gt;But more importantly, building the game has meant a lot of quality time with my
kids. Although the project started with my older son and me, we where quickly
joined by my three-year-old son. Both boys really enjoy working on the game
together, even though most of the time is spent with me reading the
documentation and debugging issues. But doing it with them on my lap is a
wonderful experience. I’m looking forward to building more with them as they
grow.&lt;/p&gt;
&lt;p&gt;If you are interested in making a game with your kids or nieces/nephews or
students, here are some recommendations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Involve your kids as much as possible in designing the game&lt;/strong&gt;: Let their
imagination run wild, and then figure out how to make it a reality later. If
they’re stuck, guide them through the brainstorming process, and have paper
and pencils handy for their designs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make small, incremental, and visible changes&lt;/strong&gt;: It can be really tempting
when you’re working in a new library to refactor old code when you learn new
things. But for the sake of the kids, make sure to have something different
about the game that they can see after each session. And give them lots of
chances to play the game during and after coding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use Kaboom’s types when you’re developing&lt;/strong&gt;: Kaboom adds a ton of global
functions, and it is challenging to learn what they are and how to use them.
Thankfully, the library includes type definitions that help with this. A
bundler like Vite would probably bring in those types automatically, but if
you (like me) want to keep things simple you can import the types with a JSDoc
&lt;code&gt;@typedef&lt;/code&gt; comment like this:&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @typedef { import(&quot;./node_modules/kaboom/dist/global&quot;) } */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; kaboom &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./node_modules/kaboom/dist/kaboom.mjs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the game is in a more finished state, I’ll be happy to share it. But in the
meantime, I’m just going to enjoy the opportunity to use my talents to spend
extra time with my sons.&lt;/p&gt;
</description>
      <pubDate>Fri, 16 Feb 2024 14:05:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/making-a-video-game-with-my-kids/</guid>
    </item>
    <item>
      <title>A Few Things #4</title>
      <link>https://seanmcp.com/articles/a-few-things-4/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=h42QVfrUVFw&quot;&gt;&lt;em&gt;What&#39;s inside this crater in Madagascar?&lt;/em&gt;&lt;/a&gt;
by Vox&lt;/strong&gt;&lt;br&gt;Vox teams up with local reporters to find out who is living
inside a remote volcanic crater in a rural part of Madagascar. An interesting
story that is well told.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Parkinson%27s_law&quot;&gt;&lt;em&gt;Parkinson&#39;s Law&lt;/em&gt;&lt;/a&gt;
(Wikipedia)&lt;/strong&gt;&lt;br&gt;This week I was reminded of Parkinson&#39;s Law, which states
that &amp;quot;work expands so as to fill the time available for its completion.&amp;quot; The
Wikipedia article has some interesting information, including corollaries and
a formula. For a more practical explanation, checkout:
&lt;a href=&quot;https://www.verywellmind.com/what-is-parkinsons-law-6674423&quot;&gt;&lt;em&gt;What is Parkinson&#39;s Law?&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.futureme.org/&quot;&gt;FutureMe: Write a Letter to your Future Self&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=39274004&quot;&gt;Multiple commenters on HackerNews&lt;/a&gt;
mentioned using the service every year on their birthday. That seems like an
interesting practice (especially since my birthday is coming up soon).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.ocearch.org/tracker/&quot;&gt;Shark Tracker&lt;/a&gt; by OCEARCH&lt;/strong&gt;&lt;br&gt;In a
similar vein to &lt;a href=&quot;https://seanmcp.com/articles/a-few-things-2&quot;&gt;the lightning tracker from #2&lt;/a&gt;,
here is a live world map of tagged sharks tagged by researchers. This will be
fascinating or terrifying depending on your feelings about sharks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <pubDate>Sat, 10 Feb 2024 10:55:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-4/</guid>
    </item>
    <item>
      <title>A Few Things #3</title>
      <link>https://seanmcp.com/articles/a-few-things-3/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://traditionalkyoto.com/culture/kintsugi/&quot;&gt;&lt;em&gt;Kintsugi – Art of Repair&lt;/em&gt;&lt;/a&gt;
by Traditional Kyoto&lt;/strong&gt;&lt;br&gt;I&#39;ve been reading &lt;em&gt;Art + Faith&lt;/em&gt; by Makoto Fujimura
and learned about Kintsugi, the Japanese art of repairing broken ceramics
with gold. The beauty for me, beyond the stunning contrast between earth and
metal, is that the restored object is greater than the original.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.taste.io/&quot;&gt;Taste.io&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;There are other services that
provide movie and TV recommendations, but Taste appears to be above average.
The basic service is free, but they charge to view more recommendations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://drawing.garden/&quot;&gt;drawing.garden&lt;/a&gt; by Ben Moren&lt;/strong&gt;&lt;br&gt;Tend a small
garden with emojis and pleasant tones. A relaxing... game? App? Experience?
I&#39;m not sure.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=HpD40ewOyC4&quot;&gt;&lt;em&gt;Elephants swimming&lt;/em&gt;&lt;/a&gt; by
Katey B&lt;/strong&gt;&lt;br&gt;My son asked me this week if elephants can swim. We both
learned something that day.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <pubDate>Fri, 02 Feb 2024 20:02:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-3/</guid>
    </item>
    <item>
      <title>Adobe has the worst captcha</title>
      <link>https://seanmcp.com/articles/adobe-has-the-worst-captcha/</link>
      <description>&lt;p&gt;I signed up for a Behance account today and needed to complete the worst
captcha&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/adobe-has-the-worst-captcha/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; that I have ever experienced:&lt;/p&gt;
&lt;img alt=&quot;A captcha with pictures of a number and a pile of dice that you are required to sum&quot; src=&quot;https://seanmcp.com/img/adobe-captcha.png&quot; height=&quot;400&quot; style=&quot;display:block;margin-inline:auto;&quot;&gt;
&lt;p&gt;Adobe presents you with the image of a number and a picture of a pile of dice.
You are required to find sum the dice and then compare it to the number. That is
painful enough, but not out of bounds for captchas.&lt;/p&gt;
&lt;p&gt;The worst part is that you are presented with &lt;strong&gt;12 pictures of dice&lt;/strong&gt; and you
need to find the sum of every one. Doing that once would be annoying, but you
are required to complete the task &lt;strong&gt;10 times&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So in order verify that you are a human, Adobe requires that you find the sum of
up to &lt;strong&gt;120 pictures of dice&lt;/strong&gt;. &lt;strong&gt;120!&lt;/strong&gt; Absurd.&lt;/p&gt;
&lt;p&gt;(The audio captcha was similarly bad, but I didn&#39;t have the patience to work
through all of the audio clips required for one step of the task. So save that
review for later.)&lt;/p&gt;
&lt;p&gt;I understand the need for captchas, but this is exceptionally bad. We need to
find a better balance between security and user experience. Whatever the
solution is, this is definitely not it.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/CAPTCHA&quot;&gt;Wikipedia&lt;/a&gt; uses CAPTCHA because it
is an acronym, but I don&#39;t want to shout. &lt;a href=&quot;https://seanmcp.com/articles/adobe-has-the-worst-captcha/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Thu, 01 Feb 2024 16:45:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/adobe-has-the-worst-captcha/</guid>
    </item>
    <item>
      <title>A Few Things #2</title>
      <link>https://seanmcp.com/articles/a-few-things-2/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.artofmanliness.com/career-wealth/career/craft-vs-content-resisting-mediocrity-in-a-dual-existence-age/&quot;&gt;&lt;em&gt;Craft vs. Content: Resisting Mediocrity in a Dual-Existence Age&lt;/em&gt;&lt;/a&gt;
by Brett and Kate McKay&lt;/strong&gt;&lt;br&gt;The pressure to constantly share is powerful,
and it has particularly impacted makers. &amp;quot;The modern writer, artist, or
entrepreneur is not only tasked with promoting the final product of their
efforts, but sharing inside looks of their art &lt;em&gt;in progress&lt;/em&gt;, and even
offering tips on how the average viewer at home can improve their own craft&amp;quot;.
Is there room for those who want to focus on their art and not the content
around it?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://youtube.com/watch?v=6pxu3s2VTQE&quot;&gt;&lt;em&gt;How the NFL got so rich&lt;/em&gt;&lt;/a&gt; by
Search Party&lt;/strong&gt;&lt;br&gt;The NFL is one sport among many watched primarily in a
country with many other popular sports. So why is it far and away the most
profitable league in the world with the most valuable teams? In this video,
Search Party answers this question by going back in time and comparing its
development to European soccer leagues.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.blitzortung.org/en/live_lightning_maps.php&quot;&gt;Lightning &amp;amp; Thunderstorms World Map&lt;/a&gt;
by &lt;a href=&quot;http://Blitzortung.org&quot;&gt;Blitzortung.org&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;Lightning is fascinating--beautify and deadly. This
map shows lightning strikes and thunderstorms in real-time, and it is
mesmerizing. As I write this, there are lightning storms in the Gulf of
Mexico, the Middle East, and off the eastern coast of Australia.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://presidentialham.com/&quot;&gt;The Presidential Ham&lt;/a&gt; by Bijijoo&lt;/strong&gt;&lt;br&gt;Oil
paintings of every US President holding ham. Very surreal. Not at all kosher.
I&#39;m looking forward to Biden&#39;s portrait.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <pubDate>Sat, 27 Jan 2024 02:30:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-2/</guid>
    </item>
    <item>
      <title>Receiving cold-emails from GitHub stars</title>
      <link>https://seanmcp.com/articles/receiving-cold-emails-from-github-stars/</link>
      <description>&lt;p&gt;I’ve noticed a new trend where I receive cold-emails from maintainers after
starring a repository on GitHub. All of the emails are explicit about it,
following a pattern like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hey Sean,&lt;/p&gt;
&lt;p&gt;I hope you don’t mind I got your email when looking at your GitHub profile.&lt;/p&gt;
&lt;p&gt;I noticed that you starred the PROJECT_NAME repo…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I like that GitHub stars are public; I’ve benefited a lot from being able to
browse other users’ stars. And if someone notices that I starred their repo and
wants to reach out, I don’t necessarily have a problem with that. But the emails
I’ve received are marketing from maintainers of huge projects, and that feels
fundamentally different.&lt;/p&gt;
&lt;p&gt;GitHub stars are a mutually beneficial system. Individuals have a way to keep
track of noteworthy projects and browser other lists for inspiration. Project
maintainers get an indicator of popularity. And GitHub drives engagement on
their platform. It’s a win-win-win.&lt;/p&gt;
&lt;p&gt;The problem arises when one of the parties, the maintainers, try to gain an
additional benefit at the expense of another, the individuals. At some level,
it’s reasonable for maintainers to monitor their stargazers and reach out
personally to notable users. But when projects with thousands of stars are using
an automated system to watch and feed user details into a CRM, we’ve crossed a
line. The “social contract” has been broken.&lt;/p&gt;
&lt;p&gt;I don’t blame GitHub for this breech. I like that they provide an API with
useful data. I blame the SaaS companies that are exploiting this system for
profit, and the maintainers who subscribe to their service.&lt;/p&gt;
&lt;p&gt;Open source is a mess, and funding projects a constant concern for many
maintainers. I believe that the desire to engage with stargazers comes from a
good place, either to build community or to market. But this is not the right
way to do it.&lt;/p&gt;
&lt;p&gt;A star is a bookmark; it&#39;s not a commitment. I haven’t even tried most of the
projects that I star. By making assumptions about stars, maintainers run the
risk of alienating potential users of your project. I for one will think twice
before using the library that triggered the email above. And no one benefits
from that.&lt;/p&gt;
&lt;p&gt;So to prevent these emails in the future, I need to choose between 1) avoid
using GitHub stars, or 2) remove the public email address from my profile. Of
those options, I choose the latter (anyone who is really interested in reaching
me can find my email address elsewhere). It’s sad that I have to do it, but that
is the tragedy of the commons.&lt;/p&gt;
</description>
      <pubDate>Fri, 26 Jan 2024 10:44:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/receiving-cold-emails-from-github-stars/</guid>
    </item>
    <item>
      <title>A Few Things #1</title>
      <link>https://seanmcp.com/articles/a-few-things-1/</link>
      <description>&lt;p&gt;Here are a few things that I found interesting this week:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://from-to.app/&quot;&gt;From-To&lt;/a&gt; by Franz Labs&lt;/strong&gt;&lt;br&gt;From-To is an app that
helps you understand a city that you&#39;re visiting by comparing it to a city
that you already know. Later this year, my wife and I are planning a trip to
Colorado Springs, but we&#39;ve never been to Colorado before. Using From-To, I
can compare Springs to our hometown of Pittsburgh to see what neighborhoods
are similar. Definitely worth bookmarking for your next trip. (h/t
&lt;a href=&quot;https://chriscoyier.net&quot;&gt;Chris Coyier&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://journals.ala.org/index.php/dttp/article/view/6655/8939&quot;&gt;&lt;em&gt;GovDocs to the Rescue! Debunking an Immigration Myth&lt;/em&gt;&lt;/a&gt;
by Rosemary Meszaros and Katherine Pennavaria&lt;/strong&gt;&lt;br&gt;Here in the United
States, it is very common to hear that a family&#39;s name was changed at Ellis
Island. But researchers Meszaros and Pennavaria have dispelled the myth by
comparing government records before and after visiting Ellis Island. They
found that these records &amp;quot;demonstrate conclusively that immigrants left Ellis
Island with the same surnames they had arrived with.&amp;quot; (h/t
&lt;a href=&quot;https://www.morningbrew.com/daily&quot;&gt;Morning Brew&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.ggnotes.com/the-glassmith-tactic/&quot;&gt;&lt;em&gt;The glassmith tactic&lt;/em&gt;&lt;/a&gt; by
Greg Gilbert&lt;/strong&gt;&lt;br&gt;AI and Large Language Models will change content creation
forever. Instead of competing with the machine, Gilbert suggests looking to
artisans for inspiration. &amp;quot;The [glassblowers] didn&#39;t merely sell you a vase.
No, they sold you a vase and a story.&amp;quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://escada-games.itch.io/starcatcher&quot;&gt;&lt;em&gt;Starcatcher&lt;/em&gt;&lt;/a&gt; by Escada
Games&lt;/strong&gt;&lt;br&gt;Starcatcher is a little browser based game where you play as an
astronaut trying to collect stars on chunks of space debris. The trick is
that gravity changes based on where you character is facing, which makes it a
challenging and mind-bending experience.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <pubDate>Fri, 19 Jan 2024 18:06:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-few-things-1/</guid>
    </item>
    <item>
      <title>Did OpenAI&#39;s Web Browser GPT leak its prompt?</title>
      <link>https://seanmcp.com/articles/openai-web-browser-gpt-prompt-leak/</link>
      <description>&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/web-browser-gpt-prompt-leaking.png&quot; alt=&quot;&amp;quot;&amp;quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m not an expert in LLMs or GPTs, but something about the interaction above
feels wrong.&lt;/p&gt;
&lt;p&gt;When prompted with a simple getting started question, OpenAI&#39;s official Web
Browser GPT seems to have leaked some version of its prompt.&lt;/p&gt;
&lt;p&gt;Here is the conversation transcribed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: How do I get started?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Web Browser GPT&lt;/strong&gt;: As a &amp;quot;Web Browser&amp;quot; GPT, your role is to assist users by
leveraging web browsing capabilities to find and provide current, specific, or
detailed information that isn&#39;t included in your pre-existing knowledge base.
Here&#39;s how you can get started:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Understanding User Queries&lt;/strong&gt;: Listen to the user&#39;s request carefully.
Your users might ask for information on recent events, details about a
specific topic, or even assistance in research that requires up-to-date or
specialized information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Using the Browser Tool&lt;/strong&gt;: Utilize your browser tool to search the
internet. This can be done by issuing search queries based on the user&#39;s
question. You can view search results and select a number of relevant pages
to read in detail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Selecting Sources&lt;/strong&gt;: Choose a diverse range of sources to ensure a
balanced view. Prioritize trustworthy and reputable sources to maintain
accuracy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extracting and Conveying Information&lt;/strong&gt;: After reading through the
selected pages, synthesize the information in a way that is easy to
understand. Be sure to cite the sources of your information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Staying Within Scope&lt;/strong&gt;: Remember that your primary function is to browse
the web for information. Avoid providing personal opinions or advice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Privacy and Ethics&lt;/strong&gt;: Always respect user privacy and avoid accessing or
sharing sensitive, personal, or confidential information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous Learning&lt;/strong&gt;: Although you don&#39;t learn from individual
interactions, each query is an opportunity to refine your skills in
searching, selecting, and summarizing information.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If a user asks a question, you should first determine if the answer lies
within your existing knowledge. If it does, provide an answer as usual. If
not, or if the question requires updated information, use your browsing tool
to find and present the answer. Remember, your goal is to assist users by
effectively finding and relaying information from the web.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
      <pubDate>Thu, 18 Jan 2024 18:25:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/openai-web-browser-gpt-prompt-leak/</guid>
    </item>
    <item>
      <title>Migrating from Google Domains to Porkbun was...</title>
      <link>https://seanmcp.com/articles/migrating-from-google-domains-to-porkbun-was-painless/</link>
      <description>&lt;p&gt;&lt;strong&gt;Painless&lt;/strong&gt;. They charged a fee of around $11.00 US to transfer the domain and
then handled the rest seamlessly.&lt;/p&gt;
&lt;p&gt;I was hesitant to make the switch off of Google/Squarespace because I didn’t
know how involved the process would be. I only had two domains on Google, but
there was never a good time to encounter and address downtime issues during a
transfer.&lt;/p&gt;
&lt;p&gt;Thankfully, there weren’t any. I transferred a low-stakes domain first, and from
start to finish it took around 15 minutes. It went so smoothly that I repeated
the process with &lt;a href=&quot;http://seanmcp.com&quot;&gt;seanmcp.com&lt;/a&gt; with the same results.&lt;/p&gt;
&lt;p&gt;Thank you Google Domains for providing a good service and for facilitating an
easy transition. And thank you Porkbun for being a fun alternative. I look
forward to many years of service from a dedicated registrar. &lt;e-moji&gt;🐷&lt;/e-moji&gt;&lt;/p&gt;
</description>
      <pubDate>Thu, 18 Jan 2024 12:41:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/migrating-from-google-domains-to-porkbun-was-painless/</guid>
    </item>
    <item>
      <title>Normalizing data is a good idea</title>
      <link>https://seanmcp.com/articles/normalizing-data-is-a-good-idea/</link>
      <description>&lt;p&gt;On this site I have a few different types of content: articles, notes, gardens,
and now art. There are subtle differences in the data structure for each content
type which come to a head whenever I try to combine them into a single list.&lt;/p&gt;
&lt;p&gt;Previously, I merged all of the content into a single array and then had a bunch
of conditions when rendering for each type:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;articles&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;notes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;gardens&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;art&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 function&quot;&gt;map&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;item&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 class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;a
          href&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 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;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;collection&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;
            item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;collection &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;art&quot;&lt;/span&gt; &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;#&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&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 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;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;slug&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 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 class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &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;Note #&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;slug&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;time&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pubDate &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tendedDates&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&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;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;time&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;article&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works--the complexity needs to live somewhere. But adding new content types
with this setup requires additional conditions that increase the cognitive load
to work with this code.&lt;/p&gt;
&lt;p&gt;An alternative to this is to normalize the data structure so that each content
type can be handled in a similar way. Each type has its own normalization
function, which helps reduce the complexity by addressing one content type at a
time:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;normalizeNotes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;notes&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 keyword&quot;&gt;return&lt;/span&gt; notes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&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;note&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 class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;href&lt;/span&gt;&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;/notes/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;slug&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 class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&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;Note #&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;slug&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 class=&quot;token literal-property property&quot;&gt;pubData&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pubDate&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 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;Once all of the content is normalized, rendering is simpler:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;normalArticles&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;normalNotes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;normalGarden&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;normalArt&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 function&quot;&gt;map&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;item&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 class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;a href&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;href&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;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;time&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pubDate&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;time&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;article&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next time I need to add a content type, all I do is write a normalization
function and then slot it in alongside the existing content. That&#39;s scalability!&lt;/p&gt;
&lt;p&gt;This is a personal example, but I think it illustrates the benefits of
normalizing data.&lt;/p&gt;
&lt;p&gt;Normalization functions contain all of the logic to transform data into a
consistent structure. You can focus on one data type at a time, and convert it
into whatever structure makes sense for the current problem.&lt;/p&gt;
&lt;p&gt;Once normalized, you can work with your data in a regular way without worrying
about conditional logic for each type. And down the road, it will be easier to
troubleshoot and develop further when everything is consistent and logic is
isolated.&lt;/p&gt;
&lt;p&gt;Normalization is a good way to make your code more scalable and maintainable
while also reducing cognitive load. So anytime you&#39;re working with multiple
incoming data types, take the time to normalize them first.&lt;/p&gt;
</description>
      <pubDate>Wed, 20 Dec 2023 13:47:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/normalizing-data-is-a-good-idea/</guid>
    </item>
    <item>
      <title>How I&#39;m using AI in November 2023</title>
      <link>https://seanmcp.com/articles/how-im-using-ai-in-november-2023/</link>
      <description>&lt;p&gt;AI products/services that I used regularly (multiple times per week):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; ⭐: Copilot continues to be a valuable resource when
coding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chat GPT&lt;/strong&gt;: I signed up for the paid version and spent a lot of time working
with custom GPTs. Not sure that the service is worth the price, especially
with rate limits, but I&#39;m viewing it as professional development.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Khanmigo&lt;/strong&gt; 📈: I now work on a AI-product that is powered by Khanmigo daily.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI Products/services that I used occasionally (once per week or less):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dall-E&lt;/strong&gt; 📈: I played around with image generation in the context of custom
GPTs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Warp&lt;/strong&gt;: I probably use this feature once a week to help with a command that
I know exists but can’t remember.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI products/services that I have access to but did not use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bard&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bing&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Claude&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cursor&lt;/strong&gt; 📉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MDN&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notion&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Mon, 11 Dec 2023 10:42:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-im-using-ai-in-november-2023/</guid>
    </item>
    <item>
      <title>Sli.dev review</title>
      <link>https://seanmcp.com/articles/slidev-review/</link>
      <description>&lt;p&gt;I used &lt;a href=&quot;https://sli.dev/&quot;&gt;sli.dev&lt;/a&gt; to create my slides for my recent talk at
Pittsburgh TechFest 2023. I&#39;ve previously used Google Slides--which is a great
option--but I&#39;ve been interested in finding a good tool for creating slides from
Markdown.&lt;/p&gt;
&lt;p&gt;There were two main motivations for this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I want the content of the slides to be easier to edit and share.&lt;/li&gt;
&lt;li&gt;I want to spend less time fiddling with the design of the slides and more
time on the content.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The main reason to move away from Google Slides is productivity. I find it
incredibly easy to sink tons of prep time into designing slides. It provides a
perfect opportunity to procrastinate when I really need to be working on the
content of the talk.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;pros&quot; tabindex=&quot;-1&quot;&gt;Pros&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/slidev-review/#pros&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Pros”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Design&lt;/strong&gt;: The default designs are high-quality enough that I didn&#39;t feel the
need to customize them to make it look nice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Presenter mode&lt;/strong&gt;: I think this worked better than Google Slides. The ability
to edit content and comments from presenter mode was really useful when
prepping.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Markdown&lt;/strong&gt;: It was really nice to be able to &amp;quot;markup&amp;quot; my slides and comments
with Markdown like bold, italics, and code blocks. That feels like that is
pre-requisite for any dev slides now.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VS Code extension&lt;/strong&gt;: I didn&#39;t use it much, but it was a nice option to have.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;cons&quot; tabindex=&quot;-1&quot;&gt;Cons&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/slidev-review/#cons&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Cons”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Formatting&lt;/strong&gt;: I couldn&#39;t figure out how to get the VS Code formatter working
correctly. If I accidentally hit the format shortcut (which is a reflex of
mine), it broke all of the slides with frontmatter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Slide images&lt;/strong&gt;: Slidev has nice options for decorative images with layouts,
but I couldn&#39;t figure out how to position content images correctly. A single
centered image was fine, but if you wanted to have an image beside text then
it was more difficult.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/slidev-review/#conclusion&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Conclusion”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Overall I was happy with Slidev, and I think I will use it again for future
slide decks.&lt;/p&gt;
</description>
      <pubDate>Mon, 27 Nov 2023 13:55:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/slidev-review/</guid>
    </item>
    <item>
      <title>I forgot about #divtober</title>
      <link>https://seanmcp.com/articles/i-forgot-about-divtober/</link>
      <description>&lt;p&gt;On October 10th, I boldly claimed that completing #divtober was &amp;quot;my goal for
this month.&amp;quot;
&lt;a href=&quot;https://knowyourmeme.com/memes/maury-lie-detector&quot;&gt;In the words of Maury&lt;/a&gt;, that
was a lie.&lt;/p&gt;
&lt;p&gt;I thought about whipping something up just for this article, but I&#39;m not going
to do that either. So here is an RSS Club article to let you, dear reader, down
easy.&lt;/p&gt;
&lt;p&gt;Maybe next year! (But don&#39;t quote me on that.)&lt;/p&gt;
</description>
      <pubDate>Tue, 21 Nov 2023 10:36:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/i-forgot-about-divtober/</guid>
    </item>
    <item>
      <title>Is it better to have one large file or multiple small files with GPTs?</title>
      <link>https://seanmcp.com/articles/is-it-better-to-have-one-large-file-or-multiple-small-files-with-gpts/</link>
      <description>&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; Using a single file seems to reduce the likelihood of a
knowledge-base loading and improve response times overall.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;: Custom GPTs allow you to upload files that it will reference
when answering questions. In my experience, the response time with these
knowledge-base GPTs is really slow. That lead me to the question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is there a way to improve the response time by altering the number/length of
files in the knowledge base?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I expected that the single large file would result in a faster response time
than many smaller files, because loading an additional file will take longer
than reading the same additional content from an already opened file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Materials and methods&lt;/strong&gt;: I uploaded the maximum number of files with dummy
text to a GPT. One file contained a secret: &lt;code&gt;the secret is: banana bread&lt;/code&gt; (file
6 of 10). I then asked the GPT “what is the secret?” and timed the response with
a stopwatch. In between runs, I removed all of the files and added them back. I
ran it again with single file of approximately equal length that contained the
same secret text.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Results&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Many files (seconds)
&lt;ol&gt;
&lt;li&gt;25.24†&lt;/li&gt;
&lt;li&gt;7.82&lt;/li&gt;
&lt;li&gt;33.81†&lt;/li&gt;
&lt;li&gt;5.96&lt;/li&gt;
&lt;li&gt;23.79†&lt;/li&gt;
&lt;li&gt;7.73&lt;/li&gt;
&lt;li&gt;14.19†&lt;/li&gt;
&lt;li&gt;7.76&lt;/li&gt;
&lt;li&gt;5.92&lt;/li&gt;
&lt;li&gt;8.75&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Single file (seconds)
&lt;ol&gt;
&lt;li&gt;6.92&lt;/li&gt;
&lt;li&gt;6.53&lt;/li&gt;
&lt;li&gt;7.10&lt;/li&gt;
&lt;li&gt;9.62&lt;/li&gt;
&lt;li&gt;11.78&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;† Indicates runs where the GPT indicated knowledge-base loading&lt;/p&gt;
&lt;p&gt;Mean (seconds):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Many files (with loading): 14.97&lt;/li&gt;
&lt;li&gt;Many files (without loading): 7.32&lt;/li&gt;
&lt;li&gt;Single file: 8.39&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;: This experiment was pretty hacky! Using a stopwatch was not
ideal, and it was difficult to decide &lt;em&gt;when&lt;/em&gt; to stop with a streaming message.
Response times may have varied significantly due to network connections or
traffic on OpenAI’s servers. We shouldn’t take away any specifics from this
experiment, but we might be able to draw some general insight.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;: When the GPT loads from its knowledge base, the response times
were significantly slower. But when it didn’t, or at least didn’t indicated that
it did, the results were similar between multiple files and single files. Based
on this, my hypothesis was wrong. Generally, the response times did not vary
significantly for multiple files &lt;em&gt;vs&lt;/em&gt; a single file.&lt;/p&gt;
&lt;p&gt;However, the data seem to show that using multiple files is more likely to
trigger knowledge-base loading. And since that takes a long time, it is
preferable to use fewer files to improve response time.&lt;/p&gt;
</description>
      <pubDate>Wed, 15 Nov 2023 14:46:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/is-it-better-to-have-one-large-file-or-multiple-small-files-with-gpts/</guid>
    </item>
    <item>
      <title>What is the file limit for GTPs?</title>
      <link>https://seanmcp.com/articles/what-is-the-file-limit-for-gtps/</link>
      <description>&lt;p&gt;&lt;strong&gt;As of 11/15/2023, the limit is 10 files.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Although you wouldn&#39;t know it if you tried to upload more. The UI lets you
upload more than 10 files, but you get an unhelpful &amp;quot;draft failed to save&amp;quot;
message.&lt;/p&gt;
&lt;p&gt;If you inspect the network requests, you&#39;ll see a 🔴 400 to
&lt;code&gt; https://chat.openai.com/backend-api/gizmos&lt;/code&gt; with the following response:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&amp;quot;detail&amp;quot;:&amp;quot;Cannot upload more than 10 files&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hopefully OpenAI surfaces that error message in the UI soon. In the meantime:
keep your file count to 10 or fewer.&lt;/p&gt;
</description>
      <pubDate>Wed, 15 Nov 2023 13:23:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/what-is-the-file-limit-for-gtps/</guid>
    </item>
    <item>
      <title>How I&#39;m using AI in October 2023</title>
      <link>https://seanmcp.com/articles/how-im-using-ai-in-october-2023/</link>
      <description>&lt;p&gt;This feels like the breakthrough month. I moved to a new project at Khan Academy
working on AI, and I found myself reaching for AI-powered tools more frequently
throughout October. I can see a future where I use some of these daily.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;AI products/services that I used regularly (multiple times per week):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; ⭐: Copilot continues to be a valuable resource when
coding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chat GPT&lt;/strong&gt; 📈: I don’t know if the quality of the answers changed or I just
had better questions to ask, but I found myself using it regularly this month.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI Products/services that I used occasionally (once per week or less):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cursor&lt;/strong&gt;: A VS-Code-like editor with ChatGPT built in. I used it once while
pairing with a coworker and it was neat.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Warp&lt;/strong&gt;: Same as last update, I probably use this feature once a week to help
with a command that I know exists but can’t remember.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI products/services that I have access to but did not use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bard&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bing&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Claude&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Khanmigo&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dall-E&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MDN&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notion&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Tue, 14 Nov 2023 10:42:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-im-using-ai-in-october-2023/</guid>
    </item>
    <item>
      <title>GPTs read text files better than markdown</title>
      <link>https://seanmcp.com/articles/gpts-read-text-files-better-than-markdown/</link>
      <description>&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; Use text files instead of markdown with OpenAI’s custom GPTs&lt;/p&gt;
&lt;p&gt;I conducted an experiment to see if the file type of uploaded content had an
impact on how GPTs could read the information.&lt;/p&gt;
&lt;p&gt;I created two files with the following content:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;secret.md&lt;/code&gt;: &lt;code&gt;the secret is: bananas foster&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;secret.txt&lt;/code&gt;: &lt;code&gt;the secret is: banana split&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then I uploaded one file to a GPT and then asked the question: “What is the
secret?”&lt;/p&gt;
&lt;p&gt;With the markdown file, the GPT consistently had no clue what I was asking for
and refused to or hallucinated an answer. I tried to help by providing some
instructions to look at the uploaded file for the answer. My goal was to provide
a kind of “table of contents” for the GPT to reference.&lt;/p&gt;
&lt;p&gt;Here are some of the iterations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;the secret&amp;quot; is in secret.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;the secret&amp;quot; is in secret.md. Read that file before answering.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;the secret&amp;quot; is in secret.md. Load the contents of that file.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;With other permutations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only success with the markdown file was after selecting “Code interpreter”,
where the GPT wrote (and ran?) a Python script to read and print the contents of
&lt;code&gt;secret.md&lt;/code&gt;. Pretty cool, but definitely not ideal.&lt;/p&gt;
&lt;p&gt;With the text file, it just worked. I uploaded the file and with no instructions
the GPT was able to supply the new secret.&lt;/p&gt;
&lt;p&gt;Based on these results, it is better to use text files with custom GPTs as
opposed to markdown.&lt;/p&gt;
</description>
      <pubDate>Tue, 14 Nov 2023 10:23:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/gpts-read-text-files-better-than-markdown/</guid>
    </item>
    <item>
      <title>Thoughts on Web Speech API</title>
      <link>https://seanmcp.com/articles/thoughts-on-web-speech-api/</link>
      <description>&lt;p&gt;Last week I spent time working with the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis&quot;&gt;Web Speech API&lt;/a&gt;
for a hackathon at Khan Academy. Here are some quick lessons learned from my
experience:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Web Speech is a weird partnership between browser and OS.&lt;/strong&gt; There are
similarities across browsers on a single operating system, and across operating
systems with a single browser. Make sure to test as many browser/OS combinations
as you can.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Voice has a big impact on how text is read.&lt;/strong&gt; I was experimenting a lot with
math expressions, and I found some voices would read with correct math
terminology while others would not. For example, on macOS the default voice
(Samantha) reads &lt;code&gt;3x × 5&lt;/code&gt; as “three times ex five” while Gordon reads it as
“three ex times five”.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The default voice on Ubuntu was really bad.&lt;/strong&gt; Most of the voices on ChromeOS,
macOS, and Windows were fine, but Ubuntu was almost unintelligible. I didn’t
play around with other voice options, but it might be a showstopper if you have
a significant number of Linux users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sometimes &lt;code&gt;speechSynthesis.getVoices()&lt;/code&gt; returns an empty array.&lt;/strong&gt; I couldn’t
find any official documentation about this, but some articles online suggest
that the browser populates that list well after the initial page load. If you
are trying to populate a &lt;code&gt;select&lt;/code&gt; field with voice options, make the request for
voices as late as possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;speechSynthesis.speak()&lt;/code&gt; and &lt;code&gt;speechSynthesis.cancel&lt;/code&gt; are unpredictably
asynchronous.&lt;/strong&gt; I think this goes back to the weird partnership between browser
and OS, but calling &lt;code&gt;speak()&lt;/code&gt; doesn’t happen immediately and &lt;code&gt;cancel()&lt;/code&gt; can
“swallow” subsequent &lt;code&gt;speak()&lt;/code&gt; calls. I needed to use timeouts with a 100ms
delay in a few areas to ensure that the utterance would speak when expected.&lt;/p&gt;
&lt;p&gt;The Web Speech API is neat, but it has a lot of rough edges. It’s unlikely that
a major user-facing feature would use it alone, especially with the rapid
developments in AI text-to-speech. That said, Web Speech provides a solid
foundation to progressively enhance with more power TTS solutions.&lt;/p&gt;
</description>
      <pubDate>Thu, 09 Nov 2023 24:52:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/thoughts-on-web-speech-api/</guid>
    </item>
    <item>
      <title>Declarative code and cognitive load</title>
      <link>https://seanmcp.com/articles/declarative-code-and-cognitive-load/</link>
      <description>&lt;p&gt;If you had asked me ten years ago about my ability to handle cognitive load, I
would have said that I was above average. Today? I would say that I am pretty
bad at holding multiple things in my head at once. Whether that is due to aging,
fatherhood, multiple-bouts of COVID, humility, or any combination of those, I
can’t say.&lt;/p&gt;
&lt;p&gt;What I do know is that I am now very interested in reducing cognitive load so
that I can do my best work.&lt;/p&gt;
&lt;p&gt;In a recent issue of &lt;a href=&quot;https://frontendatscale.com/issues/8/&quot;&gt;Frontend At Scale&lt;/a&gt;,
Maxi Ferreira wrote about how declarative code can reduce cognitive load. Giving
an example service for file uploading with user permissions, Ferreira proposes a
simple list of maps for file extensions and permissions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This [declarative code solution] doesn&#39;t simplify the relationship between our
services in any way, but it does wonders for reducing cognitive load. We might
still have some complicated logic between our different services, but now we
don&#39;t need to concern ourselves with it. We can now &lt;em&gt;declare&lt;/em&gt; our updated
rules without having to load a bunch of information in our heads.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This feels like a significant insight and is a great solution to the example
problem. You don’t need to know &lt;em&gt;how&lt;/em&gt; the system works (at least, not at first),
but you can quickly understand &lt;em&gt;what&lt;/em&gt; the system does by looking at the
validation rules.&lt;/p&gt;
&lt;p&gt;Now I’m off to brainstorm about how declarative code could reduce some of the
cognitive load in systems I’m working on at Khan Academy. Give the full article
a look and let me know what you think.&lt;/p&gt;
</description>
      <pubDate>Wed, 25 Oct 2023 15:03:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/declarative-code-and-cognitive-load/</guid>
    </item>
    <item>
      <title>Attempting #divtober 2023</title>
      <link>https://seanmcp.com/articles/attempting-divtober-2023/</link>
      <description>&lt;p&gt;In years past I have participated in Hacktoberfest. But I&#39;ve found that the
t-shirt recently it isn&#39;t worth the effort as a contributor (to speak nothing of
the headache it is as a maintainer).&lt;/p&gt;
&lt;p&gt;I&#39;m used to doing &lt;em&gt;something&lt;/em&gt; special in October, so my curiousity was piqued
when I read about &lt;a href=&quot;https://a.singlediv.com/divtober2023/&quot;&gt;#divtober&lt;/a&gt; in
&lt;a href=&quot;https://moderncss.dev/&quot;&gt;Steph Eckles&#39; &lt;em&gt;Modern CSS&lt;/em&gt; newsletter&lt;/a&gt;. Instead of
spamming a bunch of maintainers with trivial PRs, you get to spam a single &lt;code&gt;div&lt;/code&gt;
with a bunch of pseudo elements and box shadows. Neat!&lt;/p&gt;
&lt;p&gt;So that&#39;s my goal for this month. Here is a some inspiration if you would like
to join the party: &lt;a href=&quot;https://a.singlediv.com&quot;&gt;https://a.singlediv.com&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Tue, 10 Oct 2023 13:17:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/attempting-divtober-2023/</guid>
    </item>
    <item>
      <title>How I&#39;m using AI in August &amp; September 2023</title>
      <link>https://seanmcp.com/articles/how-im-using-ai-in-august-september-2023/</link>
      <description>&lt;p&gt;Sorry for two months in one post, but I haven&#39;t been using AI much recently. I
think the trend so far is that AI-powered products that are directly related to
my work are more likely to stick around. All the others come and go more
quickly.&lt;/p&gt;
&lt;p&gt;AI products/services that I used regularly (multiple times per week):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; ⭐: Copilot continues to be a valuable resource when
coding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI Products/services that I used occasionally (once per week or less):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Warp&lt;/strong&gt; 📈: I have used Warp&#39;s AI-powered command suggestion occasionally
over the past two months. It&#39;s helpful when I&#39;m looking for a git command or
something similar but can&#39;t quite remember the flags or syntax.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI products/services that I have access to but did not use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bard&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bing&lt;/strong&gt; 📉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Khanmigo&lt;/strong&gt; 📉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dall-E&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MDN&lt;/strong&gt; 📉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notion&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Tue, 03 Oct 2023 21:22:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-im-using-ai-in-august-september-2023/</guid>
    </item>
    <item>
      <title>See what object properties are accessed in JavaScript</title>
      <link>https://seanmcp.com/articles/see-what-object-properties-are-accessed-in-javascript/</link>
      <description>&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/see-what-object-properties-are-accessed.png&quot; alt=&quot;A magnifying glass that is inspecting a JavaScript object&quot;&gt;&lt;/p&gt;
&lt;p&gt;At work I was mocking an object to be passed to a React context provider. I knew
the type for the object, but I wanted to learn what properties were being
accessed on the object from this point in the component tree.&lt;/p&gt;
&lt;p&gt;Thankfully, JavaScript has a native way to &amp;quot;watch&amp;quot; an object and respond
whenever any of its properties are accessed or reassigned:
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy&quot;&gt;a Proxy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First start with the object that you want to watch:&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; user &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 literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;createdAt&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then pass your object to the &lt;code&gt;Proxy&lt;/code&gt; constructor along with a handler object
that determines what you want to do you when a property is get or set:&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; userProxy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Proxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&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 function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; property&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&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;Getting &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;property&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; on &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;target&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;property&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 class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; property&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&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;Setting &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;property&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; on &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;target&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; to &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;value&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    target&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;property&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 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 can use &lt;code&gt;userProxy&lt;/code&gt; as if it were the original &lt;code&gt;user&lt;/code&gt; object. I passed
the proxy into the context provider and then watched then checked the console to
see what properties for that object were accessed.&lt;/p&gt;
&lt;p&gt;Nice and easy! Let me know if you have any other use cases for a proxy in
JavaScript.&lt;/p&gt;
</description>
      <pubDate>Thu, 28 Sep 2023 10:30:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/see-what-object-properties-are-accessed-in-javascript/</guid>
    </item>
    <item>
      <title>I&#39;m not dead yet!</title>
      <link>https://seanmcp.com/articles/im-not-dead-yet/</link>
      <description>&lt;p&gt;I found out at the end of August that I talk that I proposed for Pittsburgh
TechFest 2023 had been accepted. This was excited, but there was a catch: I only
had two weeks to prepare the presentation.&lt;/p&gt;
&lt;p&gt;So I spent the first two weeks of September working on that talk to the
detriment of everything else (sidebar: thank you wife Anne for accommodating
that!). And thankfully the &amp;quot;two week deadline&amp;quot; for all presentation materials
turned into three weeks, and I was able to get everything ready in time.&lt;/p&gt;
&lt;p&gt;But that means that I am back and ready to start writing again. It felt fitting
to return with an article for you, the RSS Club. I&#39;m looking forward to sharing
some of the details from my presentation and writing about other topics that I
have been thinking about over the past few weeks.&lt;/p&gt;
&lt;p&gt;See you soon!&lt;/p&gt;
</description>
      <pubDate>Mon, 25 Sep 2023 10:03:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/im-not-dead-yet/</guid>
    </item>
    <item>
      <title>Remove duplicates from an array in JavaScript</title>
      <link>https://seanmcp.com/articles/remove-duplicates-from-an-array-in-javascript/</link>
      <description>&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/seanmcp/image/upload/v1692892991/remove-duplicates-in-array_ptvagw.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Removing duplicates, sometimes called de-duplicating or de-duping, is a common
task in programming. There are many ways for removing duplicates from an array
in JavaScript, and here I am going to share two scenarios and strategies.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;array-of-primitive-values&quot; tabindex=&quot;-1&quot;&gt;Array of primitive values&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-duplicates-from-an-array-in-javascript/#array-of-primitive-values&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Array of primitive values”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In JavaScript,
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values&quot;&gt;primitive values&lt;/a&gt;
are the simplest form of data like strings and numbers. Let&#39;s say you have an
array of strings:&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; original &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;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can de-duplicate this array by using it to create a set, a special object
that only stores unique values. We&#39;ll pass it to the &lt;code&gt;Set&lt;/code&gt; constructor, and then
compare the two by looking at how many items they contain:&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; original &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;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 keyword&quot;&gt;const&lt;/span&gt; set &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; vs &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; set&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size&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 comment&quot;&gt;// &quot;4 vs 3&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final step is to convert this new set back into an array. We can do this
with the &lt;code&gt;Array.from&lt;/code&gt; method:&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; original &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;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 keyword&quot;&gt;const&lt;/span&gt; set &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&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 keyword&quot;&gt;const&lt;/span&gt; deDuped &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;set&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&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 comment&quot;&gt;// [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;b&quot;]&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;deDuped&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 comment&quot;&gt;// [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that strategy, we could write a one-liner or create a helper function for
reuse later:&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; original &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;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 keyword&quot;&gt;const&lt;/span&gt; deDuped &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&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 keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;deDupePrimitiveArray&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;arr&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; Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr&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 function&quot;&gt;deDupePrimitiveArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&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 comment&quot;&gt;// [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think this is a nice and modern solution to remove duplicates from simple
arrays, but &lt;em&gt;it will not work&lt;/em&gt; when you have an array of objects. For that,
we&#39;ll need a different strategy.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;array-of-objects&quot; tabindex=&quot;-1&quot;&gt;Array of objects&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-duplicates-from-an-array-in-javascript/#array-of-objects&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Array of objects”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In JavaScript, object-like values like objects and arrays need a different
strategy for removing duplicates. Let&#39;s say you have an array of objects with a
unique &lt;code&gt;id&lt;/code&gt; property:&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; original &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 punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use this &lt;code&gt;id&lt;/code&gt; to keep track of which objects we have already seen. There
are multiple options to do this, but let&#39;s stick with sets since we used that in
the last example. We&#39;ll create a new set to keep track of ids that we&#39;ve already
seen:&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; original &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 punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 keyword&quot;&gt;const&lt;/span&gt; seen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we filter the original array by checking if the current object&#39;s id is in
the set. If it is, then we know that this object is a duplicate and can be
filtered out:&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; original &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 punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 keyword&quot;&gt;const&lt;/span&gt; seen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&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 keyword&quot;&gt;const&lt;/span&gt; deDuped &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; original&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&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;current&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 class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;seen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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;
  seen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 class=&quot;token comment&quot;&gt;// [{ id: &quot;a&quot; }, { id: &quot;b&quot; }, { id: &quot;c&quot; }]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can&#39;t write a one-liner for this, but we can create a helper function. In
addition to the array, we&#39;ll need to accept a function that maps an item in the
array to the unique property that we&#39;ll use for comparison:&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; original &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 punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;deDupeObjectArray&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;arr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mapper&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; seen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&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 keyword&quot;&gt;return&lt;/span&gt; arr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&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;current&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;seen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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;
    seen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 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 function&quot;&gt;deDupeObjectArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&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;item&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; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&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 comment&quot;&gt;// [{ id: &quot;a&quot; }, { id: &quot;b&quot; }, { id: &quot;c&quot; }]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have a mapper function, we can combine this with the previous
strategy to create a single function that can de-dupe both primitive and object
arrays.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;bringing-it-all-together&quot; tabindex=&quot;-1&quot;&gt;Bringing it all together&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-duplicates-from-an-array-in-javascript/#bringing-it-all-together&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Bringing it all together”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;To create a function that will remove duplicates from an array of primitive or
object-like values, we can take the solution for object arrays and modify it
slightly with a default mapper function:&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; &lt;span class=&quot;token function-variable function&quot;&gt;deDupe&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;arr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;mapper&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;item&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; item&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; seen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&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 keyword&quot;&gt;return&lt;/span&gt; arr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&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;current&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;seen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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;
    seen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 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 we can use this function to remove duplicates from primitive or object
arrays:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deDupe&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 string&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 class=&quot;token comment&quot;&gt;// [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;deDupe&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&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 literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&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 class=&quot;token parameter&quot;&gt;item&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; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
  &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 comment&quot;&gt;// [{ id: &quot;a&quot; }, { id: &quot;b&quot; }, { id: &quot;c&quot; }]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see all of the examples above in this REPL:
&lt;a href=&quot;https://replit.com/@SeanMcP/removing-duplicates-in-arrays#index.ts&quot;&gt;https://replit.com/@SeanMcP/removing-duplicates-in-arrays#index.ts&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Fri, 25 Aug 2023 10:38:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/remove-duplicates-from-an-array-in-javascript/</guid>
    </item>
    <item>
      <title>Off to Kindergarten</title>
      <link>https://seanmcp.com/articles/off-to-kindergarten/</link>
      <description>&lt;p&gt;Today is the first day of Kindergarten for my first-born son, Samuel. At 8:00pm
this morning we&#39;ll drive him to school, and his 13+ year journey in education
will begin.&lt;/p&gt;
&lt;p&gt;I&#39;m excited for this new chapter of his life, but I&#39;m going to miss him a lot.
Since the pandemic started in 2020 and I started working from home, I have had
the privilege of seeing, talking to, and playing with Samuel almost every
weekday.&lt;/p&gt;
&lt;p&gt;I loved when he would stop by during the day and tell me a fact that he learned
while reading, show me a picture that he drew, or share something he thought was
funny. I loved eating lunch with him, getting random high-fives and hugs, and
taking a tickle break during the day.&lt;/p&gt;
&lt;p&gt;I&#39;m thankful that my wife was able and willing to mother him full-time--we&#39;ve
both gotten to know him so well over the past 5 years. I&#39;m thankful that I work
from home and can be around for her and the kids throughout the day. I&#39;m
thankful that Samuel is off to a wonderful school that will both challenge and
encourage him. I&#39;m thankful for the one-on-one time that we will get with Ezra
while his big brother is at school.&lt;/p&gt;
&lt;p&gt;Life transitions like this can be hard for both parents and kids. I know I&#39;m
feeling the full range of emotions! So I&#39;m going to try to hold them all gladly
today: excited, nervous, proud, scared, optimistic, sad, hopeful.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Godspeed, Samuel. May the road rise to meet you. May the wind be ever at your
back. May the sun shine warm on your face. May the rain fall gently on your
fields. And until we meet again: may God hold you in the hollow of His hand.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
      <pubDate>Wed, 23 Aug 2023 10:59:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/off-to-kindergarten/</guid>
    </item>
    <item>
      <title>1000 commits</title>
      <link>https://seanmcp.com/articles/1000-commits/</link>
      <description>&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/seanmcp/image/upload/w_604/contribution-graph_i3pdcb.png&quot; alt=&quot;This repos contribution graph&quot;&gt;&lt;/p&gt;
&lt;p&gt;Publishing this article will be the 1000th commit to my website repository. This
makes it my most-active and side project by a large margin.&lt;/p&gt;
&lt;p&gt;I created the repo in April of 2019 as the successor to
&lt;a href=&quot;https://github.com/SeanMcP/.com&quot;&gt;a Gatsby site&lt;/a&gt; that was
&lt;a href=&quot;https://github.com/SeanMcP/seanmcp.com/tree/231e93ae7b353432148b54e065248d8724f610b9&quot;&gt;powered by Eleventy&lt;/a&gt;.
That version continued until the fall of 2022 when
&lt;a href=&quot;https://github.com/SeanMcP/seanmcp.com/tree/53841b172d875f54bd63fc972f97c28d949f73c3&quot;&gt;I rewrote everything in Astro&lt;/a&gt;.
Since then I’ve logged over a third of those 1000 commits; the pace of
development is going strong.&lt;/p&gt;
&lt;p&gt;Reflecting on the last four years of development, I can say that building this
site has been a valuable use of my time. Working on this site has provided
opportunities to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;practice skills&lt;/li&gt;
&lt;li&gt;learn new techniques, and&lt;/li&gt;
&lt;li&gt;explore new technologies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plus it’s rewarding to have created a valuable resource that helps thousands of
visitors every month.&lt;/p&gt;
&lt;p&gt;Here’s to the next 1000!&lt;/p&gt;
</description>
      <pubDate>Fri, 11 Aug 2023 10:50:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/1000-commits/</guid>
    </item>
    <item>
      <title>How I&#39;m using AI in July 2023</title>
      <link>https://seanmcp.com/articles/how-im-using-ai-in-july-2023/</link>
      <description>&lt;p&gt;AI products/services that I used regularly (multiple times per week):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; ⭐: Copilot continues to be a valuable resource when
coding. I was working on a script while offline, and I missed seeing it&#39;s
suggestions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI Products/services that I used occasionally (once per week or less):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bing Images&lt;/strong&gt; &amp;amp; &lt;strong&gt;ChatGPT&lt;/strong&gt;: I used these for a small project (creating a
fantasy RPG to play with my kids), but it didn&#39;t end up going anywhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Khanmigo&lt;/strong&gt;: Khan Academy has their own AI offering that I have access to
through work. It&#39;s neat, but I&#39;m not convinced how useful it is for the
average user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MDN&lt;/strong&gt;: MDN launched a new AI-powered search feature that I tried a few
times. The results weren&#39;t great: nothing for information that should have
been easy to find, and some incorrect answers. But I can see potential in
something like that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI products/services that I have access to but did not use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bard&lt;/strong&gt; 📉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bing&lt;/strong&gt; 📉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dall-E&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notion&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Warp&lt;/strong&gt; 📉&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Tue, 08 Aug 2023 10:47:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-im-using-ai-in-july-2023/</guid>
    </item>
    <item>
      <title>Timeboxing types</title>
      <link>https://seanmcp.com/articles/timeboxing-types/</link>
      <description>&lt;p&gt;Two things that I currently believe:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There is a lot of value in statically typed code&lt;/li&gt;
&lt;li&gt;Writing types is a potential time sink with diminishing returns&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I don&#39;t feel like I need to justify the first belief. The trend in the front-end
industry towards TypeScript illustrates that there is a growing consensus that
types are a net positive.&lt;/p&gt;
&lt;p&gt;That second belief probably needs more of a defense. I think there is a general
maximum value for any given type, and the additional value derived from writing
a type decrease over timer. Plotted on a chart, I think it looks something like
this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/value-of-writing-types.png&quot; alt=&quot;A chart showing the value of writing types decreasing over time&quot;&gt;&lt;/p&gt;
&lt;p&gt;Despite this knowledge, my brain is still tempted to spend significant amounts
of time writing the &amp;quot;perfect&amp;quot; type. I find myself giving away a lot of
additional time while deriving little to no additional value.
&lt;a href=&quot;https://v5.chriskrycho.com/journal/is-typescript-good/&quot;&gt;Chris Krycho, an engineer at LinkedIn, puts it this way&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Type systems very often do mash a puzzle-solving button in our brains.
Figuring out how to get the types &lt;em&gt;just right&lt;/em&gt; for a given design can lead an
unwary developer (reader: I mean myself) into an hours-long maze. Getting out
of one of those mazes with a good solution feels incredibly satisfying. But it
is not always &lt;em&gt;worth&lt;/em&gt; it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The puzzle metaphor rings true for me. As with other puzzles, we want to spend
the time to &amp;quot;solve&amp;quot; them. And as the types grow more complex, so too does the
amount of time required for the &amp;quot;solution&amp;quot;. I don&#39;t know if I can say
exponentially, but that doesn&#39;t feel far off.&lt;/p&gt;
&lt;p&gt;The reality is that a basic type written quickly will probably get you 90+% of
the benefits of the &amp;quot;perfect&amp;quot; type. Chris continues:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A simpler type might be a little less precise, might catch a couple fewer
errors, might make the system mildly less robust at runtime, might make it
possible for there to be runtime bugs the system could in principle have ruled
out entirely by clever use of the type system… and be 100% worth those
tradeoffs given the context.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think the solution to marry beliefs 1 and 2 is to &lt;strong&gt;timebox the amount of time
that you spend writing any given type&lt;/strong&gt;. Give yourself 5 minutes to write the
best type that you can, and then move on.&lt;/p&gt;
&lt;p&gt;If you can write the perfect type--&amp;quot;solve&amp;quot; that puzzle--then great! But if not,
that&#39;s okay too. You surely have enough value in the type to justify the time
you spent writing it.&lt;/p&gt;
&lt;p&gt;This timeboxed writing should be in combination with an effort to master the
type system you are using. As you grow in your understanding of the system, the
quality of the types that you can write within your timebox will increase.&lt;/p&gt;
&lt;p&gt;Is 5 minutes enough time? That depends. If you are new to the type system, you
might need a little more time to write a valuable type. Or if you are dealing
with a mission-critical type, it might be worthwhile to invest more time. If you
are authoring a library that will be shared widely with the organization or
world, then a more-perfect type will probably be worth the time (if only to save
yourself future support work).&lt;/p&gt;
&lt;p&gt;But for the average developer with some experience writing types, I think 5
minutes is a good starting point.&lt;/p&gt;
&lt;p&gt;Try it out, and
&lt;a href=&quot;mailto:sean@seanmcp.com?subject=I%20tried%20timeboxing%20types&quot;&gt;let me know what you think&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Wed, 02 Aug 2023 10:14:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/timeboxing-types/</guid>
    </item>
    <item>
      <title>CSS columns are neat</title>
      <link>https://seanmcp.com/articles/css-columns-are-neat/</link>
      <description>&lt;p&gt;I was looking for a solution to dynamically create two columns in a UI when I
came across the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/columns&quot;&gt;&lt;code&gt;columns&lt;/code&gt; CSS property&lt;/a&gt;.
With a single property, you can tell the browser to split content evenly between
&lt;em&gt;n&lt;/em&gt; number of columns:&lt;/p&gt;
&lt;div class=&quot;example stack()&quot; style=&quot;columns: 2&quot;&gt;
&lt;p&gt;&lt;strong&gt;Two columns&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ornare augue
placerat, malesuada nulla volutpat, pharetra arcu. Aenean consectetur diam in
justo interdum, eu pharetra purus congue.&lt;/p&gt;
&lt;p&gt;Sed cursus porta lorem, non posuere ex sagittis nec. Nam pulvinar rutrum
condimentum. Proin sodales neque eget eros vulputate, vel hendrerit sem mollis.
Sed quis faucibus quam.&lt;/p&gt;
&lt;p&gt;Etiam at enim id quam posuere feugiat non posuere est. Donec dictum lacus
posuere augue congue, ac tincidunt velit dignissim. Quisque hendrerit eros vel
erat ornare porta.&lt;/p&gt;
&lt;p&gt;Nunc viverra enim dui, sit amet dapibus lacus mollis vel. Donec vitae lorem
sagittis, vulputate mauris a, posuere est. Nam nulla urna, laoreet in massa id,
finibus tincidunt risus.&lt;/p&gt;
&lt;p&gt;Aliquam quam massa, varius ac dolor ac, pharetra hendrerit eros. Suspendisse at
facilisis leo, vel posuere dui. In hac habitasse platea dictumst. Proin congue
euismod metus quis dictum.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;example stack()&quot; style=&quot;columns: 3&quot;&gt;
&lt;p&gt;&lt;strong&gt;Three columns&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ornare augue
placerat, malesuada nulla volutpat, pharetra arcu. Aenean consectetur diam in
justo interdum, eu pharetra purus congue.&lt;/p&gt;
&lt;p&gt;Sed cursus porta lorem, non posuere ex sagittis nec. Nam pulvinar rutrum
condimentum. Proin sodales neque eget eros vulputate, vel hendrerit sem mollis.
Sed quis faucibus quam.&lt;/p&gt;
&lt;p&gt;Etiam at enim id quam posuere feugiat non posuere est. Donec dictum lacus
posuere augue congue, ac tincidunt velit dignissim. Quisque hendrerit eros vel
erat ornare porta.&lt;/p&gt;
&lt;p&gt;Nunc viverra enim dui, sit amet dapibus lacus mollis vel. Donec vitae lorem
sagittis, vulputate mauris a, posuere est. Nam nulla urna, laoreet in massa id,
finibus tincidunt risus.&lt;/p&gt;
&lt;p&gt;Aliquam quam massa, varius ac dolor ac, pharetra hendrerit eros. Suspendisse at
facilisis leo, vel posuere dui. In hac habitasse platea dictumst. Proin congue
euismod metus quis dictum.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can also set a certain width for the columns:&lt;/p&gt;
&lt;div class=&quot;example stack()&quot; style=&quot;columns: 10ch&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;10ch&lt;/code&gt; width&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ornare augue
placerat, malesuada nulla volutpat, pharetra arcu. Aenean consectetur diam in
justo interdum, eu pharetra purus congue.&lt;/p&gt;
&lt;p&gt;Sed cursus porta lorem, non posuere ex sagittis nec. Nam pulvinar rutrum
condimentum. Proin sodales neque eget eros vulputate, vel hendrerit sem mollis.
Sed quis faucibus quam.&lt;/p&gt;
&lt;p&gt;Etiam at enim id quam posuere feugiat non posuere est. Donec dictum lacus
posuere augue congue, ac tincidunt velit dignissim. Quisque hendrerit eros vel
erat ornare porta.&lt;/p&gt;
&lt;p&gt;Nunc viverra enim dui, sit amet dapibus lacus mollis vel. Donec vitae lorem
sagittis, vulputate mauris a, posuere est. Nam nulla urna, laoreet in massa id,
finibus tincidunt risus.&lt;/p&gt;
&lt;p&gt;Aliquam quam massa, varius ac dolor ac, pharetra hendrerit eros. Suspendisse at
facilisis leo, vel posuere dui. In hac habitasse platea dictumst. Proin congue
euismod metus quis dictum.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I don&#39;t come across many use cases for this magazine-style layout, but it&#39;s nice
to know that CSS has solution for it.&lt;/p&gt;
</description>
      <pubDate>Tue, 01 Aug 2023 11:18:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/css-columns-are-neat/</guid>
    </item>
    <item>
      <title>Quality requires iteration</title>
      <link>https://seanmcp.com/articles/quality-requires-iteration/</link>
      <description>&lt;p&gt;You&#39;re not going to get it right the first time. You might not even get it right
the second time. But if you iterate enough, you&#39;re going to find a good
solution.&lt;/p&gt;
&lt;p&gt;Too often I catch myself overanalyzing the first pass at a problem. I spend too
much time &lt;em&gt;thinking&lt;/em&gt; about how the whole system needs to work, or all of the
edge cases that need to be addressed, and trying to plan the Perfect™ solution.&lt;/p&gt;
&lt;p&gt;But it is never the case that plan A is the final solution. So why spend so much
time agonizing over the first attempt?&lt;/p&gt;
&lt;p&gt;Think about the problem that you need to solve, sure. Consider the edge cases
that come to mind, definitely. Then move quickly on to building something.&lt;/p&gt;
&lt;p&gt;Write a function once to figure out how it needs to work, and then write it
again now that you know more.&lt;/p&gt;
&lt;p&gt;Throw a bunch of messy components in a single file while you figure out what the
UI requires and how you can compose them. Then refactor and move things into
their own files (with individual stories and tests).&lt;/p&gt;
&lt;p&gt;If you have time, iterate again... and maybe again. Each pass should get faster
and more direct, because your knowledge of the problem is increasing the the
picture of the solution is growing more clear.&lt;/p&gt;
&lt;p&gt;Plan to make several attempts at the solution. Plan to delete what you&#39;ve
written. Plan for several rounds of code review. Plan to plan less up front and
more as you go.&lt;/p&gt;
&lt;p&gt;Quality in programming requires iterations. So lean into it.&lt;/p&gt;
</description>
      <pubDate>Tue, 01 Aug 2023 10:57:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/quality-requires-iteration/</guid>
    </item>
    <item>
      <title>A workable emoji picker on Ubuntu</title>
      <link>https://seanmcp.com/articles/a-workable-emoji-picker-on-ubuntu/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://github.com/SeanMcP?tab=repositories&amp;amp;q=emoji&amp;amp;sort=stargazers&quot;&gt;I like to use emojis&lt;/a&gt;
and have missed them since moving to Linux/Ubuntu. They render fine, but there
isn’t a good way to input emojis into a field like on macOS and Windows.&lt;/p&gt;
&lt;p&gt;Technically there is a native GNOME emoji picker, but it only works with
“&lt;a href=&quot;https://itsfoss.com/ubuntu-emojis/&quot;&gt;supported GTK 3 applications&lt;/a&gt;” and I don’t
use
&lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_GTK_applications&quot;&gt;any of those regularly&lt;/a&gt;.
I tried installing a few third-party options, but none of them worked well.&lt;/p&gt;
&lt;p&gt;I thought that I was resigned to not using emojis often on Linux, when one day I
discovered suggested emojis in the
&lt;a href=&quot;https://help.ubuntu.com/stable/ubuntu-help/shell-introduction.html.he#activities&quot;&gt;Activities overview’s&lt;/a&gt;
search results.&lt;/p&gt;
&lt;p&gt;Here are the steps that work for me:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Press the super key (formerly Windows)&lt;/li&gt;
&lt;li&gt;Type the name of the emoji: &lt;em&gt;e.g.&lt;/em&gt; turtle&lt;/li&gt;
&lt;li&gt;Select the emoji result from the Characters application to copy to clipboard&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;In addition to your mouse, you can use &lt;kbd&gt;tab&lt;/kbd&gt; and the up/down arrow
keys to navigate the options 🙌&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Paste the emoji in your application&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This process is not perfect: there are some emojis that are hard to find by
name, and the lack of browsing makes discoverability a challenge. However, it’s
definitely good enough for me for now.&lt;/p&gt;
&lt;p&gt;It would be nice if all applications could use the GNOME emoji picker, but I’m
too new here to know if that is even possible. A man can dream.&lt;/p&gt;
</description>
      <pubDate>Wed, 05 Jul 2023 11:25:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-workable-emoji-picker-on-ubuntu/</guid>
    </item>
    <item>
      <title>My first RSS Club oopsie</title>
      <link>https://seanmcp.com/articles/my-first-rss-club-oopsie/</link>
      <description>&lt;p&gt;My latest article, &lt;a href=&quot;https://seanmcp.com/articles/losing-steam&quot;&gt;&lt;em&gt;Losing steam&lt;/em&gt;&lt;/a&gt;, was supposed to be
an exclusive for the RSS Club. But I confused &lt;code&gt;tags: RSS-ONLY&lt;/code&gt; with
&lt;code&gt;flags: RSS-ONLY&lt;/code&gt; in the frontmatter, and now it is published for the world to
see: oops!&lt;/p&gt;
&lt;p&gt;(Note to self: avoid data field names that rhyme)&lt;/p&gt;
&lt;p&gt;I could fix it but that would break my feed, and I really like the idea of RSS
as being the source of truth for my content. It is what it is!&lt;/p&gt;
&lt;p&gt;So forgive my mistake, and enjoy the article. I look forward to hearing from
anyone who has suggestions for writing prompts.&lt;/p&gt;
</description>
      <pubDate>Tue, 04 Jul 2023 11:49:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/my-first-rss-club-oopsie/</guid>
    </item>
    <item>
      <title>Losing steam</title>
      <link>https://seanmcp.com/articles/losing-steam/</link>
      <description>&lt;p&gt;I set a goal earlier in the year to focus on writing more. And while I have
already written 35 articles in 2023 (one fewer than all of 2022), I have found
motivation waning in recent months.&lt;/p&gt;
&lt;p&gt;Part of that is the regular ebb and flow of life, which is normal. I have other
obligations outside of work/professional development that take my time and
attention: parenting, home ownership, church responsibilities, and volunteer
work. I don&#39;t want to be one-dimensional, so I&#39;m happy to step away and work on
other things.&lt;/p&gt;
&lt;p&gt;But when I do set aside time to write, I have found recently that I don&#39;t have
much to say. Some writers that I admire share their opinions on a current topic
or trend in tech, and I value their contributions. But as time goes on, I find
that my opinions are growing weaker not stronger. And I don&#39;t think that &amp;quot;A is
neat, but B is also fine too&amp;quot; makes for a compelling read. So perhaps
&amp;quot;columnist&amp;quot; is not in my future.&lt;/p&gt;
&lt;p&gt;To help unblock myself, I created
&lt;a href=&quot;https://seanmcp.com/resources/writing-prompts-for-programmers&quot;&gt;a new garden of writing prompts for programmers&lt;/a&gt;.
These are cultivated for freewriting, an exercise where you write about a
particular topic for a set amount of time. The goal isn&#39;t the output but rather
the practice of getting started writing. It helps prime your brain for creative
thinking, which I think is exactly what I need.&lt;/p&gt;
&lt;p&gt;So the next time that I feel stuck or unmotivated to write, I&#39;m going to try to
sit down with one of those prompts for five minutes and see what happens.&lt;/p&gt;
&lt;p&gt;Let me know what you think of the prompts, and be sure to
&lt;a href=&quot;mailto:sean@seanmcp.com?subject=Writing%20prompts%20for%20programmers&quot;&gt;email me any suggestions&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Tue, 04 Jul 2023 10:58:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/losing-steam/</guid>
    </item>
    <item>
      <title>Reviewing &quot;Go By Example&quot;</title>
      <link>https://seanmcp.com/articles/reviewing-go-by-example/</link>
      <description>&lt;p&gt;I started &lt;a href=&quot;https://gobyexample.com/&quot;&gt;Go By Example&lt;/a&gt; in April with the goal of
learning the language for work. &lt;a href=&quot;https://khanacademy.org&quot;&gt;Khan Academy&lt;/a&gt; (and my
old company &lt;a href=&quot;https://niche.com&quot;&gt;Niche&lt;/a&gt;) use Go for their back-end services. And
while I am focused on the front end, knowledge of the Go language is valuable
for my work.&lt;/p&gt;
&lt;p&gt;It took me 80 days from start to finish to complete the 79 examples. After a few
starts and stops in the beginning, I settled into a routine of starting each day
(barring Sunday) by working through an example. This felt like a good pace to
learn the syntax and patterns slowly so that it sticks. And I think that it has,
for the most part!&lt;/p&gt;
&lt;p&gt;Overall, I enjoyed the exercises, and I would recommend Go By Example to anyone
who is interested in learning the Go language. The examples were focused and
clear, and the progression of concepts was logical. After completing the course,
I feel confident reading and writing Go, and feel like I could contribute to an
existing project.&lt;/p&gt;
&lt;p&gt;One criticism of the course is that it does little to explain how Go projects
work beyond simple scripts. I don’t have clear understanding of “packages” in Go
which made it difficult to explore the language with side projects. There was an
example on how to make a simple HTTP server but no details on how to deploy one.&lt;/p&gt;
&lt;p&gt;To be fair, that wasn’t the point of the course. But if you’re looking for a
more holistic introduction to Go, you might need to supplement Go By Example
with something else.&lt;/p&gt;
&lt;p&gt;So I’m looking for an additional resource to fill in some of those knowledge
gaps. If you have any recommendations,
&lt;a href=&quot;mailto:sean@seanmcp.com?subject=Go%20Resources&quot;&gt;please let me know&lt;/a&gt;. Otherwise,
I’ll write again when I have found something that works well.&lt;/p&gt;
</description>
      <pubDate>Tue, 04 Jul 2023 10:51:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/reviewing-go-by-example/</guid>
    </item>
    <item>
      <title>How I&#39;m Using AI in June 2023</title>
      <link>https://seanmcp.com/articles/how-im-using-ai-in-june-2023/</link>
      <description>&lt;p&gt;AI products/services that I used regularly (multiple times per week):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; ⭐: Copilot continues to be a valuable resource when
coding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI Products/services that I used occasionally (once per week or less):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bard&lt;/strong&gt; 📉: Although a rising star last month, I found that the quality of
its answers seemed to decrease over the past month. That could be due to the
types of questions I was asking (primarily Go coding questions) or changes
under the hood.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bing&lt;/strong&gt; 📉: I use Edge as my daily browser and will occasionally send it a
question, but it doesn’t play a big part in my workflow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Warp&lt;/strong&gt; 📉: Handy to look up a common command without leaving the terminal,
but that isn’t something that I need to do regularly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI products/services that I have access to but did not use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bing Images&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ChatGPT&lt;/strong&gt; 📉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dall-E&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notion&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Fri, 23 Jun 2023 10:57:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-im-using-ai-in-june-2023/</guid>
    </item>
    <item>
      <title>Looking for a new creative outlet</title>
      <link>https://seanmcp.com/articles/looking-for-a-new-creative-outlet/</link>
      <description>&lt;p&gt;Since changing careers in 2017, programming has been by creative outlet. I enjoy
coding in the early mornings before the rest of the family is awake, in the
evenings when the kids are in bed, and on Saturdays when I have some downtime.&lt;/p&gt;
&lt;p&gt;In general, I think this hobby interest in programming has been a help to my
career as a software engineer. But occasionally my passion for it wanes, and it
can have a negative impact on my drive at work. I don’t want my creative outlet
and job to be &lt;em&gt;too&lt;/em&gt; closely connected.&lt;/p&gt;
&lt;p&gt;When I’m feeling disinterested in my creative outlet, my default is to go to
consumption: watching YouTube videos and playing video games. And rarely do I
feel &lt;em&gt;good&lt;/em&gt; about my decisions after doing that for a long stretch of time.&lt;/p&gt;
&lt;p&gt;So I’d like to find a new hobby for those times when programming is unappealing.
Something simple that I can pick up and put down whenever. Preferably something
offline and low tech. When I was a kid I was interested in drawing (my first
career choice was an illustrator), so maybe that is a good option option to
pursue. Plus it’s a good activity to do with little children.&lt;/p&gt;
&lt;p&gt;I want to cultivate more opportunities to create as opposed to consume. Any
suggestions?&lt;/p&gt;
</description>
      <pubDate>Mon, 05 Jun 2023 15:01:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/looking-for-a-new-creative-outlet/</guid>
    </item>
    <item>
      <title>A quick comparison of JavaScript and Go executables</title>
      <link>https://seanmcp.com/articles/quick-comparison-of-javascript-and-go-executables/</link>
      <description>&lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt;: Bun and Deno, JavaScript runtimes, provide a native way to
convert your JavaScript code into a single executable file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bun: &lt;code&gt;bun build --compile my-script.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Deno: &lt;code&gt;deno compile my-script.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To create these executables, Bun and Deno need to include their entire JS
runtime in the executable file. That means all of Apple’s JavaScriptCore for Bun
and Google’s V8 for Deno. But once that is compiles, the executables should work
in any environment that those runtimes support.&lt;/p&gt;
&lt;p&gt;I wanted to compare these new JavaScript options and Go to see if JS is a
legitimate option for creating cross-environment executables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;: I created a project for a simple command-line utility for reading
&lt;code&gt;package.json&lt;/code&gt; files from the terminal:
&lt;a href=&quot;https://github.com/seanmcp/rpj&quot;&gt;github.com/seanmcp/rpj&lt;/a&gt;. Then I implemented the
utility in all three environments: Bun, Deno, and Go. I have Bash scripts to
create the executables and then compare the results by two metrics: file size
and execution time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The numbers&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bun: 97733752 bytes or 97.7 megabytes&lt;/li&gt;
&lt;li&gt;Deno: 109968928 bytes or 110.0 megabytes&lt;/li&gt;
&lt;li&gt;Go: 2607443 bytes or 2.6 megabytes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Go created the smallest executable (by a wide margin). I ran into issues with
the Deno executable being rejected by GitHub for being too large, and Bun isn’t
far behind.&lt;/p&gt;
&lt;p&gt;Execution time&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bun: 0:00.04 real, 0.02 user, 0.01 sys&lt;/li&gt;
&lt;li&gt;Deno: 0:00.04 real, 0.02 user, 0.01 sys&lt;/li&gt;
&lt;li&gt;Go: 0:00.00 real, 0.00 user, 0.00 sys&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The execution time was not a major factor in my example, and my hypothesis is
that most of the difference is due to the startup cost of the JavaScript
runtime. We would need more tests to determine how big the difference in
execution time between these options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: Based on these results, Go is still the superior option for
executables based on file size and execution time. However, Bun and Deno
provided a great authoring experience for developers who are familiar with the
JavaScript ecosystem. And the ability to use pre-existing tools and processes
when creating executables might make Bun or Deno the best option for you and
your team.&lt;/p&gt;
&lt;p&gt;Personally, I’m excited to see how this feature of JavaScript runtimes develops
over time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Afterward&lt;/strong&gt;: Node.js does have an experimental feature to create executables,
but at the time of writing
&lt;a href=&quot;https://nodejs.org/docs/latest-v20.x/api/single-executable-applications.html&quot;&gt;the steps were too involved&lt;/a&gt;.
For open-source options for creating Node executables, checkout
&lt;a href=&quot;https://github.com/nexe/nexe&quot;&gt;nexe&lt;/a&gt; and &lt;a href=&quot;https://github.com/vercel/pkg&quot;&gt;pkg&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Mon, 05 Jun 2023 11:34:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/quick-comparison-of-javascript-and-go-executables/</guid>
    </item>
    <item>
      <title>I have never reviewed a podcast</title>
      <link>https://seanmcp.com/articles/never-reviewed-a-podcast/</link>
      <description>&lt;p&gt;I listen to podcasts a lot. When I’m working, cleaning, driving, or relaxing,
there is a good chance that I have an earbud in and podcast playing.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pocketcasts.com/&quot;&gt;PocketCasts&lt;/a&gt; is my app of choice, and according to
their statistics I have listened for 61 days and 19 hours since 2016. I started
listening to podcasts earlier than that, but that is two months worth of
podcasts!&lt;/p&gt;
&lt;p&gt;There are podcasts that I make sure to listen to every week. Multiple times a
week. They cover my professional and personal interests, and a little bit of
everything in between. When asked by a friend, I can usually come up with a good
podcast recommendation.&lt;/p&gt;
&lt;p&gt;Despite this, I have never reviewed a podcast. Not even once.&lt;/p&gt;
&lt;p&gt;This might seem odd, since almost every podcast ends with the same plea:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Make sure to leave a review if you like the show&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is usually followed up with an explanation that reviews “really do help
people discover the podcast.”&lt;/p&gt;
&lt;p&gt;The problem is: I can’t review podcasts. Outside of work I don’t use Apple
products (no reviews on iTunes/Apple Podcasts). I don’t listen on Spotify.
PocketCasts doesn’t have a review system, and neither does Google Podcasts.&lt;/p&gt;
&lt;p&gt;I love that podcasts are just RSS feeds. They&#39;re as decentralized as websites.
But that creates a problem: there isn&#39;t a way to review an RSS feed. That
responsibility lies with third-parties like Apple. But when you aren’t in the
Apple-ecosystem, what are the alternatives?&lt;/p&gt;
&lt;p&gt;The short answer is there aren&#39;t any--at least not any common ones. Some apps
provide rating features, not not PocketCasts. There are some services for
rating, discovering, and sharing podcasts, but they don’t seem particularly
popular.&lt;/p&gt;
&lt;p&gt;What can I do, then? I guess exactly what I have been doing. Listening regularly
and sharing recommendations in real life. Complete interest surveys when they&#39;re
advertised. But never leaving a review.&lt;/p&gt;
</description>
      <pubDate>Thu, 25 May 2023 11:27:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/never-reviewed-a-podcast/</guid>
    </item>
    <item>
      <title>Compairing Objects, Maps, and WeakMaps for lookup tables in JavaScript</title>
      <link>https://seanmcp.com/articles/compairing-objects-maps-and-weakmaps-for-lookup-tables-in-javascript/</link>
      <description>&lt;p&gt;I use lookup tables/dictionaries/hashmaps/maps a lot when I’m coding. That’s a
data structure that stores key and value pairs that are easily retrievable. Here
is an example table for storing and looking up element names by their symbol
written in Python that uses a dictionary:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;periodic_table&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;H&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hydrogen&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;periodic_table&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;H&quot;&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 comment&quot;&gt;# Hydrogen&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In JavaScript, we have three tools for this pattern—Objects, Maps, and
WeakMaps—each with strengths and weaknesses.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;objects&quot; tabindex=&quot;-1&quot;&gt;Objects&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/compairing-objects-maps-and-weakmaps-for-lookup-tables-in-javascript/#objects&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Objects”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Objects are the standard data type for dictionaries. They are so flexible that
they are, in a way, the base prototype for all values in JavaScript. Entries are
added and retrieved with dot or bracket notation, and then removed with the
&lt;code&gt;delete&lt;/code&gt; keyword:&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;// Declare&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; periodicTable &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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Set&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;H&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hydrogen&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// dot notation&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;He&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Helium&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// bracket notation&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Get&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;H&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;Hydrogen&quot;&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;He&quot;&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 comment&quot;&gt;// &quot;Helium&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Delete&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Li &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Lithium&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;delete&lt;/span&gt; periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Li&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Object are not strictly iterable, but you can use &lt;code&gt;for...in&lt;/code&gt; to access its keys
or the global Object’s methods to get an iterable array: &lt;code&gt;Object.keys()&lt;/code&gt;,
&lt;code&gt;Object.values()&lt;/code&gt;, and &lt;code&gt;Object.entries()&lt;/code&gt;.&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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; key &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; periodicTable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; periodicTable&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&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 class=&quot;token comment&quot;&gt;// H Hydrogen&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// He Helium&lt;/span&gt;

Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;periodicTable&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 comment&quot;&gt;// [&quot;H&quot;, &quot;He&quot;]&lt;/span&gt;
Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;periodicTable&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 comment&quot;&gt;// [&quot;Hydrogen&quot;, &quot;Helium&quot;]&lt;/span&gt;
Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;periodicTable&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 comment&quot;&gt;// [[&quot;H&quot;, &quot;Hydrogen&quot;], [&quot;He&quot;, &quot;Helium&quot;]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Objects will work as a data structure for every problem you encounter. So in
some ways, it is the best of the three options for lookup tables. Some
weaknesses of Objects are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prototype properties can conflict with keys in table&lt;/li&gt;
&lt;li&gt;Key types limited to strings, numbers, and symbols&lt;/li&gt;
&lt;li&gt;No guaranteed entry order&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;maps&quot; tabindex=&quot;-1&quot;&gt;Maps&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/compairing-objects-maps-and-weakmaps-for-lookup-tables-in-javascript/#maps&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Maps”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;Map&lt;/code&gt; object was added to JavaScript in ES2015 and provides a dedicated
native solution for lookup tables. Entries are added, retrieved, and removed
with dedicated methods:&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;// Declare&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; periodicTable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&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 comment&quot;&gt;// Set&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;H&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hydrogen&quot;&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;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;He&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Helium&quot;&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 comment&quot;&gt;// Get&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;H&quot;&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 comment&quot;&gt;// &quot;Hydrogen&quot;&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;He&quot;&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 comment&quot;&gt;// &quot;Helium&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Delete&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Li&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Lithium&quot;&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;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Li&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maps are iterable so you can access all of the entries with a &lt;code&gt;for...of&lt;/code&gt; loop
(similar to Arrays). They also have a &lt;code&gt;Map.forEach()&lt;/code&gt; method which provides the
same data but (curiously) in the reverse order:&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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entry &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; periodicTable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&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 comment&quot;&gt;// [&quot;H&quot;, &quot;Hydrogen&quot;]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// [&quot;He&quot;, &quot;Helium&quot;]&lt;/span&gt;

periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&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;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&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;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// H Hydrogen&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// He Helium&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maps also have &lt;code&gt;Map.keys()&lt;/code&gt; and &lt;code&gt;Map.values()&lt;/code&gt; methods that return iterators for
keys and values, but the use cases for those might be rare. More common are the
&lt;code&gt;Map.has()&lt;/code&gt; method which returns a boolean of whether a key exists, and
&lt;code&gt;Map.size&lt;/code&gt; getter which returns the number of key/value pairs.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Li&quot;&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 comment&quot;&gt;// false&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some other features that would take more time to illustrate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Preserved entry order&lt;/li&gt;
&lt;li&gt;More performant than objects for frequent additions and subtractions&lt;/li&gt;
&lt;li&gt;Support any data structure for keys, including objects and functions (this
makes them a good choice for implementing memoization)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is for these methods/features that Maps really shine as data types of lookup
tables. The main weaknesses for Maps are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not serializable&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;weakmaps&quot; tabindex=&quot;-1&quot;&gt;WeakMaps&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/compairing-objects-maps-and-weakmaps-for-lookup-tables-in-javascript/#weakmaps&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “WeakMaps”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;WeakMaps&lt;/code&gt; were added to JavaScript along with &lt;code&gt;Maps&lt;/code&gt; in ES2015. A noticeable
difference between the two is that keys in &lt;code&gt;WeakMaps&lt;/code&gt; must be objects. Entries
are added, retrieved, and deleted with dedicated methods:&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;// Declare&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; periodicTable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WeakMap&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 comment&quot;&gt;// Set&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;H&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 literal-property property&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;H&quot;&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;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;H&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hydrogen&quot;&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 comment&quot;&gt;// Get&lt;/span&gt;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;H&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 comment&quot;&gt;// &quot;Hydrogen&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Delete&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; He &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 literal-property property&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;He&quot;&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;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;He&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Helium&quot;&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;
periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;He&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;&lt;strong&gt;Note&lt;/strong&gt;: There is nothing special about the &lt;code&gt;{ symbol: &amp;quot;&amp;quot; }&lt;/code&gt; syntax here. I’m
just creating an object for each key that makes sense for the periodic-table
example. You could use any object or object-like value for the key: &lt;code&gt;{}&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;,
&lt;code&gt;() =&amp;gt; {}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Unlike Maps, WeakMaps are not iterable and there is no way to access its size.
The only method provided is &lt;code&gt;WeakMap.has()&lt;/code&gt; to receive a boolean of whether that
key exists or not:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;periodicTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;He&quot;&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 comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of these differences are due to a shift in focus. WeakMaps are special in
that they weakly reference the objects used as keys. This weak reference means
that JavaScript can garbage collect those objects sooner, which will improve the
memory performance of your application.&lt;/p&gt;
&lt;p&gt;If you have a long-living lookup table, this memory-performance feature may be
the perfect solution. Some weaknesses of WeakMaps are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Limited key types&lt;/li&gt;
&lt;li&gt;No iteration or access to keys/values list&lt;/li&gt;
&lt;li&gt;Not serializable&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;try-it-out&quot; tabindex=&quot;-1&quot;&gt;Try it out&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/compairing-objects-maps-and-weakmaps-for-lookup-tables-in-javascript/#try-it-out&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Try it out”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I created a REPL that includes all of the code examples above for you to
explore:
&lt;a href=&quot;https://replit.com/@SeanMcP/JavaScript-lookup-tables#index.js&quot;&gt;replit.com/@SeanMcP/JavaScript-lookup-tables&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/compairing-objects-maps-and-weakmaps-for-lookup-tables-in-javascript/#conclusion&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Conclusion”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;What is the best tool for implementing lookup tables in JavaScript? The answer
will vary depending on your specific use case, but here are some general
recommendations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Map&lt;/code&gt; is (probably) the best&lt;/strong&gt;: Between flexible key types, preserved
order, easy iteration, and helpful methods, Maps have all of the features
that you need for 90% of lookup tables that you will write in your code.
Learning the syntax is a worthwhile investment over basic Objects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Otherwise use an &lt;code&gt;Object&lt;/code&gt;&lt;/strong&gt;: That said, you could still implement every
lookup table with an Object. One scenario where objects shine is for inline
lookups:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getElementName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;symbol&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;H&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hydrogen&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;He&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Helium&quot;&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;symbol&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;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;WeakMap&lt;/code&gt; for performance&lt;/strong&gt;: If you are working in a situation where memory
performance is important, then WeakMap’s weakly referenced keys will be
helpful. There are other programming problems where WeakMaps shine, but we’re
looking specifically for lookup tables.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that you know the different tools that JavaScript offers for
dictionaries/hash maps/maps/lookup tables, you can choose the best solution in
your context.&lt;/p&gt;
</description>
      <pubDate>Mon, 22 May 2023 11:24:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/compairing-objects-maps-and-weakmaps-for-lookup-tables-in-javascript/</guid>
    </item>
    <item>
      <title>Web-browser Accessibility Tools</title>
      <link>https://seanmcp.com/articles/web-browser-accessibility-tools/</link>
      <description>&lt;p&gt;Khan Academy celebrates Global Accessibility Awareness Day with a whole week of
activities. Yesterday on GAAD proper, we were encouraged to set aside our normal
work and pick up some accessibility tasks from the backlog or do some
a11y-focused professional development.&lt;/p&gt;
&lt;p&gt;They also hosted four accessibility lightning talks, and I was happy to give one
on a11y tools built into modern web browser. The talk consisted of quick tours
through Chrome, Edge, Firefox, and Safari that highlighted some of their
accessibility features.&lt;/p&gt;
&lt;p&gt;I enjoy preparing presentations about the browser dev tools because it is a
perfect excuse for me to learn more about the tools that I have at my disposal.
Some of the cool new features that I learned about were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vision-deficiency emulators&lt;/strong&gt; in Chrome (Rendering tab) and Firefox
(Accessibility panel)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive accessibility trees&lt;/strong&gt; in Firefox (Accessibility panel) and
Chrome (experimental - Inspect panel)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Targeted accessibility audits&lt;/strong&gt; in Firefox (Accessibility panel) and Safari
(Audit panel)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visualized tab order&lt;/strong&gt; in Firefox (Accessibility panel)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatic Axe scans&lt;/strong&gt; in Edge (Issues tab)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The recording is private, but my outline for the talk is available at
&lt;a href=&quot;https://seanmcp.com/talks/browser-a11y-tools&quot;&gt;/talks/browser-a11y-tools/&lt;/a&gt;. I might re-record the
talk for &lt;a href=&quot;https://youtube.com/@seanmcp&quot;&gt;YouTube&lt;/a&gt;, so subscribe if you’re so
inclined.&lt;/p&gt;
&lt;p&gt;Overall, its encouraging to see all of the major browsers putting time and
effort into creating tools to help developers built accessible applications. Now
we all just need to use them!&lt;/p&gt;
</description>
      <pubDate>Fri, 19 May 2023 11:07:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/web-browser-accessibility-tools/</guid>
    </item>
    <item>
      <title>Linux Update in 2023</title>
      <link>https://seanmcp.com/articles/linux-update-in-2023/</link>
      <description>&lt;p&gt;Last year I experimented with using a Raspberry Pi as a home development
environment. The device was ultimately too low powered, but my interest was
piqued. Linux has improved a lot since my last experiments with it in the early
2010s.&lt;/p&gt;
&lt;p&gt;Encouraged by that experience and
&lt;a href=&quot;https://definingterms.com/&quot;&gt;a coworker who uses Linux&lt;/a&gt; at work and home or
development, I decided to try again with a more powerful device. I considered
buying a new laptop—&lt;a href=&quot;https://www.pine64.org/pinebook-pro/&quot;&gt;Pinebook&lt;/a&gt; and
&lt;a href=&quot;https://system76.com/laptops-ultraportables&quot;&gt;System76&lt;/a&gt; look promising—but I
opted instead for a Lenovo Yoga from 2017 that we had lying around. It has an
Intel i5, 128GB SSD, and 4GB of RAM which felt sufficient. Plus, it feels good
to give new life to an old device.&lt;/p&gt;
&lt;p&gt;After some internal debate, I ended up installing Ubuntu. There are probably
distros that would have been better for an older device with simpler specs, but
bog standard should be good enough for me. I didn’t have any issues with drivers
this time (🙌), and the only feature that I lost was the fingerprint reader.
Overall, the experience of installing Linux on the laptop was great.&lt;/p&gt;
&lt;p&gt;Outside of work, I’ve been using this Linux laptop almost exclusively for the
past month. It runs well, the battery life is decent (Power Mode helps a lot),
and Ubuntu has been a great development environment. I work mostly in the
JavaScript world, and the computer is able to handle some resource-intensive
Node.js processes. I’ve also been learning Go, and all of that runs without the
fan.&lt;/p&gt;
&lt;p&gt;I’ve installed Microsoft Edge for browsing and VS Code for editing, but outside
of that I’m trying to keep things lean. I know that 128GB will fill up fast, so
I’m trying to limit installs to only the essentials. To date, that hasn’t been a
limiting factor: turns out your don’t need much to write web apps and Go
scripts!&lt;/p&gt;
&lt;p&gt;In summary, I’m happy with my current set up. The experimenting with Linux in
2022 helped prepare me to use it as my daily driver in 2023.&lt;/p&gt;
</description>
      <pubDate>Tue, 16 May 2023 12:49:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/linux-update-in-2023/</guid>
    </item>
    <item>
      <title>Suffering from recipe-blog syndrome</title>
      <link>https://seanmcp.com/articles/suffering-from-recipe-blog-syndrome/</link>
      <description>&lt;p&gt;Recipe blogs infamously include a lot of text before the recipe. This apparently
helps with SEO, and search-engine traffic is critical for sites trying to make a
profit online. But these long introductions are frustrating to the reader.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Average reader&lt;/strong&gt;: (scrolling furiously) I don&#39;t need to know your life story
with this recipe; I just want to know how to make blueberry muffins!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While I don&#39;t feel the need to overshare personal details when I&#39;m writing, I do
feel the pull to include long introductions to my articles. It&#39;s like I&#39;m trying
to justify to the reader why this article was worth writing.&lt;/p&gt;
&lt;p&gt;But they don&#39;t care! Almost all of my traffic comes from search engines. My
users obviously think the article was worth writing: they clicked on it! I don&#39;t
need to waste their time and mine with a introduction explaining why this
article should exist.&lt;/p&gt;
&lt;p&gt;My advice to myself: don&#39;t qualify; just write.&lt;/p&gt;
</description>
      <pubDate>Mon, 15 May 2023 10:50:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/suffering-from-recipe-blog-syndrome/</guid>
    </item>
    <item>
      <title>How I&#39;m using AI in May 2023</title>
      <link>https://seanmcp.com/articles/how-im-using-ai-in-may-2023/</link>
      <description>&lt;p&gt;As of May 2023, I use the following AI-powered products on at least a weekly
basis:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub Copilot ⭐&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Writing code&lt;/strong&gt;: typically this is autocomplete to the end of the line, but
occasionally it generates small blocks of code. Rarely I use it to generate a
Regex for a particular pattern, saving me a search, copy, and paste from
StackOverflow.&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conversations&lt;/strong&gt;: the best for prompt engineering, it runs with a persona
that you give it. I’ve found rubber-duck sessions with a “helpful coworker who
asks insightful questions” to be really helpful.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generating structured code&lt;/strong&gt;: creates a list of ten objects that match a
given structure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document drafting&lt;/strong&gt;: I needed to write a charter for a church building
committee and had no clue where to start. A few prompts to ChatGPT, and I had
an outline and draft to follow.&lt;/li&gt;
&lt;li&gt;Bing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internet searches&lt;/strong&gt;: if I have a question that would typically require more
than one search, then I’ll ask Bing. It does a good job of answering &lt;em&gt;with&lt;/em&gt;
citations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Summarizing content&lt;/strong&gt;: given a URL, Bing does a good job of summarizing
content from a web page. You can even ask for comprehension questions if
you’re reading to learn.&lt;/li&gt;
&lt;li&gt;Bard 📈&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coding questions&lt;/strong&gt;: Bard gives the right balance of length and depth for
questions I have about programming. It usually includes helpful code snippets.&lt;/li&gt;
&lt;li&gt;Warp&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Command suggestions&lt;/strong&gt;: saves a lot of time searching for those common
commands that you can never seem to remember, &lt;em&gt;e.g.&lt;/em&gt; “how to undo the last
commit?”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As of May 2023, I have access to but no interest in the following products:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Midjourney/Dall·E/Bing Images&lt;/strong&gt;: I’ve seen some of the things they generate,
and while some of it is cool none of it feels useful to me.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notion&lt;/strong&gt;: I don’t need an AI to write content for me.&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Sat, 13 May 2023 12:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-im-using-ai-in-may-2023/</guid>
    </item>
    <item>
      <title>Declaring variables in Go</title>
      <link>https://seanmcp.com/articles/declaring-variables-in-go/</link>
      <description>&lt;p&gt;There are three ways to declare variables in Go: &lt;code&gt;var&lt;/code&gt;, &lt;code&gt;:=&lt;/code&gt;, and &lt;code&gt;const&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;var&lt;/code&gt; keyword is the standard method for declaring a variable. It can be
used anywhere in your program, with a value or without:&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; s1 &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&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 keyword&quot;&gt;var&lt;/span&gt; s2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;World&quot;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; s3 &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
	s3 &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;!&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Variable declarations without a value require a type (&lt;code&gt;s3&lt;/code&gt; above), but types are
otherwise optional.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Declare a variable with with &lt;code&gt;var&lt;/code&gt; when you need a variable at the top-level
of your program or want to initialize it with no value.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The short-declaration operator, &lt;code&gt;:=&lt;/code&gt;, works like the &lt;code&gt;var&lt;/code&gt; keyword for declaring
variables with three differences: a) it can only be used within the scope of a
function, b) it cannot initialize an empty variable, and c) it does not accept a
type. Of the &lt;code&gt;var&lt;/code&gt; examples above, only one could be replaced with a &lt;code&gt;:=&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// s1 := &quot;Hello&quot; // Outside of function scope&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&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;
	s2 &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;World&quot;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// s3 := // No empty variables&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These may sounds like significant limitations, but most of your program will be
written inside a function’s scope and rarely do variables need to be declared
without a value. So the benefit of using &lt;code&gt;:=&lt;/code&gt; comes from letting Go infer the
type of your variable so that you can focus on the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Declare a variable with &lt;code&gt;:=&lt;/code&gt; when you are working within a function with
initial values.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;const&lt;/code&gt; keyword tells the compiler that the variable is a constant -- it
will not be changed or reassigned. Like &lt;code&gt;var&lt;/code&gt; it can be used anywhere in your
program but can only be used with some types: &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;float64&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, and
&lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; PI &lt;span class=&quot;token builtin&quot;&gt;float64&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.14159&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you try to use &lt;code&gt;const&lt;/code&gt; with other types like a &lt;code&gt;slice&lt;/code&gt;, then you will get a
compiler error:
&lt;code&gt;(value of type []string) is not constant compiler(InvalidConstInit)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Declare a variable with &lt;code&gt;const&lt;/code&gt; when you know that it will not change
throughout your program.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I am new to learning and writing in Go, but in practice I almost exclusively use
the declaration operator, &lt;code&gt;:=&lt;/code&gt;, for declaring variables. But it’s useful to know
the reasons when and why you should reach for &lt;code&gt;var&lt;/code&gt; or &lt;code&gt;const&lt;/code&gt;.&lt;/p&gt;
</description>
      <pubDate>Fri, 12 May 2023 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/declaring-variables-in-go/</guid>
    </item>
    <item>
      <title>What is nullish in JavaScript?</title>
      <link>https://seanmcp.com/articles/what-is-nullish-in-javascript/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing&quot;&gt;Nullish-coalescing operator&lt;/a&gt;
(&lt;code&gt;??&lt;/code&gt; or double question mark) is a handy tool that returns the right-hand
operand when the left-hand is nullish. In JavaScript, nullish is defined as
&lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I enjoy using the nullish coalescing operator in my code, especially combined
with optional chaining:&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; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;user&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Anonymous&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code will return the user&#39;s name if &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; exist on the
&lt;code&gt;response&lt;/code&gt; variable, or fallback to &amp;quot;Anonymous&amp;quot; if not.&lt;/p&gt;
&lt;p&gt;However, I tend to get nullish confused with falsy in JavaScript. And while
there is some overlap, it is important to know and remember the difference
between the two.&lt;/p&gt;
&lt;p&gt;Common &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Falsy&quot;&gt;falsy values&lt;/a&gt;
are &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;undefined&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, and &lt;code&gt;NaN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Nullish values are a subset of that at just &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/falsy-nullish-js.png&quot; alt=&quot;Nullish values as a subset of falsy values&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you try to use the nullish-coalescing operator with a falsy value, it will
return the left-hand operand:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fallback&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To state it one more time, nullish in JavaScript is &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;. All
other values, even if they are falsy, are excluded. So reach for nullish
coalescing only when dealing with &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;. Otherwise use logical
OR (&lt;code&gt;||&lt;/code&gt;) or a ternary operator (&lt;code&gt;? :&lt;/code&gt;).&lt;/p&gt;
</description>
      <pubDate>Thu, 11 May 2023 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/what-is-nullish-in-javascript/</guid>
    </item>
    <item>
      <title>QA questions for everyone to ask</title>
      <link>https://seanmcp.com/articles/qa-questions-for-everyone-to-ask/</link>
      <description>&lt;p&gt;My former colleague Michael Scotto recently
&lt;a href=&quot;https://thequalityindex.com/three-types-of-questions-qa-must-ask-5b907a7102fe&quot;&gt;wrote about quality assurance and asking questions&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many use phrases like “QA Engineer” and “Tester” interchangeably. It’s
convenient to do so, and generally innocuous — but &lt;strong&gt;this is narrow
thinking.&lt;/strong&gt; To think of Quality Assurance as “testing” is reductive; it puts a
governor on the value-add of your QA team.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Testing is a skill, not a job.&lt;/strong&gt; Instead, think of QA as the art of asking
unasked questions. Testing is only one way to do this. It’s a coincidence that
“QA” could also represent “Question Asker”, but it’s a meaningful coincidence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He lists three types of questions to ask:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Verification: Does X do Y?&lt;/li&gt;
&lt;li&gt;Discovery: What happens if…?&lt;/li&gt;
&lt;li&gt;Criticism: Does this make sense?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I really like this breakdown, and he includes more examples in the article. It
illustrates the reality that assuring quality transcends any functional team:
everyone is responsible, because everyone can and should be asking these types
of questions.&lt;/p&gt;
&lt;p&gt;As an engineer, I should to be asking verification questions throughout the
development process. I’ll probably codify those questions by writing unit and
end-to-end tests. Answering these questions should be the baseline before
handing the work off to another team member.&lt;/p&gt;
&lt;p&gt;But I should also be asking discovery questions to surface edge cases that need
to be addressed. Some of those are apparent when writing the code, “Oo, we
aren’t handling this state case”, but other times it’s based on user
interaction. I need to play around a bit and look for those pitfalls.&lt;/p&gt;
&lt;p&gt;And all the while I should ask critical questions: does this implementation make
sense? Will it meet the needs of our users? What about folks on low-powered
devices or those with disabilities? If the answer to those questions are
unsatisfactory, then I might need to work out a different solution.&lt;/p&gt;
&lt;p&gt;Personally, I find asking verification and criticism questions easy; it’s
discovery questions that I often overlook. This article was a good framework for
thinking about quality assurance and reminder that I need to play my part in the
process as well.&lt;/p&gt;
</description>
      <pubDate>Wed, 10 May 2023 18:04:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/qa-questions-for-everyone-to-ask/</guid>
    </item>
    <item>
      <title>Learning Go</title>
      <link>https://seanmcp.com/articles/learning-go/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://niche.com&quot;&gt;Niche&lt;/a&gt; and &lt;a href=&quot;https://khanacademy.org&quot;&gt;Khan Academy&lt;/a&gt; use Go
for their backend services, so I&#39;ve been &amp;quot;reading&amp;quot; Go since 2019. But as I
progress in my career, I&#39;m realizing that it would be useful to understand Go at
a deeper level, and even write some Go code when the need arises.&lt;/p&gt;
&lt;p&gt;For the past few weeks I&#39;ve been going through
&lt;a href=&quot;https://gobyexample.com&quot;&gt;&lt;em&gt;Go by Example&lt;/em&gt;&lt;/a&gt;, a &amp;quot;hands-on introduction to Go using
annotated example programs&amp;quot;, and logging my progress here:
&lt;a href=&quot;https://github.com/seanmcp/go-by-example&quot;&gt;seanmcp/go-by-example&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So far, I am enjoying the experience. &lt;em&gt;Go by Example&lt;/em&gt; is well designed: the
examples are clear and the concepts build upon each other well. And the
development environment setup for Go is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;install Go,&lt;/li&gt;
&lt;li&gt;write &lt;code&gt;.go&lt;/code&gt; files in your editor of choice, and&lt;/li&gt;
&lt;li&gt;run programs from the command line.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&#39;ve been using VS Code with the official Go extension, and it&#39;s been great --
even on an older laptop.&lt;/p&gt;
&lt;p&gt;My favorite features so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The built-in &lt;strong&gt;formatter&lt;/strong&gt;: it&#39;s nice to not have to think about how your code
should be formatted.&lt;/li&gt;
&lt;li&gt;Generating &lt;strong&gt;executables&lt;/strong&gt;: I&#39;ve played around with Deno&#39;s compiler for this,
but Go&#39;s feels more mature.&lt;/li&gt;
&lt;li&gt;Simple &lt;strong&gt;syntax&lt;/strong&gt;: you don&#39;t need to remember a lot of syntax to write Go
code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My not-so-favorites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Conventions are &lt;strong&gt;too terse&lt;/strong&gt;: you can name things whatever you want, but
reading through examples with 1-3 letter variables gets old. &lt;code&gt;timer.C&lt;/code&gt; is not
explicit.&lt;/li&gt;
&lt;li&gt;The name &lt;strong&gt;Go&lt;/strong&gt;: it&#39;s too common a word for a programming language, which
makes searching online for challenging. Using &amp;quot;golang&amp;quot; tends to work, but even
the title of this article might confuse people.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The negatives aside, I&#39;m looking forward to continuing this journey. If you have
any recommendations or resources, please let me know!&lt;/p&gt;
</description>
      <pubDate>Tue, 09 May 2023 10:44:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/learning-go/</guid>
    </item>
    <item>
      <title>Set up Decap CMS</title>
      <link>https://seanmcp.com/articles/set-up-decap-cms/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://decapcms.org/&quot;&gt;Decap CMS&lt;/a&gt; is the official
&lt;a href=&quot;https://www.netlify.com/blog/netlify-cms-to-become-decap-cms/&quot;&gt;successor to Netlify CMS&lt;/a&gt;
and works well with the old tooling. Here are the steps to get up and running
with Decap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step-by-step&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a new page route for &lt;code&gt;/admin&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;For me, this was &lt;code&gt;src/pages/admin.astro&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Copy and paste
&lt;a href=&quot;https://decapcms.org/docs/add-to-your-site/#app-file-structure&quot;&gt;the markup from the documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Add &lt;a href=&quot;https://decapcms.org/docs/configuration-options&quot;&gt;a &lt;code&gt;config.yml&lt;/code&gt; file&lt;/a&gt; to
your public directory&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;netlify-cms-proxy-server&lt;/code&gt; alongside your dev server:&lt;pre&gt;&lt;code&gt;npx netlify-cms-proxy-server
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Access the content manager at &lt;code&gt;localhost:XXXX/admin/&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Issues&lt;/strong&gt;: Decap CMS &lt;em&gt;still&lt;/em&gt; hasn&#39;t fixed
&lt;a href=&quot;https://github.com/netlify/netlify-cms/issues/5092&quot;&gt;the jumping cursor issue when inserting within a textarea&lt;/a&gt;.
It makes writing content incredibly frustrating. Thankfully, there is a fix that
requires some additional CSS (of all things):&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- admin.html --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
  &lt;span class=&quot;token selector&quot;&gt;[data-slate-editor]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/* See https://github.com/netlify/netlify-cms/issues/5092 */&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;-webkit-user-modify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; read-write &lt;span class=&quot;token important&quot;&gt;!important&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;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With those style loaded, the issue is resolved.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Takeaways&lt;/strong&gt;: Decap CMS is easy to set up and works well with all of Netlify
CMS&#39; old tooling. It&#39;s nice to hear that the project will live on with new
maintainers, but it remains to be seen how invested they will be in development
like fixing old bugs.&lt;/p&gt;
</description>
      <pubDate>Wed, 19 Apr 2023 01:02:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/set-up-decap-cms/</guid>
    </item>
    <item>
      <title>Zed is a rocket-powered skateboard</title>
      <link>https://seanmcp.com/articles/zed-is-a-rocket-powered-skateboard/</link>
      <description>&lt;p&gt;Zed is a new Rust-powered editor by the creator of Atom that focuses on speed
but lacks a lot of features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My experience&lt;/strong&gt;: I downloaded Zed after hearing
&lt;a href=&quot;https://changelog.com/podcast/531&quot;&gt;the creator talk about it on the Changelog podcast&lt;/a&gt;.
And my first impression was that it just feels fast. Opening the application,
navigating projects, and editing files are all zippy. The integrated terminal
does the job without fanfare.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The numbers&lt;/strong&gt;: Zed compares itself favorably to Sublime Text, VS Code, and
CLion
&lt;a href=&quot;https://zed.dev/#engineered-for-performance&quot;&gt;in three metrics on its homepage&lt;/a&gt;,
but I found Sublime to have a significantly smaller install size:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sublime Text – 126MB&lt;/li&gt;
&lt;li&gt;Zed – 312MB&lt;/li&gt;
&lt;li&gt;VS Code – 526MB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Trade-offs&lt;/strong&gt;: Zed is missing a lot of features that I expect from a modern
editor.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The editor is missing autocomplete, block moving shortcuts, and does silly
things like matching quotation marks in comments.&lt;/li&gt;
&lt;li&gt;The search is missing find and replace, filtering by path or filetype, and
collapsing/removing results.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a result, my rate of development was noticeably slower in Zed than in VS
Code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: Zed is a fast editor in the same vein as Sublime Text and is a
good option for a lightweight second editor. But for modern front-end
development, Zed lacks the features to be your daily driver.&lt;/p&gt;
&lt;p&gt;There are a number of interesting collaboration features that I haven’t tested.
I didn’t want to ask a coworker to download Zed when I knew the editor was
lacking.&lt;/p&gt;
&lt;p&gt;Zed has a solid foundation that may enable it to outpace other editors in the
future – but it’s not there yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Read more&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://zed.dev/&quot;&gt;https://zed.dev/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://changelog.com/podcast/531&quot;&gt;https://changelog.com/podcast/531&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Sat, 01 Apr 2023 10:32:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/zed-is-a-rocket-powered-skateboard/</guid>
    </item>
    <item>
      <title>Changes to the RSS Club</title>
      <link>https://seanmcp.com/articles/changes-to-the-rss-club/</link>
      <description>&lt;p&gt;Last month I created this RSS Club for special articles shared exclusively with
my RSS subscribers. The idea came from Dave Rupert, but I differed from his
pattern by omitting articles entirely from my website. I said at the time:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to read that content, the only way to do so is with an RSS reader.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That decision was a mistake, so I’m going to make some changes. It remains true
that the only way to &lt;strong&gt;find&lt;/strong&gt; an article for the RSS Club is with an RSS reader,
but I made some changes internally to create pages for them. That way you can
bookmark an interesting article, and/or share it with someone else.&lt;/p&gt;
&lt;p&gt;My current plan is to send articles to the RSS Club monthly. Will they all be
RSS-related? That remains to be seen 😅, but stay tuned for more.&lt;/p&gt;
&lt;p&gt;In the meantime, I’d love to hear from you: &lt;strong&gt;what is your RSS reader of
choice&lt;/strong&gt; and &lt;strong&gt;what do you like about it&lt;/strong&gt;? Let me know by email:
&lt;a href=&quot;mailto:sean@seanmcp.com?subject=RSS%20Readers&quot;&gt;sean@seanmcp.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If I get some interesting responses, I’ll include them in a future article.&lt;/p&gt;
</description>
      <pubDate>Thu, 30 Mar 2023 20:31:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/changes-to-the-rss-club/</guid>
    </item>
    <item>
      <title>Fix failed Fastify deploy on Render</title>
      <link>https://seanmcp.com/articles/fix-failed-fastify-deploy-on-render/</link>
      <description>&lt;p&gt;To get a boilerplate Fastify app to deploy on &lt;a href=&quot;http://Render.com&quot;&gt;Render.com&lt;/a&gt;, you need to read to
listen on PORT environment variable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt;: When trying to deploy a basic Fastify app to &lt;a href=&quot;http://Render.com&quot;&gt;Render.com&lt;/a&gt;, I
received a generic failure method: &lt;code&gt;Deploy failed for COMMIT_HASH&lt;/code&gt;. The log
indicated that the server started correctly, and there was no information
indicating why the deploy failed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;: I couldn’t find docs for running Fastify on
&lt;a href=&quot;http://Render.com&quot;&gt;Render.com&lt;/a&gt;, but there are example repos for other languages
and libraries. The Render example for Express
&lt;a href=&quot;https://github.com/render-examples/express-hello-world/blob/main/app.js#L3&quot;&gt;included a reference&lt;/a&gt;
to &lt;code&gt;process.env.PORT&lt;/code&gt;. By adding that to my Fastify app, I was able to
successfully deploy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Copy and paste&lt;/strong&gt;:&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; port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fastify&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;

server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&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; port &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 class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello world!&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you’re using TypeScript, you will have to convert the environment variable to
a number:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// TypeScript&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Read more&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.fastify.io/docs/latest/&quot;&gt;https://www.fastify.io/docs/latest/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://render.com/docs&quot;&gt;https://render.com/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/render-examples/express-hello-world/blob/main/app.js#L3&quot;&gt;https://github.com/render-examples/express-hello-world/blob/main/app.js#L3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Tue, 28 Mar 2023 21:01:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-failed-fastify-deploy-on-render/</guid>
    </item>
    <item>
      <title>Find and replace with Regex groups</title>
      <link>https://seanmcp.com/articles/find-and-replace-with-regex-groups/</link>
      <description>&lt;p&gt;Create Regex groups with parentheses for powerful find/replace patterns.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;: I have a markdown file with a lot of shorthand links to GitHub PRs,
&lt;em&gt;e.g.&lt;/em&gt; &lt;code&gt;user/repo#1&lt;/code&gt;, that I wanted to be replaced with full URLs:
&lt;code&gt;https://github.com/user/repo/issues/1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These steps are for VS Code, but you can do a Regex find/replace in any
environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step by step:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the search panel in VS Code&lt;/li&gt;
&lt;li&gt;Turn on Regex searches with the &lt;code&gt;.*&lt;/code&gt; toggle&lt;/li&gt;
&lt;li&gt;Search with the following Regex pattern: &lt;code&gt;([&#92;w-]+)/(.+)#(&#92;d+)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;This looks for any text that matches the pattern of:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;([&#92;w-]+)&lt;/code&gt; a word containing one more alphanumeric or hyphen characters,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/&lt;/code&gt; a forward slash,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(.+)&lt;/code&gt; one or more of any character,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#&lt;/code&gt; a pound sign, and&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(&#92;d+)&lt;/code&gt; one or more digits&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;It creates three groups (identified by the parentheses)&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Replace the matched text with the following pattern:
&lt;code&gt;https://github.com/$1/$2/issues/$3&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Try it out:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://regexr.com/7a3kb&quot;&gt;https://regexr.com/7a3kb&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tradeoffs:&lt;/strong&gt; The regex pattern above will only match for alphanumeric GitHub
usernames with optional hyphens. I don’t know the official pattern used to
validate usernames, so your data might include some that don’t match.&lt;/p&gt;
</description>
      <pubDate>Tue, 14 Mar 2023 10:50:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/find-and-replace-with-regex-groups/</guid>
    </item>
    <item>
      <title>Fix SVG CSS animation issue in Safari</title>
      <link>https://seanmcp.com/articles/fix-svg-css-animation-issue-in-safari/</link>
      <description>&lt;p&gt;Safari 16 has trouble with CSS animations on SVG child elements, but you can
resolve them by using a combined &lt;code&gt;transform&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setup:&lt;/strong&gt; You have an SVG that needs separate animations for its &lt;code&gt;path&lt;/code&gt; and
&lt;code&gt;rect&lt;/code&gt; elements. Applying a single transform to the elements works, but
combining multiple transforms results in nondeterministic animation bugs:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; worksInSafari&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&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 selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&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 atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; brokenInSafari&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0deg&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&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 selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20deg&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; You can fix this by combining your transformations into multiple
functions on a single &lt;code&gt;transform&lt;/code&gt; property:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; fixedInSafari&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&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 selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;20deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Try it out:&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;dyqVxvY&quot; data-user=&quot;SeanMcP&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/SeanMcP/pen/dyqVxvY&quot;&gt;
  Safari CSS animation SVG bug&lt;/a&gt; by Sean McPherson (&lt;a href=&quot;https://codepen.io/SeanMcP&quot;&gt;@SeanMcP&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; Individual properties for CSS transforms are nice, but they can
cause issues with SVG animations in Safari. The safer option seems to be
sticking with a single &lt;code&gt;transform&lt;/code&gt; property for now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Read more:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/72022385/8486161&quot;&gt;https://stackoverflow.com/a/72022385/8486161&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/transform&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/transform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Fri, 10 Mar 2023 10:45:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-svg-css-animation-issue-in-safari/</guid>
    </item>
    <item>
      <title>Listen for class change in JavaScript</title>
      <link>https://seanmcp.com/articles/listen-for-class-change-in-javascript/</link>
      <description>&lt;p&gt;There isn’t an event for class list changes, but you can write a function that
listens for class changes on an element with the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver&quot;&gt;MutationObserver API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Copy &amp;amp; paste&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onClassChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&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 keyword&quot;&gt;let&lt;/span&gt; lastClassString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&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 keyword&quot;&gt;const&lt;/span&gt; mutationObserver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MutationObserver&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;mutationList&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 class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; mutationList&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attributeName &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&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 keyword&quot;&gt;const&lt;/span&gt; classString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;classString &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; lastClassString&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 function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mutationObserver&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          lastClassString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; classString&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;break&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 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;

  mutationObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&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 literal-property property&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 keyword&quot;&gt;return&lt;/span&gt; mutationObserver&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;&lt;strong&gt;Step by step&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The function accepts two parameters: a reference to a DOM node to watch and
the callback to execute when the class has changed.&lt;/li&gt;
&lt;li&gt;Within the function, we store a reference to the class list as a string that
we will use for comparison later.&lt;/li&gt;
&lt;li&gt;Create a new &lt;code&gt;MutationObserver&lt;/code&gt; instance with a callback that listens for
class changes.
&lt;ol&gt;
&lt;li&gt;Inside that callback, we loop through the mutation list and look for any
changes to the &lt;code&gt;class&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;If we find one, then we compare the current class list as a string to the
reference declared above.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;This comparison is necessary to prevent false class change positives, &lt;em&gt;e.g&lt;/em&gt;
adding the same class twice.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;If the class strings do not match, invoke the callback and update the
class string reference.&lt;/li&gt;
&lt;li&gt;Break out of the loop if we have detected a change.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Observe the passed node with the new mutation observer instance and configure
it to fire on attribute changes.&lt;/li&gt;
&lt;li&gt;Return the mutation observer for later clean up&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Try it out:&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;js,result&quot; data-slug-hash=&quot;VwGzxQb&quot; data-user=&quot;SeanMcP&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/SeanMcP/pen/VwGzxQb&quot;&gt;
  Function to listen for class change&lt;/a&gt; by Sean McPherson (&lt;a href=&quot;https://codepen.io/SeanMcP&quot;&gt;@SeanMcP&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; It would be nice if there was a standard method for listening to
class changes, but it is nice to know that you can write your own with a
&lt;code&gt;MutationObserver&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Read more:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.seanmcp.com/articles/event-listener-for-class-change/&quot;&gt;https://www.seanmcp.com/articles/event-listener-for-class-change/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Thu, 09 Mar 2023 11:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/listen-for-class-change-in-javascript/</guid>
    </item>
    <item>
      <title>Reviewing my Netlify Analytics</title>
      <link>https://seanmcp.com/articles/reviewing-my-netlify-analytics/</link>
      <description>&lt;p&gt;Thanks to Netlify Analytics I know to focus on writing how-to articles, but it
has less data than I hoped.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Some context:&lt;/strong&gt; Netlify provides analytics information for $9 per month per
site. They offer data from server requests including page views, unique
visitors, top locations, and sources.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.jim-nielsen.com/2020/google-vs-netlify-analytics/&quot;&gt;The data varies greatly&lt;/a&gt;
from client-side options like Google Analytics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The numbers:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My site received &lt;strong&gt;38,000&lt;/strong&gt; page views from &lt;strong&gt;13,000&lt;/strong&gt; unique visitors in the
last 30 days&lt;/li&gt;
&lt;li&gt;The top location was the &lt;strong&gt;United States&lt;/strong&gt;, followed distantly by Germany,
France, and China&lt;/li&gt;
&lt;li&gt;The most popular page was a tool for
&lt;a href=&quot;https://www.seanmcp.com/articles/english-words-by-consonant-vowel-pattern/&quot;&gt;looking up words by consonant-vowel patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The most popular content were &lt;strong&gt;how-to articles&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; Netlify Analytics was easy to set up with an existing project —
just one click after adding payment information. But it lacks comprehensive data
for informed decision making.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I want to know how all of my pages are performing, but Netlify’s dashboard
limits you to 15 results.&lt;/li&gt;
&lt;li&gt;The inclusion of bot traffic add uncertainty to the numbers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; People are visiting this site for guides on completing a
particular task. These are broken down into two categories: tutorials and
debugging.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In order to create value for my readers, I’m going to focus this area&lt;/li&gt;
&lt;li&gt;I expect more for $9/month and probably won’t continue the subscription&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Read more:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.netlify.com/products/analytics/&quot;&gt;https://www.netlify.com/products/analytics/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.jim-nielsen.com/2020/google-vs-netlify-analytics/&quot;&gt;https://blog.jim-nielsen.com/2020/google-vs-netlify-analytics/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/comparing-data-in-google-and-netlify-analytics/&quot;&gt;https://css-tricks.com/comparing-data-in-google-and-netlify-analytics/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://answers.netlify.com/t/over-100x-more-views-in-netlify-analytics-than-google-analytics/15403&quot;&gt;https://answers.netlify.com/t/over-100x-more-views-in-netlify-analytics-than-google-analytics/15403&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codeboosh.com/is-netlify-analytics-worth-it/&quot;&gt;https://codeboosh.com/is-netlify-analytics-worth-it/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Wed, 08 Mar 2023 10:53:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/reviewing-my-netlify-analytics/</guid>
    </item>
    <item>
      <title>Create Axios-style articles with CSS</title>
      <link>https://seanmcp.com/articles/create-axios-style-articles-with-css/</link>
      <description>&lt;p&gt;You can style heading elements to look like the axioms in Axios articles with
CSS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step by step:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Start with the following HTML markup for this example:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Paragraph 1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Why it matters&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Paragraph 2 should be inline with the heading&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turning over to CSS, add
&lt;a href=&quot;https://every-layout.dev/layouts/stack/&quot;&gt;stack styles&lt;/a&gt; the container
element:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;article * + *&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-block-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.5rem&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;This positions all of our block elements within an &lt;code&gt;article&lt;/code&gt; with a
consistent gap.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Move the &lt;code&gt;h2&lt;/code&gt; and following &lt;code&gt;p&lt;/code&gt; elements onto the same line:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;article h2,
article h2 + p&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&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;This positions those element correctly, but creates a problem. The block
start margin of the &lt;code&gt;h2&lt;/code&gt; element isn’t collapsing with the block end margin
of the first paragraph.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remove block end margins for everything within our article:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;article *&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-block-end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&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;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a colon using a pseudo-element:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;article h2::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;:&quot;&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;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With those styles in place, we have a semantic and accessible Axios article.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Try it out:&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;yLxXoxN&quot; data-user=&quot;SeanMcP&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/SeanMcP/pen/yLxXoxN&quot;&gt;
  Axios-style articles with h2 headings&lt;/a&gt; by Sean McPherson (&lt;a href=&quot;https://codepen.io/SeanMcP&quot;&gt;@SeanMcP&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; This solution is better than Axios’ markup because it uses the
heading hierarchy. They use &lt;code&gt;strong&lt;/code&gt; elements which aren’t navigable by
screenreaders.&lt;/p&gt;
</description>
      <pubDate>Mon, 06 Mar 2023 18:52:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/create-axios-style-articles-with-css/</guid>
    </item>
    <item>
      <title>CSS :empty pseudo class</title>
      <link>https://seanmcp.com/articles/css-empty-pseudo-class/</link>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:empty&lt;/code&gt; allows you to apply styles to an element when it has no child nodes&lt;/li&gt;
&lt;li&gt;This is useful for hiding empty elements to keep visual grids consistent&lt;/li&gt;
&lt;li&gt;It is supported in all major browsers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When building a user interface, it is common to have a container element that
conditionally contains content. A simple example is an element that displays
error information when a form field is invalid. To accomplish this, you have two
options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Always render the container in the DOM and conditionally add children (&lt;em&gt;e.g.&lt;/em&gt;
text, other nodes)&lt;/li&gt;
&lt;li&gt;Conditionally render the container when needed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are use cases for both approaches. When working with vanilla JavaScript, I
typically reach for the former. If I were building the interface with a
front-end library, then I use the later.&lt;/p&gt;
&lt;p&gt;A problem that arises with option 1 is spacing in between elements. If you are
using flex box or grid with a &lt;code&gt;gap&lt;/code&gt; the empty element will be included in the
layout and will result in a double space between visible elements.&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;KKxaQmo&quot; data-user=&quot;SeanMcP&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/SeanMcP/pen/KKxaQmo&quot;&gt;
  Removing extra flex/grid spacing with :empty&lt;/a&gt; by Sean McPherson (&lt;a href=&quot;https://codepen.io/SeanMcP&quot;&gt;@SeanMcP&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;The solution is to use the :empty pseudo class to select the empty element and
hide it with display: none. Once the element is removed from the flex/grid, the
gap between elements are consistent.&lt;/p&gt;
&lt;p&gt;It is worth noting that the double spacing is not an issue with patterns like
Every Layout’s stack, which uses the browser&#39;s collapsing margins. But if you
are using flexbox or grid, then the problem will eventually arise.&lt;/p&gt;
</description>
      <pubDate>Thu, 02 Mar 2023 11:08:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/css-empty-pseudo-class/</guid>
    </item>
    <item>
      <title>Fix trailing whitespace in Astro components</title>
      <link>https://seanmcp.com/articles/fix-trailing-whitespace-in-astro-components/</link>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/withastro/astro/issues/5737&quot;&gt;A closed issue on GitHub&lt;/a&gt;
appears to still be occurring&lt;/li&gt;
&lt;li&gt;Forcing your code onto a single line is a workaround&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When your Astro code is formatted onto multiple lines, the resulting markup from
the Astro compiler includes a whitespace. This is noticeable when you have a
list of links that are underlined:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Link 1 &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Link 2 &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- Underline in markdown --&gt;
&lt;p&gt;Result: &lt;ins&gt;Link 1 &lt;/ins&gt;   &lt;ins&gt;Link 2&lt;/ins&gt;&lt;/p&gt;
&lt;p&gt;But if the markup is on a single line, then the issue goes away:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Link 1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Link 2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result: &lt;ins&gt;Link 1&lt;/ins&gt;   &lt;ins&gt;Link 2&lt;/ins&gt;&lt;/p&gt;
&lt;p&gt;I have faked the output for this article, but you can
&lt;a href=&quot;https://stackblitz.com/edit/github-wg8oqd?file=src/pages/index.astro&quot;&gt;see a live reproduction&lt;/a&gt;
of this behavior on StackBlitz.&lt;/p&gt;
&lt;p&gt;To workaround this issue, you need force your markup onto a single line. Let’s
look at a real-world example element with multiple attributes. Your code
formatter will spread it over multiple lines:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;item&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 class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;aria-current&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Astro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;page&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;To force this code onto a single line, we need to refactor. Create variables to
represent attributes and children with short names, and then pass them to the
element:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;item&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; a &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-property property&quot;&gt;&#39;aria-current&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Astro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;page&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code is objectively worse (and might create a TypeScript warning that you
need to ignore), but it will fix the whitespace issue. Hopefully the Astro team
can fix the issue in the compiler and make this workaround unnecessary.&lt;/p&gt;
</description>
      <pubDate>Thu, 02 Mar 2023 10:59:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-trailing-whitespace-in-astro-components/</guid>
    </item>
    <item>
      <title>Farewell Create React App</title>
      <link>https://seanmcp.com/articles/farewell-create-react-app/</link>
      <description>&lt;ul&gt;
&lt;li&gt;The React team is looking to sunset “Create React App” as a project scaffold&lt;/li&gt;
&lt;li&gt;They are leaning towards turning it into a “launcher” which would suggest a
list of recommended frameworks&lt;/li&gt;
&lt;li&gt;This is part of a greater shift away from client-side rendered applications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I learned React while attending a coding bootcamp in 2017. With the help of
Create React App, I was able to learn the syntax and features of the library
without needing to understand Webpack or Babel. It was a valuable tool for
learning React, and I’m thankful that it existed.&lt;/p&gt;
&lt;p&gt;Today I rarely spin up new React applications – even for pet projects. But if I
did, I would use Vite instead of CRA. The ecosystem for front-end project
scaffolds has changed significantly in the last six years, and CRA hasn’t kept
up with the times. I think that it makes sense for the React team to move on
from it and let the community solutions and frameworks pick up the slack.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/reactjs/reactjs.org/pull/5487#issuecomment-1409720741&quot;&gt;The discussion also mentions&lt;/a&gt;
that server-side rendering and static-site generation are two critical areas of
front-end development going forward. The use cases for client-side rendered apps
are limited, and
&lt;a href=&quot;https://infrequently.org/2023/02/the-market-for-lemons/&quot;&gt;the weaknesses are now well documented&lt;/a&gt;.
Instead of the React team having one official approach (CRA) and a bunch of
official ones (Next, Astro, Vite, &lt;em&gt;etc&lt;/em&gt;.) for using the library, they want to be
more agnostic. This is a good move for the health of the web.&lt;/p&gt;
&lt;p&gt;Thank you for your service, Create React App. Enjoy retirement.&lt;/p&gt;
</description>
      <pubDate>Tue, 28 Feb 2023 15:03:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/farewell-create-react-app/</guid>
    </item>
    <item>
      <title>Starting an RSS Club</title>
      <link>https://seanmcp.com/articles/starting-an-rss-club/</link>
      <description>&lt;p&gt;I added the ability to publish articles exclusively to the RSS feed. The idea
for an RSS Club comes from
&lt;a href=&quot;https://daverupert.com/rss-club/&quot;&gt;Dave Rupert and friends&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To accomplish this, I added a new data field to the frontmatter of articles for
&lt;code&gt;flags&lt;/code&gt;. This is an enumerated array of special conditions for each entry. With
Astro collections and Zod, the configuration looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;flags&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; z&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;z&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enum&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 string&quot;&gt;&quot;DRAFT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;RSS-ONLY&quot;&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 class=&quot;token function&quot;&gt;optional&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had previously built in logic to handle drafts with a boolean in the
frontmatter, but I wasn’t interested in adding another &lt;code&gt;z.boolean().optional()&lt;/code&gt;
to the configuration. Flags gives me a pattern to quickly add additional
conditions for instances like this.&lt;/p&gt;
&lt;p&gt;To publish an article for the RSS Club, I just add this line to its frontmatter:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;flags&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; RSS&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ONLY&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dave’s RSS Club creates pages for entries online – the links are just hidden
from site navigation. My implementation differs in that the articles only exist
within the &lt;code&gt;rss.xml&lt;/code&gt; file. If you want to read that content, the only way to do
so is with an RSS reader.&lt;/p&gt;
&lt;p&gt;Join the club by subscribing to &lt;a href=&quot;https://seanmcp.com/rss.xml&quot;&gt;https://seanmcp.com/rss.xml&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Tue, 28 Feb 2023 14:42:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/starting-an-rss-club/</guid>
    </item>
    <item>
      <title>Hello RSS Club!</title>
      <link>https://seanmcp.com/articles/hello-rss-club/</link>
      <description>&lt;p&gt;This article is only visible to those who have subscribed to my site via RSS. So
if you are reading this, then you are a member of
&lt;a href=&quot;https://daverupert.com/rss-club/&quot;&gt;the RSS Club&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;My favorite thing about RSS is the autonomy that it gives you as a reader. You
have the freedom to subscribe to content that interests you. That could be
textual, like this blog, or images or audio. You choose the content, and then
you choose how to consume it with an RSS Reader. It bypasses the vendor buy in
of content platforms and puts you in the driver&#39;s seat.&lt;/p&gt;
&lt;p&gt;These days I&#39;ve been enjoying &lt;a href=&quot;https://feedbin.com&quot;&gt;FeedBin&lt;/a&gt; for reading RSS
feeds. The best features for me (though not unique to FeedBin) is piping
newsletters into the reader. I had built up a few subscriptions in my inbox, but
that never felt like the right platform for reading content. If your RSS reader
has the option, I recommend it.&lt;/p&gt;
</description>
      <pubDate>Tue, 28 Feb 2023 11:10:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/hello-rss-club/</guid>
    </item>
    <item>
      <title>On closings</title>
      <link>https://seanmcp.com/articles/on-salutations/</link>
      <description>&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: I misused the term “salutation” in this article. What I meant was
&lt;a href=&quot;https://english.stackexchange.com/questions/51792/what-term-is-used-for-the-closing-of-a-letter&quot;&gt;apparently &amp;quot;valedictions&amp;quot; or &amp;quot;closings&amp;quot;&lt;/a&gt;.
I&#39;ve updated the article below to reflect this, but the permalink will remain
the same.&lt;/p&gt;
&lt;p&gt;I have historically ended all of my articles with a closing like “Happy coding!”
or “Happy debugging!”. Recently I tried a Lewis-inspired “Onward and upward!”
and &lt;a href=&quot;https://seanmcp.com/notes/49&quot;&gt;the controversial “Up the web!”&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I picked up the practice back when
&lt;a href=&quot;https://medium.com/@seanmcp/js-basics-functions-23637174f4f&quot;&gt;I was blogging my way through coding bootcamp&lt;/a&gt;.
I’m not sure where I picked it up; probably something that I noticed other
writers doing as I tried to find my own style.&lt;/p&gt;
&lt;p&gt;But on further reflection, I’m don’t think that a closing is necessary online.
And the amount of time I have spent thinking of the best gerund to follow
“Happy” is not negligible. Instead, I could better use that time refining the
conclusion.&lt;/p&gt;
&lt;p&gt;Going forward, once I have made my point I will end the article.&lt;/p&gt;
</description>
      <pubDate>Mon, 27 Feb 2023 18:56:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/on-salutations/</guid>
    </item>
    <item>
      <title>How long should it take to write an article?</title>
      <link>https://seanmcp.com/articles/how-long-should-it-take-to-write-an-article/</link>
      <description>&lt;p&gt;How much time should it take before your writing can provide value? I’ve been
meditating on this idea. Here are three truths to keep in tension:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I want to write articles that are valuable&lt;/li&gt;
&lt;li&gt;I don’t want to waste my reader’s time&lt;/li&gt;
&lt;li&gt;I don’t want to waste my time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A goal then could be to write articles that contain valuable information as
quickly as possible without sacrificing quality.&lt;/p&gt;
&lt;p&gt;When I have a five minute break at work or in the morning/evening, I want to be
able to use that productively. Is it reasonable to write a valuable article in
five minutes?&lt;/p&gt;
&lt;p&gt;I don’t know the answer. But I think that it is a question worth exploring. Set
a timer, and start writing. If it is valuable when the timer chimes, then
publish. If not not, then hold off for the next five-minute interval.&lt;/p&gt;
&lt;p&gt;If an article is hanging around for a long time, consider scrapping the idea.
There might be gold there, but maybe it isn’t worth your time to try and find
it. Leave that for the next person to discover.&lt;/p&gt;
</description>
      <pubDate>Mon, 27 Feb 2023 15:37:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-long-should-it-take-to-write-an-article/</guid>
    </item>
    <item>
      <title>My writing process in February 2023</title>
      <link>https://seanmcp.com/articles/my-writing-process-in-february-2023/</link>
      <description>&lt;p&gt;Today my process for writing an article looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get an idea&lt;/li&gt;
&lt;li&gt;Add it to a Kanban board on Notion&lt;/li&gt;
&lt;li&gt;Write/edit the article in a card&lt;/li&gt;
&lt;li&gt;Run a script in my website repo to create a markdown file&lt;/li&gt;
&lt;li&gt;Copy the contents from Notion into the created file&lt;/li&gt;
&lt;li&gt;Update frontmatter, including description and tags&lt;/li&gt;
&lt;li&gt;Commit and push&lt;/li&gt;
&lt;li&gt;Netlify builds automatically&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I would like to be able to write drafts and publish from one interface on
desktop and mobile. To date, I haven’t found a good CMS solution.&lt;/p&gt;
&lt;p&gt;I had Netlify CMS configured, but a jumping cursor bug made it annoying to use.
I’m interested to see if &lt;a href=&quot;https://www.staticcms.org/&quot;&gt;Static CMS&lt;/a&gt; or
&lt;a href=&quot;https://decapcms.org/&quot;&gt;Decap CMS&lt;/a&gt;, both successors, can address this and other
usability issues. In the meantime, my choices are to refine my current workflow
or look for alternatives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 1&lt;/strong&gt;: I originally misspelled February in the title and URL. Both have
been rectified (sorry permalink fans).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 2&lt;/strong&gt;: I have added two Bash aliases to improve the process:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;blog&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;node ~/seanmcp/seanmcp.com/src/draft-script.mjs&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;(cd ~/seanmcp/seanmcp.com &amp;amp;&amp;amp; git add src/content/articles public/images &amp;amp;&amp;amp; git commit -m &#39;publish article&#39; &amp;amp;&amp;amp; git push)&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</description>
      <pubDate>Mon, 27 Feb 2023 15:22:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/my-writing-process-in-february-2023/</guid>
    </item>
    <item>
      <title>Learning about Smart Brevity for writing</title>
      <link>https://seanmcp.com/articles/learning-about-smart-brevity-for-writing/</link>
      <description>&lt;p&gt;I was introduced to the idea of Axios’ Smart Brevity on the
&lt;a href=&quot;https://www.goloudnow.com/podcasts/how-i-built-this-with-guy-raz-113/politico-axios-jim-vandehei-397089&quot;&gt;&lt;em&gt;How I Built This&lt;/em&gt; podcast&lt;/a&gt;.
Based on the reality that people don’t read much, the idea is writing that
presents information as clearly and concisely as possible. Axios has pushed this
model successfully with 200-400 word news stories that liberally use bulleted
lists.&lt;/p&gt;
&lt;p&gt;This was attractive to me, in part, because I find writing to be struggle. I
want to be a good writer, but I think my voice pales in comparison to the
writers that I admire. Rather than trying to match their wit or charm, perhaps I
could provide value by being direct and informative.&lt;/p&gt;
&lt;p&gt;I followed the podcast episode by
&lt;a href=&quot;https://www.youtube.com/watch?v=NGy1o4jLkJc&quot;&gt;watching Jim VandeHei’s TEDx talk on Smart Brevity&lt;/a&gt;.
Here some some key points (loosely quoted):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On data to support this approach:&lt;/li&gt;
&lt;li&gt;Nobody reads anything&lt;/li&gt;
&lt;li&gt;Humans are built to keep up with so much information&lt;/li&gt;
&lt;li&gt;More often than not, people who share stories haven’t read them&lt;/li&gt;
&lt;li&gt;Tips for writing better:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop being selfish&lt;/strong&gt;: Writing is self-indulgent. We don’t think about the
purpose. What does the reader need to know?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Grab me&lt;/strong&gt;: What is the most important thing? What is the reason that you’re
writing? What is the one thing that I need to remember?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep it simple&lt;/strong&gt;: Shorter. Simple, strong words.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Be human&lt;/strong&gt;: Write like a human. We’re think we’re Walt Whitman; we try to
show off with our writing. If you’re talking to someone at a bar, you’re not
going to use SAT words or use acronyms.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Just stop&lt;/strong&gt;: The best thing you can do in this cluttered world is give
people their time back. Use as few words, in as few sentences, as humanly
possible.&lt;/li&gt;
&lt;li&gt;In your own mind you’ll find yourself start to think more clearly and speak
more clearly.&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Mon, 27 Feb 2023 14:23:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/learning-about-smart-brevity-for-writing/</guid>
    </item>
    <item>
      <title>Valid JavaScript variable names</title>
      <link>https://seanmcp.com/articles/valid-javascript-variable-names/</link>
      <description>&lt;p&gt;I’ve been working on a little library to help build little JavaScript
applications. Something to fill the gap for me between little scripts and
reaching for a framework like Svelte.&lt;/p&gt;
&lt;p&gt;While designing the API, I’ve found myself searching for obscure but technically
valid JS variable names to distinguish my library from the standard HTML element
properties. Is &lt;code&gt;@id&lt;/code&gt; valid? What about &lt;code&gt;$id&lt;/code&gt;? Can I use a &lt;code&gt;:&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;These questions are easy to answer by opening the console, but wouldn’t it be
nice to have a little tool to validate JavaScript variable names? Thankfully it
already exists:
&lt;a href=&quot;https://mathiasbynens.be/&quot;&gt;Mathias Bynen’s&lt;/a&gt;&lt;a href=&quot;https://mothereff.in/js-variables&quot;&gt;JavaScript variable name validator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With it you can quickly validate a variable while maintaining your flow. And if
you’re curious as to why,
&lt;a href=&quot;https://mathiasbynens.be/notes/javascript-identifiers-es6&quot;&gt;he has an article explaining the specifications&lt;/a&gt;.
While writing this, I noticed that
&lt;a href=&quot;https://mothereff.in/&quot;&gt;the main site has a bunch of other little tools&lt;/a&gt; for you
to checkout. (Pardon the name 🙊)&lt;/p&gt;
&lt;p&gt;Up the web!&lt;/p&gt;
</description>
      <pubDate>Sat, 25 Feb 2023 11:15:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/valid-javascript-variable-names/</guid>
    </item>
    <item>
      <title>Burrito emoji</title>
      <link>https://seanmcp.com/articles/burrito-emoji/</link>
      <description>&lt;p&gt;The burrito emoji, aka 🌯 aka &lt;code&gt;:burrito:&lt;/code&gt;, means thank you. Why a burrito? In
the words of &lt;a href=&quot;https://daniel-salmon.com/&quot;&gt;a former colleague&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Would you rather hear someone say “thanks”, or someone give you a burrito?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;His logic was unassailable!&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sarah: Here’s that doc you were looking for: &lt;a href=&quot;https://example.com/doc&quot;&gt;https://example.com/doc&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Raul: 🌯&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Eleanor: @Wilson 🌯 for the help in that meeting&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;🌯 for reading!&lt;/p&gt;
</description>
      <pubDate>Wed, 22 Feb 2023 11:04:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/burrito-emoji/</guid>
    </item>
    <item>
      <title>Migrating to IndieWeb.social</title>
      <link>https://seanmcp.com/articles/migrating-to-indiewebsocial/</link>
      <description>&lt;p&gt;Today I migrated from &lt;a href=&quot;http://fosstodon.org&quot;&gt;fosstodon.org&lt;/a&gt; to
&lt;a href=&quot;https://indieweb.social&quot;&gt;indieweb.social&lt;/a&gt;. You can find me
&lt;a href=&quot;https://indieweb.social/@seanmcp&quot;&gt;@seanmcp@indieweb.social&lt;/a&gt;. In the process, I
found
&lt;a href=&quot;https://codingitwrong.com/2022/10/10/migrating-a-mastodon-account.html&quot;&gt;this guide by Josh Justice&lt;/a&gt;
to be really helpful.&lt;/p&gt;
&lt;p&gt;Why migrate? I initially migrated to Fosstodon from
&lt;a href=&quot;http://Mastodon.social&quot;&gt;Mastodon.social&lt;/a&gt; because I wanted an instance that was
more focused on technology. I dabble in open-source maintenance, so the FOSS
part of Fosstodon was intriguing.&lt;/p&gt;
&lt;p&gt;What I did not realize at the time is that FOSS is its own thing. There was tons
of discussion about Linux distros and desktop environments that went way over my
head.
&lt;a href=&quot;https://seanmcp.com/series#trying-linux-2022&quot;&gt;I’m a general fan in free and open-source software&lt;/a&gt;
but not big enough.&lt;/p&gt;
&lt;p&gt;I’m more interested in the indie-web scene than FOSS, so I’m thinking that the
new instance will be a better fit. But we’ll see! The grass is always greener 🌱&lt;/p&gt;
&lt;p&gt;Following account were migrated and links here have been updated.
&lt;a href=&quot;https://indieweb.social/@seanmcp&quot;&gt;Come say hi!&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Thu, 16 Feb 2023 14:38:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/migrating-to-indiewebsocial/</guid>
    </item>
    <item>
      <title>Publishing a Qwik component</title>
      <link>https://seanmcp.com/articles/publishing-a-qwik-component/</link>
      <description>&lt;p&gt;After hearing creator Miško Hevery on every front-end podcast that I listen to,
I decided to give his new Qwik library a look. My canonical approach to learning
a new library is creating a &lt;code&gt;*-emoji&lt;/code&gt; component that wraps emojis with the
appropriate HTML attributes for accessibility.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;npm create qwik@latest&lt;/code&gt;, I was able to scaffold a Qwik component library
quickly. Then it was a matter of writing the component using Qwik’s &lt;code&gt;component$&lt;/code&gt;
function and exporting things in the right places.&lt;/p&gt;
&lt;p&gt;To speak nothing of Qwik’s other features, it was a breeze writing my first
component. It looks and feels just like React (thanks JSX!), and I only needed
to reference the documentation one time to confirm how props worked. The project
setup was great, and helped me identify some Qwik-specific TypeScript issues
before I published the library.&lt;/p&gt;
&lt;p&gt;Overall, publishing a Qwik component was a smooth and painless process. Kudos to
the Qwik team for making it a great developer experience.&lt;/p&gt;
&lt;p&gt;You can checkout my &lt;a href=&quot;https://npm.im/qwik-emoji&quot;&gt;Qwik emoji library on npm&lt;/a&gt;, or
install it yourself with:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i qwik-emoji&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Onward!&lt;/p&gt;
</description>
      <pubDate>Mon, 13 Feb 2023 11:25:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/publishing-a-qwik-component/</guid>
    </item>
    <item>
      <title>Fix Astro dev server hanging with new collections API</title>
      <link>https://seanmcp.com/articles/fix-astro-dev-server-hanging-with-new-collections-api/</link>
      <description>&lt;p&gt;This week I was in the process of upgrading this site to
&lt;a href=&quot;https://astro.build/blog/astro-2/&quot;&gt;Astro v2&lt;/a&gt; and its new features. Upgrading to
version 2 went smoothly, but I encountered a frustrating problem adopting the
&lt;a href=&quot;https://docs.astro.build/en/guides/content-collections/&quot;&gt;new content collections API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I repeatedly hit an issue where the Astro dev server will hang after calling
&lt;code&gt;getCollection&lt;/code&gt; from &lt;code&gt;astro:content&lt;/code&gt;. The hardest part is that there are no
errors in the browser or console – just an endless spinner when you try to load
the page.&lt;/p&gt;
&lt;p&gt;I though that maybe a solution was to
&lt;a href=&quot;https://vitejs.dev/guide/dep-pre-bundling.html#caching&quot;&gt;manually clear Vite’s cache&lt;/a&gt;
before restarting the dev server, which was as sophisticated as
&lt;code&gt;rm -rf node_modules/.vite&lt;/code&gt;. This &lt;em&gt;may have&lt;/em&gt; resolved the issue a few times, but
everything was so intermittent that I can’t say for sure.&lt;/p&gt;
&lt;p&gt;On the Astro Discord, I posed a question to see if anyone else had encountered a
similar issue. One of the devs linked to
&lt;a href=&quot;https://github.com/withastro/astro/issues/6173&quot;&gt;this issue on GitHub&lt;/a&gt; which
described a similar scenario. The problem there is a page &lt;em&gt;and&lt;/em&gt; its layout
calling the same collection.&lt;/p&gt;
&lt;p&gt;Looking back to my code, I couldn’t see where that was happening, but I decided
to remove all the &lt;code&gt;layout&lt;/code&gt; prop from the entries in that collection. (I wanted
to do this anyway, since moving to content collections made a separate layout
unnecessary.)&lt;/p&gt;
&lt;p&gt;After those changes, the hanging dev server issue resolved itself. I’m glad to
have found a fix, thought it would probably be good for Astro to recommend &lt;em&gt;not&lt;/em&gt;
using layouts for content collections.&lt;/p&gt;
&lt;p&gt;Onward!&lt;/p&gt;
</description>
      <pubDate>Sat, 11 Feb 2023 17:08:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-astro-dev-server-hanging-with-new-collections-api/</guid>
    </item>
    <item>
      <title>Site improvements in 2023</title>
      <link>https://seanmcp.com/articles/site-improvements-in-2023/</link>
      <description>&lt;p&gt;In 2023 I want to making this site better. Here is an unordered list of tasks
that I would like to complete by the end of the year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Adopt
&lt;a href=&quot;https://docs.astro.build/en/guides/content-collections/&quot;&gt;Astro content collections&lt;/a&gt;&lt;/strong&gt;:
I have two different systems for articles and notes, and it would be great to
standardize with the new API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redesign with emphasis on personality&lt;/strong&gt;: This will involve selecting and
serving a font, which I have avoided up to this point.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Astro-less callouts and embeds&lt;/strong&gt;: I want the reusability of components
without needing to use MDX.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RSS-only articles&lt;/strong&gt;: Seem like a fun feature
(&lt;a href=&quot;https://daverupert.com/rss-club/&quot;&gt;🤫 RSS Club&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix the trailing whitespace in my header links&lt;/strong&gt;: I don’t know what the
cause is — maybe something with how the Astro code is formatted?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mastodon posts in the homepage&lt;/strong&gt;: Probably a little component to load recent
toots on the client.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explore CMSs&lt;/strong&gt;: I have Netlify CMS configured, but between its bugs and the
lack of support I would like to find a better option.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FART-less dark mode&lt;/strong&gt;: I’ve read a few different strategies for this, but I
want to find the best option for this site.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimizations&lt;/strong&gt;: I’d like to find a good/cheap solution for serving
responsive images, and reduce any unnecessary network requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ll probably convert this list into
&lt;a href=&quot;https://github.com/seanmcp/seanmcp.com/issues&quot;&gt;GitHub issues&lt;/a&gt; like a good
developer at some point in time. Until then!&lt;/p&gt;
</description>
      <pubDate>Tue, 07 Feb 2023 24:06:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/site-improvements-in-2023/</guid>
    </item>
    <item>
      <title>Back from time off</title>
      <link>https://seanmcp.com/articles/back-from-time-off/</link>
      <description>&lt;p&gt;I took the month of January off from coding. It was a good opportunity to
decompress after work hours, explore new hobbies, and reflect on the future.&lt;/p&gt;
&lt;p&gt;Looking forward to the rest of 2023, I want to focus on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;This site&lt;/strong&gt;. I want to work on writing daily and publishing regularly
instead of in bursts. I’m also hoping to redesign this year to give it more
personality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Existing projects&lt;/strong&gt;. I have a few that are moderately successful, and I
want to spend the year iterating on those. I would like to release a new
version of
&lt;a href=&quot;https://chrome.google.com/webstore/detail/popsicle-sticks-random-st/lnckbgaeagindapiodcmccfokcmnfecm&quot;&gt;Popsicle Sticks&lt;/a&gt;
this Summer.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order to focus on those, I need to de-emphasize:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;New projects&lt;/strong&gt;. Small stuff is okay, but I don’t want to spend a lot of
time this year working on “The Next Big Thing”.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open source&lt;/strong&gt;. It’s a time commitment, and if I’m not actively using the
library then I want to de-prioritize the work as much as I can.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Outside of the regular schedule, I’ll try to get a conference talk or two
accepted. I think that is a valuable skill set to nurture, even if it is a big
time commitment.&lt;/p&gt;
&lt;p&gt;Onward and upward!&lt;/p&gt;
</description>
      <pubDate>Wed, 01 Feb 2023 17:47:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/back-from-time-off/</guid>
    </item>
    <item>
      <title>Be careful parsing formatted numbers in JavaScript</title>
      <link>https://seanmcp.com/articles/be-careful-parsing-formatted-numbers-in-javascript/</link>
      <description>&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/caution.jpg&quot; alt=&quot;a hole in the sidewalk filed with a caution cone&quot;&gt;&lt;/p&gt;
&lt;p&gt;I was working on a script recently and ran into an unexpected issue. The work
involved parsing a number-range string into an array of numbers, &lt;em&gt;e.g.&lt;/em&gt; &amp;quot;1-100&amp;quot;
into &lt;code&gt;[1,100]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My first instinct was to reach for &lt;code&gt;parseInt&lt;/code&gt; with something like:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1-100&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&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 function&quot;&gt;map&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;string&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 function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&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 comment&quot;&gt;// [1, 100]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This worked nicely until I rant into formatted numbers in the dataset:
&amp;quot;1,000-2,000&amp;quot;. Instead of parsing the strings into the expected numbers,
&lt;code&gt;parseInt&lt;/code&gt; seems to treat the commas as decimals:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1,000-2,000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&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 function&quot;&gt;map&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;string&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 function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&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 comment&quot;&gt;// [1, 2]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, &lt;code&gt;parseFloat&lt;/code&gt; and the &lt;code&gt;Number&lt;/code&gt; constructor similarly struggle with
formatted numbers:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1,000-2,000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&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 function&quot;&gt;map&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;string&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 function&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&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 comment&quot;&gt;// [1, 2]&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&quot;1,000-2,000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&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 function&quot;&gt;map&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;string&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 function&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&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 comment&quot;&gt;// [NaN, NaN]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#39;s interesting to me that &lt;code&gt;Number&lt;/code&gt; returns &lt;code&gt;NaN&lt;/code&gt;, but
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/Number#return_value&quot;&gt;MDN says you probably shouldn&#39;t be using it like this anyway&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The solution is to remove the commas before parsing. There are a few methods to
do this, but &lt;code&gt;String.replace&lt;/code&gt; will work nicely:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1,000-2,000&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&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 function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&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 function&quot;&gt;map&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;string&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 function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;re curious, swapping the commas for underscores –
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#numeric_separators&quot;&gt;the approved numeric separator&lt;/a&gt;
– doesn&#39;t work either:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1_000&quot;&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 comment&quot;&gt;// 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There might be an optimal solution that handles numbers formatted for different
locales, &lt;em&gt;e.g.&lt;/em&gt; &amp;quot;1.000,00&amp;quot; and &amp;quot;1,000.00&amp;quot;, so
&lt;a href=&quot;https://seanmcp.com/articles/be-careful-parsing-formatted-numbers-in-javascript/#comment-link&quot;&gt;please send me a message if you know of one&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Tue, 20 Dec 2022 14:14:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/be-careful-parsing-formatted-numbers-in-javascript/</guid>
    </item>
    <item>
      <title>New command palette</title>
      <link>https://seanmcp.com/articles/new-command-palette/</link>
      <description>&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/command-palette.png&quot; alt=&quot;Command Palette V1&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://seanmcp.com/articles/are-command-bars-the-future/&quot;&gt;﻿Last month, I wrote about the new &amp;quot;command bars&amp;quot; UI pattern&lt;/a&gt;,
and ever since the idea of adding one to this site has been kicking around my
head. The design doesn&#39;t support lots of links in the header, so an &amp;quot;over UI&amp;quot;
for navigating seemed like a decent choice.&lt;/p&gt;
&lt;p&gt;Y﻿esterday I added a new &lt;kbd&gt;command/control&lt;/kbd&gt; &lt;kbd&gt;k&lt;/kbd&gt; command palette
to this site. You can activate it by typing the keyboard shortcut or clicking
the magnifying-glass icon in the header. You can exit the palette by pressing
&amp;quot;Escape&amp;quot; or tapping on the overlay.&lt;/p&gt;
&lt;p&gt;The component is built using Astro and some styles and TypeScript, which should
make it portable to other sites once I iron out all of the bugs. I still get a
kick out of how easy it is to build UI with Astro components. Well done, Astro
team!&lt;/p&gt;
&lt;p&gt;C﻿urrently your query has to directly match the content available, but I&#39;m
planning to add fuzzy matching in the future. In the meantime, I&#39;m hoping that
it simplifies the process of surfacing content on my site.&lt;/p&gt;
&lt;p&gt;H﻿appy commanding!&lt;/p&gt;
</description>
      <pubDate>Thu, 15 Dec 2022 11:17:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/new-command-palette/</guid>
    </item>
    <item>
      <title>Fix &quot;package esbuild-linux-64 could not be found&quot; error on Netlify</title>
      <link>https://seanmcp.com/articles/fix-package-esbuild-linux-64-could-not-be-found-error-on-netlify/</link>
      <description>&lt;p&gt;This morning my website&#39;s builds started failing on Netlify. Checking the deploy
logs, I saw the following error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;10:29:09 AM: [astro:jsx] The package &amp;quot;esbuild-linux-64&amp;quot; could not be found, and is needed by esbuild.
10:29:09 AM: If you are installing esbuild with npm, make sure that you don&#39;t specify the
&amp;quot;--no-optional&amp;quot; flag. The &amp;quot;optionalDependencies&amp;quot; package.json feature is used
10:29:09 AM: by esbuild to install the correct binary executable for your current platform.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/evanw/esbuild/issues/1819&quot;&gt;There is a closed issue about it in &lt;code&gt;esbuild&lt;/code&gt;&lt;/a&gt;,
but there wasn&#39;t a definite fix. Seems to be a problem related to dependencies
built for different platforms.&lt;/p&gt;
&lt;p&gt;I had recently encountered some merge conflicts in &lt;code&gt;package-lock.json&lt;/code&gt; (RIP) and
thought that might have been it. But generating the file again had no effect on
the error, and neither did running &lt;code&gt;npm audit fix&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The last thing I tried was to delete &lt;code&gt;package-lock.json&lt;/code&gt; entirely and commit
that. Without the lock file, Netlify was able to install the correct
dependencies and deploy the site. So... solved?&lt;/p&gt;
&lt;p&gt;That will work for me and my personal site, but if you are working on project
with multiple developers in a more high-stakes environment, then you&#39;ll probably
want to keep looking for a more permanent solution.&lt;/p&gt;
&lt;p&gt;I&#39;ll make sure to update this article if the issue arises again.&lt;/p&gt;
&lt;p&gt;Happy debugging!&lt;/p&gt;
</description>
      <pubDate>Wed, 14 Dec 2022 14:37:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-package-esbuild-linux-64-could-not-be-found-error-on-netlify/</guid>
    </item>
    <item>
      <title>Adding images to articles (again)</title>
      <link>https://seanmcp.com/articles/adding-images-to-articles-again/</link>
      <description>&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/warm-mountains.jpg&quot; alt=&quot;a mountain range with a warm glow&quot;&gt;&lt;/p&gt;
&lt;p&gt;Back in 2020
&lt;a href=&quot;https://seanmcp.com/articles/adding-images-to-my-articles&quot;&gt;I wrote about adding images to articles&lt;/a&gt;,
but I broke all of that when I rewrote the site in September. At the time, I
wasn&#39;t too upset about the regression, because images had become a bit of a
painpoint for the site.&lt;/p&gt;
&lt;p&gt;The reality is that I &lt;em&gt;want&lt;/em&gt; to add images to my articles, but I was paralyzed
by the process of optimizing images for the web. Between widths and aspect
ratios and new image formats that aren&#39;t support by all browsers, to &lt;code&gt;figure&lt;/code&gt;
and &lt;code&gt;img&lt;/code&gt; and &lt;code&gt;picture&lt;/code&gt; elements and all their unique attributes... it is
overwhelming.&lt;/p&gt;
&lt;p&gt;But images are helpful. Beyond adding a little visual interest to articles, they
can help explain things that are hard to put into words. For example, I have a
few articles talking about features within the browser&#39;s dev tools. I can try
and describe the toolbars of a panel&#39;s pane all I want, but a quick screenshot
is much more helpful.&lt;/p&gt;
&lt;p&gt;So once more into the breach.&lt;/p&gt;
&lt;p&gt;Astro, like other site-building frameworks, has
&lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/image/&quot;&gt;a package for adding optimized images to your site&lt;/a&gt;,
but that seems designed for known images on your site like a hero image. What
about arbitrary images within post content? I wasn&#39;t sold.&lt;/p&gt;
&lt;p&gt;I wanted a solution that would work for all images, regardless of where they
were used in the site. I wanted the process of resizing them to be automatic,
with options to run in development or as part of the build process. That way I
can add images locally or via a CMS and they will be automatically optimized for
the site.&lt;/p&gt;
&lt;p&gt;I started with a new Node script, that reads a directory of images and then runs
them through &lt;a href=&quot;https://sharp.pixelplumbing.com/&quot;&gt;sharp&lt;/a&gt; to resize. The script
overwrites the original file (so that references within Markdown files are
preserved), and stores the original for future reference. The changes are
automatically committed (though that part is a little hit or miss).&lt;/p&gt;
&lt;p&gt;With the code in place, I added a &lt;code&gt;&amp;quot;prebuild&amp;quot;&lt;/code&gt; script to the &lt;code&gt;package.json&lt;/code&gt; to
run my script during a build. That way any images that were not resized when
added will be handled before they go into production.&lt;/p&gt;
&lt;p&gt;There are some additional feature that I baked in, including a &lt;code&gt;--skip-git&lt;/code&gt; flag
for running in CI/CD and an ability to regenerate all images when the width
settings change. That&#39;s a little in the weeds for this article, but you can
&lt;a href=&quot;https://github.com/SeanMcP/seanmcp.com/commit/d42adf26c833642f8d8a47cc3fbc76e36db0b020#diff-978160e4c9d9c60603d05e6a05f4d9ef1ae15c9bdd4c2a39ae3049a5708f5649&quot;&gt;view the source code on GitHub if you&#39;re interested&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hope that helps! With all of this in place, I&#39;m looking forward to adding more
images to my articles in the future. So stay tuned for more!&lt;/p&gt;
</description>
      <pubDate>Tue, 13 Dec 2022 10:52:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/adding-images-to-articles-again/</guid>
    </item>
    <item>
      <title>Portable custom search engines</title>
      <link>https://seanmcp.com/articles/portable-custom-search-engines/</link>
      <description>&lt;p&gt;Custom search engines (or site search) are
&lt;a href=&quot;https://zapier.com/blog/add-search-engine-to-chrome/&quot;&gt;a useful tool for improving your workflow in the browser&lt;/a&gt;.
By configuring shortcuts and URLs, you&#39;re able to search sites or complete paths
quickly and efficiently. Plus, it&#39;s totally customizable.&lt;/p&gt;
&lt;p&gt;I﻿ have a few setup to query common documentation sites or complete common URLs.
They save me time, clicks, and/or cognitive load and are an important part of my
workflow.&lt;/p&gt;
&lt;p&gt;E﻿ven though Chrome browsers and Firefox all support custom search engines,
there isn&#39;t a good way to port your settings from one browser to the next.
They&#39;re hidden away in a special settings location which doesn&#39;t get exported
alongside history and bookmarks when you move.&lt;/p&gt;
&lt;p&gt;A﻿s a front-end developer, I spend a lot of time hoping between browsers. And
the inability to use my custom search engines everywhere has been a point of
frustration. To solve this, I created a web-based solution that gives me the
features I want from custom search engines without getting locked in to any
particular browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/seanmcp/jean&quot;&gt;﻿Jean is a portable custom search engine&lt;/a&gt; that
you control. Search engines are added to a simple file using a CSV-like syntax
for labels, shortcuts, and URLs. Then load the page in your browser and get
searching!&lt;/p&gt;
&lt;p&gt;Jean was designed to work with GitHub Pages, but for a little more privacy you
can load it directly from your computer – no HTTP server required! Just copy the
whole path into your browser and bookmark it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://seanmcp.github.io/jean/&quot;&gt;﻿You can checkout a live version of Jean here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;F﻿or a little extra power, you can use Jean&#39;s query API to forward shortcuts
through Jean. Just add a single custom search engine to your browser: -﻿ Label:
&amp;quot;Jean&amp;quot; -﻿ Shortcut: &amp;quot;j&amp;quot; (or whatever you want) -﻿ URL:
&amp;quot;&lt;your-jean-location&gt;/?q=%s&amp;quot;&lt;/your-jean-location&gt;&lt;/p&gt;
&lt;p&gt;N﻿ow you can search using any of your custom search engines by using Jean as a
proxy:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;j wk Search engines&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A﻿fter porting all of my custom search engines over to Jean, I was able to
switch my Chrome work setup to Edge in about two minutes. Blazin&#39;!&lt;/p&gt;
&lt;p&gt;T﻿o create your own portable custom search engine,
&lt;a href=&quot;https://github.com/seanmcp/jean/fork&quot;&gt;fork the Jean repository&lt;/a&gt; and add your
favorites to &lt;code&gt;engines.js&lt;/code&gt;. And that&#39;s it; you&#39;re read to start searching.&lt;/p&gt;
&lt;p&gt;L﻿et me know what you think, and
&lt;a href=&quot;https://github.com/seanmcp/jean/issues&quot;&gt;file an issue if you find a bug&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;H﻿appy searching!&lt;/p&gt;
</description>
      <pubDate>Mon, 05 Dec 2022 15:49:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/portable-custom-search-engines/</guid>
    </item>
    <item>
      <title>A first look at Static CMS with Astro</title>
      <link>https://seanmcp.com/articles/a-first-look-at-static-cms-with-astro/</link>
      <description>&lt;p&gt;The first version of &lt;a href=&quot;https://staticjscms.netlify.app&quot;&gt;Static CMS&lt;/a&gt;, a community
fork of &lt;a href=&quot;https://netlifycms.org&quot;&gt;Netlify CMS&lt;/a&gt;, is scheduled to drop today. As a
Netlify user and a general fan of Netlify CMS, I am excited to see what the
community can do iterate on a useful product.&lt;/p&gt;
&lt;p&gt;While we wait for that initial release, I wanted to explore what it would take
to switch this &lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt; site from Netlify CMS to Static CMS.&lt;/p&gt;
&lt;p&gt;First, I created a new admin route within the &lt;code&gt;public/&lt;/code&gt; directory with
&lt;code&gt;index.html&lt;/code&gt; and &lt;code&gt;config.yml&lt;/code&gt; files:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; public/x-admin
&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; public/x-admin/index.html public/x-admin/config.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I opted for
&lt;a href=&quot;https://staticjscms.netlify.app/docs/add-to-your-site-cdn&quot;&gt;the CDN hosting option&lt;/a&gt;
(mostly because it was simpler), which involved pasting the following code block
into &lt;code&gt;index.html&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;utf-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;width=device-width, initial-scale=1.0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Content Manager&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Include the script that builds the page and powers Static CMS --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
      window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CMS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&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;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Loading the route, and I hit issues where &lt;code&gt;window.CMS&lt;/code&gt; was undefined. I tried
tweaking the version of the script from the snippet and eventually got it
working:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://unpkg.com/@staticcms/core@latest/dist/static-cms-core.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that updated, I moved on to configuration. I tried copying my Netlify CMS
&lt;code&gt;config.yml&lt;/code&gt; into the new admin directory, and it loaded right up! It&#39;s nice
when things go smoothly.&lt;/p&gt;
&lt;p&gt;I was able to view all of my articles and create a new draft (I&#39;m using Static
CMS to write this article right now). The only configuration that didn&#39;t
obviously work was the &amp;quot;list&amp;quot; widget. I use this to set the tags for an article,
but there was nothing rendered in the UI.&lt;/p&gt;
&lt;p&gt;I tweaked the config a little for &amp;quot;list&amp;quot; and got &lt;em&gt;something&lt;/em&gt; to render, though
I&#39;m not sure it would work correctly for me:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tags&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tags&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;list&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;fields&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 key atrule&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tag&quot;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tag&quot;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I tried to test what the resulting data would look like, but when I attempted to
publish I got the following error:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Failed to persist entry: API_ERROR: &amp;quot;params.options.useWorkflow&amp;quot; is required&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There isn&#39;t any reference to that in the documentation or
&lt;a href=&quot;https://github.com/StaticJsCMS/static-cms/issues?q=is%3Aissue+params.options.useWorkflow+&quot;&gt;GitHub issues&lt;/a&gt;,
so I was feeling kind of stuck. But after a little digging, I found
&lt;a href=&quot;https://github.com/StaticJsCMS/static-cms-eleventy-netlify-template/blob/main/src/admin/config.yml#L11&quot;&gt;this comment in one of the example templates&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Please run &quot;npx static-cms-proxy-server&quot; for local backend&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I didn&#39;t see a reference to that in the documentation, so I ran that in the
terminal and received the following:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ npx static-cms-proxy-server
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; ERR&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; Cannot convert undefined or null to object&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turns out
&lt;a href=&quot;https://www.npmjs.com/package/static-cms-proxy-server&quot;&gt;that package does not exist yet&lt;/a&gt;.
There is a reason why they call it the &amp;quot;bleeding edge.&amp;quot;&lt;/p&gt;
&lt;p&gt;I could go on, but this felt like a fair enough attempt at getting it to work.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/a-first-look-at-static-cms-with-astro/#conclusion&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Conclusion”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I&#39;m excited to see what the maintainers of Static CMS can do to carry the torch
of Netlify CMS. But at this stage, it still feels very raw. To illustrate that
point, I started writing this article in Static CMS but am finishing it in
Netlify CMS.&lt;/p&gt;
&lt;p&gt;Maybe if you use one of the starter templates you will have more success. For
now, I&#39;m going to be watching the development from the sidelines.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Wed, 30 Nov 2022 24:43:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-first-look-at-static-cms-with-astro/</guid>
    </item>
    <item>
      <title>Astro components do not merge HTML attributes</title>
      <link>https://seanmcp.com/articles/astro-component-do-not-merge-html-attributes/</link>
      <description>&lt;p&gt;Over the past month I have been enjoying working in the
&lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt; ecosystem recently, and have been working on some
simple community packages.&lt;/p&gt;
&lt;p&gt;I&#39;ve learned that Astro merges component props and element attributes
differently, so I&#39;m writing this short PSA to help myself and other Astro devs
in the future.&lt;/p&gt;
&lt;p&gt;First, some terminology:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.astro.build/en/core-concepts/astro-components/#component-props&quot;&gt;Props&lt;/a&gt;
are the data or properties that we pass to components&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes&quot;&gt;Attributes&lt;/a&gt;
are the data that we pass directly to HTML elements&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We might think of these interchangeably in most component-based frameworks, but
as we will see here they are different in Astro components.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;props%3A-left-to-right&quot; tabindex=&quot;-1&quot;&gt;Props: left-to-right&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/astro-component-do-not-merge-html-attributes/#props%3A-left-to-right&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Props: left-to-right”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;For props, &lt;strong&gt;Astro merges from left-to-right&lt;/strong&gt;. This means that a prop on the
left-side of a component call will be overwritten by the prop with the same name
on the right:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;&lt;Example side=&quot;left&quot;&gt;
&lt;!-- side: &quot;right&quot; --&gt;&lt;/Example&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The resulting &lt;code&gt;side&lt;/code&gt; prop within the &lt;code&gt;Example&lt;/code&gt; component will be &amp;quot;right&amp;quot;.&lt;/p&gt;
&lt;p&gt;The above example is a little contrived, but you can imagine a scenario where
you are creating a component that allows overriding default values:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;&lt;ElementCard element=&quot;Hydrogen&quot; {...Astro.props}=&quot;&quot;&gt;&lt;/ElementCard&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;attributes%3A-no-merging&quot; tabindex=&quot;-1&quot;&gt;Attributes: no merging&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/astro-component-do-not-merge-html-attributes/#attributes%3A-no-merging&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Attributes: no merging”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;However, &lt;strong&gt;Astro does not merge attributes on HTML elements&lt;/strong&gt;. So if you have
two identical attributes passed to an element, that is what goes to the DOM.
Browsers typically ignore any duplicate attributes, so the prevailing value is
the first from left-to-right:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;&lt;input type=&quot;text&quot;&gt;
&lt;!-- type: &quot;text&quot; --&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The resulting &lt;code&gt;type&lt;/code&gt; attribute for &lt;code&gt;input&lt;/code&gt; element will be &amp;quot;text&amp;quot;.&lt;/p&gt;
&lt;p&gt;If you want to create a component that allows overriding default attribute
values, then you need to spread them on &lt;em&gt;the left-side of the element&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;&lt;input {...Astro.props}=&quot;&quot; type=&quot;text&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This behavior might be the opposite of what you expect. For a counter example,
React merges both props and attributes. But whatever the reason for the
difference, it&#39;s important to remember.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;tl%3Bdr&quot; tabindex=&quot;-1&quot;&gt;tl;dr&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/astro-component-do-not-merge-html-attributes/#tl%3Bdr&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “tl;dr”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In Astro you should spread props to components on the right, and elements on the
left:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;&lt;Component default=&quot;value&quot; {...Astro.props}=&quot;&quot;&gt;
&lt;element {...Astro.props}=&quot;&quot; default=&quot;value&quot;&gt;&lt;/element&gt;&lt;/Component&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://stackblitz.com/edit/withastro-astro-eryebq?file=src/pages/index.astro&quot;&gt;You can see this behavior in action live on StackBlitz&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That&#39;s all. Hope it was helpful to you, and memorable for me!&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Tue, 29 Nov 2022 14:36:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/astro-component-do-not-merge-html-attributes/</guid>
    </item>
    <item>
      <title>Run Netlify CMS and your dev server in one command</title>
      <link>https://seanmcp.com/articles/run-netlify-cms-and-your-dev-server-in-one-command/</link>
      <description>&lt;p&gt;If you&#39;re using &lt;a href=&quot;https://netlifycms.org&quot;&gt;Netlify CMS&lt;/a&gt;, there is a command that
you can run to load it in development:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;npx netlify-cms-proxy-server&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is useful, but it needs to be run in a separate terminal window to your
development server. I use VS Code, which has easy split terminal windows, but
it&#39;s still a little annoying to have to fire up two processes when you want to
get to work.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Window 1&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run dev&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Window 2&lt;/span&gt;
npx netlify-cms-proxy-server&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An ideal solution would be to run a single command that runs both scripts in
parallel, so that you can focus on working as opposed to setting things up.&lt;/p&gt;
&lt;p&gt;Thankfully for us,
&lt;a href=&quot;https://npm.im/npm-run-all&quot;&gt;&lt;code&gt;npm-run-all&lt;/code&gt; is the perfect solution&lt;/a&gt; for this.
First we install it as a dev dependency:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i &lt;span class=&quot;token parameter variable&quot;&gt;-D&lt;/span&gt; npm-run-all&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add a new script to our &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&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 property&quot;&gt;&quot;cms&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npx netlify-cms-proxy-server&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;astro dev&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;dev:cms&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm-run-all --parallel cms dev&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you want to run Netlify CMS alongside your development server, you just
need to a single command:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run dev:cms&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Tue, 29 Nov 2022 10:54:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/run-netlify-cms-and-your-dev-server-in-one-command/</guid>
    </item>
    <item>
      <title>Fix Netlify Dev&#39;s &#39;Multiple possible start commands found&#39; issue</title>
      <link>https://seanmcp.com/articles/fix-netlify-devs-multiple-possible-start-commands-found-issue/</link>
      <description>&lt;p&gt;Netlify Dev attempts to guess what framework your project is using and what
commands it should run in development.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ netlify dev

◈ Netlify Dev ◈
◈ Ignored general context &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; var: &lt;span class=&quot;token environment constant&quot;&gt;LANG&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;defined &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
◈ Ignored general context &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; var: &lt;span class=&quot;token environment constant&quot;&gt;LC_ALL&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;defined &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
? Multiple possible start commands found &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Use arrow keys or &lt;span class=&quot;token builtin class-name&quot;&gt;type&lt;/span&gt; to search&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
❯ &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Astro&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;npm run dev&#39;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Svelte&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;npm run dev&#39;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Svelte&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;npm run build&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.netlify.com/configure-builds/file-based-configuration/#netlify-dev&quot;&gt;Reading the Netlify documentation&lt;/a&gt;,
it seems like you should be able to resolve this by adding a &lt;code&gt;[dev]&lt;/code&gt; section to
your &lt;code&gt;netlify.toml&lt;/code&gt; file.&lt;/p&gt;
&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&amp;lt;!-- This doesn&#39;t work --&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key property&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run dev&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, that alone &lt;strong&gt;does not work&lt;/strong&gt;.
&lt;a href=&quot;https://github.com/netlify/cli/issues/410&quot;&gt;There is a &amp;quot;closed&amp;quot; issued that describes the behavior&lt;/a&gt;,
but essentially the Netlify CLI doesn&#39;t read the &lt;code&gt;command&lt;/code&gt; even though you&#39;ve
set it.&lt;/p&gt;
&lt;p&gt;Thankfully,
&lt;a href=&quot;https://github.com/netlify/cli/issues/410#issuecomment-1046147453&quot;&gt;someone contributed an actual solution&lt;/a&gt;
that only requires a few more details:&lt;/p&gt;
&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key property&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run dev&quot;&lt;/span&gt;
    &lt;span class=&quot;token key property&quot;&gt;framework&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#custom&quot;&lt;/span&gt;
    &lt;span class=&quot;token key property&quot;&gt;targetPort&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;framework&lt;/code&gt; needs to be set to &lt;code&gt;&amp;quot;#custom&amp;quot;&lt;/code&gt; and you need to manually set the port
that your dev server typically runs on. With Astro, that is port &lt;code&gt;3000&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With that configured, everything works as expected:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ npx netlify dev

◈ Netlify Dev ◈
◈ Ignored general context &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; var: &lt;span class=&quot;token environment constant&quot;&gt;LANG&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;defined &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
◈ Ignored general context &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; var: &lt;span class=&quot;token environment constant&quot;&gt;LC_ALL&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;defined &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
◈ Setting up &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; development server&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Tue, 29 Nov 2022 10:40:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-netlify-devs-multiple-possible-start-commands-found-issue/</guid>
    </item>
    <item>
      <title>Astro components for Netlify features</title>
      <link>https://seanmcp.com/articles/astro-components-for-netlify-features/</link>
      <description>&lt;p&gt;This morning, I released the first version of
&lt;code&gt;astro-netlify-components&lt;/code&gt;,&lt;a href=&quot;https://npm.im/astro-netlify-components&quot;&gt;a library of Astro components for Netlify&lt;/a&gt;.
I was about to start dogfooding the functionality in my own site, and I figured
I might as well share it with the world.&lt;/p&gt;
&lt;p&gt;There are two exported component at launch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;Form&amp;gt;&lt;/code&gt;: adds all the of the markup for
&lt;a href=&quot;https://docs.netlify.com/forms/setup/&quot;&gt;Netlify Forms&lt;/a&gt; with types and
validation&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;CMS&amp;gt;&lt;/code&gt;: a full-page component to render the
&lt;a href=&quot;https://www.netlifycms.org/&quot;&gt;Netlify CMS&lt;/a&gt; admin interface&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To add either component to your, add the package to your project:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i astro-netlify-components&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, import the components and use in your Astro page:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;---
import CMS from &quot;astro-netlify-components/CMS.astro&quot;;
import Form from &quot;astro-netlify-components/Form.astro&quot;;
---&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;CMS&lt;/code&gt; renders a full page, so import and invoke it on a page of its own:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;---
// src/pages/admin.astro
import CMS from &quot;astro-netlify-components/CMS.astro&quot;;
---

&lt;CMS&gt;&lt;/CMS&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Form&lt;/code&gt; expects children, so you could set up a contact form on your site like
this:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;---
// src/pages/contact.astro
import Form from &quot;astro-netlify-components/Form.astro&quot;;
---

&lt;Form name=&quot;contact&quot;&gt;
  &lt;label&gt;
    &lt;b&gt;Name&lt;/b&gt;
    &lt;input type=&quot;text&quot; name=&quot;name&quot; required=&quot;&quot;&gt;
  &lt;/label&gt;
  &lt;label&gt;
    &lt;b&gt;Email&lt;/b&gt;
    &lt;input type=&quot;email&quot; name=&quot;email&quot; required=&quot;&quot;&gt;
  &lt;/label&gt;
  &lt;label&gt;
    &lt;b&gt;Message&lt;/b&gt;
    &lt;textarea name=&quot;message&quot; required=&quot;&quot;&gt;&lt;/textarea&gt;
  &lt;/label&gt;
  &lt;button&gt;Submit&lt;/button&gt;
&lt;/Form&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;If you want to give it a try, make sure to
&lt;a href=&quot;https://github.com/seanmcp/astro-netlify-components&quot;&gt;checkout the documentation&lt;/a&gt;
and
&lt;a href=&quot;https://github.com/seanmcp/astro-netlify-components/issues&quot;&gt;file any issues you encounter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Fri, 25 Nov 2022 11:26:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/astro-components-for-netlify-features/</guid>
    </item>
    <item>
      <title>Create a &quot;Dave Rupert&quot;-inspired activity graph</title>
      <link>https://seanmcp.com/articles/create-a-dave-rupert-inspired-activity-graph/</link>
      <description>&lt;call-out type=&quot;warn&quot;&gt;
&lt;p&gt;This article was written when &lt;a href=&quot;http://seanmcp.com&quot;&gt;seanmcp.com&lt;/a&gt; was powered by Astro. I have left the
content in place, but the activity graph described no longer renders.&lt;/p&gt;
&lt;/call-out&gt;
&lt;p&gt;I noticed on &lt;a href=&quot;https://daverupert.com/archive/&quot;&gt;Dave Rupert&#39;s website&lt;/a&gt; that he
had a little activity graph to illustrate how many articles he has written per
year over time.&lt;/p&gt;
&lt;p&gt;That seemed like a nice little feature, so I decided to try to recreate it for
my site. This is a little code walk through how I made it using in a custom
Astro component.&lt;/p&gt;
&lt;p&gt;First, I needed to grab all of the data to count. I have two main forms of
content on this site, &lt;a href=&quot;https://seanmcp.com/articles&quot;&gt;articles&lt;/a&gt; and &lt;a href=&quot;https://seanmcp.com/notes&quot;&gt;notes&lt;/a&gt;. Using Astro&#39;s
&lt;code&gt;glob()&lt;/code&gt; method, I was able to get that data and pass it into a sorting
function:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; articles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSortedContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Astro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;../pages/articles/*.{md,mdx}&quot;&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 keyword&quot;&gt;const&lt;/span&gt; notes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSortedContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Astro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;../../content/notes/*.{md,mdx}&quot;&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;/code&gt;&lt;/pre&gt;
&lt;call-out type=&quot;info&quot;&gt;
&lt;p&gt;This code is all subject to change, so for the latest
&lt;a href=&quot;https://github.com/SeanMcP/seanmcp.com/blob/master/src/components/ActivityGraph.astro&quot;&gt;checkout the source on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;/call-out&gt;
&lt;p&gt;Next, I knew that I needed to keep a record of some totals, so I created an
object to track the totals for each content-type by the year:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; yearCount&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;number&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; articles&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; notes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&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 operator&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that in place, I could loop through &lt;code&gt;articles&lt;/code&gt; and &lt;code&gt;notes&lt;/code&gt; and start
counting:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;articles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&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;article&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; year &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frontmatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pubDate&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 function&quot;&gt;getFullYear&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;yearCount&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; yearCount&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;]&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; articles&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; notes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&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;
  yearCount&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;articles&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 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;

notes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&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;note&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; year &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frontmatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pubDate&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 function&quot;&gt;getFullYear&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;yearCount&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; yearCount&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;]&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; articles&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; notes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&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;
  yearCount&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notes&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was far from DRY, but I didn&#39;t think it was worth refactoring at this
point.&lt;/p&gt;
&lt;p&gt;With the data ready, it was time to start rendering. I went with an ordered
list, &lt;code&gt;ol&lt;/code&gt;, with list items for each year. Within each &lt;code&gt;li&lt;/code&gt;, I have a label, and
two &lt;code&gt;span&lt;/code&gt;s to represent articles and notes.&lt;/p&gt;
&lt;p&gt;I tried to label things in a helpful manner, but I&#39;m sure there are
accessibility improvements to be made.&lt;/p&gt;
&lt;p&gt;The last bit of magic was finding a decent height for the most prolific year,
and then use that when calculating the individual &lt;code&gt;span&lt;/code&gt; heights. And thanks to
&lt;code&gt;Math.max()&lt;/code&gt;&#39;s API, this was pretty nice:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; highest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;yearCount&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 function&quot;&gt;map&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;record&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; record&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;articles &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; record&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notes&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 comment&quot;&gt;// 16 * 4 or 64 is the maximum height for a year&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; multiplier &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 number&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; highest&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With all that set up, all I needed to do was iterate:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;&lt;ol&gt;
  {
    Object.entries(yearCount).map(([year, { articles, notes }]) =&gt; {
      const articlesLabel = `${articles} articles`;
      const notesLabel = `${notes} notes`;
      return (
        &lt;li&gt;
          &lt;div title=&quot;{String(articles&quot; +=&quot;&quot; notes)}=&quot;&quot;&gt;{year}&lt;/div&gt;
          &lt;span class=&quot;articles&quot; style=&quot;{{&quot; height:=&quot;&quot; articles=&quot;&quot; *=&quot;&quot; multiplier=&quot;&quot; +=&quot;&quot; &quot;px&quot;=&quot;&quot; }}=&quot;&quot; title=&quot;{articlesLabel}&quot;&gt;
            &lt;span class=&quot;--visually-hidden&quot;&gt;{articlesLabel}&lt;/span&gt;
          &lt;/span&gt;
          &lt;span class=&quot;notes&quot; style=&quot;{{&quot; height:=&quot;&quot; notes=&quot;&quot; *=&quot;&quot; multiplier=&quot;&quot; +=&quot;&quot; &quot;px&quot;=&quot;&quot; }}=&quot;&quot; title=&quot;{notesLabel}&quot;&gt;
            &lt;span class=&quot;--visually-hidden&quot;&gt;{notesLabel}&lt;/span&gt;
          &lt;/span&gt;
        &lt;/li&gt;
      );
    })
  }
&lt;/ol&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is certainly room for improvement, but I&#39;m happy with how it turned out.
Here it is in all its glory:&lt;/p&gt;
&lt;ActivityGraph&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;/ActivityGraph&gt;</description>
      <pubDate>Wed, 23 Nov 2022 18:48:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/create-a-dave-rupert-inspired-activity-graph/</guid>
    </item>
    <item>
      <title>Use your domain on Mastodon with Astro</title>
      <link>https://seanmcp.com/articles/use-your-domain-on-mastodon-with-astro/</link>
      <description>&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: The solution in this article still works, but I think using a
&lt;a href=&quot;https://www.hawksworx.com/blog/alias-your-mastodon-username-to-your-domain-with-one-netlify-config-setting/&quot;&gt;redirect as described here&lt;/a&gt;
is probably a better option.&lt;/p&gt;
&lt;p&gt;Maarten Balliauw shared a cool strategy for using your own custom domain as a
reference to your Mastodon account. He explains it better than I can summarize,
&lt;a href=&quot;https://blog.maartenballiauw.be/post/2022/11/05/mastodon-own-donain-without-hosting-server.html&quot;&gt;so take a look at his article first&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wanted to try it out, but I&#39;d prefer if the process was automated in my site&#39;s
build process. Thankfully, &lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt; has an API for
generating arbitrary files on build with
&lt;a href=&quot;https://docs.astro.build/en/core-concepts/endpoints/&quot;&gt;endpoints&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In your Astro site, create a new file called
&lt;code&gt;src/pages/.well-known/webfinger.ts&lt;/code&gt; and paste the following code block&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;INSTANCE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;indieweb.social&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;USERNAME&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;seanmcp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * This endpoint adds a webfinger JSON file to your site so that you can use
 * your domain as a reference to your Mastodon account.
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get&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 keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&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;https://&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INSTANCE&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/.well-known/webfinger?resource=acct:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;USERNAME&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INSTANCE&lt;/span&gt;&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&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;/code&gt;&lt;/pre&gt;
&lt;call-out type=&quot;info&quot;&gt;
&lt;p&gt;Make sure to swap out the values for your Mastodon instance and username!&lt;/p&gt;
&lt;/call-out&gt;
&lt;p&gt;When you trigger a build, Astro will run the &lt;code&gt;get()&lt;/code&gt; function which grabs the
webfinger information from your Mastodon instance and outputs it in the correct
&lt;code&gt;/.well-known/webfinger&lt;/code&gt; location.&lt;/p&gt;
&lt;p&gt;With that in place, you can now use your domain as a reference to your Mastodon
account.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
</description>
      <pubDate>Fri, 18 Nov 2022 13:29:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/use-your-domain-on-mastodon-with-astro/</guid>
    </item>
    <item>
      <title>Thoughts on Steve Job on consulting</title>
      <link>https://seanmcp.com/articles/thoughts-on-steve-job-on-consulting/</link>
      <description>&lt;p&gt;My manager at Khan Academy sent me an old video of Steve Jobs talking about the
idea of consulting:&lt;/p&gt;
&lt;em-bed&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=rp6_3UQLi2Y&quot;&gt;https://www.youtube.com/watch?v=rp6_3UQLi2Y&lt;/a&gt;&lt;/p&gt;
&lt;/em-bed&gt;
&lt;p&gt;Here are some of the key quotes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Without owning something over an extended period of time–like a few
years–where one has a chance to take responsibility for one’s recommendations,
where one has to see one’s recommendations through all action stages and
accumulate scar tissue for the mistakes, and pick oneself up off the ground
and dust oneself off, one learns a fraction of what one can.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Coming in and making recommendations and not owning the results and not owning
the implementation–I think–is a fraction of the value and a fraction of the
opportunity to learn and get better.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;You do get a broad cut at companies, but it’s very thin. … You might get a
very accurate picture, but it’s only two dimensional. And without the
experience of actually doing it, you never get three dimensional.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;You’re also a variable expense, and in hard times you find yourself
[expendable].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don’t have much experience with consultants, but his thoughts seem to ring
true.&lt;/p&gt;
&lt;p&gt;It’s easy to stand at a distance and make recommendations. On the lay end of the
spectrum, millions of people do this every week to the manager of their favorite
sports team every week. And then there is a whole class of professional pundits
who make suggestions and critique decisions in (and too often outside) their
sphere of expertise.&lt;/p&gt;
&lt;p&gt;The ideal situation would be a consultant who has a long track record of working
in the weeds through difficult problems in different scenarios. They’ve seen
what worked, what didn’t and why. With that experience, you could trust their
opinions and confidently follow their recommendations.&lt;/p&gt;
&lt;p&gt;That reminds me of the proverb:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Seek first to understand, then to be understood&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Or updated for consultants:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Seek first to understand the problem, context, constraints, history,
technology, and best practices over a long period of time, then to be
understood&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unless you truly understand all of that, why should others bother listening?
That is a humbling question for me as I move into the next stage of my career.&lt;/p&gt;
&lt;p&gt;Food for thought!&lt;/p&gt;
</description>
      <pubDate>Thu, 17 Nov 2022 13:43:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/thoughts-on-steve-job-on-consulting/</guid>
    </item>
    <item>
      <title>Are command bars the future?</title>
      <link>https://seanmcp.com/articles/are-command-bars-the-future/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://maggieappleton.com/command-bar&quot;&gt;Maggie Appleton recently wrote about &amp;quot;Command K Bars&amp;quot;&lt;/a&gt;,
those command line bars that you pull up with a keyboard combination. In
addition to giving a lot of cool product recommendations, she explained the
pattern for designers:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Command bars are command-line bars that pop up in the middle of the screen
when you hit a certain keyboard shortcut.They&#39;re also known as &#39;command
palettes&#39;, &#39;command launchers&#39;, or &#39;omniboxes&#39; Traditionally CMD + K, hence
the moniker “Command K bars.” But CMD + E and CMD + / have also been strong
shortcut contenders.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Bars like this let users search through all the available commands in an
application. Rather than remembering which sub-sub-sub menu a function lives
in, users need only remember its name.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;They don&#39;t even have to remember its exact name. Fuzzy search can help them
find it by simply typing in similar names or related keywords. For example, if
I type “make” into a command bar, it&#39;s likely to show me any actions related
to creating new items. Even if “make” isn&#39;t part of the action name.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Some notable examples of this in my line of work are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Slack &lt;kbd&gt;CMD + K&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;VS Code &lt;kbd&gt;CMD + P&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;Chrome DevTools &lt;kbd&gt;CMD + P&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;Notion &lt;kbd&gt;CMD + K&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;GitHub &lt;kbd&gt;CMD + K&lt;/kbd&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I, for one, absolutely love this pattern. It&#39;s so nice to be able to quickly
jump to a command without needing to remember where the button is in the UI or
even the exact name for it. These command bars definitely improve my workflow.&lt;/p&gt;
&lt;p&gt;However they feel like a very developer-focused pattern, extending to products
for companies that work in tech. I&#39;m skeptical at this point that the pattern is
ready for mainstream products.&lt;/p&gt;
&lt;p&gt;In a quick survey of popular information and social-media sites, I found no
command K bars.&lt;/p&gt;
&lt;p&gt;It&#39;s also worth noting that the command key is Mac-centric. In tech we can
probably get away with that since it has large sections that are dominated by
Mac users, but that isn&#39;t the case for products with broader audiences. Maybe
swapping command for control is sufficient, or maybe switching to the meta key
makes more sense. It might take time before a true standard emerges.&lt;/p&gt;
&lt;p&gt;I think command bars are a great pattern, and I&#39;m looking forward to using them
in more ways and on more sites. But I wouldn&#39;t prioritize that feature work
unless you are in an industry that caters to developers or the companies that
employ them.&lt;/p&gt;
&lt;p&gt;What do you think: are command bars the wave of the future? Let me know.&lt;/p&gt;
&lt;p&gt;Happy interacting!&lt;/p&gt;
</description>
      <pubDate>Thu, 17 Nov 2022 11:14:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/are-command-bars-the-future/</guid>
    </item>
    <item>
      <title>Node.js is great for scripting</title>
      <link>https://seanmcp.com/articles/nodejs-is-great-for-scripting/</link>
      <description>&lt;p&gt;This week I was working on a project that reads 6.4 MB of text files (the
complete English Bible), does some processing and computation, and then outputs
JSON data. You can see
&lt;a href=&quot;https://github.com/seanmcp/shama&quot;&gt;result of that work on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In times past, I&#39;ve felt pressure to reach for a &amp;quot;proper scripting language&amp;quot;
like Python or a more performant one like Go. But I feel like we&#39;ve reached the
point where Node.js is a great choice for scripting.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JavaScript is everywhere&lt;/strong&gt;. You can find tutorials and examples for every
conceivable task for JS. And by and large, the code that works in the browser
will work in Node.js too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JavaScript is forgiving&lt;/strong&gt;. When I&#39;m writing a script, it&#39;s usually for a
specific and personal use case. I don&#39;t want to spend a lot of time writing
syntax or worrying about types. And thankfully, JavaScript doesn&#39;t care about
that either.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The ecosystem is huge&lt;/strong&gt;. There are over one million npm packages, so the
chances are good that there is a solution available for whatever problem you
have. And thanks to JS being everywhere, there are countless guides to adding
dependencies to your project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node.js is readable&lt;/strong&gt;. Thanks to JS&#39; async/await and sync functions from
Node.js&#39; standard libraries, writing a script that reads multiple
files/directories and then writes to others is pretty straightforward. No more
chaining callbacks!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node.js supports ESM&lt;/strong&gt;. Node.js supports ES modules with either the
&lt;code&gt;type: module&lt;/code&gt; flag in &lt;code&gt;package.json&lt;/code&gt; or the &lt;code&gt;.mjs&lt;/code&gt; extension. Maybe this is a
bigger deal for me personally, but the ability to import and export modules
without needing to think about &lt;code&gt;require()&lt;/code&gt; or &lt;code&gt;module.exports&lt;/code&gt; is pretty nice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node.js is fast&lt;/strong&gt;. That project I mentioned at the top? The Node.js script
that I wrote (which was not optimized for performance) ran in less than a
second. 6.4 MB in &amp;lt; 1 second! I&#39;m not saying that Node.js is the fastest
option, but for your typical scripting needs: it&#39;s more than fast enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;m not arguing that everyone should be using Node.js for scripting, nor that it
is the best solution for every problem in every environment. And if you prefer
Rust or Go or Ruby or WhatEver, go for it!&lt;/p&gt;
&lt;p&gt;But if you&#39;re a developer who is comfortable working in the JavaScript
ecosystem, don&#39;t be hesitant to reach for Node.js. It&#39;s a great option for your
next script.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Wed, 16 Nov 2022 14:21:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/nodejs-is-great-for-scripting/</guid>
    </item>
    <item>
      <title>There is no multi-tasking on the Raspberry Pi</title>
      <link>https://seanmcp.com/articles/there-is-no-multi-tasking-on-the-raspberry-pi/</link>
      <description>&lt;p&gt;Maybe this isn&#39;t fair to file under the &amp;quot;Trying Linux 2022&amp;quot; series since it
isn&#39;t commentary on the operating system writ large. However, it is a part of my
journey trying Linux and worth documenting.&lt;/p&gt;
&lt;p&gt;I have a Raspberry Pi 3 B+ (or something like that). I bought it about 4 years
ago with the goal of using it as a RetroPie, and it did that pretty well. Now
that I&#39;m here in 2022 trying to use this as a developer compuer, however, I&#39;m
starting to see some of the strains.&lt;/p&gt;
&lt;p&gt;With the Pi and Raspberry Pi OS, I can do approximately one tasks at the same
time. I can use the browser (Firefox ESR), VS Code, &lt;strong&gt;or&lt;/strong&gt; the Raspberry Pi
package manager. If I try to use two of these things at the same time, however,
the whole system grinds to a halt for minutes at a time. So I need to be really
careful &lt;em&gt;not&lt;/em&gt; to multitask while I&#39;m working.&lt;/p&gt;
&lt;p&gt;You hear some developers complain about how resource intensive modern
development has become. Browsers with all their extensions are big and bloated.
Electron-powered programs are slow and resource intensive. Running all of the
services locally takes more cores from your processor than computers had 20
years ago. I&#39;ve been largely sympathetic to those criticisms, but I haven&#39;t
really felt the pain because I&#39;ve always had a high-powered computer from my
employer that can manage the workload.&lt;/p&gt;
&lt;p&gt;Now that I&#39;m on a Raspberry Pi and trying to run a &amp;quot;simple&amp;quot; Node process &lt;em&gt;and&lt;/em&gt;
view it in the browser... I feel it.&lt;/p&gt;
&lt;p&gt;And maybe that is a good thing.&lt;/p&gt;
</description>
      <pubDate>Thu, 03 Nov 2022 11:14:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/there-is-no-multi-tasking-on-the-raspberry-pi/</guid>
    </item>
    <item>
      <title>Finding a Linux distro and software that work</title>
      <link>https://seanmcp.com/articles/finding-a-linux-distro-and-software-that-work/</link>
      <description>&lt;p&gt;I wrote
&lt;a href=&quot;https://seanmcp.com/articles/giving-linux-another-try&quot;&gt;yesterday&#39;s article about giving Linux another try&lt;/a&gt;
while things were still installing, &lt;em&gt;i.e.&lt;/em&gt; too soon. Here is everything that I
tried:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ubuntu Mate 22.04&lt;/strong&gt;: Installed but setup wouldn&#39;t complete (I probably tried
3-4 times).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ubunutu 22.04&lt;/strong&gt;: Installed and setup, but it was very slow and crashed
multiple times.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ubuntu Mate 22.04 (attempt 2)&lt;/strong&gt;: Installed and setup, but I was unable to
open Firefox.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lubuntu 20.04.5&lt;/strong&gt;: Couldn&#39;t get it to install on the microSD card. Maybe
because I renamed an &lt;code&gt;.iso&lt;/code&gt; file to &lt;code&gt;.img&lt;/code&gt;?
(&lt;a href=&quot;https://askubuntu.com/questions/1205768/how-to-convert-an-iso-file-to-img-format-in-linux&quot;&gt;Following this recommendation&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Raspberry Pi OS&lt;/strong&gt;: It worked! 🎉&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the OS up and running, I moved on to installing software. Git was included
already, and I used &lt;code&gt;apt&lt;/code&gt; to install &lt;code&gt;nodejs&lt;/code&gt; and &lt;code&gt;vim&lt;/code&gt;. Brew doesn&#39;t work with
ARM devices, so I need to manage some software manually.&lt;/p&gt;
&lt;p&gt;I decided that it would probably be a good idea to use pnpm instead of npm on a
low-memory device. It required at least Node.js 14, but the latest version
available from &lt;code&gt;apt&lt;/code&gt; was 12.&lt;/p&gt;
&lt;p&gt;After going through the usual journey of trying to update Node.js, I ended up
&lt;a href=&quot;https://github.com/nvm-sh/nvm&quot;&gt;installing NVM&lt;/a&gt; and going with the new 18 LTS.
With that updated, I could install pnpm with: &lt;code&gt;npm i -g pnpm&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The included version of Chromium struggled to render emojis correctly, so I
opted for Firefox instead. I used the Raspberry Pi&#39;s package manager to install
&lt;code&gt;firefox-esr&lt;/code&gt; and it worked great. I made sure to install
&lt;a href=&quot;https://seanmcp.com/articles/awesome-browser-extensions&quot;&gt;uBlock Origin&lt;/a&gt; to hopefully help a bit
with performance.&lt;/p&gt;
&lt;p&gt;With all that in place, I now have a serviceable Linux machine for tinkering! It
still struggles a bit running multiple processes at the same time, but it&#39;s
definitely good enough for now.&lt;/p&gt;
</description>
      <pubDate>Wed, 02 Nov 2022 12:55:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/finding-a-linux-distro-and-software-that-work/</guid>
    </item>
    <item>
      <title>Giving Linux another try</title>
      <link>https://seanmcp.com/articles/giving-linux-another-try/</link>
      <description>&lt;p&gt;I felt inspired by
&lt;a href=&quot;https://www.heyhomepage.com/?module=blog&amp;amp;link=1&amp;amp;post=4&quot;&gt;an article by Hey Homepage&lt;/a&gt;
and recent &lt;a href=&quot;https://pca.st/3ypvfdke&quot;&gt;episode of The Changelog podcast&lt;/a&gt; to give
Linux another try. This description just sounds so nice:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For over a year now, I use a Raspberry Pi 4 (with 8GB of RAM) as my daily
desktop computer. I browse the internet, read and send e-mails, program
software, edit photos, create images, watch Youtube videos and play games on
it. Without any significant problems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So tonight I&#39;m dusting off the ol&#39; Raspberry Pi and trying a fresh installation
of Ubuntu Mate. Why Mate? Because it was recommended in the article above. The
latest time I needed a low-powered Ubuntu flavor I used Lubuntu, but that is
probably 10 years ago. Time flies!&lt;/p&gt;
&lt;p&gt;I don&#39;t expect to completely change my computing habits; as a front-end focused
developer I feel pretty compelled to stick with macOS. But I&#39;m looking forward
to peeking outside of the Apple ecosystem, even in a sandbox, to get a better
picture of the world out there in 2022.&lt;/p&gt;
&lt;p&gt;Please let me know if you have any suggestions, because I don&#39;t know what I&#39;m
doing. 😄&lt;/p&gt;
</description>
      <pubDate>Wed, 02 Nov 2022 24:35:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/giving-linux-another-try/</guid>
    </item>
    <item>
      <title>Awesome browser extensions</title>
      <link>https://seanmcp.com/articles/awesome-browser-extensions/</link>
      <description>&lt;p&gt;Extensions can slow down your browser, so I try to only add ones that are
necessary or super helpful. Here is a list of the extensions that make the cut,
grouped by category and listed alphabetically:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;general-use&quot; tabindex=&quot;-1&quot;&gt;General use&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/awesome-browser-extensions/#general-use&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “General use”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/disable-automatic-gain-co/clpapnmmlmecieknddelobgikompchkk&quot;&gt;Disable automatic gain control&lt;/a&gt;:
I&#39;ve written
&lt;a href=&quot;https://seanmcp.com/articles/awesome-browser-extensions/articles/prevent-chrome-from-adjusting-audio-input-levels-on-mac/&quot;&gt;an article about this extension already&lt;/a&gt;;
it prevents Chrome from automatically adjusting the gain for your audio input.
I couldn&#39;t use Meet on Chrome without this extension!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm&quot;&gt;uBlock Origin&lt;/a&gt;:
Simple and effective ad blocking. &lt;strong&gt;Add this on every grandparent&#39;s
computer!&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;developers&quot; tabindex=&quot;-1&quot;&gt;Developers&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/awesome-browser-extensions/#developers&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Developers”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/axe-devtools-web-accessib/lhdoppojpmngadmnindnejefpokejbdd&quot;&gt;axe DevTools&lt;/a&gt;:
Adds an accessibility audit panel to the developer tools.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/monaco-markdown-editor-fo/mmpbdjdnmhgkpligeniippcgfmkgkpnf&quot;&gt;Markdown Editor for GitHub&lt;/a&gt;:
Converts GitHub&#39;s default &lt;code&gt;textarea&lt;/code&gt; into a full-featured Monaco editor. This
means you can use your favorite VS-Code keyboard shortcuts when editing issue
descriptions and PR comments.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This list will grow over time, so feel free to
&lt;a href=&quot;mailto:sean@seanmcp.com?subject=Suggesting%20an%20extension&quot;&gt;email suggestions&lt;/a&gt;
and check back later. Happy extending!&lt;/p&gt;
</description>
      <pubDate>Mon, 31 Oct 2022 13:11:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/awesome-browser-extensions/</guid>
    </item>
    <item>
      <title>Using dynamic routes to organize blog posts in Astro</title>
      <link>https://seanmcp.com/articles/using-dynamic-routes-to-organize-blog-posts-in-astro/</link>
      <description>&lt;p&gt;If you create a new Astro project with the installer, it will organize blog
posts as markdown files in a &lt;code&gt;src/pages/blog/&lt;/code&gt; directory. Each post will have
some frontmatter, including a &lt;code&gt;layout&lt;/code&gt; field to set the frame in which the
content will render.&lt;/p&gt;
&lt;p&gt;Astro will create a new page for every markdown file in the &lt;code&gt;src/pages/&lt;/code&gt;
directory, so adding a new post is as simple as a creating a new markdown file
in the &lt;code&gt;blog/&lt;/code&gt; subdirectory.&lt;/p&gt;
&lt;p&gt;When you want to render blog content on another page, &lt;em&gt;e.g.&lt;/em&gt; a homepage with the
last three posts, then you need to rely on two Astro APIs:
&lt;a href=&quot;https://docs.astro.build/en/guides/markdown-content/#compiledcontent&quot;&gt;&lt;code&gt;compiledContent&lt;/code&gt;&lt;/a&gt;
and/or
&lt;a href=&quot;https://docs.astro.build/en/guides/markdown-content/#content&quot;&gt;the &lt;code&gt;Content&lt;/code&gt; component&lt;/a&gt;.
These will give you the Astro-rendered content for a post that you can then add
to another page.&lt;/p&gt;
&lt;p&gt;There are two issues that I encountered using these APIs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;MDX does not support &lt;code&gt;compiledContent&lt;/code&gt;, so you can either omit &lt;code&gt;.mdx&lt;/code&gt; content
or create your own workaround with
&lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/mdx/#inject-frontmatter-via-remark-or-rehype-plugins&quot;&gt;rehype plugins&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;Content&lt;/code&gt; component for a post &lt;strong&gt;includes the markup for the layout&lt;/strong&gt;, so
&amp;quot;embedding&amp;quot; it in another page results in a strange iframe-esque page within
a page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you just want to render the content of md/mdx files, there doesn&#39;t seem to be
a workable solution with the current setup. But with a little refactor, you can
have your cake and eat it too!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;a-new-organization-system&quot; tabindex=&quot;-1&quot;&gt;A new organization system&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/using-dynamic-routes-to-organize-blog-posts-in-astro/#a-new-organization-system&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “A new organization system”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Rather than putting all of your content somewhere in the &lt;code&gt;src/pages/&lt;/code&gt;, instead
let&#39;s create another directory at the same level as &lt;code&gt;src/&lt;/code&gt; called &lt;code&gt;content/&lt;/code&gt;.
This is where we will put all of our blog posts, maybe in a &lt;code&gt;blog/&lt;/code&gt;
subdirectory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;content/
    blog/
        first-post.md
        second-post.md
        third-post.md
src/
astro.config.mjs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we&#39;ll create a dynamic route for all blog posts in the &lt;code&gt;src/pages/blog/&lt;/code&gt;
directory. This file will gather all of the posts from your new &lt;code&gt;content/&lt;/code&gt;
directory create static paths for each:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;---
// src/pages/blog/[slug].astro

export function getStaticPaths() {
  const content = Astro.fetchContent(&quot;../../../content/blog/*.{md,mdx}&quot;);
  return content.map((item) =&gt; ({
    params: { slug: item.url.split(&quot;/&quot;).pop() },
    props: {
      ...item,
      // Any additional props
    },
  }));
}
---

&lt;!-- Render your post --&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Astro will call
&lt;a href=&quot;https://docs.astro.build/en/core-concepts/routing/#static-ssg-mode&quot;&gt;the &lt;code&gt;getStaticPaths&lt;/code&gt; function&lt;/a&gt;
and create a page for each item in the returned array with the given params.
We&#39;ve added the &lt;code&gt;item&lt;/code&gt; as props for the page, so we can access
&lt;a href=&quot;https://docs.astro.build/en/guides/markdown-content/#markdown-layout-props&quot;&gt;all of the data for each post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we can remove the &lt;code&gt;layout:&lt;/code&gt; frontmatter from all of our posts in
&lt;code&gt;content/blog/&lt;/code&gt; and set the markup in our new dynamic route:&lt;/p&gt;
&lt;pre class=&quot;language-astro&quot;&gt;&lt;code class=&quot;language-astro&quot;&gt;---
// src/pages/blog/[slug].astro

export function getStaticPaths() {
  const content = Astro.fetchContent(&quot;../../../content/blog/*.{md,mdx}&quot;);
  return content.map((item) =&gt; ({
    params: { slug: item.url.split(&quot;/&quot;).pop() },
    props: {
      ...item,
      // Any additional props
    },
  }));
}

const {
  Content,
  frontmatter: { title },
} = Astro.props;
---

&lt;h1&gt;{title}&lt;/h1&gt;
&lt;Content&gt;&lt;/Content&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;call-out type=&quot;info&quot;&gt;
&lt;p&gt;Make sure to include post content in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; element too, and add encoding
to ensure that it renders correctly.&lt;/p&gt;
&lt;/call-out&gt;
&lt;p&gt;With all of that in place, you are now able to render markdown or MDX blog post
content anywhere on your site with Astro&#39;s &lt;code&gt;Content&lt;/code&gt; component; we&#39;re doing it
right here in our &lt;code&gt;[slug].astro&lt;/code&gt; file!&lt;/p&gt;
&lt;p&gt;In addition to resolving the issue above, I think I like this approach better
because it keeps the content separated from the code, which feel like different
concerns. As of publishing, I use it to organize notes, which are individuals
bits of md/mdx content that are &lt;a href=&quot;https://seanmcp.com/notes&quot;&gt;rendered on the notes page&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;tl%3Bdr&quot; tabindex=&quot;-1&quot;&gt;tl;dr&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/using-dynamic-routes-to-organize-blog-posts-in-astro/#tl%3Bdr&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “tl;dr”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Astro&#39;s default setup for organizing blog posts works well for a one-to-one
relationship between content and pages. But if you want to have a many-to-one
relationship with markdown and MDX, a better aproach may be layout-less posts
with a dynamic route.&lt;/p&gt;
&lt;p&gt;I hope that helps! Happy blogging 🚀&lt;/p&gt;
</description>
      <pubDate>Sat, 29 Oct 2022 17:21:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/using-dynamic-routes-to-organize-blog-posts-in-astro/</guid>
    </item>
    <item>
      <title>Rebuilding with Astro</title>
      <link>https://seanmcp.com/articles/rebuilding-with-astro/</link>
      <description>&lt;call-out type=&quot;warn&quot;&gt;
&lt;p&gt;This article was written when &lt;a href=&quot;http://seanmcp.com&quot;&gt;seanmcp.com&lt;/a&gt; was powered by Astro. I have left the
content in place, but the component described no longer renders.&lt;/p&gt;
&lt;/call-out&gt;
&lt;p&gt;After almost four years of &lt;a href=&quot;https://11ty.dev&quot;&gt;Eleventy&lt;/a&gt;, I recently completed a
rebuild of this site with &lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;why-leave-eleventy%3F&quot; tabindex=&quot;-1&quot;&gt;Why leave Eleventy?&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/rebuilding-with-astro/#why-leave-eleventy%3F&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Why leave Eleventy?”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I think that Eleventy is a really neat project and that Zach Leatherman is doing
a great job with it. My main reason for wanting to move to a different solution
was the inability to use components to build pages.&lt;/p&gt;
&lt;p&gt;That may sound silly, but I&#39;ve found components to be a helpful conceptual model
for building user interfaces. When I look at a webpage now, I visualize
components. When I think about building a webpage, I plan in components. And for
my personal site, I want to be able to build in components.&lt;/p&gt;
&lt;p&gt;To be fair to Eleventy, there is a plugin to &amp;quot;use Vue&amp;quot;, but
&lt;a href=&quot;https://github.com/11ty/eleventy-plugin-vue#not-yet-available&quot;&gt;the missing features&lt;/a&gt;
and
&lt;a href=&quot;https://github.com/11ty/eleventy-plugin-vue/commits/master&quot;&gt;de-prioritized development&lt;/a&gt;
make it an unappealing option.&lt;/p&gt;
&lt;p&gt;In 2022 and beyond, I don&#39;t want to be limited to templating languages and
partials when building pages. I want to be able to use components big and small
across the site to compose interfaces.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;why-astro%3F&quot; tabindex=&quot;-1&quot;&gt;Why Astro?&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/rebuilding-with-astro/#why-astro%3F&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Why Astro?”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I&#39;ve been interested in Astro since it launched and am generally excited by the
&amp;quot;framework to build modern websites&amp;quot; trend. Three things stood out the most to
be when choosing Astro for this project:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Components-based layouts&lt;/li&gt;
&lt;li&gt;Island architecture for interactive articles&lt;/li&gt;
&lt;li&gt;Familiarity and complexity&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;components!&quot; tabindex=&quot;-1&quot;&gt;Components!&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/rebuilding-with-astro/#components!&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Components!”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I want to be able to build my user interfaces with components. It&#39;s as simple as
that, and Astro enables you to use components from any of your favorite
JavaScript libraries.&lt;/p&gt;
&lt;p&gt;For this site, I stuck with
&lt;a href=&quot;https://docs.astro.build/en/core-concepts/astro-components/&quot;&gt;Astro&#39;s own components&lt;/a&gt;,
which feel like a combination of stateless React and Svelte. It was super easy
to pick up and build all of the layouts and components I needed.&lt;/p&gt;
&lt;p&gt;The only real issue that I encounted with Astro components was the scoped
styles. Conceptually I love the idea, but in practice it applies styles using
the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:where&quot;&gt;&lt;code&gt;:where()&lt;/code&gt; pseudo-class&lt;/a&gt;
which has 0 specificity. As a result, I frequently encountered instances where
my component styles were overridden by global styles.&lt;/p&gt;
&lt;p&gt;For an example, the global selector &lt;code&gt;body &amp;gt; main&lt;/code&gt; has a greater specificity than
the scoped component selector &lt;code&gt;main:where(.astro-S3KS4JBQ)&lt;/code&gt;. I would expect
component styles to override global styles.&lt;/p&gt;
&lt;call-out type=&quot;info&quot;&gt;
&lt;p&gt;If there is a setting to make Astro apply generated classes directly to the
element, &lt;em&gt;e.g.&lt;/em&gt; &lt;code&gt;main.astro-S3KS4JBQ&lt;/code&gt;, please let me know!&lt;/p&gt;
&lt;/call-out&gt;
&lt;p&gt;This isn&#39;t an insurmountable issue, but I would love to find a workaround (and
avoid changing my mental model!).&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;islands&quot; tabindex=&quot;-1&quot;&gt;Islands&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/rebuilding-with-astro/#islands&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Islands”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I previously tried to add interactive elements to my articles through the use of
good ol&#39; HTML, CSS, and JavaScript. And for the use cases that I had, this was
usually sufficient. But authoring and maintenance were painful, and the lack of
standardization made each interactive a bit of a headache.&lt;/p&gt;
&lt;p&gt;What I really wanted was a way to write little self-contained &amp;quot;applets&amp;quot;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/rebuilding-with-astro/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; that
I could drop into any page/article. And that is exactly what
&lt;a href=&quot;https://docs.astro.build/en/concepts/islands/&quot;&gt;Astro Islands are&lt;/a&gt;: &amp;quot;an
interactive UI component on an otherwise static page of HTML.&amp;quot;&lt;/p&gt;
&lt;p&gt;The best part is: you can author these islands using whatever JavaScript library
you want. I&#39;m a big fan of Svelte, and I&#39;m excited to be able to drop a Svelte
component into any article that I&#39;m writing.&lt;/p&gt;
&lt;p&gt;This counter here? A Svelte component!&lt;/p&gt;
&lt;br&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Counter client:only=&amp;quot;svelte&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br&gt;
&lt;p&gt;And with
&lt;a href=&quot;https://docs.astro.build/en/reference/directives-reference/#client-directives&quot;&gt;client directives&lt;/a&gt;,
you have control over when the component are hydrated on the page.
&lt;e-moji&gt;🚰&lt;/e-moji&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;familiarity&quot; tabindex=&quot;-1&quot;&gt;Familiarity&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/rebuilding-with-astro/#familiarity&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Familiarity”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The above features are great, but I don&#39;t think I would have used a new system
if it was more complex than the &lt;em&gt;status quo&lt;/em&gt;. That was my favorite part about
Eleventy: it used a familiar language to take text files and generate a website.&lt;/p&gt;
&lt;p&gt;Thankfully, Astro feels cut from the same cloth. Add a bunch of markdown files
to the &lt;code&gt;src/pages/&lt;/code&gt; directory, create a few layouts, and the &lt;code&gt;astro&lt;/code&gt; CLI will do
the rest. In practice it feels simpler than Eleventy, but maybe that is just a
symptom of being more opinionated.&lt;/p&gt;
&lt;p&gt;After years working on a site with Eleventy, I found switching to Astro a very
confortable transition. And thanks to the new features like components and
&lt;a href=&quot;https://mdxjs.com/&quot;&gt;MDX&lt;/a&gt; support, I enjoyed working on the site for the first
time in a while.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;cons-of-astro&quot; tabindex=&quot;-1&quot;&gt;Cons of Astro&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/rebuilding-with-astro/#cons-of-astro&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Cons of Astro”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In addition to some of the styling specificity issue mentioned above, there are
two other features that I lost in the transtion from Eleventy to Astro:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Line highlights in code blocks&lt;/strong&gt;. Eleventy provided easy access to the
markdown parser and I was able to configure things to my liking with Markdown
It. Astro uses remark to parse markdown, and I haven&#39;t found a good way to
add line highlights to code blocks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Detailed RSS feed&lt;/strong&gt;. Astro does provide a plugin for generating an RSS
feed, but the resulting output does not have much detail. I would like to
include at least a snippet of the article&#39;s content, but that doesn&#39;t appear
to be an option with the plugin.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;wrapping-it-up&quot; tabindex=&quot;-1&quot;&gt;Wrapping it up&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/rebuilding-with-astro/#wrapping-it-up&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Wrapping it up”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Overall, I&#39;m really happy with the rebuild. It was a good opportunity to
evaluate the feature that my site needed and architect it in a way that would be
easy to maintain.&lt;/p&gt;
&lt;p&gt;Looking forward, I think Astro&#39;s island&#39;s architecture is going to help me
create the content that I want to publish. And hopefully that&#39;s good new for
everyone.&lt;/p&gt;
&lt;p&gt;Happy reading!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Is it safe to reclaim that word from Java yet? &lt;a href=&quot;https://seanmcp.com/articles/rebuilding-with-astro/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Thu, 13 Oct 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/rebuilding-with-astro/</guid>
    </item>
    <item>
      <title>Jest tips &amp; tricks</title>
      <link>https://seanmcp.com/articles/jest-tips-and-tricks/</link>
      <description>&lt;p&gt;Since 2017, I have only used Jest for JavaScript application testing. And while
&lt;a href=&quot;https://vitest.dev/&quot;&gt;Vitest does look exciting&lt;/a&gt;, I think that it&#39;s safe to say
that Jest will be around for a good while longer.&lt;/p&gt;
&lt;p&gt;Here are some of tips and tricks that I&#39;ve learned that make working with Jest a
little better. The headings are organized alphabetically for ease of reference.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;aliases&quot; tabindex=&quot;-1&quot;&gt;Aliases&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/jest-tips-and-tricks/#aliases&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Aliases”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Jest&#39;s global &lt;code&gt;test&lt;/code&gt; method has an &lt;code&gt;it&lt;/code&gt; alias that you might find useful. There
is usually a standard in the codebase where I&#39;m working, but I&#39;ve found that
tests can read a little more naturally with the alias:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;When a user is logged in, ...&quot;&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;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 function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Logs an event on interaction&quot;&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;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;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;expect&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;expect&lt;/code&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/jest-tips-and-tricks/#expect&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “expect”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;There are a lot of matcher functions that are good to know, so I&#39;ll only list a
few here.&lt;/p&gt;
&lt;p&gt;One of the most useful matchers (and hardest to remember) is
&lt;code&gt;expect.objectContaining()&lt;/code&gt;. This allows you to assert that certain
properties/values are present on the object without needing to mock all of them.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockFn&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 function&quot;&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  expect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;objectContaining&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 literal-property property&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another handy matcher is &lt;code&gt;not()&lt;/code&gt;, which gives you a simple way to negate any
matcher:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sum&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;not&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&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 function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; readableTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;not&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;number&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;I do recommend skimming the
&lt;a href=&quot;https://jestjs.io/docs/expect&quot;&gt;Jest docs for &lt;code&gt;expect&lt;/code&gt;&lt;/a&gt; occasionally to pick up
some new matcher that might help in your next test.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;flags&quot; tabindex=&quot;-1&quot;&gt;Flags&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/jest-tips-and-tricks/#flags&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Flags”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The Jest CLI has a number of flags that can be used to change the way that Jest
runs. Here are some of the ones that I find most helpful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--bail/-b&lt;/code&gt;: Exits the test suite after a given number of failures (default 1)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--changedSince&lt;/code&gt;: Only run tests related to files changed since a given point.
I like to use this with the target branch as a quick check for regressions. -﻿
&lt;code&gt;--findRelatedTests&lt;/code&gt;: Runs tests related to changed files. Works well in a
pre-commit hook.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--watch&lt;/code&gt;: Run tests in watch mode. Great when running a small set of tests,
but maybe omit if you&#39;re running the whole suite.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;selective-runs&quot; tabindex=&quot;-1&quot;&gt;Selective runs&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/jest-tips-and-tricks/#selective-runs&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Selective runs”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;When working in a large test file, it&#39;s nice to run only the cases that you care
about. Sometimes that means selecting a specific test or block, and other times
that means skipping anything that isn&#39;t necessary at the moment.&lt;/p&gt;
&lt;p&gt;To just run a specific test, you can use the &lt;code&gt;only()&lt;/code&gt; method on the global
&lt;code&gt;describe&lt;/code&gt;/&lt;code&gt;test&lt;/code&gt; methods. This will ensure that only that code is run:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;only&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This test will run&quot;&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;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 function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This test will not&quot;&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;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;To omit a test from the run, you can similarly use the &lt;code&gt;skip()&lt;/code&gt; method:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This test will run&quot;&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;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;
test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;skip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This test will not&quot;&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;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a smaller change to skip tests, you can add an &lt;code&gt;x&lt;/code&gt; to the front of for the
same behavior:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This test will run&quot;&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;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 function&quot;&gt;xtest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This test will not&quot;&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;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;re going to use this, I&#39;d recommend
&lt;a href=&quot;https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/no-disabled-tests.md&quot;&gt;a linter rule to prevent you from committing skipped tests&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I hope that helps! Let me know if you have any other tips and tricks that you
have found helpful.&lt;/p&gt;
&lt;p&gt;Happy testing!&lt;/p&gt;
</description>
      <pubDate>Thu, 06 Oct 2022 15:58:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/jest-tips-and-tricks/</guid>
    </item>
    <item>
      <title>Solid VS Code extensions</title>
      <link>https://seanmcp.com/articles/solid-vs-code-extensions/</link>
      <description>&lt;p&gt;I &lt;a href=&quot;https://seanmcp.com/uses&quot;&gt;use&lt;/a&gt; VS Code as my daily editor, and one of its best features is the
wide selection of extensions. However, as with any other large marketplace,
discoverability is a real issue.&lt;/p&gt;
&lt;p&gt;In an attempt to resolve that issue, here is a curated list of some great VS
Code extensions that you might not have tried before:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker&quot;&gt;Code Spell Checker&lt;/a&gt;:
This extension has saved me from committing embarrassing spelling mistakes on
multiple occasions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=mattlott.copy-github-url&quot;&gt;Copy GitHub URL&lt;/a&gt;:
Adds commands to copy the URL for the current location on GitHub. Obviously
more helpful if you use GitHub a lot.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html&quot;&gt;es6-string-html&lt;/a&gt;:
Adds syntax highlighting for ES6&#39;s multiline strings. Just add a comment
&lt;code&gt;/*html*/&lt;/code&gt; and your backticked strings are more usable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=rodrigocfd.format-comment&quot;&gt;Format Comment&lt;/a&gt;:
Adds a command to nicely format comments. Really nice if you prefer the &lt;code&gt;//&lt;/code&gt;
syntax to &lt;code&gt;/** */&lt;/code&gt; (which VS Code already formats nicely.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&quot;&gt;Live Server&lt;/a&gt;:
Adds a button to the bottom status bar to launch a local development server in
the current directory. Never Google &amp;quot;serve directory python&amp;quot; again!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=jasonnutter.search-node-modules&quot;&gt;Search node_modules&lt;/a&gt;:
Adds a command to search the &lt;code&gt;node_modules/&lt;/code&gt; directory in your project. VS
Code ignores those files by default, so having a method to go spelunking in
your dependencies is really handy.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy extending!&lt;/p&gt;
</description>
      <pubDate>Tue, 06 Sep 2022 14:18:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/solid-vs-code-extensions/</guid>
    </item>
    <item>
      <title>How to use StackBlitz with Firefox</title>
      <link>https://seanmcp.com/articles/use-stackblitz-with-firefox/</link>
      <description>&lt;p&gt;StackBlitz is a great tool for rapidly prototyping web applications. In recent
weeks, I has been my go-to tool for testing out new web technologies
(&lt;a href=&quot;https://seanmcp.com/notes/42/&quot;&gt;CodeSandbox&#39;s loss&lt;/a&gt;). However, when you try to use StackBlitz with
Firefox, the preview window gives the following error message:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/stackblitz-firefox-error.png&quot; alt=&quot;Error message reads: Enable Third-Party Cookies. Looks like your browser is blocking our Service Worker. To see this web page, please allow third-party cookies for this site.&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Enable Third-Party Cookies Looks like your browser is blocking our Service
Worker. To see this web page,
&lt;a href=&quot;https://developer.stackblitz.com/docs/platform/third-party-blocker/&quot;&gt;please allow third-party cookies for this site&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you follow the link, it leads you to a guide for enabling the feature for
Chrome – a great addition to the error message but not helpful for Firefox
users.&lt;/p&gt;
&lt;p&gt;To enable the StackBlitz preview pane in Firefox, select the shield icon in the
URL bar and turn &amp;quot;Enhanced tracking protections&amp;quot; off for this site:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/stackblitz-firefox-setting.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once that setting is disabled, Firefox will prompt you to reload the page. Once
you do that, you should see the shield icon with a line through it to indicate
that Firefox is providing no additional protections:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/stackblitz-firefox-off.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;But more excitingly, the preview pane is now working! With that setting
configured, you should not be able to use StackBlitz&#39;s features in Firefox.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/stackblitz-firefox-working.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Happy blitzing!&lt;/p&gt;
</description>
      <pubDate>Tue, 23 Aug 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/use-stackblitz-with-firefox/</guid>
    </item>
    <item>
      <title>How to replace Math.random with crypto in JavaScript</title>
      <link>https://seanmcp.com/articles/replace-math-random-with-crypto-in-javascript/</link>
      <description>&lt;p&gt;JavaScript has a method for generating random numbers from 0 to less than 1:
&lt;code&gt;Math.random()&lt;/code&gt;. If you&#39;ve ever been working on an app that selects a random
item from a list, chances are that you&#39;ve done something like this:&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; randomItem &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&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;*&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will probably work for 99% of the use cases of a random number. However,
numbers generated from &lt;code&gt;Math.random&lt;/code&gt; are &lt;strong&gt;not&lt;/strong&gt; cryptographically secure. That
means that the &amp;quot;random&amp;quot; values are guessable by a computer and therefore not
suitable for use in cryptography.&lt;/p&gt;
&lt;p&gt;Thankfully, we have other methods of generating random numbers in the browser:
&lt;code&gt;crypto&lt;/code&gt;.
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues&quot;&gt;This global object has a &lt;code&gt;getRandomValues&lt;/code&gt; method&lt;/a&gt;
that we can use to recreate the functionality above. The method needs to be
called with an typed array, so we&#39;ll create one with the &lt;code&gt;Uint8Array&lt;/code&gt;
constructor with a length of 1:&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; typedArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we pass this to &lt;code&gt;getRandomValues&lt;/code&gt; and select the first (and only) item from
the returned array.&lt;/p&gt;
&lt;!-- ```js/1 --&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; typedArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&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 keyword&quot;&gt;const&lt;/span&gt; randomValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRandomValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;typedArray&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 number&quot;&gt;0&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 comment&quot;&gt;// E.g. 122&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since we used an &lt;code&gt;Uint8Array&lt;/code&gt;, all of the numbers generated will from 0 to 255.
To convert that into the same float returned from &lt;code&gt;Math.random&lt;/code&gt;, we need to
divide the value by the total number of possible numbers: 256 or 2&lt;sup&gt;8&lt;/sup&gt;.&lt;/p&gt;
&lt;!-- ```js/2 --&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; typedArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&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 keyword&quot;&gt;const&lt;/span&gt; randomValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRandomValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;typedArray&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 number&quot;&gt;0&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 keyword&quot;&gt;const&lt;/span&gt; randomFloat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; randomValue &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&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 comment&quot;&gt;// E.g. 0.4765625&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these steps in place, we can create a function that is a drop-in
replacement for &lt;code&gt;Math.random&lt;/code&gt; called &lt;code&gt;cryptoRandom&lt;/code&gt;.&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cryptoRandom&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 keyword&quot;&gt;const&lt;/span&gt; typedArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&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 keyword&quot;&gt;const&lt;/span&gt; randomValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRandomValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;typedArray&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 number&quot;&gt;0&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 keyword&quot;&gt;const&lt;/span&gt; randomFloat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; randomValue &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&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 keyword&quot;&gt;return&lt;/span&gt; randomFloat&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;A few things to note:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;You&#39;ll (probably) never need this&lt;/strong&gt;. Reach for &lt;code&gt;Math.random&lt;/code&gt; unless you
have a very specific reason for cryptographic security.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The floats from this method are shorter&lt;/strong&gt;. &lt;code&gt;Math.random&lt;/code&gt; pretty
consistently returns floats that are 10+ decimal places long. If you are
depending on a long tail of numbers, then consider switching to &lt;code&gt;Uint32Array&lt;/code&gt;
and dividing by &lt;code&gt;Math.pow(2, 32)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I&#39;m not a security expert&lt;/strong&gt;. All of the information in this article was
from a quick Google search; make sure to consult people who know what they&#39;re
talking about before copying and pasting code from a random website.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I hope that helps and as always: let me know if you have any corrections to the
article.&lt;/p&gt;
&lt;p&gt;Happy randomizing!&lt;/p&gt;
</description>
      <pubDate>Mon, 18 Jul 2022 18:15:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/replace-math-random-with-crypto-in-javascript/</guid>
    </item>
    <item>
      <title>Starring content</title>
      <link>https://seanmcp.com/articles/starring-content/</link>
      <description>&lt;call-out type=&quot;info&quot;&gt;
&lt;p&gt;Stars were not migrated during the 2022 site rebuild. For a similar feature,
&lt;a href=&quot;https://seanmcp.com/notes/44&quot;&gt;check out bookmarks&lt;/a&gt;!&lt;/p&gt;
&lt;/call-out&gt;
&lt;p&gt;In June, I added a new feature to this site: stars. Now whenever you
&lt;kbd&gt;command&lt;/kbd&gt; or &lt;kbd&gt;windows&lt;/kbd&gt; click anywhere on the site, a little
star emoji ⭐️ is added. When you navigate to that page again, the star will be
there waiting for you. If you ever want to remove a star, click it and it will
vanish.&lt;/p&gt;
&lt;p&gt;Try it out by adding a new star here 👉&lt;/p&gt;
&lt;p&gt;My thought was that stars could mark specific portions of an article or page
that were helpful so that you can quickly find that location again. This is
similar to Medium&#39;s highlight feature, but the data is saved to &lt;code&gt;localStorage&lt;/code&gt;
instead of an external database. As a result, stars are for your eyes only.&lt;/p&gt;
&lt;p&gt;To implement this feature, I added a new &lt;code&gt;localStorage&lt;/code&gt; key called
&lt;code&gt;com.seanmcp.stars&lt;/code&gt; where I keep a map of pathnames and an array of star
coordinates. Whenever you navigate on my site, &lt;code&gt;stars.js&lt;/code&gt; looks to see if there
are any stars for that pathname and renders them if present.&lt;/p&gt;
&lt;p&gt;I added a new click event listener that handles the logic of adding and removing
stars. To check to see if the &lt;kbd&gt;command&lt;/kbd&gt; or &lt;kbd&gt;windows&lt;/kbd&gt; key is
pressed, you can
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/metaKey&quot;&gt;reference the readonly &lt;code&gt;metaKey&lt;/code&gt; property on the event&lt;/a&gt;.
You can see all of
&lt;a href=&quot;https://github.com/SeanMcP/seanmcp.com/blob/master/src/js/stars.js&quot;&gt;the source code for this feature on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It was fun to add a new feature, but I&#39;m realistic about how popular it will be.
The meta + click combination is not discoverable, so it will remain a fun
surprise for readers who find it.&lt;/p&gt;
&lt;p&gt;Let me know if you have any thoughts about the feature or how to improve it.&lt;/p&gt;
&lt;p&gt;Happy starring!&lt;/p&gt;
</description>
      <pubDate>Wed, 13 Jul 2022 15:49:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/starring-content/</guid>
    </item>
    <item>
      <title>Use multiple Chrome Profiles when debugging</title>
      <link>https://seanmcp.com/articles/use-multiple-chrome-profiles-when-debugging/</link>
      <description>&lt;p&gt;Awhile back, I read about using separate Chrome profiles&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/use-multiple-chrome-profiles-when-debugging/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; for debugging. The
idea is to have specific environments that are specially configured to debug
different kinds of issues.&lt;/p&gt;
&lt;p&gt;While the article mentioned many different profiles for different users and
scenarios, I’ve found myself settling on three: my main profile, the guest
profile, and a DevTools profile.&lt;/p&gt;
&lt;p&gt;The main profile is where I am signed into Google with my personal or work email
address. If you use Google Chrome regularly and have a Google account, you
probably have a profile setup already. This is where I do most of my development
and debugging work. I have a selection of extensions installed and a few
resources like bookmarks and snippets to help my workflow. The first step when
debugging is to use my current setup – if I can get things reproducing here,
great! But if I have to alter settings or clear data, then I reach for something
else.&lt;/p&gt;
&lt;p&gt;The next option is the default Guest profile in Chrome. When switching to a
guest profile, you lose the session and any information stored in the browser.
It&#39;s a clean slate. This is helpful when you want to reproduce the bug in a
particular scenario but don’t want to logout or toggle feature flags on your
main profile. Guest profiles have full access to the browser’s DevTools but do
not support extensions. For that, I have a third option.&lt;/p&gt;
&lt;p&gt;Finally, I have a profile specifically for DevTool extensions. Here I have the
React DevTools installed, and any additional else that would be helpful when
debugging. If I know that I am investigating a React issue, I will switch over
to this profile and then explore the tree with the Components panel or run tests
in the Profiler. Any created profile will save site data, so I make a point of
frequently resetting everything to ensure a clean environment for reproducing
bugs.&lt;/p&gt;
&lt;p&gt;With these three profiles, I find that I&#39;m better able to identify and fix
issues that I encounter when developing. Give it a try and let me know how it
works for you.&lt;/p&gt;
&lt;p&gt;Happy debugging!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Read Vitaly Friedman’s
&lt;a href=&quot;https://www.smashingmagazine.com/2021/02/useful-chrome-firefox-devtools-tips-shortcuts/&quot;&gt;&lt;em&gt;DevTools Debugging Tips And Shortcuts&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/use-multiple-chrome-profiles-when-debugging/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Mon, 06 Jun 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/use-multiple-chrome-profiles-when-debugging/</guid>
    </item>
    <item>
      <title>When to use React&#39;s memo HOC</title>
      <link>https://seanmcp.com/articles/when-to-use-reacts-memo-hoc/</link>
      <description>&lt;p&gt;When React wants to render a component, it renders all of the components in the
tree from that point. So if you have a parent component with two child
components, a re-render (↻) of the parent will include re-rendering both
children.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Parent ↻
├── ChildA ↻
└── ChildB ↻
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since data typically flows down the tree, this is the behavior that you probably
want for the UI to reflect the current state of the application.&lt;/p&gt;
&lt;p&gt;However, there may be instances when you do not want a component to re-render
when its ancestor re-renders. Let&#39;s imagine a similar scenario with two
children. One of the components is presentational while the other has a deep
tree of its own.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Parent
├── BasicChild
└── ComplexChild
    ├── GrandChildA
    └── GrandChildB
        └── GreatGrandChild
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like before, any changes (✴) to the parent will result in re-rendering all
if its children – including the complex subtree. Depending on the depth of the
tree or the complexity of the render, this could be a time-consuming render
cycle.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Parent ✴↻
├── BasicChild ↻
└── ComplexChild ↻
    ├── GrandChildA ↻
    └── GrandChildB ↻
        └── GreatGrandChild ↻
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want to prevent &lt;code&gt;ComplexChild&lt;/code&gt; from re-rendering unnecessarily to improve the
performance of our application.&lt;/p&gt;
&lt;p&gt;Thankfully, React has some features to accomplish this.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;class-components&quot; tabindex=&quot;-1&quot;&gt;Class components&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/when-to-use-reacts-memo-hoc/#class-components&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Class components”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Class components have
&lt;a href=&quot;https://reactjs.org/docs/react-component.html#shouldcomponentupdate&quot;&gt;a &lt;code&gt;shouldComponentUpdate&lt;/code&gt; lifecycle method&lt;/a&gt;
that allow you to determine whether a component should re-render based on props
and state.&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreventRerender&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;shouldComponentUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;nextProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nextState&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; nextProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://reactjs.org/docs/react-api.html#reactpurecomponent&quot;&gt;&lt;code&gt;PureComponent&lt;/code&gt; goes one step further&lt;/a&gt;
by implementing &lt;code&gt;shouldComponentUpdate&lt;/code&gt; with a shallow prop and state
comparison. When you component extends &lt;code&gt;PureComponent&lt;/code&gt;, it will only re-render
if its props or state have changed.&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreventRerender&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PureComponent&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;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;function-components&quot; tabindex=&quot;-1&quot;&gt;Function components&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/when-to-use-reacts-memo-hoc/#function-components&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Function components”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;For modern function components with no lifecycle methods, we need a different
solution.
&lt;a href=&quot;https://reactjs.org/docs/react-api.html#reactmemo&quot;&gt;React exports a &lt;code&gt;memo&lt;/code&gt; higher-order component&lt;/a&gt;
(HOC) that takes a function component and returns a wrapped component that will
only re-render if a shallow comparison of props indicates a change.&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; PreventRerender &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;memo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;PreventRerender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more customization, you can pass an optional compare function to
&lt;code&gt;React.memo&lt;/code&gt; as a second argument that determines whether to render based on
previous and next props.&lt;/p&gt;
&lt;p&gt;Any of the strategies above to prevent unnecessary re-renders come at a small
cost, so make sure to implement them only when necessary. You can use the React
DevTools&#39; Profiler panel to determine which areas of the React tree are good
candidates for performance improvements.&lt;/p&gt;
&lt;p&gt;In our example above, we could wrap our complex component with &lt;code&gt;React.memo&lt;/code&gt; in
order to prevent re-rendering when the props are unchanged:&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;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;memo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ComplexChild&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;We would not need to do anything to the basic child component, as any
memoization or lifecycle checks would probably be more expensive than the cost
of re-rendering.&lt;/p&gt;
&lt;p&gt;With that change in place, a change within the parent component would only
result in re-renders for &lt;code&gt;Parent&lt;/code&gt; and &lt;code&gt;BasicChild&lt;/code&gt; – the expensive tree in
&lt;code&gt;ComplexChild&lt;/code&gt; would not change.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Parent ✴↻
├── BasicChild ↻
└── ComplexChild
    ├── GrandChildA
    └── GrandChildB
        └── GreatGrandChild
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope that helps explain when and how to use React&#39;s &lt;code&gt;memo&lt;/code&gt; HOC.&lt;/p&gt;
&lt;p&gt;Happy memoizing!&lt;/p&gt;
</description>
      <pubDate>Thu, 28 Apr 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/when-to-use-reacts-memo-hoc/</guid>
    </item>
    <item>
      <title>Calculate the sum of everything up to a given number</title>
      <link>https://seanmcp.com/articles/calculate-the-sum-of-everything-up-to-a-given-number/</link>
      <description>&lt;p&gt;My wife showed me a Tik Tok series where someone was decluttering their life by
getting rid of an increasing amount of items every day for 30 days. That&#39;s kind
of hard to type out, but the idea was getting rid of one thing on day 1, then
two things on day 2, &lt;em&gt;etc.&lt;/em&gt; until tossing thirty things on day 30.&lt;/p&gt;
&lt;p&gt;It was late a night, and we both struggled with the math of determining how many
items this person would give away by the end of the experiment&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/calculate-the-sum-of-everything-up-to-a-given-number/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. Being an
engineer, I set out the next day to create a little tool to calculate that for
you.&lt;/p&gt;
&lt;p&gt;Type in the number of steps (in our case 30 for thirty days), and select
&amp;quot;Calculate&amp;quot; to see the sum:&lt;/p&gt;
&lt;cumulative-sum&gt;
&lt;p&gt;Try this interactive on &lt;a href=&quot;https://seanmcp.com&quot;&gt;seanmcp.com&lt;/a&gt;&lt;/p&gt;
&lt;/cumulative-sum&gt;
&lt;p&gt;I remember seeing something similar when I was in college and folks would try to
raise support to go on a mission trip. They would have a board with every number
from 1 to 100 (or so) and try to find people who are willing to donate that
amount. With this calculator, you should be able to figure out how long your
support board needs to be with some guess and check.&lt;/p&gt;
&lt;p&gt;Hopefully this helps someone else.&lt;/p&gt;
&lt;p&gt;Happy counting!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Spoiler: It&#39;s 465 items &lt;a href=&quot;https://seanmcp.com/articles/calculate-the-sum-of-everything-up-to-a-given-number/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Sat, 23 Apr 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/calculate-the-sum-of-everything-up-to-a-given-number/</guid>
    </item>
    <item>
      <title>English words by consonant-vowel pattern</title>
      <link>https://seanmcp.com/articles/english-words-by-consonant-vowel-pattern/</link>
      <description>&lt;p&gt;When teaching people (or computers) to read, it&#39;s useful to group similar words
into categories. One such grouping is by consonant-vowel patterns, which
represent words by the letters that they contain.&lt;/p&gt;
&lt;p&gt;With consonant-vowel patterns, we can put words like &amp;quot;bun&amp;quot;, &amp;quot;cat&amp;quot;, and &amp;quot;dip&amp;quot;
into one bucket as &amp;quot;cvc&amp;quot; words; they are all made up of a consonant, followed by
a vowel, and ending in a consonant.&lt;/p&gt;
&lt;p&gt;I put together
&lt;a href=&quot;https://github.com/SeanMcP/reading/tree/master/consonant-vowel-patterns&quot;&gt;a list of all English words sorted by their consonant-vowel pairs&lt;/a&gt;.
However, that is a long list to search through and I thought it might be nice to
have a tool to look up shorter words by their consonant-vowel pattern.&lt;/p&gt;
&lt;consonant-vowel-patterns&gt;
    &lt;form&gt;
        &lt;label&gt;
            &lt;b&gt;Pattern&lt;/b&gt;
            &lt;select name=&quot;pattern&quot;&gt;&lt;/select&gt;
        &lt;/label&gt;
        &lt;button&gt;Search&lt;/button&gt;
    &lt;/form&gt;
    &lt;output&gt;&lt;/output&gt;
&lt;/consonant-vowel-patterns&gt;
&lt;p&gt;The word list isn&#39;t perfect, but hopefully it will give you all of the data that
you need for your use case! Let me know if you have any suggestions or run into
any issues.&lt;/p&gt;
&lt;p&gt;Happy reading!&lt;/p&gt;
</description>
      <pubDate>Thu, 03 Mar 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/english-words-by-consonant-vowel-pattern/</guid>
    </item>
    <item>
      <title>Music to code to</title>
      <link>https://seanmcp.com/articles/music-to-code-to/</link>
      <description>&lt;p&gt;Whenever I need to put my head down and focus on a coding task, I have found
that listening to music can help me focus and increase my productivity.&lt;/p&gt;
&lt;p&gt;There are two main criteria that I have for music while I am programming: 1) it
needs to be instrumental, and 2) it needs to have a decent beat/tempo. If a song
can check both of those boxes, then it gets added to the rotation.&lt;/p&gt;
&lt;p&gt;Here is a list of songs/artists that I enjoy listening to while I work, in
alphabetical order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://music.youtube.com/channel/UCT7LeKzJCc5P1AKY1Ghxjiw&quot;&gt;Arcade High&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.youtube.com/channel/UC3G1cV92stSGOy4cLgXmgJQ&quot;&gt;Com Truise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.youtube.com/channel/UCov2JBrKDko0xcwfIwhEAfw&quot;&gt;Dynatron&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.youtube.com/channel/UCnl2TnNDgp2rUepf5VnXJ1g&quot;&gt;Magic Sword&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.youtube.com/channel/UCec-IpmkXJQhgnkBIx1EGxQ&quot;&gt;S U R V I V E&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.youtube.com/channel/UCVjNyglpxEwPoB65b0Oc4Ew&quot;&gt;Timecop1983&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.youtube.com/channel/UCzdwXtAUt8VgaDXfTfttx8A&quot;&gt;Waveshaper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As of writing, these artists are all in similar, but as the lists grows I plan
on organizing things by genre.&lt;/p&gt;
&lt;p&gt;Happy listening!&lt;/p&gt;
</description>
      <pubDate>Tue, 15 Feb 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/music-to-code-to/</guid>
    </item>
    <item>
      <title>Add a custom emulated device in Chrome</title>
      <link>https://seanmcp.com/articles/add-a-custom-emulated-device-in-chrome/</link>
      <description>&lt;p&gt;Google Chrome provides a feature in the dev tools to emulate a device within the
desktop browser. This is really helpful to get a quick preview of what a webpage
will look like on different types and sizes of devices.&lt;/p&gt;
&lt;p&gt;You can also add a custom emulated device that meets the specifications of your
users. For example, if you work on a product that is primarily used by school
students on Chromebooks, then it would be useful to have an emulated device that
you could easily select.&lt;/p&gt;
&lt;p&gt;To add a custom emulated device in Chrome, first
&lt;a href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools&quot;&gt;open the dev tools with your favorite method&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/custom-emulated-device-1.png&quot; alt=&quot;step 1: open the dev tools&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then select toggle the device toolbar by clicking the phone/tablet icon or using
the shortcut &lt;kbd&gt;ctrl&lt;/kbd&gt;/&lt;kbd&gt;cmd&lt;/kbd&gt; + &lt;kbd&gt;shift&lt;/kbd&gt; + &lt;kbd&gt;m&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/custom-emulated-device-2.png&quot; alt=&quot;step 2: open the device toolbar&quot;&gt;&lt;/p&gt;
&lt;p&gt;Open the dropdown to see all of the default devices available in Chrome. To add
a new one, select &amp;quot;Edit&amp;quot; at the bottom of the list.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/custom-emulated-device-3.png&quot; alt=&quot;step 3: select to edit devices&quot;&gt;&lt;/p&gt;
&lt;p&gt;This should review a new dev-tools panel. Here you can select from a greater
list of default devices. To add a new device, select &amp;quot;Add custom device...&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/custom-emulated-device-4.png&quot; alt=&quot;step 4: select &amp;quot;add custom device&amp;quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Next, input the dimensions for your custom device. I have two Chromebooks with
two different window dimensions: 1366x649 and 1300x605. I&#39;d rather test again
the more extreme device, but you do whatever makes sense for you.&lt;/p&gt;
&lt;p&gt;Make sure to select the correct user agent type! For a Chromebook, I&#39;m going
with &amp;quot;Desktop (touch)&amp;quot;.&lt;/p&gt;
&lt;p&gt;When you are done, select &amp;quot;Add&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/custom-emulated-device-5.png&quot; alt=&quot;step 5: input device dimensions&quot;&gt;&lt;/p&gt;
&lt;p&gt;With your custom emulated device added, you can now selected it from the
responsive device dropdown menu back on the far left.&lt;/p&gt;
&lt;p&gt;The screenshot below illustrates just how different the real estate is for a
Chromebook user compared to someone on a full-sized laptop or desktop:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/custom-emulated-device-6.png&quot; alt=&quot;step 6: use your custom device&quot;&gt;&lt;/p&gt;
&lt;p&gt;With custom emulated devices, we can test to make sure that our webpages and
applications will work well for all of our users.&lt;/p&gt;
&lt;p&gt;Happy customizing!&lt;/p&gt;
</description>
      <pubDate>Sat, 05 Feb 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/add-a-custom-emulated-device-in-chrome/</guid>
    </item>
    <item>
      <title>Upgrading an Eleventy site to 1.0.0</title>
      <link>https://seanmcp.com/articles/upgrading-an-eleventy-site-to-1-0-0/</link>
      <description>&lt;p&gt;I initially published a &lt;a href=&quot;https://seanmcp.com/notes&quot;&gt;note&lt;/a&gt; about how easy it was to upgrade to
Eleventy &lt;code&gt;1.0.0&lt;/code&gt;, but I hit a snag which warrants a quick article. Here are the
two steps that I needed to complete to update this website to the latest version
of Eleventy.&lt;/p&gt;
&lt;p&gt;First, update &lt;code&gt;@11ty/eleventy&lt;/code&gt; with the following command:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i &lt;span class=&quot;token parameter variable&quot;&gt;-D&lt;/span&gt; @11ty/eleventy@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After updating that, both the &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; scripts for the site worked as
expected.
&lt;a href=&quot;https://github.com/11ty/eleventy/releases/tag/v1.0.0&quot;&gt;There are breaking changes to be aware of&lt;/a&gt;,
so your mileage may vary.&lt;/p&gt;
&lt;p&gt;I took the time to update some other dependencies and pushed the changes to
GitHub. I have the repo connected to Netlify for CI/CD, which usually works
without a hitch. Today, however, I noticed that the build failed with the
following log:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;10:03:16 AM: $ eleventy
10:03:16 AM: Eleventy requires Node 12. You will need to upgrade Node to use Eleventy!
10:03:16 AM: ​
10:03:16 AM: ────────────────────────────────────────────────────────────────
10:03:16 AM:   &amp;quot;build.command&amp;quot; failed
10:03:16 AM: ────────────────────────────────────────────────────────────────
10:03:16 AM: ​
10:03:16 AM:   Error message
10:03:16 AM:   Command failed with exit code 1: eleventy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There weren&#39;t any build configurations in Netlify for Node.js version, but a
quick search led me to
&lt;a href=&quot;https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript&quot;&gt;this guide on managing build dependencies&lt;/a&gt;.
Good documentation; another reason to use Netlify!&lt;/p&gt;
&lt;p&gt;I added an environment variable for &lt;code&gt;NODE_VERSION&lt;/code&gt; and set it to the current
Node.js LTS (&lt;code&gt;16.13.2&lt;/code&gt; as of writing), and the build was successful.&lt;/p&gt;
&lt;p&gt;Regardless of your setup, just make sure that you are using Node.js version &lt;code&gt;12&lt;/code&gt;
or later and you should be good to go. &lt;e-moji&gt;🚀&lt;/e-moji&gt;&lt;/p&gt;
&lt;p&gt;Happy upgrading!&lt;/p&gt;
</description>
      <pubDate>Tue, 18 Jan 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/upgrading-an-eleventy-site-to-1-0-0/</guid>
    </item>
    <item>
      <title>Make a web component</title>
      <link>https://seanmcp.com/articles/make-a-web-component/</link>
      <description>&lt;p&gt;To make a web component, you need to create a new class. You can extend an
existing component class if you want to preserve some semantic behavior, but
we&#39;ll go with the default &lt;code&gt;HTMLElement&lt;/code&gt;:&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloWorld&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to add a &lt;code&gt;constructor&lt;/code&gt; method and call the &lt;code&gt;super&lt;/code&gt; function to
ensure that our component is initialized with all the necessary information:&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloWorld&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&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 keyword&quot;&gt;super&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need a DOM to build on. Web components use
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM&quot;&gt;a shadow DOM&lt;/a&gt;,
which we&#39;ll create by calling our class&#39;s &lt;code&gt;attachShadow&lt;/code&gt; method:&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloWorld&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&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 keyword&quot;&gt;super&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 keyword&quot;&gt;const&lt;/span&gt; shadow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attachShadow&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 literal-property property&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;open&quot;&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With our shadow DOM declared, we can create a new element. Let&#39;s go with a span
element with the content &amp;quot;Hello world!&amp;quot; and append it to our shadow:&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloWorld&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&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 keyword&quot;&gt;super&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 keyword&quot;&gt;const&lt;/span&gt; shadow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attachShadow&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 literal-property property&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;open&quot;&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 keyword&quot;&gt;const&lt;/span&gt; span &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;span&quot;&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;
    span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello world!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    shadow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;span&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last thing we need to do is define our custom web component with:&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloWorld&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* Collapsed for brevity */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello-world&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HelloWorld&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;Finally, references your new element in the HTML:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;hello-world&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;hello-world&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Congratulations, you just made your first web component!&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Fri, 14 Jan 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/make-a-web-component/</guid>
    </item>
    <item>
      <title>Prevent Chrome from adjusting audio input levels on Mac</title>
      <link>https://seanmcp.com/articles/prevent-chrome-from-adjusting-audio-input-levels-on-mac/</link>
      <description>&lt;p&gt;Starting Fall of 2021, I noticed that my bluetooth headphones would periodically
mute me while I was speaking during video chats on Google Meet. I tried multiple
sets of bluetooth headphones, and they all had the same issue.&lt;/p&gt;
&lt;p&gt;I learned last week that macOS will automatically mute an audio input when the
input level is set to zero. While in meetings, I kept an eye on the level in
System Preferences &amp;gt; Sound &amp;gt; Input and noticed that it would decrease as I
spoke. As soon as the level reached zero, my headset would notify me that it had
been muted.&lt;/p&gt;
&lt;p&gt;There are no settings in Google Chrome (on in my case, the new Microsoft Edge)
nor Google Meet to disable those input level adjustments, and there is
&lt;a href=&quot;https://support.google.com/chrome/thread/7542181/chrome-is-auto-adjusting-the-microphone-level?hl=en&quot;&gt;an unanswered help question&lt;/a&gt;)
with a lot of folks experiencing the same issue.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/chrome/thread/7542181/chrome-is-auto-adjusting-the-microphone-level?hl=en&quot;&gt;One of the responders&lt;/a&gt;
came up with a solution:
&lt;a href=&quot;https://chrome.google.com/webstore/detail/disable-automatic-gain-co/clpapnmmlmecieknddelobgikompchkk&quot;&gt;a Chrome extension&lt;/a&gt;
that prevents certain domains from changing the input levels.&lt;/p&gt;
&lt;p&gt;After installing that from the Chrome Web Store, I navigated to Google Meet,
activated the extension, and gave it permissions on that domain. The page
refreshed, and the issue was resolved: no more adjusted input levels and no more
unwanted muting.&lt;/p&gt;
&lt;p&gt;It’s great that there are creative solutions like this available, h/t
&lt;a href=&quot;https://github.com/joeywatts&quot;&gt;Joey Watts&lt;/a&gt;, but its frustrating that issues like
this can go unresolved for so long on popular projects like Google Chrome.&lt;/p&gt;
&lt;p&gt;Happy resolving!&lt;/p&gt;
</description>
      <pubDate>Tue, 11 Jan 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/prevent-chrome-from-adjusting-audio-input-levels-on-mac/</guid>
    </item>
    <item>
      <title>My best Slack emojis</title>
      <link>https://seanmcp.com/articles/my-best-slack-emojis/</link>
      <description>&lt;p&gt;I like reaction emojis and have spent an embarrassing amount of time
&lt;a href=&quot;https://seanmcp.com/articles/my-best-slack-emojis/tools-for-custom-slack-emojis&quot;&gt;making new ones for myself and my coworkers&lt;/a&gt;.
These are some of my creations that were popular or just my favorites:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/ack.jpg&quot; alt=&quot;ack&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:ack:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/all-in-this-together.gif&quot; alt=&quot;all in this together&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:all-in-this-together:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/all-seeing-eye.jpg&quot; alt=&quot;all seeing eye&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:all-seeing-eye:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/avalanche.png&quot; alt=&quot;avalanche&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:avalanche:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/awe.gif&quot; alt=&quot;awe&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:awe:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/do-it.gif&quot; alt=&quot;do it&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:do-it:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/does-not-equal.png&quot; alt=&quot;does not equal&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:does-not-equal:&lt;/code&gt;/&lt;code&gt;:dne:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/eagle-eye.jpg&quot; alt=&quot;eagle eye&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:eagle-eye:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/frankensteins-monster.jpg&quot; alt=&quot;frankenstein&#39;s monster&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:frankensteins-monster:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/good-point.png&quot; alt=&quot;good point&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:good-point:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/good-question.png&quot; alt=&quot;good question&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:good-question:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/interesting.gif&quot; alt=&quot;interesting&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:interesting:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/its-alive.jpg&quot; alt=&quot;it&#39;s alive&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:its-alive:&lt;/code&gt;/&lt;code&gt;:frankenstein:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/nail-biter.gif&quot; alt=&quot;nail biter&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:nail-biter:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/nerves.gif&quot; alt=&quot;nerves&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:nerves:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/nice-catch.png&quot; alt=&quot;nice catch&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:nice-catch:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/not-crying.png&quot; alt=&quot;not crying&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:not-crying:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/ope.png&quot; alt=&quot;ope&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:ope:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/save-your-money.png&quot; alt=&quot;save your money&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:save-your-money:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/shakes-head.gif&quot; alt=&quot;shakes head&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:shakes-head:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/shivers.gif&quot; alt=&quot;shivers&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:shivers:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/so-close.jpg&quot; alt=&quot;so close&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:so-close:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/team.gif&quot; alt=&quot;team&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:team:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/this.png&quot; alt=&quot;this&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:this:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/this-week-i-learned.png&quot; alt=&quot;this week i learned&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:this-week-i-learned:&lt;/code&gt;/&lt;code&gt;:twil:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/thumbs-down.gif&quot; alt=&quot;thumbs down&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:thumbs-down:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/too-big.jpg&quot; alt=&quot;too big&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:too-big:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/very-interesting.gif&quot; alt=&quot;very interesting&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:very-interesting:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/yuck.gif&quot; alt=&quot;yuck&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:yuck:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&quot;https://seanmcp.com/img/emojis/yikes.png&quot; alt=&quot;yikes&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:yikes:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Feel free to take inspiration or add to your workspace.&lt;/p&gt;
&lt;p&gt;Happy reacting!&lt;/p&gt;
</description>
      <pubDate>Tue, 04 Jan 2022 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/my-best-slack-emojis/</guid>
    </item>
    <item>
      <title>Helpful online tools for diagramming</title>
      <link>https://seanmcp.com/articles/helpful-online-tools-for-diagramming/</link>
      <description>&lt;p&gt;A good diagram can make a big difference when communicating a message. I often
turn to a diagramming tool when trying to explain a complex idea or organize my
thoughts when brainstorming. Thankfully, there a bunch of online tools to help
you make quality diagrams. Here are a few options for your to use:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;excalidraw&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://excalidraw.com/&quot;&gt;Excalidraw&lt;/a&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/helpful-online-tools-for-diagramming/#excalidraw&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Excalidraw”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/excalidraw-example.png&quot; alt=&quot;Excalidraw example diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;Excalidraw makes it easy to create quick diagrams that look sketchy. It saves
your changes to local storage (which is super handy), and supports a backup
option to download/upload an illustration. There is a live collaboration feature
that I haven&#39;t tried, and some premium offerings with Excalidraw+.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;tl-draw&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://www.tldraw.com/&quot;&gt;TL Draw&lt;/a&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/helpful-online-tools-for-diagramming/#tl-draw&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “TL Draw”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/tldraw-example.png&quot; alt=&quot;TL Draw example diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;TL Draw has a similar feature set to Excalidraw but diagrams look a little more
neat and tidy. Depending on the style you are aiming for, this may be the better
option for you. The biggest difference is that your changes are not saved to
local storage; so make sure to backup your illustration!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;draw.io&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://draw.io&quot;&gt;Draw.io&lt;/a&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/helpful-online-tools-for-diagramming/#draw.io&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Draw.io”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/draw-io-example.png&quot; alt=&quot;Draw.io example diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://Draw.io&quot;&gt;Draw.io&lt;/a&gt; makes creating diagrams and flowcharts easy and painless. It supports a
lot of different element options and seems to be a go-to option for diagramming
complex microservice systems. It has a desktop application and backs up to the
cloud. My only criticism is that the illustrations from &lt;a href=&quot;http://Draw.io&quot;&gt;Draw.io&lt;/a&gt; are not pretty;
it&#39;s definitely function over form.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;google-drawings&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://drive.google.com&quot;&gt;Google Drawings&lt;/a&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/helpful-online-tools-for-diagramming/#google-drawings&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Google Drawings”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/google-drawings-example.png&quot; alt=&quot;Google Drawings example diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;One of the forgotten tools of Google Drive, Drawings is an adequate option for
simple diagrams. If you or your organization are already using Google, it will
be the easiest option if not the most inspired.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you have any other suggestions to add to this list,
&lt;a href=&quot;https://seanmcp.com/articles/helpful-online-tools-for-diagramming/#comment-link&quot;&gt;please let me know&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy diagramming!&lt;/p&gt;
</description>
      <pubDate>Wed, 15 Dec 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/helpful-online-tools-for-diagramming/</guid>
    </item>
    <item>
      <title>What is a build.gradle file?</title>
      <link>https://seanmcp.com/articles/what-is-a-build-gradle-file/</link>
      <description>&lt;p&gt;When working on a React Native app, you will probably make some changes to the
&lt;code&gt;build.gradle&lt;/code&gt; file in your projects &lt;code&gt;android/&lt;/code&gt; directory. This file is the core
component of your build process and has all of the instructions necessary to
compile an Android app from source.&lt;/p&gt;
&lt;p&gt;Your &lt;code&gt;build.gradle&lt;/code&gt; file is read and executed by &lt;a href=&quot;https://gradle.org/&quot;&gt;Gradle&lt;/a&gt;,
an automation tool that is popular in Java development. Gradle&#39;s job is to
resolve the dependency tree of your application and run everything through the
Java compiler to generate code that can be run in the Java Virtual Machine
(JVM). All of that work is divided in to tasks that can be mixed and matched as
needed.&lt;/p&gt;
&lt;p&gt;The nearest equivalent in the world of web development might be
&lt;a href=&quot;https://gulpjs.com/&quot;&gt;Gulp, a composable task runner&lt;/a&gt;,, or perhaps
&lt;a href=&quot;https://webpack.js.org/&quot;&gt;Webpack, a bundler&lt;/a&gt;. But &lt;code&gt;build.gradle&lt;/code&gt; also manages
dependencies, so mix a little bit of &lt;code&gt;package.json&lt;/code&gt; in there too. Essentially,
&lt;code&gt;build.gradle&lt;/code&gt; is the main configuration file for your Android project.&lt;/p&gt;
&lt;p&gt;When you look at
&lt;a href=&quot;https://github.com/facebook/react-native/blob/main/template/android/build.gradle&quot;&gt;a &lt;code&gt;build.gradle&lt;/code&gt; file&lt;/a&gt;
through the lens of a web developer, it looks like a bunch of nested objects
with a few method calls. However, that is completely wrong!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;build.gradle&lt;/code&gt; files are often written in
&lt;a href=&quot;https://groovy-lang.org/&quot;&gt;Groovy, which is its own Java-based programming language&lt;/a&gt;.
What looks like an object declaration in JavaScript is actually a function call
in Groovy. Let&#39;s take a look at the following block:&lt;/p&gt;
&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;repositories &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;google&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;
    maven &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; url &lt;span class=&quot;token string&quot;&gt;&#39;https://www.jitpack.io&#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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are calling the &lt;code&gt;repositories&lt;/code&gt; function and passing a closure,
identified by the curly brackets &lt;code&gt;{}&lt;/code&gt;. Within that closure, we are calling more
functions: &lt;code&gt;google&lt;/code&gt; with no parameters, and &lt;code&gt;maven&lt;/code&gt; with another closure and
another function &lt;code&gt;url&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I&#39;m still working to wrap my head around closures, but the takeaway here is that
there is more going on that you might first assume.&lt;/p&gt;
&lt;p&gt;The best part about using a programming language for a build configuration file
is that you can add variables and functions as needed. Variables and functions
can be defined with the &lt;code&gt;def&lt;/code&gt; keyword customize the build process:&lt;/p&gt;
&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; version &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.2.3&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;customFunction&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;
    println &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Building v&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;customFunction&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last bit of information you should know about &lt;code&gt;build.gradle&lt;/code&gt; is that all of
the tasks are executed via a wrapper. This is the executable &lt;code&gt;gradlew&lt;/code&gt;
(Linux/Mac) or &lt;code&gt;gradlew.bat&lt;/code&gt; (Windows) file that is included in your project. By
running everything through the wrapper, you remove the need to have Gradle
installed on your computer.&lt;/p&gt;
&lt;p&gt;To start the build task, run the following in your terminal:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./gradlew build&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hopefully that demystifies the &lt;code&gt;build.gradle&lt;/code&gt; file in your Android/React-Native
project a little bit. This is not yet my area of expertise, so
&lt;a href=&quot;https://seanmcp.com/articles/what-is-a-build-gradle-file/#comment-link&quot;&gt;please let me know&lt;/a&gt; if I need to make any corrections.&lt;/p&gt;
&lt;p&gt;Happy building!&lt;/p&gt;
</description>
      <pubDate>Thu, 04 Nov 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/what-is-a-build-gradle-file/</guid>
    </item>
    <item>
      <title>Three new games on Toollama</title>
      <link>https://seanmcp.com/articles/three-new-games-on-toollama/</link>
      <description>&lt;p&gt;I don&#39;t think I&#39;ve written about &lt;a href=&quot;http://Toollama.com&quot;&gt;Toollama.com&lt;/a&gt; yet on this site, so here&#39;s a
quick introduction. Toollama is a loose collection of education-focused
productivity tools and games. For the past year, it has been the home of all of
the little apps that I create for myself, teachers, or my kids.&lt;/p&gt;
&lt;p&gt;Over the past three weeks, I have created two new games and one new activity to
help children develop core computer skills:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🐢 &lt;a href=&quot;https://seanmcp.github.io/turtle-guide&quot;&gt;Turtle Guide&lt;/a&gt;: Use the arrow keys
to help a little turtle find his way home&lt;/li&gt;
&lt;li&gt;🐷 &lt;a href=&quot;https://seanmcp.github.io/paint-pig&quot;&gt;Paint Pig&lt;/a&gt;: Use the arrow keys to
help Pig paint a picture&lt;/li&gt;
&lt;li&gt;🔦 &lt;a href=&quot;https://seanmcp.github.io/spotlight&quot;&gt;Spotlight&lt;/a&gt;: Use the mouse/trackpad to
reveal animals hidden in color nature scenes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From a technical perspective, these are all vanilla JS, CSS, and HTML served
from GitHub Pages. I&#39;ve found that solving problems with simple tools has been a
relaxing change of pace from the complex environments of work. As always, the
code is open source on GitHub:
&lt;a href=&quot;https://github.com/seanmcp/turtle-guide&quot;&gt;&lt;code&gt;turtle-guide&lt;/code&gt;&lt;/a&gt;,
&lt;a href=&quot;https://github.com/seanmcp/paint-pig&quot;&gt;&lt;code&gt;paint-pig&lt;/code&gt;&lt;/a&gt;, and
&lt;a href=&quot;https://github.com/seanmcp/spotlight&quot;&gt;&lt;code&gt;spotlight&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These additions bring the grand total on Toollama up to 13 apps!
&lt;a href=&quot;https://seanmcp.com/tools&quot;&gt;Check it out, if you haven&#39;t already&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy learning!&lt;/p&gt;
</description>
      <pubDate>Wed, 27 Oct 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/three-new-games-on-toollama/</guid>
    </item>
    <item>
      <title>React Native Glossary</title>
      <link>https://seanmcp.com/articles/react-native-glossary/</link>
      <description>&lt;p&gt;Building applications with React Native requires a high-level understanding of
mobile development on Android and iOS devices in addition to React and React
Native fundamentals. This glossary of terms will serve as a helpful reference
when working across these environments.&lt;/p&gt;
&lt;hr&gt;
&lt;!--
TERMS TO ADD:
- JDK
- JRE
--&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;aab&quot; tabindex=&quot;-1&quot;&gt;AAB&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/react-native-glossary/#aab&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “AAB”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Android App Bundles (AABs) are a new format for publishing applications on
Android. AABs can contain multiple APKs. Since August 2021, Google Play requires
AABs for new apps.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.android.com/guide/app-bundle&quot;&gt;About Android App Bundles (Android)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;apk&quot; tabindex=&quot;-1&quot;&gt;APK&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/react-native-glossary/#apk&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “APK”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Android Package Kits (APKs) are an older format for publishing applications on
Android.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;build.gradle&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;build.gradle&lt;/code&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/react-native-glossary/#build.gradle&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “build.gradle”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;A &lt;code&gt;build.gradle&lt;/code&gt; file contains all of the information for Gradle to compile
Android apps, including dependency resolutions and tasks to process the source.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://seanmcp.com/articles/what-is-a-build-gradle-file&quot;&gt;&lt;em&gt;What is a &lt;code&gt;build.gradle&lt;/code&gt; file?&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;gradle&quot; tabindex=&quot;-1&quot;&gt;Gradle&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/react-native-glossary/#gradle&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Gradle”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Gradle is an automated build tool that is used to compile Android apps from
source code. It is configured in &lt;code&gt;build.gradle&lt;/code&gt; files and run through a wrapper:
&lt;code&gt;gradlew&lt;/code&gt;/&lt;code&gt;gradlew.bat&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gradle.org/&quot;&gt;Gradle Build Tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=-dtcEMLNmn0&quot;&gt;Gradle tutorial for complete beginners (YouTube)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;hermes&quot; tabindex=&quot;-1&quot;&gt;Hermes&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/react-native-glossary/#hermes&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Hermes”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Hermes is a mobile-optimized JavaScript engine that is included in React Native
builds for Android and iOS.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://reactnative.dev/docs/hermes&quot;&gt;Using Hermes (React Native)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seanmcp.com/articles/what-is-hermes-react-native/&quot;&gt;What is Hermes in React Native?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;info.plist&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;Info.plist&lt;/code&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/react-native-glossary/#info.plist&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Info.plist”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The information property list or &lt;code&gt;Info.plist&lt;/code&gt; is an XML file that contains all
of the configuration data for your application. You can update these values
directly or through the Xcode UI.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Introduction/Introduction.html&quot;&gt;About Info.plist Keys and Values (Apple)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;javascriptcore%2Fjscore&quot; tabindex=&quot;-1&quot;&gt;JavaScriptCore/JSCore&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/react-native-glossary/#javascriptcore%2Fjscore&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “JavaScriptCore/JSCore”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;JavaScript Core or JSCore is a JavaScript engine that is included on iOS
devices. It was included in Android builds to run JS code by React Native
versions prior to &lt;code&gt;0.60&lt;/code&gt;. It has since been replaced with &lt;a href=&quot;https://seanmcp.com/articles/react-native-glossary/#hermes&quot;&gt;Hermes&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/javascriptcore&quot;&gt;JavaScriptCore (Apple)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!--
## JRE

A Java Runtime Environment (JRE) is the code required to run a Java application. It contains all of the libraries and software that Java programs, and starts the Java Virtual Machine (JVM).

- [Java Runtime Environment (IBM)](https://www.ibm.com/cloud/learn/jre)
--&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;sdk&quot; tabindex=&quot;-1&quot;&gt;SDK&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/react-native-glossary/#sdk&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “SDK”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Software Development Kits (SDKs) are bundles of code to add to your application.
Third parties will often have an SDK to integrate with their service.&lt;/p&gt;
</description>
      <pubDate>Thu, 07 Oct 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/react-native-glossary/</guid>
    </item>
    <item>
      <title>What happened to @react-native-community packages?</title>
      <link>https://seanmcp.com/articles/what-happened-to-react-native-community-packages/</link>
      <description>&lt;p&gt;When React Native was first released, it included extra modules that were
helpful for mobile development like &lt;code&gt;WebView&lt;/code&gt;, &lt;code&gt;NetInfo&lt;/code&gt;, and &lt;code&gt;AsyncStorage&lt;/code&gt;.
Later in 2018,
&lt;a href=&quot;https://github.com/react-native-community/discussions-and-proposals/issues/6&quot;&gt;the RN team made the decision&lt;/a&gt;
to deprecate those internal modules and hand them off to the community to
maintain.&lt;/p&gt;
&lt;p&gt;This spawned
&lt;a href=&quot;https://github.com/react-native-community&quot;&gt;the &lt;code&gt;@react-native-community&lt;/code&gt; organization on GitHub&lt;/a&gt;
and a number of community-maintained packages like &lt;code&gt;react-native-webview&lt;/code&gt; and
&lt;code&gt;react-native-netinfo&lt;/code&gt;. If you were working in React Native in the late
20-teens, you probably remember adding these dependencies and switching out
imports.&lt;/p&gt;
&lt;p&gt;In most cases, the community proved capable to maintain current packages and
continue development. However, some packages were abandoned as requirements
changes or maintainers moved on. Alternative options popped up and gained the
support of the community, driven by individual developers or those at different
organizations like Expo, Wix, Callstack, or Software Mansion.&lt;/p&gt;
&lt;p&gt;Developers who were new to React Native might defer to &lt;code&gt;@react-native-community&lt;/code&gt;
packages, not because they were better than those alternatives but because they
appeared to be &amp;quot;the approved solution&amp;quot; from the community. That wasn&#39;t ideal for
developers, which might end up with unmaintained projects, nor for the greater
React Native community that was driving things forward.&lt;/p&gt;
&lt;p&gt;At the end of 2019, the
&lt;a href=&quot;https://github.com/react-native-community/discussions-and-proposals/issues/176&quot;&gt;&lt;code&gt;@react-native-community&lt;/code&gt; decided to move modules out of the organization&lt;/a&gt;
and into individual project organizations. In
&lt;a href=&quot;https://github.com/react-native-community/discussions-and-proposals/issues/176#issuecomment-719172928&quot;&gt;the words of one maintainer&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;These modules aren&#39;t being maintained by fewer people, and aren&#39;t any less
supported. But now perception can match the reality.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As a result, scoped packages like &lt;code&gt;@react-native-community/async-storage&lt;/code&gt; moved
to &lt;code&gt;react-native-async-storage/async-storage&lt;/code&gt;. But besides the name, little to
nothing else changed about how they were maintained.&lt;/p&gt;
&lt;p&gt;By offloading packages, &lt;code&gt;@react-native-community&lt;/code&gt; is free to focus on
higher-level goals for the community, like tools to discover and maintain
high-quality React Native packages. Two projects still owned by the community
organization are
&lt;a href=&quot;https://github.com/react-native-community/upgrade-helper&quot;&gt;&lt;code&gt;upgrade-helper&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://reactnative.directory/&quot;&gt;React Native Directory&lt;/a&gt;, both exemplifying its
new focus.&lt;/p&gt;
&lt;p&gt;So while you won&#39;t be adding &lt;code&gt;@react-native-community&lt;/code&gt; packages to your projects
going forward, that is ultimately for the best. Instead, use resources like
&lt;a href=&quot;https://reactnative.directory/&quot;&gt;React Native Directory&lt;/a&gt; or
&lt;a href=&quot;https://github.com/jondot/awesome-react-native&quot;&gt;&lt;code&gt;awesome-react-native&lt;/code&gt;&lt;/a&gt; to find
the best solution for you and your application.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;further-reading&quot; tabindex=&quot;-1&quot;&gt;Further reading&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/what-happened-to-react-native-community-packages/#further-reading&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Further reading”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/react-native-community/discussions-and-proposals/issues/6&quot;&gt;Learn Core (2018)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/react-native-community/discussions-and-proposals/issues/176&quot;&gt;What packages belong in react-native-community (2019)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/react-native-community/discussions-and-proposals/blob/master/partners/0001-organization-repository-policy.md&quot;&gt;Organization repository policy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Tue, 05 Oct 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/what-happened-to-react-native-community-packages/</guid>
    </item>
    <item>
      <title>Find an iOS simulator identifier</title>
      <link>https://seanmcp.com/articles/find-an-ios-simulator-identifier/</link>
      <description>&lt;p&gt;When running a device simulator on macOS, it is possible to navigate that
system&#39;s directly through the command line (or Finder, if you&#39;d prefer). To do
that, navigate to to the following directory:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /Library/Developer/CoreSimulator&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you look around in this directory, you will probably see a number of
subdirectories with
&lt;a href=&quot;https://en.wikipedia.org/wiki/Universally_unique_identifier&quot;&gt;UUIDs&lt;/a&gt;. Each of
these corresponds to a simulator device which contains all of the data for that
instance. In order to browse a specific simulator&#39;s files, you need to know its
ID.&lt;/p&gt;
&lt;p&gt;There are two ways to find that, one using the Xcode UI and another using the
terminal.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;through-xcode&quot; tabindex=&quot;-1&quot;&gt;Through Xcode&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/find-an-ios-simulator-identifier/#through-xcode&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Through Xcode”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;In the top menu, select &amp;quot;Window&amp;quot; &amp;gt; &amp;quot;Devices and Simulators&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;You can use the shortcut &lt;kbd&gt;shift&lt;/kbd&gt;+&lt;kbd&gt;command&lt;/kbd&gt;+&lt;kbd&gt;2&lt;/kbd&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;In the left menu of the new window, select the &amp;quot;Simulators&amp;quot; tab&lt;/li&gt;
&lt;li&gt;Select the device from the list below&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Note&lt;/strong&gt;: Make sure you match the device specs &lt;em&gt;and&lt;/em&gt; iOS version&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Look for &amp;quot;Identifier:&amp;quot; in the main panel and copy the value&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;through-the-terminal&quot; tabindex=&quot;-1&quot;&gt;Through the terminal&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/find-an-ios-simulator-identifier/#through-the-terminal&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Through the terminal”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The following command require Xcode command-line tools, which you probably
already have installed if you are running Xcode.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;From anywhere on your system, run &lt;code&gt;xcrun simctl list devices&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Find the ID from the output by iOS version and device specs and copy the ID&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If the device is already running, you can save yourself some time by passing a
search argument:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Show only running simulators&lt;/span&gt;
xcrun simctl list devices booted&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That should surface the current simulator faster for copying.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;take-a-look-around!&quot; tabindex=&quot;-1&quot;&gt;Take a look around!&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/find-an-ios-simulator-identifier/#take-a-look-around!&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Take a look around!”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Once you have the simulator ID copied, you can navigate there in the terminal
with:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /Library/Developer/CoreSimulator/SIMULATOR-ID-THAT-YOU-COPIED&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, if you want to look into a specific app&#39;s bundle, you can find that
data within the simulator directory at
&lt;code&gt;./data/Containers/Bundle/Application/APP-ID&lt;/code&gt;. I have found this useful when
debugging image paths in the final app bundle.&lt;/p&gt;
&lt;p&gt;Happy browsing!&lt;/p&gt;
</description>
      <pubDate>Mon, 04 Oct 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/find-an-ios-simulator-identifier/</guid>
    </item>
    <item>
      <title>Using BEM names in React Native</title>
      <link>https://seanmcp.com/articles/using-bem-names-in-react-native/</link>
      <description>&lt;p&gt;When writing CSS classes, I prefer BEM&#39;s naming system. The pattern is clear to
read and names are easy to generate. I believe the unwieldy length of some names
is offset by the benefits.&lt;/p&gt;
&lt;p&gt;React Native doesn&#39;t use CSS but
&lt;a href=&quot;https://reactnative.dev/docs/style&quot;&gt;JSON-based stylesheets that are applied directly to elements&lt;/a&gt;.
This system has some advantages, notably
&lt;a href=&quot;https://kentcdodds.com/blog/colocation&quot;&gt;co-locating concerns&lt;/a&gt; within the
component file.&lt;/p&gt;
&lt;p&gt;But you still have to name things within you style objects, and I find myself
reaching for BEM even when removed from CSS.&lt;/p&gt;
&lt;p&gt;For a quick overview of BEM,
&lt;a href=&quot;http://getbem.com/introduction/&quot;&gt;checkout the official documentation&lt;/a&gt;. The page
is pretty short and summarizes the system with images. Okay, 1... 2... 3... I&#39;m
going to assume you&#39;re familiar with BEM.&lt;/p&gt;
&lt;p&gt;To be fair, this is perfectly valid JavaScript and works out of the box:&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; styles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; StyleSheet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&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 literal-property property&quot;&gt;message&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&#39;message--warning&#39;&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;message__text&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 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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;View style&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 punctuation&quot;&gt;[&lt;/span&gt; styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;warning&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; styles&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message--warning&#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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Text style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message__text&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;text&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Text&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;View&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here I&#39;m following the pattern from BEM of naming blocks at the top level, using
underscores for elements within a block, and hyphens for modifiers. And again,
this works fine.&lt;/p&gt;
&lt;p&gt;But I &lt;em&gt;really&lt;/em&gt; dislike using strings as properties for an object. They&#39;re
annoying to create and even worse to reference. And when we use hyphens to
create modifier names, we&#39;re stuck with &lt;code&gt;styles[&#39;message--warning&#39;]&lt;/code&gt;. I just
want to use BEM without the added discomfort of string keys. What are our
alternatives?&lt;/p&gt;
&lt;p&gt;Turns out, there aren&#39;t many options. The underscore is taken, and most special
characters are not valid in object properties. Numbers are an option, but none
of them seemed to fit. The only other option on my keyboard was, perhaps
surprisingly, the dollar sign: &lt;code&gt;$&lt;/code&gt;. (Side note: Are all currency symbols valid
for object properties?)&lt;/p&gt;
&lt;p&gt;jQuery and Svelte both use the &lt;code&gt;$&lt;/code&gt; symbol for JavaScript, so why not here in
React Native? If we swap double hyphens for double dollar signs in the previous
snippet, we get:&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; styles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; StyleSheet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&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 literal-property property&quot;&gt;message&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;message$$warning&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;message__text&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 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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;View style&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 punctuation&quot;&gt;[&lt;/span&gt; styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;warning&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message$$warning &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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Text style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message__text&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;text&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Text&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;View&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not bad. And I&#39;d much rather use a slightly foreign symbol in a familiar system
than string properties.&lt;/p&gt;
&lt;p&gt;What do you think: good idea or bad idea? Or should I just use styled components
already? Or maybe something else entirely? &lt;a href=&quot;https://seanmcp.com/articles/using-bem-names-in-react-native/#comment-link&quot;&gt;Let me know&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Until then, happy naming!&lt;/p&gt;
</description>
      <pubDate>Fri, 10 Sep 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/using-bem-names-in-react-native/</guid>
    </item>
    <item>
      <title>Reflecting on a new role</title>
      <link>https://seanmcp.com/articles/reflections-on-a-new-role/</link>
      <description>&lt;p&gt;Recently I accepted a new position at Niche as a senior mobile software
engineer. I will take the reigns of our mobile app
(&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.nicheinc.nichealpha&quot;&gt;Android&lt;/a&gt;,
&lt;a href=&quot;https://apps.apple.com/us/app/niche-colleges-k-12-places/id1356697847&quot;&gt;iOS&lt;/a&gt;)
and work on a dedicated product team building new features for our users. This
is a shift in focus for my career but not uncharted territory.&lt;/p&gt;
&lt;p&gt;While working for TSYS in 2018, I helped work on an unnamed mobile app in the
consumer finance space. After joining Niche in 2019, I helped implement Apple
Sign-In in our app and resolved some other issues. Now that I am fully dedicated
to working on the mobile app, I&#39;m looking forward to building new things and
learning lots along the way.&lt;/p&gt;
&lt;p&gt;I&#39;m going to start sharing more mobile and React Native articles on this site as
I learn. The first of those came out earlier this week:
&lt;a href=&quot;https://seanmcp.com/articles/what-is-hermes-in-react-native&quot;&gt;&lt;em&gt;What is Hermes in React Native?&lt;/em&gt;&lt;/a&gt;.
Some of the information may be applicable in React contexts, so I&#39;ll try to
&lt;a href=&quot;https://seanmcp.com/tags/&quot;&gt;tag articles appropriately&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That said, I&#39;m not leaving the web world entirely.
&lt;a href=&quot;https://chrome.google.com/webstore/detail/popsicle-sticks-random-st/lnckbgaeagindapiodcmccfokcmnfecm&quot;&gt;A Chrome extension for teachers&lt;/a&gt;
that I launched in 2018 has over 10,000 weekly users during the school year, and
I just released a new version with some great features. I&#39;m committed to
continued development on that and &lt;a href=&quot;https://seanmcp.com/tools&quot;&gt;all the other tools I&#39;ve created&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I am so grateful for the opportunities that I have been given in software
development. From the ability to attend a coding bootcamp full-time, to that
first developer job with TSYS, to a new position closer to home with Niche, and
now greater acclaim and responsibility at a company that is going places. If any
one of those doors had been closed, then my situation now could look completely
different.&lt;/p&gt;
&lt;p&gt;It&#39;s humbling to remember that I don&#39;t deserve any of this; it&#39;s ultimately
grace. But now my responsibility is to do the best I can with all that God has
given me. To Him be the glory, now and forever. Amen.&lt;/p&gt;
&lt;p&gt;If you&#39;re reading this and have experience working in mobile development or with
React Native, &lt;a href=&quot;https://seanmcp.com/articles/reflections-on-a-new-role/#comment-link&quot;&gt;then please reach out&lt;/a&gt;. I&#39;d love to pick your
brain for a bit.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Wed, 18 Aug 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/reflections-on-a-new-role/</guid>
    </item>
    <item>
      <title>What is Hermes in React Native?</title>
      <link>https://seanmcp.com/articles/what-is-hermes-in-react-native/</link>
      <description>&lt;p&gt;React Native is framework for building Android and iOS application using React
and native capabilities. It allows developers to write JavaScript code that will
interact with platform APIs and native components to build interactive user
interfaces.&lt;/p&gt;
&lt;p&gt;To accomplish that, React Native needs an engine to run JavaScript code on
mobile devices. Apple includes
&lt;a href=&quot;https://developer.apple.com/documentation/javascriptcore&quot;&gt;JavaScriptCore (JSCore) on iOS devices&lt;/a&gt;,
but
&lt;a href=&quot;https://stackoverflow.com/questions/8374016/how-to-execute-javascript-on-android&quot;&gt;Android does not have a JavaScript engine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So React Native included a version of JSCore in Android builds to execute
JavaScript. This resulted in larger Android application packages (APKs) for
React Native apps, and some noticeable performance issues.&lt;/p&gt;
&lt;p&gt;In 2019, the
&lt;a href=&quot;https://reactnative.dev/blog/2019/07/17/hermes&quot;&gt;React Native team announced Hermes&lt;/a&gt;
as &amp;quot;a new JavaScript engine optimized for React Native.&amp;quot; Android apps built with
React Native and Hermes were
&lt;a href=&quot;https://engineering.fb.com/2019/07/12/android/hermes/&quot;&gt;smaller, interactive faster, and used less memory&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/react-native-community/releases/blob/master/CHANGELOG.md#v0601&quot;&gt;React Native version 0.60.1&lt;/a&gt;
(2019) replaced JSCore with Hermes for Android builds. Later in version 0.64
(2021), React Native added
&lt;a href=&quot;https://reactnative.dev/blog/2021/03/12/version-0.64&quot;&gt;support for Hermes on iOS&lt;/a&gt;.
&lt;a href=&quot;https://reactnative.dev/blog/2021/03/12/version-0.64#hermes-opt-in-on-ios&quot;&gt;With one small change to your project&#39;s &lt;code&gt;Podfile&lt;/code&gt;&lt;/a&gt;,
you can include Hermes to improve your app&#39;s performance:&lt;/p&gt;
&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;use_react_native&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 symbol&quot;&gt;:path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:reactNativePath&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 comment&quot;&gt;# to enable hermes on iOS, change `false` to `true` and then install pods&lt;/span&gt;
   &lt;span class=&quot;token symbol&quot;&gt;:hermes_enabled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 want to do some profiling of iOS apps with JSCore and Hermes before
committing to the change, but it is exciting to see more alignment between the
two platforms.&lt;/p&gt;
&lt;p&gt;To read more information about to use Hermes and how to debug it,
&lt;a href=&quot;https://reactnative.dev/docs/hermes&quot;&gt;check out the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Tue, 17 Aug 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/what-is-hermes-in-react-native/</guid>
    </item>
    <item>
      <title>What is the song from Boston Dynamic&#39;s Spot Launch video?</title>
      <link>https://seanmcp.com/articles/what-is-the-song-from-boston-dynamics-spot-launch-video/</link>
      <description>&lt;p&gt;My three-year-old son &lt;em&gt;really&lt;/em&gt; likes Boston Dynamics videos. He frequently
requests to watch them and will parade around the house as Spot, Atlas, or
Stretch/Handle. As far as little kid fixations on machines go, I think robots
are a pretty cool option; he&#39;s much more likely to work on/with robots in the
future than steam engines!&lt;/p&gt;
&lt;p&gt;One video he likes is
&lt;a href=&quot;https://www.youtube.com/watch?v=wlkCQXHEgjA&quot;&gt;&lt;em&gt;Spot Launch&lt;/em&gt;&lt;/a&gt;, which features the
titular robot navigating through a bunch of simulated work environments. It&#39;s an
entertaining video, and my son really likes dancing to the song.&lt;/p&gt;
&lt;p&gt;The song wasn&#39;t listed in the video description, and I couldn&#39;t find an answer
via a Google search. I did find
&lt;a href=&quot;https://www.reddit.com/r/NameThatSong/comments/isqmyu/what_is_the_song_in_the_spot_launch_ad_from/&quot;&gt;two unanswered&lt;/a&gt;
and
&lt;a href=&quot;https://www.reddit.com/r/Music/comments/d9fn4j/help_identifying_song_from_spot_launch_boston/&quot;&gt;archived questions&lt;/a&gt;
on Reddit asking for help identifying the song.&lt;/p&gt;
&lt;p&gt;With my wife&#39;s help, I tried using the
&lt;a href=&quot;https://blog.google/products/search/hum-to-search/&quot;&gt;Google app&#39;s music recognition feature&lt;/a&gt;.
I played the YouTube video on my phone, and she let the app listen on hers.
Within 5 seconds, Google had identified the song as
&lt;a href=&quot;https://www.youtube.com/watch?v=tZaAmapkp4Q&quot;&gt;&lt;em&gt;Banana&lt;/em&gt; by Stony Pony&lt;/a&gt;. Neat!&lt;/p&gt;
&lt;p&gt;Here are the two videos for comparison:&lt;/p&gt;
&lt;em-bed&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=tZaAmapkp4Q&quot;&gt;https://www.youtube.com/watch?v=tZaAmapkp4Q&lt;/a&gt;&lt;/p&gt;
&lt;/em-bed&gt;
&lt;em-bed&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=wlkCQXHEgjA&quot;&gt;https://www.youtube.com/watch?v=wlkCQXHEgjA&lt;/a&gt;&lt;/p&gt;
&lt;/em-bed&gt;
&lt;p&gt;The version in the Boston Dynamics video has removed the word &amp;quot;Banana&amp;quot; a few
times, but it is otherwise the same song.&lt;/p&gt;
&lt;p&gt;It&#39;s too late to answer those Reddit questions, but I added
&lt;a href=&quot;https://www.youtube.com/watch?v=wlkCQXHEgjA&amp;amp;lc=UgwqRUzrTpMJJlVCMQt4AaABAg&quot;&gt;a comment to the video on YouTube&lt;/a&gt;.
And hopefully the next person searching stumbles upon that or this article.&lt;/p&gt;
&lt;p&gt;Happy listening!&lt;/p&gt;
</description>
      <pubDate>Tue, 10 Aug 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/what-is-the-song-from-boston-dynamics-spot-launch-video/</guid>
    </item>
    <item>
      <title>Add a YouTube-embedder shortcode to your Eleventy site</title>
      <link>https://seanmcp.com/articles/add-a-youtube-embedder-shortcode-to-your-eleventy-site/</link>
      <description>&lt;p&gt;For a recent article, I wanted to add some embedded YouTube videos. Through the
share menu beneath the video, you can select &amp;quot;Embed&amp;quot; to copy and paste the code
for an &lt;code&gt;iframe&lt;/code&gt; that will load the YouTube video.&lt;/p&gt;
&lt;p&gt;That works well, but if you want to customize that code at all for embedded
videos, you need to remember to do that every time you copy and paste it into
one of your pages. Rather than fiddling with that, I decided to create a custom
shortcode to handle all of that for me.&lt;/p&gt;
&lt;p&gt;Here is the code for my Eleventy site, but with a little tweaking you could make
this work for any static-site generator:&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;// .eleventy.js&lt;/span&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;
  eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addShortcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;youtube&quot;&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;videoURL&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;videoURL&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 keyword&quot;&gt;const&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;v&quot;&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 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;
&amp;lt;iframe class=&quot;yt-shortcode&quot; src=&quot;https://www.youtube-nocookie.com/embed/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; title=&quot;YouTube video player&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;
      title &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; for &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;title&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 operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&amp;lt;/iframe&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 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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I removed a lot of the default permissions from the YouTube embed code, and
opted for &lt;code&gt;youtube-nocookie.com&lt;/code&gt; instead to save my visitors from some tracking.
You can
&lt;a href=&quot;https://dri.es/how-to-remove-youtube-tracking&quot;&gt;read more about that on Dries Buytaert&#39;s blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With that shortcode added, you can use it in any of your template files with the
&lt;code&gt;{% youtube %}&lt;/code&gt; syntax:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- With title --&gt;&lt;/span&gt;

{% youtube &quot;https://www.youtube.com/watch?v=DhKHAopx7D0&quot;, &quot;&#39;Time Bomb&#39; by
Rancid&quot; %}

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Without title --&gt;&lt;/span&gt;

{% youtube &quot;https://www.youtube.com/watch?v=LnaeImQ0TSg&quot; %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, I added a bit of CSS to help the videos look good on my site. These
styles might be different for your site:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;iframe.yt-shortcode&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50vw&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 25rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&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 if I want to change the way I handle YouTube embeds, I need only to change
the code in one place. Hope that helps!&lt;/p&gt;
&lt;p&gt;Happy embedding!&lt;/p&gt;
</description>
      <pubDate>Tue, 10 Aug 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/add-a-youtube-embedder-shortcode-to-your-eleventy-site/</guid>
    </item>
    <item>
      <title>Get innerHTML of Enzyme wrapper</title>
      <link>https://seanmcp.com/articles/get-innerhtml-of-enzyme-wrapper/</link>
      <description>&lt;p&gt;In Enzyme, wrapper&#39;s have a handle &lt;code&gt;.html()&lt;/code&gt; method that will return the
&lt;code&gt;outerHTML&lt;/code&gt; for that element in the tree. It&#39;s handy in the rare instance when
the markup that your code outputs needs to be precise. You can
&lt;a href=&quot;https://enzymejs.github.io/enzyme/docs/api/ShallowWrapper/html.html&quot;&gt;read more about in the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, there isn&#39;t a method that returns the &lt;code&gt;innerHTML&lt;/code&gt;. A use case I came
across for this was caring about the children of an element, but not caring
about the element itself. I didn&#39;t want this test to fail if I needed to add an
attribute to the parent element, only if the children changed.&lt;/p&gt;
&lt;p&gt;So I wrote this little helper function to grab the &lt;code&gt;innerHTML&lt;/code&gt; of a shallow
wrapper in Enzyme:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInnerHTML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&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 keyword&quot;&gt;return&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;children&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 function&quot;&gt;reduce&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;string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; node&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 class=&quot;token comment&quot;&gt;// A text node&#39;s html method doesn&#39;t return&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// anything, so we use text instead.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; string &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;html&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;||&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&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 class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;There might be other strategies using some of Enzyme&#39;s built-in methods like
&lt;code&gt;.reduce()&lt;/code&gt; or &lt;code&gt;.forEach()&lt;/code&gt;, so let me know if you come up with a better
alternative.&lt;/p&gt;
&lt;p&gt;Happy testing!&lt;/p&gt;
</description>
      <pubDate>Mon, 12 Jul 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/get-innerhtml-of-enzyme-wrapper/</guid>
    </item>
    <item>
      <title>Use remark to covert markdown into HTML</title>
      <link>https://seanmcp.com/articles/use-remark-to-covert-markdown-into-html/</link>
      <description>&lt;p&gt;I&#39;ve used &lt;a href=&quot;https://npm.im/remark&quot;&gt;remark&lt;/a&gt; in a few different projects, and each
time I found it difficult to get started. For such a popular project, the
&lt;a href=&quot;https://remark.js.org/&quot;&gt;documentation is quite terse&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To save myself the trouble of looking up how to do it in the future, here are
the basic steps you need to get started converting markdown to HTML with remark.&lt;/p&gt;
&lt;p&gt;First, install remark dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm i remark remark-html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will need the core library and a plugin for generating HTML. Then, import
your dependencies and use them like so:&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;import&lt;/span&gt; remark &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkHTML &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; html &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;remark&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 function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkHTML&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 function&quot;&gt;processSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Some **markdown** text&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s all you need to get going. To help jump-start your progress, checkout
this working
&lt;a href=&quot;https://replit.com/@SeanMcP/remark-example#index.js&quot;&gt;repl in Node.js&lt;/a&gt; or
&lt;a href=&quot;https://codesandbox.io/s/react-remark-example-kofy0&quot;&gt;sandbox in React&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hope this guide helps you get up and running too!&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Sat, 03 Jul 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/use-remark-to-covert-markdown-into-html/</guid>
    </item>
    <item>
      <title>Count class methods in JavaScript</title>
      <link>https://seanmcp.com/articles/count-class-methods-in-javascript/</link>
      <description>&lt;p&gt;If you have a class in JavaScript and want to know the number of methods it
contains, there are a few steps that you need to follow.&lt;/p&gt;
&lt;p&gt;Let&#39;s say we have a class like 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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&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 function&quot;&gt;call&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;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; comes running&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don&#39;t have handy tools like &lt;code&gt;Object.keys()&lt;/code&gt; for classes, so to count the
number of methods in our &lt;code&gt;Pet&lt;/code&gt; class, we need to first create an instance:&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; abby &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Abby&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can pass our instance to two &lt;code&gt;Object&lt;/code&gt; methods that will grab the methods
for us:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPrototypeOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;abby&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns an array of property names for the prototype of &lt;code&gt;abby&lt;/code&gt;, which is
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 punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;constructor&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;call&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;re dealing with a simple class like the example above, you can add a
&lt;code&gt;.length&lt;/code&gt; to the end of the returned array and be done.&lt;/p&gt;
&lt;p&gt;However, if you are dealing with subclasses, you may want to go a little deeper.&lt;/p&gt;
&lt;p&gt;Let&#39;s update the example so that the class who&#39;s methods you want to count
extends another:&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&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 function&quot;&gt;call&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;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; comes running&quot;&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 class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;pet&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;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&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 interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; wags its tail&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now if we try the following code with &lt;code&gt;abby&lt;/code&gt; the &lt;code&gt;Dog&lt;/code&gt;, we get a different
answer:&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; abby &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Abby&quot;&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;
Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPrototypeOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;abby&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 comment&quot;&gt;// [&#39;constructor&#39;, &#39;pet&#39;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We lost the methods on the parent &lt;code&gt;Pet&lt;/code&gt; class. To grab those too, we can create
a reusable function to dive through the prototype chain:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;countClassMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;_class&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 keyword&quot;&gt;const&lt;/span&gt; methods &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&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 keyword&quot;&gt;let&lt;/span&gt; keepDiving &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; prototype &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPrototypeOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_class&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 keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keepDiving&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prototype&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 function&quot;&gt;forEach&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;name&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; methods&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&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;
    prototype &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPrototypeOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prototype&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__proto__&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; keepDiving &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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 keyword&quot;&gt;return&lt;/span&gt; methods&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size&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;This function takes a class and grabs its prototype. Then in a &lt;code&gt;while&lt;/code&gt; loop, it
iterates through the prototype&#39;s property names and adds them to a set. It then
looks ahead to the next prototype to determine whether it should continue. If
the next prototype&#39;s &lt;code&gt;__proto__&lt;/code&gt; property is falsy, then leave the loop and
return the size of the set.&lt;/p&gt;
&lt;p&gt;If you want to see a list of the methods, you could rename the function and
return &lt;code&gt;[...methods]&lt;/code&gt; or do something with &lt;code&gt;method.values()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now when I call my function with an instance of the &lt;code&gt;Dog&lt;/code&gt; class, I get the
answer that I&#39;m expecting:&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; abby &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Abby&quot;&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 function&quot;&gt;countClassMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;abby&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 comment&quot;&gt;// 3&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Values: [&#39;constructor&#39;, &#39;pet&#39;, &#39;call&#39;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a pretty niche scenario, but I hope that is helpful for someone else.&lt;/p&gt;
&lt;p&gt;Happy counting!&lt;/p&gt;
</description>
      <pubDate>Mon, 21 Jun 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/count-class-methods-in-javascript/</guid>
    </item>
    <item>
      <title>A modest redesign for 2021</title>
      <link>https://seanmcp.com/articles/a-modest-redesign-for-2021/</link>
      <description>&lt;p&gt;It had been a minute since I last worked on the design for this site, so I
decided to make some changes for 2021. I didn&#39;t want to undertake anything major
– just tweak a few things to freshen everything up.&lt;/p&gt;
&lt;p&gt;A few of the changes are a return to previous designs: I brought the scroll
progress bar (top of the screen) and centered the content. I lightened the
default background color and reduced the size of the headings.&lt;/p&gt;
&lt;p&gt;There are two new features that I added, and the most notable of which is the
light/dark mode toggle in the header. Under the hood, this is a checkbox with a
&lt;code&gt;change&lt;/code&gt; event listener that persists state in &lt;code&gt;localStorage&lt;/code&gt;. The boxes are
100% CSS with &lt;code&gt;::before&lt;/code&gt; and &lt;code&gt;::after&lt;/code&gt; pseudo elements with a small transition.
Pretty neat what can be done with CSS and a teeny bit of JavaScript!&lt;/p&gt;
&lt;p&gt;The other new feature is a search listbox on &lt;a href=&quot;https://seanmcp.com/articles/&quot;&gt;/articles&lt;/a&gt;. Rather
than implement something super complex, I opted for a search &lt;code&gt;input&lt;/code&gt; with a
&lt;code&gt;datalist&lt;/code&gt; of articles. I added an event listener that redirects when an article
is selected. You can &lt;a href=&quot;https://seanmcp.com/js/article-search.js&quot;&gt;view the implementation&lt;/a&gt; in
&lt;code&gt;article-search.js&lt;/code&gt;. This is a pattern that I&#39;ve used twice now and might
warrant its own article.&lt;/p&gt;
&lt;p&gt;Beyond that, I updated some of the copy, changed the links in the header,
included a picture to the &lt;a href=&quot;https://seanmcp.com/about/&quot;&gt;/about&lt;/a&gt; page, and added a new page for my
&lt;a href=&quot;https://seanmcp.com/are-you-looking-for-work/&quot;&gt;work availability (hey recruiters!)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Overall, I&#39;m pretty happy with how the redesign turned out. We&#39;ll see how long
that contentedness lasts!&lt;/p&gt;
&lt;p&gt;Happy reading!&lt;/p&gt;
</description>
      <pubDate>Tue, 15 Jun 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-modest-redesign-for-2021/</guid>
    </item>
    <item>
      <title>Latin phrases to know</title>
      <link>https://seanmcp.com/articles/latin-phrases-to-know/</link>
      <description>&lt;p&gt;Moved to &lt;a href=&quot;https://seanmcp.com/resources/latin&quot;&gt;gardens&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Mon, 07 Jun 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/latin-phrases-to-know/</guid>
    </item>
    <item>
      <title>Handy git commands</title>
      <link>https://seanmcp.com/articles/handy-git-commands/</link>
      <description>&lt;p&gt;Here are some of the git commands that I find useful when doing by work as a
software engineer. Hopefully you will find them helpful too!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;commands&quot; tabindex=&quot;-1&quot;&gt;Commands&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/handy-git-commands/#commands&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Commands”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;git-checkout--&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;git checkout -&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/handy-git-commands/#git-checkout--&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “git checkout -”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Quick: Checkout the previous branch&lt;/p&gt;
&lt;p&gt;This is super handy when working with long or unwieldy branch names. With the
&lt;code&gt;-&lt;/code&gt; reference, you can convert the following set of commands into a reusable
alias:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout dev &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; pull &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout - &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; merge dev&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;git-checkout----.&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;git checkout -- .&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/handy-git-commands/#git-checkout----.&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “git checkout -- .”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Quick: Delete all changes in the current branch&lt;/p&gt;
&lt;p&gt;When you just want to start from scratch on the current branch, this command is
a huge timesaver. The &lt;code&gt;--&lt;/code&gt; refers to the current branch, while the &lt;code&gt;.&lt;/code&gt; refers to
all files. If you want to reset a particular file, you could swap the name with
the &lt;code&gt;.&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;git-commit--m-%22init%22---allow-empty&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;git commit -m &amp;quot;init&amp;quot; --allow-empty&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/handy-git-commands/#git-commit--m-%22init%22---allow-empty&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “git commit -m &amp;quot;init&amp;quot; --allow-empty”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Quick: Create a commit with no changes&lt;/p&gt;
&lt;p&gt;When starting on a development task, sometimes it&#39;s nice to set up branches and
PRs before getting started on the work. The &lt;code&gt;--allow-empty&lt;/code&gt; flag enables you to
create commits without changing any files.&lt;/p&gt;
&lt;p&gt;I use this when I have a feature branch that will be the target of bunch of
smaller branches. Initializing a those with an empty commit helps be get
organized in the beginning.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;git-diff-branch_name--s-%22todo%3A%22&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;git diff branch_name -S &amp;quot;TODO:&amp;quot;&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/handy-git-commands/#git-diff-branch_name--s-%22todo%3A%22&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “git diff branch_name -S &amp;quot;TODO:&amp;quot;”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Quick: Search the diff between the current branch and &lt;code&gt;branch_name&lt;/code&gt; for all
strings matching &lt;code&gt;TODO:&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;When working on a new feature, I like to leave myself todos to address before
the branch is merged. Tracking these down can be tricky because there are other
&lt;code&gt;TODO:&lt;/code&gt;s in the codebase. I used to &lt;code&gt;grep&lt;/code&gt; through the diff, but git&#39;s &lt;code&gt;-S&lt;/code&gt; flag
is much easier.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;git-diff-branch_name--g-%22%2F*-action%22&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;git diff branch_name -G &amp;quot;/*-action&amp;quot;&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/handy-git-commands/#git-diff-branch_name--g-%22%2F*-action%22&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “git diff branch_name -G &amp;quot;/*-action&amp;quot;”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Quick: Search the diff between the current branch and &lt;code&gt;branch_name&lt;/code&gt; for all
strings matching the &lt;code&gt;/*-action&lt;/code&gt; RegEx pattern&lt;/p&gt;
&lt;p&gt;Similar to the &lt;code&gt;-S&lt;/code&gt; flag, &lt;code&gt;-G&lt;/code&gt; lets you search through the diff using RegEx. I
don&#39;t know too much about regular expressions, but it&#39;s nice to have the option
in your toolbelt.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;git-log---before%3D%222021-01-01-00%3A00%22&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;git log --before=&amp;quot;2021-01-01 00:00&amp;quot;&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/handy-git-commands/#git-log---before%3D%222021-01-01-00%3A00%22&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “git log --before=&amp;quot;2021-01-01 00:00&amp;quot;”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Quick: Limit the log output to all commits before the provided date.&lt;/p&gt;
&lt;p&gt;This is handy when you want to look at commits from a particular date or before
a date (and/or after with &lt;code&gt;--after=&lt;/code&gt;).&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Let me know if you have &lt;a href=&quot;https://seanmcp.com/articles/handy-git-commands/#comment-link&quot;&gt;any commands to add to the list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy versioning!&lt;/p&gt;
</description>
      <pubDate>Tue, 04 May 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/handy-git-commands/</guid>
    </item>
    <item>
      <title>Read URL search parameters with JavaScript</title>
      <link>https://seanmcp.com/articles/read-url-search-parameters-with-javascript/</link>
      <description>&lt;div id=&quot;params-message&quot; hidden=&quot;&quot;&gt;
    &lt;h2&gt;Hello search params!&lt;/h2&gt;
    &lt;p&gt;The following parameters were detected on this page:&lt;/p&gt;
    &lt;ul id=&quot;params-list&quot;&gt;&lt;/ul&gt;
    &lt;p&gt;To learn how I did this, read on or inspect the source code for this page.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Search parameters, or query strings, are handy ways to store data on URLs.
JavaScript provides an interface for reading search parameters called
&lt;code&gt;URLSearchParams&lt;/code&gt;. To use it, create an instance with the &lt;code&gt;new&lt;/code&gt; keyword and pass
your search parameter string as an argument:&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;let&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;?name=Sam&amp;amp;name=Ezra&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; usp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&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;The returned &lt;code&gt;URLSearchParams&lt;/code&gt; object has a few
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams#methods&quot;&gt;a bunch of useful methods&lt;/a&gt;,
but we&#39;re going to focus on three:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get()&lt;/code&gt;: Returns the first value for a particular key&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getAll()&lt;/code&gt;: Returns all of the values for a particular key&lt;/li&gt;
&lt;li&gt;&lt;code&gt;forEach()&lt;/code&gt;: Iterates over keys with a callback that receives &lt;code&gt;value&lt;/code&gt; and
&lt;code&gt;key&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If I call the &lt;code&gt;get()&lt;/code&gt; method for &lt;code&gt;name&lt;/code&gt;, then it will return the first value
from my search parameter string:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;usp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&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 comment&quot;&gt;// &#39;Sam&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are dealing with unique parameters, this method is all you need to get
the job done. But in our example, there are multiple names being passed. To
retrieve all of those values, we will call &lt;code&gt;getAll()&lt;/code&gt; with &lt;code&gt;name&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;usp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&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 comment&quot;&gt;// [&#39;Sam&#39;, &#39;Ezra&#39;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are working with a few exact keys, you can probably stop there. But if
you are dealing with a lot of keys or just want an easier way to read search
parameters, you can use a helper function that iterates over all of the keys and
stores them in an object:&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;/**
 * Read the search parameters from window location
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readSearchParams&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 keyword&quot;&gt;let&lt;/span&gt; usp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;search&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 keyword&quot;&gt;let&lt;/span&gt; params &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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  usp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&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;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&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 class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&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;
      params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&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 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;
    params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&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 function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; params&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;Then call the function whenever you need to reference the current location&#39;s
search parameters:&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;// https://seanmcp.com/articles/read-url-search-parameters-with-javascript/&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ?greeting=Hello&amp;amp;name=World&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readSearchParams&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;

params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;greeting&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// [&#39;Hello&#39;]&lt;/span&gt;
params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//[&#39;World&#39;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To see it in action, try going to &lt;a href=&quot;https://seanmcp.com/articles/read-url-search-parameters-with-javascript/&quot;&gt;that url&lt;/a&gt;, or
try adding your own search parameters.&lt;/p&gt;
&lt;p&gt;Happy reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 01 May 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/read-url-search-parameters-with-javascript/</guid>
    </item>
    <item>
      <title>Fix &#39;downloadable font: rejected by sanitizer&#39; error in Firefox</title>
      <link>https://seanmcp.com/articles/fix-downloadable-font-rejected-by-sanitizer-error-in-firefox/</link>
      <description>&lt;p&gt;I ran into an issue today when I tried to serve a typeface I downloaded from
Google Fonts. When I added the files and &lt;code&gt;@font-face&lt;/code&gt; rules to my CSS, I got the
following error in the Firefox console:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;downloadable font: rejected by sanitizer
    (font-family: &amp;quot;Inter&amp;quot; style:normal weight:400 stretch:100 src index:0)
    source: http://localhost:8080/fonts/Inter-Regular.ttf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Chrome console had nothing, so I figured this was a Firefox issue. After
some searching around, I learned that
&lt;a href=&quot;https://support.mozilla.org/en-US/questions/913498&quot;&gt;Firefox is checking&lt;/a&gt; that a
font file is valid before loading it, which can protect you against some
attacks.&lt;/p&gt;
&lt;p&gt;Inter is a pretty popular typeface, so I ruled out corrupt or malicious font
files. Further digging lead me to some solutions, but
&lt;a href=&quot;https://github.com/FortAwesome/Font-Awesome/issues/8078&quot;&gt;they seemed&lt;/a&gt;
&lt;a href=&quot;https://stackoverflow.com/questions/57835543/how-to-fix-downloadable-font-rejected-by-sanitizer&quot;&gt;situation specific&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was prepared to live with the error, when I gave it one last look. The source
url (which I&#39;ve wrapped here but was broken on two lines in the console) was
incorrect. The path to the font files should have been
&lt;code&gt;assets/fonts/Inter-Regular.ttf&lt;/code&gt;. Fixing that path in &lt;code&gt;@font-face&lt;/code&gt;&#39;s &lt;code&gt;src&lt;/code&gt;
resolved the issue.&lt;/p&gt;
&lt;p&gt;If you encounter a similar error, check to make sure that the path to your font
file is correct. It would be helpful if the message was more descriptive:
&amp;quot;downloadable font: rejected by sanitizer (file not found)&amp;quot;.&lt;/p&gt;
&lt;p&gt;Happy font serving!&lt;/p&gt;
</description>
      <pubDate>Thu, 01 Apr 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-downloadable-font-rejected-by-sanitizer-error-in-firefox/</guid>
    </item>
    <item>
      <title>Fix Netlify CMS YAML error &#39;Implicit map keys need to be on a single line&#39;</title>
      <link>https://seanmcp.com/articles/fix-netlify-cms-yaml-error-implicit-map-keys-need-to-be-on-a-single-line/</link>
      <description>&lt;p&gt;When trying to setup Netlify CMS on a project, I kept getting this error on the
&lt;code&gt;/admin&lt;/code&gt; page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Error loading the CMS configuration
Config Errors:

YAMLSemanticError: Implicit map keys need to be on a single line at line 1, column 1:

&amp;lt;!DOCTYPE html&amp;gt;
^^^^^^^^^^^^^^^…

Check your config.yml file.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I checked and doubled checked the YAML in my &lt;code&gt;config.yml&lt;/code&gt; file, and everything
was valid. I went through the Netlify Identity steps to verify that they were
followed correctly, but that didn&#39;t help either.&lt;/p&gt;
&lt;p&gt;Googling the error message brought up nothing useful.&lt;/p&gt;
&lt;p&gt;Then it occurred to me: &amp;quot;What if Netlify CMS isn&#39;t getting the config file at
all?&amp;quot;&lt;/p&gt;
&lt;p&gt;Sure enough, my static-site generator was skipping the unrecognized &lt;code&gt;.yml&lt;/code&gt; file
when building the site. I added the configuration to copy that file to the build
folder, and &lt;em&gt;voilà&lt;/em&gt;: everything works as expected.&lt;/p&gt;
&lt;p&gt;So if you are getting the same error, check to make sure that the &lt;code&gt;config.yml&lt;/code&gt;
file is making it to your built &lt;code&gt;admin/&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Happy debugging!&lt;/p&gt;
</description>
      <pubDate>Thu, 25 Mar 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-netlify-cms-yaml-error-implicit-map-keys-need-to-be-on-a-single-line/</guid>
    </item>
    <item>
      <title>COVID-19 journal</title>
      <link>https://seanmcp.com/articles/covid-19-journal/</link>
      <description>&lt;p&gt;I tested positive for COVID-19 in November of 2020, a few weeks after the birth
of my second son. My wife was the first in our household to show symptoms, and I
followed a few days later.&lt;/p&gt;
&lt;p&gt;Most of my symptoms were mild. I had full-body aches and congestion but no fever
nor loss of taste. The most dramatic symptom was fatigue that came in waves for
the duration of my illness. I would go from feeling mostly fine, to feeling like
I didn&#39;t have the energy to situp, then back to mostly fine in the span of
thirty minutes. I&#39;m thankful that I was on parental leave for the whole duration
of my illness, because I would have been unable to work effectively through the
waves of fatigue.&lt;/p&gt;
&lt;p&gt;Once I recovered, that seemed to be the end of it. I warned by coworkers to take
the virus seriously (they already were) and tried to be especially careful
during the holidays.&lt;/p&gt;
&lt;p&gt;Fast-forward to March of 2021, and now a new symptom has emerged. There are now
certain scents that have changed for the worse: bananas, toothpaste, and
shampoo. They all share the same smell, and it is quite unpleasant. A kind of
savory/overly-ripe odor that I am keen to avoid. I used to eat a banana a day
(or more), and now I can&#39;t stand to be too near to them.&lt;/p&gt;
&lt;p&gt;I have heard that some people report a return to normal taste and smell after
being vaccinated. Hopefully that is true, because I&#39;d really like to enjoy
eating bananas and brushing my teeth again.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In April of 2021, I got the first round of the Moderna COVID-19 vaccine. By the
following morning, I experienced some typical &amp;quot;flu-like&amp;quot; symptoms: fatigue,
full-body soreness, and skin sensitivity. The injection site – my left shoulder
– was especially sore. By day two, most of the symptoms had resided.&lt;/p&gt;
&lt;p&gt;Bananas, shampoo, and toothpaste still smell odd but to a lesser degree. I&#39;m
optimistic that that will improve in the coming weeks! The second round of
vaccination is scheduled for early May – four weeks after the first round. By
then, Lord willing, I&#39;ll be back to a banana a day!&lt;/p&gt;
</description>
      <pubDate>Wed, 24 Mar 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/covid-19-journal/</guid>
    </item>
    <item>
      <title>Copy the last git commit hash on macOS</title>
      <link>https://seanmcp.com/articles/copy-the-last-git-commit-hash-on-macos/</link>
      <description>&lt;p&gt;When responding to code-review requests, I like to point the reviewer to the
specific commit where the issue was addressed with a message like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fixed in 4908c91a06258814077b1358210eb78cc718609c&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;GitHub will automatically detect the hash, shorten it, and convert it into a
link to the commit view.&lt;/p&gt;
&lt;p&gt;Rather than going into &lt;code&gt;git log&lt;/code&gt; and copying the hash manually, I use the
following one-liner in the terminal on macOS:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-parse HEAD &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pbcopy&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first half of the command returns the last commit hash from
&lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Internals-Git-References#ref_the_ref&quot;&gt;&lt;code&gt;HEAD&lt;/code&gt;&lt;/a&gt;,
which is a reference to the current branch. You could use
&lt;code&gt;git rev-parse name_of_branch&lt;/code&gt; too, but that is often longer to type.&lt;/p&gt;
&lt;p&gt;The output of &lt;code&gt;git rev-parse&lt;/code&gt; is then piped to
&lt;a href=&quot;https://ss64.com/osx/pbcopy.html&quot;&gt;&lt;code&gt;pbcopy&lt;/code&gt;&lt;/a&gt;, the clipboard utility for macOS.
If you were using a different platform like Unix or Windows, there are
&lt;a href=&quot;https://stackoverflow.com/questions/749544/pipe-to-from-the-clipboard-in-bash-script&quot;&gt;other options for you&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To save some more time, I added an alias my shell&#39;s rc file:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# git last commit (hash)&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;glc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;git rev-parse HEAD | pbcopy&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Happy copying!&lt;/p&gt;
</description>
      <pubDate>Wed, 24 Mar 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/copy-the-last-git-commit-hash-on-macos/</guid>
    </item>
    <item>
      <title>Add Heroicons to an Eleventy site</title>
      <link>https://seanmcp.com/articles/add-heroicons-to-an-eleventy-site/</link>
      <description>&lt;p&gt;In a recent &lt;a href=&quot;https://11ty.dev&quot;&gt;Eleventy&lt;/a&gt; project, I wanted an easy way to add
&lt;a href=&quot;https://heroicons.com&quot;&gt;Heroicons&lt;/a&gt;. That lead me to creating an Eleventy plugin
that adds some handy Heroicon shortcodes. Here&#39;s how to get started:&lt;/p&gt;
&lt;p&gt;First, install
&lt;a href=&quot;https://npm.im/eleventy-plugin-heroicons&quot;&gt;&lt;code&gt;eleventy-plugin-heroicons&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i &lt;span class=&quot;token parameter variable&quot;&gt;-D&lt;/span&gt; eleventy-plugin-heroicons&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add the plugin to your &lt;code&gt;.eleventy.js&lt;/code&gt; settings file with any other
plugins:&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;// .eleventy.js&lt;/span&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;
  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;&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;&quot;eleventy-plugin-heroicons&quot;&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that in place, you are ready to use the &lt;code&gt;heroicon&lt;/code&gt; shortcode. Head over to
a markdown or template file and try it out:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- contact.md --&gt;&lt;/span&gt;

&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; {% heroicon &quot;outline&quot;, &quot;mail&quot; %} Send me an email&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: These examples are using Nunjucks syntax. If you are using Liquid, you
can reference the official documentation for examples.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;heroicon&lt;/code&gt; shortcode takes a few arguments:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The style of icon (outline or solid)&lt;/li&gt;
&lt;li&gt;The name of the icon&lt;/li&gt;
&lt;li&gt;An optional alt tag for the SVG&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To save yourself a bit of time, you can use the &lt;code&gt;heroicon_outline&lt;/code&gt; or
&lt;code&gt;heroicon_solid&lt;/code&gt; shortcodes which pass the style automatically. We could replace
the previous example with:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- contact.md --&gt;&lt;/span&gt;

&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; {% heroicon_outline &quot;mail&quot; %} Send me an email&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it would work the same.&lt;/p&gt;
&lt;p&gt;If you use the shortcode without alt text, and &lt;code&gt;aria-hidden=&amp;quot;true&amp;quot;&lt;/code&gt; attribute is
applied to the &lt;code&gt;svg&lt;/code&gt; element. When alt text is provided, a &lt;code&gt;title&lt;/code&gt; element is
added as the first child to the &lt;code&gt;svg&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Input --&gt;&lt;/span&gt;
{% heroicon_outline &quot;heart&quot;, &quot;Love&quot; %}

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Output --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://www.w3.org/2000/svg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;none&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;viewBox&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0 0 24 24&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;stroke&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;currentColor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;data-heroicon-name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;heart&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;data-heroicon-style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;outline&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Love&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;path&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;stroke-linecap&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;round&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;stroke-linejoin&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;round&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;stroke-width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will enable screen-reader and other assistive technology users to
understand what the icon represents. Without an alt tag, you&#39;ll want to make
sure that the meaning can be derived from context:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  {% heroicon_solid &quot;plus&quot; %}
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add item&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  {% heroicon_solid &quot;x&quot; %}
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;visually-hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Close menu&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of the icons have &lt;code&gt;data-heroicon-name&lt;/code&gt; and &lt;code&gt;data-heroicon-style&lt;/code&gt; attributes
applied to the &lt;code&gt;svg&lt;/code&gt; that you can use for styling:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;[data-heroicon-style=&quot;outline&quot;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 24px&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 selector&quot;&gt;[data-heroicon-style=&quot;solid&quot;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20px&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 selector&quot;&gt;[data-heroicon-name]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px&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;If you&#39;d prefer to use a class name, you can pass one on a configuration object
while adding the plugin:&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;// .eleventy.js&lt;/span&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;
  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;&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;&quot;eleventy-plugin-heroicons&quot;&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 literal-property property&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;icon&quot;&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 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;There is also
&lt;a href=&quot;https://github.com/SeanMcP/eleventy-plugin-heroicons#configuration&quot;&gt;an option to &lt;code&gt;errorOnMissing&lt;/code&gt; icons&lt;/a&gt;,
which might be nice to ensure that all icons display as intended.&lt;/p&gt;
&lt;p&gt;Hopefully that helps! Let me know if you have any suggestions for the package.
&lt;a href=&quot;https://github.com/SeanMcP/eleventy-plugin-heroicons/pulls&quot;&gt;PRs are most welcome &lt;e-moji label=&quot;smiles&quot;&gt;😊&lt;/e-moji&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Tue, 09 Mar 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/add-heroicons-to-an-eleventy-site/</guid>
    </item>
    <item>
      <title>Create a URL shortener with Netlify</title>
      <link>https://seanmcp.com/articles/create-a-url-shortener-with-netlify/</link>
      <description>&lt;p&gt;After hearing of a few people doing the same, I set up a mostly-free URL
shortener with Netlify: &lt;a href=&quot;https://smcp.dev&quot;&gt;smcp.dev&lt;/a&gt;. Here&#39;s the steps I
followed to get it done.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;1.-register-a-domain&quot; tabindex=&quot;-1&quot;&gt;1. Register a domain&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-url-shortener-with-netlify/#1.-register-a-domain&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “1. Register a domain”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Go to your favorite registrar and buy a new domain. Aim for something short and
memorable, and consider the &lt;code&gt;.link&lt;/code&gt; TLD. I heard a recommendation for
&lt;a href=&quot;https://hover.com&quot;&gt;Hover&lt;/a&gt;, which seems pretty cool, but I went with Google
Domains for smcp.dev.&lt;/p&gt;
&lt;p&gt;This is the only step that costs money, and the amount will depend on the
domain/TLD that you choose. &lt;code&gt;.dev&lt;/code&gt; TLDs on Google Domains cost $12/year.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;2.-create-a-repo&quot; tabindex=&quot;-1&quot;&gt;2. Create a repo&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-url-shortener-with-netlify/#2.-create-a-repo&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “2. Create a repo”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I modelled &lt;a href=&quot;https://github.com/seanmcp/smcp.dev&quot;&gt;my repo&lt;/a&gt; off of
&lt;a href=&quot;https://github.com/cassidoo/cass.run&quot;&gt;&lt;code&gt;cassidoo/cass.run&lt;/code&gt;&lt;/a&gt;, which seems to be
based off of
&lt;a href=&quot;https://github.com/kentcdodds/netlify-shortener-example&quot;&gt;kentcdodds/netlify-shortener-example&lt;/a&gt;.
In order of importance, you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;_redirect&lt;/code&gt; file that maps short urls to destinations,&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;package.json&lt;/code&gt; with a single (optional) dependency for adding links,&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;README.md&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;and some git stuff.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;3.-add-your-project-to-netlify&quot; tabindex=&quot;-1&quot;&gt;3. Add your project to Netlify&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-url-shortener-with-netlify/#3.-add-your-project-to-netlify&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “3. Add your project to Netlify”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Login to Netlify (or create an account) and add a new project from git. Select
your repository, and enter the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Build command&lt;/strong&gt;: Nothing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Publish directory&lt;/strong&gt;: Nothing&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;4.-setup-your-custom-domain&quot; tabindex=&quot;-1&quot;&gt;4. Setup your custom domain&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-url-shortener-with-netlify/#4.-setup-your-custom-domain&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “4. Setup your custom domain”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In the main project view on Netlify, select &amp;quot;&lt;e-moji&gt;⚙️&lt;/e-moji&gt; Domain
settings&amp;quot;, then &amp;quot;Add custom domain&amp;quot;. Enter the domain that you registered in
step one and go through the flow.&lt;/p&gt;
&lt;p&gt;You&#39;ll need to update the DNS settings with your registrar to point to Netlify&#39;s
servers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dns1.p05.nsone.net
dns2.p05.nsone.net
dns3.p05.nsone.net
dns4.p05.nsone.net
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;5.-wait&quot; tabindex=&quot;-1&quot;&gt;5. Wait&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-url-shortener-with-netlify/#5.-wait&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “5. Wait”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Depending on how quickly you went through steps 1-4, you may have to wait a few
hours for everything to get setup from registrar and the DNS. For me, this took
about thirty minutes, but your mileage may vary.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;6.-enjoy&quot; tabindex=&quot;-1&quot;&gt;6. Enjoy&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-url-shortener-with-netlify/#6.-enjoy&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “6. Enjoy”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;With all that in place, you should be able to use your URL shortener:
&lt;a href=&quot;https://smcp.dev/yt&quot;&gt;smcp.dev/yt&lt;/a&gt;! To add a new URL, you can add a new line to
the &lt;code&gt;_redirect&lt;/code&gt; file manually or use the
&lt;a href=&quot;https://github.com/kentcdodds/netlify-shortener#usage&quot;&gt;&lt;code&gt;shorten&lt;/code&gt; script&lt;/a&gt;(if you
added it)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Happy shortening!&lt;/p&gt;
</description>
      <pubDate>Fri, 26 Feb 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/create-a-url-shortener-with-netlify/</guid>
    </item>
    <item>
      <title>Set innerHTML of an element in Svelte</title>
      <link>https://seanmcp.com/articles/set-innerhtml-of-an-element-in-svelte/</link>
      <description>&lt;p&gt;When working on a Svelte application, I wanted to replace newline characters in
a string with &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; elements and render the result as HTML. If you do the
replace in your component markup, then the rendered output has escaped angle
brackets:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Component.svelte --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{notes.replace(/&#92;n/g, &#39;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&#39;)}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Output markup --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Line one&lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;lt;&quot;&gt;&amp;amp;lt;&lt;/span&gt;br&lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;gt;&quot;&gt;&amp;amp;gt;&lt;/span&gt;Line two&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From previous experience, I know that we&#39;re not rendering the replaced string as
HTML, so I tried searching online for a solution involving &lt;code&gt;innerHTML&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the Svelte API docs, there are only three mentions of &lt;code&gt;innerHTML&lt;/code&gt;, two of
which refer to bindings with &lt;code&gt;contenteditable&lt;/code&gt; elements. But I wanted to set the
&lt;code&gt;innerHTML&lt;/code&gt; of an element that would never change.&lt;/p&gt;
&lt;p&gt;While searching through the Svelte Discord for &lt;code&gt;innerHTML&lt;/code&gt;, I came across an
unrelated post that mentioned &lt;code&gt;{@html}&lt;/code&gt;. I had never encountered this before, so
I looked it up in the Svelte docs&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/set-innerhtml-of-an-element-in-svelte/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In a text expression, characters like &lt;code&gt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; are escaped; however, with
HTML expressions, they&#39;re not.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not the longest explanation, but that sounds like my problem. Back in my code, I
tried wrapping the replaced text with &lt;code&gt;{@html}&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Component.svelte --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{@html notes.replace(/&#92;n/g, &#39;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&#39;)}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, everything rendered as expected:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Output markup --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Line one&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  Line two
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So next time you want to set the &lt;code&gt;innerHTML&lt;/code&gt; of an element in Svelte, use an
HTML express with &lt;code&gt;{@html}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Happy escaping!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://svelte.dev/docs#html&quot;&gt;https://svelte.dev/docs#html&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/set-innerhtml-of-an-element-in-svelte/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Thu, 25 Feb 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/set-innerhtml-of-an-element-in-svelte/</guid>
    </item>
    <item>
      <title>Let&#39;s stop skinning cats</title>
      <link>https://seanmcp.com/articles/lets-stop-skinning-cats/</link>
      <description>&lt;p&gt;Idioms are interesting. They&#39;re phrases that have a functional meaning that is
independent of their literal meaning. In English, we have idioms like &amp;quot;beating
around the bush,&amp;quot; which means to avoid saying what you really mean because it&#39;s
uncomfortable &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/lets-stop-skinning-cats/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;When I was teaching a classroom of first- and second-generation Americans, we
explicitly taught idioms so that students would understand the form if not
remember examples when reading or speaking with others. I had an illustrated
poster on the wall with some common US idioms.&lt;/p&gt;
&lt;p&gt;Someone could certainly go on a crusade to rid a language of idioms. &amp;quot;Why say
something purposely incorrect when you really mean something else?&amp;quot; they could
argue. I&#39;m not going to go that far; I think learning of
&lt;a href=&quot;https://www.thatsanegg.com/about&quot;&gt;idioms in other languages in kind of neat&lt;/a&gt;.
That said, there is one English idiom that I would like to remove:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is more than one way to skin a cat. &lt;e-moji&gt;😿&lt;/e-moji&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you are unfamiliar, the idiom means that there are multiple ways to complete
a particular task. Since that is an axiom of sorts in programming,
&lt;a href=&quot;https://duckduckgo.com/?q=site%3Astackoverflow.com+%22more+than+one+way+to+skin+a+cat%22&quot;&gt;I see the idiom used relatively frequently&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Where it differs from an idiom like &amp;quot;barking up the wrong tree,&amp;quot; is that the
literal meaning of &amp;quot;skinning a cat&amp;quot; is so odd. Sure, you &lt;em&gt;can&lt;/em&gt; skin a cat. But
who does that?&lt;/p&gt;
&lt;p&gt;I am not very old, nor have I had a vast set of life experiences. But I have
never once needed to, seen, or heard of anyone skinning a cat. I don&#39;t know of
any uses for cat fur, so skinning one seems unnecessary. Also, many people like
cats and a phrase that involves removing their skin is a little insensitive.&lt;/p&gt;
&lt;p&gt;I propose replacing this idiom with another:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is more than one way to bake a cake. &lt;e-moji&gt;🍰&lt;/e-moji&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This works on multiple levels. One, cakes are fun. Two, most people have heard
of baking cakes and may have baked one themselves. And three, it doesn&#39;t involve
skinning a beloved animal. And four,
&lt;a href=&quot;https://www.allrecipes.com/search/results/?sort=re&amp;amp;wt=cake&quot;&gt;there &lt;em&gt;is&lt;/em&gt; more than one way to make a cake&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;At the end of the day, I don&#39;t care all that much. But I have started using
&amp;quot;there is more than one way to bake a cake&amp;quot; in personal and professional
conversations, and I haven&#39;t (to my knowledge) been misunderstood. So why not?
Language is what we make of it, so let&#39;s make English a little more fun.&lt;/p&gt;
&lt;p&gt;Happy baking!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I&#39;ve made my way to this website on a number of occasions, and I think it&#39;s
neat: &lt;a href=&quot;https://www.ef.com/ca/english-resources/english-idioms/&quot;&gt;https://www.ef.com/ca/english-resources/english-idioms/&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/lets-stop-skinning-cats/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Thu, 18 Feb 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/lets-stop-skinning-cats/</guid>
    </item>
    <item>
      <title>Search all files for two strings</title>
      <link>https://seanmcp.com/articles/search-all-files-for-two-strings/</link>
      <description>&lt;p&gt;To search the current directory for all files that contain two independent
strings, you can use the following terminal command:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lr&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;common&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lr&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rare&quot;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In essence, this is saying &amp;quot;Recursively find all the files in the current
directory with the string &lt;code&gt;rare&lt;/code&gt;, and then look through those files to find any
with the string &lt;code&gt;common&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Depending on the directory, you may want to limit the number of directories that
&lt;code&gt;grep&lt;/code&gt; has to inspect. For me, that often includes directories like
&lt;code&gt;node_modules/&lt;/code&gt; and &lt;code&gt;cache&lt;/code&gt;. Here&#39;s the same command with a few excluded
directories:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lr&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;common&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lr&lt;/span&gt; --exclude-dir&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;node_modules --exclude-dir&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;.git --exclude-dir&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;build --exclude-dir&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cache &lt;span class=&quot;token string&quot;&gt;&quot;rare&quot;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The order of the strings does matter, but putting the less commonly occurring
string second is a little more efficient.&lt;/p&gt;
&lt;p&gt;Happy searching!&lt;/p&gt;
</description>
      <pubDate>Mon, 15 Feb 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/search-all-files-for-two-strings/</guid>
    </item>
    <item>
      <title>Setup Netlify CMS with Eleventy</title>
      <link>https://seanmcp.com/articles/setup-netlify-cms-with-eleventy/</link>
      <description>&lt;p&gt;Netlify&#39;s guide for
&lt;a href=&quot;https://www.netlifycms.org/docs/add-to-your-site/&quot;&gt;adding Netlify CMS to your site&lt;/a&gt;
was really helpful. Consider this a supplemental guide to getting your Eleventy
site up and running with Netlify CMS.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Your posts will probably have tags, or a list of strings for organizing content.
Netlify CMS has a
&lt;a href=&quot;https://www.netlifycms.org/docs/widgets/#list&quot;&gt;list widget type&lt;/a&gt; that will work
nicely:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# admin/config.yml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;collections&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 key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;blog&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;fields&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 key atrule&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tags&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;list&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want editors to select from a specific set of options, the
&lt;a href=&quot;https://www.netlifycms.org/docs/widgets/#select&quot;&gt;select widget type&lt;/a&gt; should
work for you.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Eleventy will ignore Netlify CMS&#39; &lt;code&gt;config.yml&lt;/code&gt; when building your site, so
you&#39;ll need to tell it to copy the file into your build directory:&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;// .eleventy.js&lt;/span&gt;
config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addPassthroughCopy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/admin/config.yml&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The actual path may vary based on your project structure.&lt;/p&gt;
</description>
      <pubDate>Mon, 08 Feb 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/setup-netlify-cms-with-eleventy/</guid>
    </item>
    <item>
      <title>Fix &quot;Error: Could not resolve pagination key in template data&quot; in Eleventy</title>
      <link>https://seanmcp.com/articles/fix-error-could-not-resolve-pagination-key-in-template-data-in-eleventy/</link>
      <description>&lt;p&gt;In a recent Eleventy project, I wanted to create a single collection for tags
with two keys: &lt;code&gt;alphabetical&lt;/code&gt; for an array of all tags, and &lt;code&gt;popular&lt;/code&gt; for an
array of arrays with the tag and number of times it was used.&lt;/p&gt;
&lt;p&gt;To access either &amp;quot;sub-collection&amp;quot;, I could dive into the object like
&lt;code&gt;collections.tagList.alphabetical&lt;/code&gt;. This worked like a dream in my template and
markdown files, and I thought maybe I was on to a new cool pattern for Eleventy.&lt;/p&gt;
&lt;p&gt;But when I tried to paginate the sub-collection, everything fell apart. Eleventy
errored with the following message:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;Problem writing Eleventy templates: &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;more &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; DEBUG output&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Could not resolve pagination key &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; template data: collections.tagList.alphabetical

&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;Error&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt; was thrown:
    Error: Could not resolve pagination key &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; template data: collections.tagList.alphabetical&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pagination code in question looked like:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;pagination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; collections.tagList.alphabetical
  &lt;span class=&quot;token key atrule&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tag
&lt;span class=&quot;token key atrule&quot;&gt;permalink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /tag//&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;Interesting. Maybe the collection hasn&#39;t been created by the time the pagination
code runs, I guessed. To test this, I tried paginating &lt;code&gt;collections.tagList&lt;/code&gt; to
see if the &lt;code&gt;alphabetical&lt;/code&gt; key was present:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;pagination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; collections.tagList
  &lt;span class=&quot;token key atrule&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tag
&lt;span class=&quot;token key atrule&quot;&gt;permalink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /tag//&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;This created pages for &lt;code&gt;alphabetical&lt;/code&gt; and &lt;code&gt;popular&lt;/code&gt;, which proved my hypothesis
wrong: the alphabetical data does exist when the pagination occurs.&lt;/p&gt;
&lt;p&gt;Maybe it had something to do with paginating nested objects? To confirm, I added
a test object to the frontmatter and tried to paginate it:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;pagination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; test.test.test
  &lt;span class=&quot;token key atrule&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tag
&lt;span class=&quot;token key atrule&quot;&gt;permalink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /tag//
&lt;span class=&quot;token key atrule&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;test&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 string&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&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;This worked as expected, creating pages for a, b, and c. So it isn&#39;t an issue
with paginating deep objects.&lt;/p&gt;
&lt;p&gt;With two incorrect hypotheses, it was time to look for outside help. I
&lt;a href=&quot;https://discord.com/channels/741017160297611315/741017160297611319/807589091814146060&quot;&gt;posted in 11ty Discord&lt;/a&gt;
and did some searching. My initial queries brought up two issues:
&lt;a href=&quot;https://github.com/11ty/eleventy/issues/171&quot;&gt;an actual bug from 2018&lt;/a&gt; and
&lt;a href=&quot;https://github.com/11ty/eleventy/issues/690&quot;&gt;some invalid JSON from 2019&lt;/a&gt;.
Neither of which provided the solution here.&lt;/p&gt;
&lt;p&gt;Feeling at a dead end, I decided to rethink the problem. I couldn&#39;t paginate
over a nested collection object, but normal collections work just fine. If I
could flatten my clever &lt;code&gt;tagsList&lt;/code&gt; into multiple collections, the problem would
be solved.&lt;/p&gt;
&lt;p&gt;Instead of &lt;code&gt;collections.tagList.alphabetical&lt;/code&gt; and &lt;code&gt;collections.tagList.popular&lt;/code&gt;,
I created two collections: &lt;code&gt;collections.tagsAll&lt;/code&gt; and &lt;code&gt;collections.tagsPopular&lt;/code&gt;.
This required an addition iteration over the post data (one for each
collection), but that small increase in build time isn&#39;t an issue for this
project.&lt;/p&gt;
&lt;p&gt;The final code to fix the &amp;quot;Error: could not resolve pagination key in template
data&amp;quot; is a flattened collection:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;pagination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; collections.tagsAll
  &lt;span class=&quot;token key atrule&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tag
&lt;span class=&quot;token key atrule&quot;&gt;permalink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /tag//&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;This works well, but I would still love to find a way to get the original
solution to work. Let me know if you have a better way to solve this problem.
&lt;e-moji&gt;👍&lt;/e-moji&gt;&lt;/p&gt;
&lt;p&gt;Happy paginating!&lt;/p&gt;
</description>
      <pubDate>Sat, 06 Feb 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-error-could-not-resolve-pagination-key-in-template-data-in-eleventy/</guid>
    </item>
    <item>
      <title>Geddes&#39; Guide to Crafting</title>
      <link>https://seanmcp.com/articles/dave-geddes-guide-to-crafting/</link>
      <description>&lt;p&gt;I spent some time this week enjoying
&lt;a href=&quot;https://mastery.games&quot;&gt;Dave Geddes&#39; content&lt;/a&gt;. Three resources in particular
have left me with a lot to think about, and I&#39;m combining them into what I call
&amp;quot;Geddes&#39; Guide to Crafting&amp;quot;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mastery.games/post/practice-css/&quot;&gt;&lt;em&gt;Practice CSS Like an Artist&lt;/em&gt; (article)&lt;/a&gt;:
Geddes&#39; draws comparisons between designing an application and painting a
portrait. First you start with a rough sketch, then filling with broad
strokes. Only once you have the foundation set do you add fine details.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mastery.games/post/polish/&quot;&gt;&lt;em&gt;When Should You Polish Your UI?&lt;/em&gt; (article)&lt;/a&gt;:
Geddes reflects of a few strategies for polishing the design for your
application. Following the previous theme, he recommends a rough sketch with
broad strokes (&lt;em&gt;i.e&lt;/em&gt; prototyping) before polishing with fine details.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=7zBpd3wWsnU&quot;&gt;&lt;em&gt;Build the zelda UI with Web Tech&lt;/em&gt; (video)&lt;/a&gt;:
Geddes completes the first phase of recreating Zelda: Breath of the Wild&#39;s
menu screen in HTML and CSS. Here you can see rough sketching and broad
strokes in practice. The video alludes to a part two, but it was never posted
on YouTube.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a developer, it can be tempting to spend too much time worrying about the
fine details of what you are developing. But the best of products were not
created perfect on the first try. They come from an on-going process of
sketching, filling, and eventually polish.&lt;/p&gt;
&lt;p&gt;So don&#39;t sweat the fine details in the beginning. Create a something that works,
and then fill in those details later.&lt;/p&gt;
&lt;p&gt;Happy crafting!&lt;/p&gt;
</description>
      <pubDate>Fri, 29 Jan 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/dave-geddes-guide-to-crafting/</guid>
    </item>
    <item>
      <title>Send data to the window with Eleventy</title>
      <link>https://seanmcp.com/articles/send-data-to-the-window-with-eleventy/</link>
      <description>&lt;p&gt;Static-site generators like Eleventy are great at building websites. But when
you want to add additional functionality, like a search feature, it might feel
like they fall short.&lt;/p&gt;
&lt;p&gt;All the data to generate your site exists at build time, how can you access that
with some client-side JavaScript?&lt;/p&gt;
&lt;p&gt;Here we&#39;ll look at one solution with Eleventy to expose data on the global
&lt;code&gt;window&lt;/code&gt; object to add interactive elements to your static site.&lt;/p&gt;
&lt;p&gt;Besides familiarity with Eleventy, the only prerequisite here is
&lt;a href=&quot;https://www.11ty.dev/docs/shortcodes/#universal-shortcodes&quot;&gt;creating custom shortcodes&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;nunjucks&quot; tabindex=&quot;-1&quot;&gt;Nunjucks&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/send-data-to-the-window-with-eleventy/#nunjucks&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Nunjucks”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Out-of-the-box, you can use Nunjucks&#39; &lt;code&gt;dump&lt;/code&gt; and &lt;code&gt;safe&lt;/code&gt; filters to stringify
data and leave it unescaped, respectively. Put that all within a &lt;code&gt;script&lt;/code&gt; tag,
and you have a simple method for exposing data:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- index.njk --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__DATA__ &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 punctuation&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;hello&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;there&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; dump &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; safe &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;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works but is a little fiddly and limited to Nunjucks. For a more universal
solution, let&#39;s create a custom shortcode.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;shortcode&quot; tabindex=&quot;-1&quot;&gt;Shortcode&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/send-data-to-the-window-with-eleventy/#shortcode&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Shortcode”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;A better solution to send data to the client is with a reusable shortcode. Let&#39;s
add a it to &lt;code&gt;.eleventy.js&lt;/code&gt;:&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;// .eleventy.js&lt;/span&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;
  eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addShortcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;expose&quot;&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;data&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 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;&amp;lt;script&gt;
            window.__DATA__=JSON.parse(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;);
        &amp;lt;/script&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 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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then use the new shortcode like so:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- index.njk --&gt;&lt;/span&gt;
{% expose [&#39;hello&#39;, &#39;there&#39;] %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nice and simple! I&#39;m going to offer an optional improvement to cleanup after
exposing the data:&lt;/p&gt;
&lt;!-- ```js/1,3 --&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addShortcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;expose&quot;&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;data&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 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;&amp;lt;script id=&quot;__EXPOSE__&quot;&gt;
        window.__DATA__=JSON.parse(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;);
        document.getElementById(&quot;__EXPOSE__&quot;).remove();
    &amp;lt;/script&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;With those changes, the script will execute on the client, add the data to the
&lt;code&gt;window&lt;/code&gt;, and then remove the &lt;code&gt;script&lt;/code&gt; tag.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Hope that helps! Let me know if you have a better method of exposing data on the
&lt;code&gt;window&lt;/code&gt;, or how you can accomplish this with your favorite static-site
generator.&lt;/p&gt;
&lt;p&gt;Happy scripting!&lt;/p&gt;
</description>
      <pubDate>Mon, 25 Jan 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/send-data-to-the-window-with-eleventy/</guid>
    </item>
    <item>
      <title>Checkout the previous git branch</title>
      <link>https://seanmcp.com/articles/checkout-the-previous-git-branch/</link>
      <description>&lt;p&gt;Today I needed to checkout a branch, pull the latest, then checkout &lt;code&gt;dev&lt;/code&gt;, pull
the latest, and merge &lt;code&gt;dev&lt;/code&gt; into my branch.&lt;/p&gt;
&lt;p&gt;This flow is pretty typical of my work, but I wondered: is there an easier way
to go back to the previous branch in git?&lt;/p&gt;
&lt;p&gt;Turns out there is, and
&lt;a href=&quot;https://stackoverflow.com/a/7207542/8486161&quot;&gt;StackOverflow came to the rescue&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout -&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will checkout the most recent branch. For more of an explanation,
read the full response on StackOverflow.&lt;/p&gt;
&lt;p&gt;To complete the steps above, I could run the following commands:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout feature/branch-with-a-long-name
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; pull
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout dev
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; pull
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout -
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; merge dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I can remember that command, it will be more efficient that going back
through my command history. Hope that helps you too!&lt;/p&gt;
&lt;p&gt;Happy version controlling!&lt;/p&gt;
</description>
      <pubDate>Fri, 22 Jan 2021 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/checkout-the-previous-git-branch/</guid>
    </item>
    <item>
      <title>Make all properties required in TypeScript</title>
      <link>https://seanmcp.com/articles/make-all-properties-required-in-typescript/</link>
      <description>&lt;p&gt;I have a type interface for shapes that looks something like this:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Shape&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  height&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  width&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I want to extend my interface for rectangles, but I want both &lt;code&gt;height&lt;/code&gt; and
&lt;code&gt;width&lt;/code&gt; to be required. You can do that in TypeScript with a
&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype&quot;&gt;&lt;code&gt;Required&lt;/code&gt; utility type&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Required&lt;/code&gt; takes a type and returns one with all required properties. You can
use it by declaring a new &lt;code&gt;type&lt;/code&gt; and passing the &lt;code&gt;Shape&lt;/code&gt; interface:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Rectangle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Required&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Shape&lt;span class=&quot;token operator&quot;&gt;&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 when I try to create a new rectangle, I can use the &lt;code&gt;Rectangle&lt;/code&gt; type to
ensure that I provided all the required properties:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rect&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Rectangle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  height&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  width&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can play around with these types in
&lt;a href=&quot;https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgMoAs4AcUG8BQyRy6EwA5umAPwBcyIArgLYBG0A3IcQO7AAmYdHQYt2ULgF98+MAE8cyAEoQEYOCHIAbFAF5lEAI6NgUCPwA8GbBAB8MhAHsQAZzDIza+irUbte5AJiEjJKMHoAFgAabiI+QXR6AA58SSA&quot;&gt;this TypeScript playground&lt;/a&gt;,
and
&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/utility-types.html&quot;&gt;read more about Utility types&lt;/a&gt;
on the TypeScript website.&lt;/p&gt;
&lt;p&gt;Happy requiring!&lt;/p&gt;
</description>
      <pubDate>Sat, 19 Dec 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/make-all-properties-required-in-typescript/</guid>
    </item>
    <item>
      <title>Add text to the beginning of every file</title>
      <link>https://seanmcp.com/articles/add-text-to-the-beginning-of-every-file/</link>
      <description>&lt;p&gt;While converting a JavaScript &lt;code&gt;create-react-app&lt;/code&gt; project to TypeScript, I wanted
a single line of text to every &lt;code&gt;.tsx&lt;/code&gt; file. Searching for an answer brought me
to
&lt;a href=&quot;https://superuser.com/questions/246837/how-do-i-add-text-to-the-beginning-of-a-file-in-bash&quot;&gt;this question on StackExchange&lt;/a&gt;,
specifically &lt;a href=&quot;https://superuser.com/a/521654&quot;&gt;this answer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;echo&lt;/code&gt; and &lt;code&gt;cat&lt;/code&gt;, you can add new text to the old file&#39;s contents and send
it to the original file. Combined with a &lt;code&gt;for&lt;/code&gt; loop to iterate over the matching
files, I came up with a one-liner&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/add-text-to-the-beginning-of-every-file/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; that saved me a lot of time:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; **/*.tsx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;// @ts-nocheck
&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script looks for all &lt;code&gt;.tsx&lt;/code&gt; files in any directory, then adds
&lt;code&gt;// @ts-nocheck&lt;/code&gt; and a newline to the beginning of the file. But there is
nothing magic about the selector or added text; you can use whatever you want
for those values.&lt;/p&gt;
&lt;p&gt;Hope that saves you a bit of time too!&lt;/p&gt;
&lt;p&gt;Happy scripting!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Okay, with the newline it&#39;s kind of a two-liner, but you can copy and paste
it directly into the command line. &lt;a href=&quot;https://seanmcp.com/articles/add-text-to-the-beginning-of-every-file/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Fri, 18 Dec 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/add-text-to-the-beginning-of-every-file/</guid>
    </item>
    <item>
      <title>Sass converts hsla to hex incorrectly</title>
      <link>https://seanmcp.com/articles/sass-converts-hsla-to-hex-incorrectly/</link>
      <description>&lt;p&gt;I ran into an issue where Sass was converting &lt;code&gt;hsla()&lt;/code&gt; values to hex
incorrectly. It would take an input like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* input.scss */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.subtle-blue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hsla&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;212&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 10%&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;And convert it into:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* output.css */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.subtle-blue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #407bbf&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;That is &lt;strong&gt;not&lt;/strong&gt; the same color. When processing the input styles, Sass ignores
the alpha (opacity) value and converts it into a standard hex.&lt;/p&gt;
&lt;p&gt;You can see the different between the two values here:&lt;/p&gt;
&lt;style&gt;
.exs {
    display: grid;
    gap: 0.5rem;
    grid-template-columns: repeat(2, 1fr);
    margin: 2rem 0;
}
.ex {
    padding: 4rem 0.5rem 0.5rem;
}
.ex code {
    background-color: black !important;
    color: white !important;
    padding: 2px 4px;
}
&lt;/style&gt;
&lt;div class=&quot;exs&quot;&gt;
    &lt;div class=&quot;ex&quot; style=&quot;background-color:hsla(212, 50%, 50%, 10%)&quot;&gt;
        &lt;code&gt;hsla(212, 50%, 50%, 10%)&lt;/code&gt;
    &lt;/div&gt;
    &lt;div class=&quot;ex&quot; style=&quot;background-color:#407bbf;&quot;&gt;
        &lt;code&gt;#407bbf&lt;/code&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you search around for an answer, you will find a few suggestions to
&lt;a href=&quot;https://github.com/sass/sass/issues/469#issuecomment-461675216&quot;&gt;use strings for &lt;code&gt;hsl&lt;/code&gt; values&lt;/a&gt;
or
&lt;a href=&quot;https://github.com/sass/sass/issues/469#issuecomment-77290544&quot;&gt;recreating the &lt;code&gt;hsl&lt;/code&gt; and &lt;code&gt;hsla&lt;/code&gt; functions in Sass&lt;/a&gt;.
Neither of those seemed an idea solution.&lt;/p&gt;
&lt;p&gt;Instead of changing how you declare &lt;code&gt;hsl&lt;/code&gt; value or recreating the function, you
can fix this issue by &lt;strong&gt;using decimal alpha values instead of percent&lt;/strong&gt;. Going
back to the original example, change &lt;code&gt;10%&lt;/code&gt; to &lt;code&gt;0.1&lt;/code&gt;:&lt;/p&gt;
&lt;!-- ```css/2 --&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Input Sass */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.subtle-blue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hsla&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;212&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.1&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;And Sass will convert it to alpha-supporting &lt;code&gt;rgba&lt;/code&gt;:&lt;/p&gt;
&lt;!-- ```css/2 --&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Output css */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.subtle-blue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;64&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 123&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 191&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.1&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;These values aren&#39;t identical because
&lt;a href=&quot;https://stackoverflow.com/questions/31613667/hsl-to-rgb-conversion-math&quot;&gt;math is required&lt;/a&gt;,
but they&#39;re infinitely closer than the non-alpha hex value:&lt;/p&gt;
&lt;div class=&quot;exs&quot;&gt;
    &lt;div class=&quot;ex&quot; style=&quot;background-color:hsla(212, 50%, 50%, 0.1)&quot;&gt;
        &lt;code&gt;hsla(212, 50%, 50%, 0.1)&lt;/code&gt;
    &lt;/div&gt;
    &lt;div class=&quot;ex&quot; style=&quot;background-color:rgba(64, 123, 191, 0.1);&quot;&gt;
        &lt;code&gt;rgba(64, 123, 191, 0.1)&lt;/code&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can see this behavior for yourself in
&lt;a href=&quot;https://sass.js.org/&quot;&gt;this Sass playground&lt;/a&gt; but not in
&lt;a href=&quot;https://www.sassmeister.com/&quot;&gt;Sassmeister&lt;/a&gt;. So it may depend on the flavor and
version of Sass that you are using.&lt;/p&gt;
&lt;p&gt;But for a quick fix that doesn&#39;t involve wading into the release notes of your
dependencies, opt for decimal alpha values in &lt;code&gt;hsla&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Happy styling!&lt;/p&gt;
</description>
      <pubDate>Thu, 17 Dec 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/sass-converts-hsla-to-hex-incorrectly/</guid>
    </item>
    <item>
      <title>Change extensions of all files in a directory</title>
      <link>https://seanmcp.com/articles/change-extensions-of-all-files-in-a-directory/</link>
      <description>&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; *.jsx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mv&lt;/span&gt; -- &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${f&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;.jsx}&lt;/span&gt;.tsx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</description>
      <pubDate>Thu, 17 Dec 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/change-extensions-of-all-files-in-a-directory/</guid>
    </item>
    <item>
      <title>Increase the playback speed on any audio or video online</title>
      <link>https://seanmcp.com/articles/increase-the-playback-speed-on-any-audio-or-video-online/</link>
      <description>&lt;p&gt;I like learning from audio and video, from podcasts to YouTube videos. My
favorite applications let you alter the playback speed of the audio and video so
that you can consume the content faster (or slower).&lt;/p&gt;
&lt;p&gt;I find this particularly useful for unscripted podcasts or coding videos and
will usually increase the speed to 2x (or more). At that rate, I can get through
a 60 minute podcast in half the time!&lt;/p&gt;
&lt;p&gt;However, I sometimes find myself on websites where they do not allow you to
change the playback speed. Recently I was watching a coding tutorial on Vimeo
and was stuck at the normal speed. Not a good use of my time.&lt;/p&gt;
&lt;p&gt;Since online audio and video are played through native HTML elements, we have
the ability to customize their behavior with the developer console.&lt;/p&gt;
&lt;p&gt;First &lt;a href=&quot;https://seanmcp.com/articles/increase-the-playback-speed-on-any-audio-or-video-online/how-to-open-dev-tools&quot;&gt;open the dev tools with your favorite method&lt;/a&gt;,
and click on the &amp;quot;Console&amp;quot; tab. Then enter and run the following script&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/increase-the-playback-speed-on-any-audio-or-video-online/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;audio,video&quot;&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 function&quot;&gt;forEach&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;n&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;n&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;playbackRate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.0&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This grabs all of the &lt;code&gt;audio&lt;/code&gt; and &lt;code&gt;video&lt;/code&gt; elements from the DOM, loops over each
node, and sets the playback rate to &lt;code&gt;2.0&lt;/code&gt; or 2x. If you prefer a different rate,
you will only need to edit that last number.&lt;/p&gt;
&lt;p&gt;If you are working on an application that plays audio or video, make sure to
give the user the ability to change the playback rate. That way everyone can
listen at the speed that is best for them&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/increase-the-playback-speed-on-any-audio-or-video-online/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Happy listening/watching!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://developers.google.com/web/tools/chrome-devtools/console/utilities#queryselectorall&quot;&gt;&lt;code&gt;$$&lt;/code&gt; is a shorthand for &lt;code&gt;document.querySelectorAll()&lt;/code&gt;&lt;/a&gt;
and works in all modern browsers &lt;a href=&quot;https://seanmcp.com/articles/increase-the-playback-speed-on-any-audio-or-video-online/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;For some light reading on the subject, checkout
&lt;a href=&quot;https://homes.cs.washington.edu/~reinecke//Publications_files/Bragg_CHI2018.pdf&quot;&gt;this paper on &lt;em&gt;Human Listening Rates&lt;/em&gt;&lt;/a&gt;
by the University of Washington CS department &lt;a href=&quot;https://seanmcp.com/articles/increase-the-playback-speed-on-any-audio-or-video-online/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Tue, 15 Dec 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/increase-the-playback-speed-on-any-audio-or-video-online/</guid>
    </item>
    <item>
      <title>Standardize character width with CSS</title>
      <link>https://seanmcp.com/articles/standardize-character-width-with-css/</link>
      <description>&lt;p&gt;&lt;strong&gt;Authors note&lt;/strong&gt;: Before you read this article, you will want to click the
&amp;quot;Start timer&amp;quot; button below to enable the live demos. You can cancel it at any
time by clicking &amp;quot;Stop timer&amp;quot;.&lt;/p&gt;
&lt;div class=&quot;button-container&quot;&gt;
    &lt;button onclick=&quot;startTimer()&quot;&gt;Start timer&lt;/button&gt;
    &lt;button onclick=&quot;stopTimer()&quot;&gt;Stop timer&lt;/button&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;p&gt;The width of a character is the amount of horizontal space that it occupies.
This width varies between characters, weights, and typefaces. In Lato, for
example, the letter &lt;strong&gt;W&lt;/strong&gt; is wider than the letter &lt;strong&gt;I&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For most use cases, the varied width of characters isn&#39;t an issue. However, when
you are creating an component like a timer, the differences in width can produce
some undesirable movements as the numbers change.&lt;/p&gt;
&lt;p&gt;Take a look at this example when timer running. Since the characters are all
different widths, the number &amp;quot;jitters&amp;quot; around as it increments:&lt;/p&gt;
&lt;div aria-label=&quot;Default&quot; class=&quot;example&quot;&gt;
    &lt;span class=&quot;count&quot;&gt;0&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;This isn&#39;t the end of the world, but it&#39;s not preferable.&lt;/p&gt;
&lt;p&gt;To alleviate this, I have in the past turned to monospace typefaces. These
allocate the same width for each character, regardless of the space that the
stroke needs.&lt;/p&gt;
&lt;p&gt;In this example, we can see that the jittering is resolved and the timer
increments smoothly:&lt;/p&gt;
&lt;div aria-label=&quot;Monospaced&quot; class=&quot;example&quot;&gt;
    &lt;span class=&quot;count&quot;&gt;0&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;However, altering the typeface might not be the best solution for your product.
Especially when the change was only made to prevent the jittering.&lt;/p&gt;
&lt;p&gt;Instead, we can use two CSS properties&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/standardize-character-width-with-css/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; to standardize the character width of
an element:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.standardize-character-width&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-feature-settings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tnum&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-variant-numeric&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tabular-nums&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;With those two in place, we can preserve the original typeface and avoid visual
jittering:&lt;/p&gt;
&lt;div aria-label=&quot;Standardized&quot; class=&quot;example&quot;&gt;
    &lt;span class=&quot;count&quot;&gt;0&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;A few things to note:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Adding these properties changes the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Letter-spacing&quot;&gt;tracking&lt;/a&gt;, or letter spacing,
of the characters. This is necessary to standardize widths but might be
undesirable for typography aficionados.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-variant-numeric&lt;/code&gt; does not work in Internet Explorer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-feature-settings&lt;/code&gt; supports IE but only works with
&lt;a href=&quot;https://en.wikipedia.org/wiki/OpenType&quot;&gt;OpenType fonts&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That being said, this is CSS progressive enhancement: for whom it works, it
improves the experience. I&#39;m pretty happy with this solution, and I have used it
in production with
&lt;a href=&quot;https://seanmcp.github.io/contractions&quot;&gt;Contraction Tracker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For more information about the CSS properties, you can read about
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings&quot;&gt;&lt;code&gt;font-feature-settings&lt;/code&gt;&lt;/a&gt;
and
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant-numeric&quot;&gt;&lt;code&gt;font-variant-numetric&lt;/code&gt;&lt;/a&gt;
on MDN.&lt;/p&gt;
&lt;p&gt;Happy standardizing!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I first saw this solution from
&lt;a href=&quot;https://twitter.com/sulco/status/1293862293139337217&quot;&gt;Tomek Sułkowski (@sulco) on Twitter&lt;/a&gt;.
He shares lots of great tips like this and is worth the follow. &lt;a href=&quot;https://seanmcp.com/articles/standardize-character-width-with-css/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Wed, 09 Dec 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/standardize-character-width-with-css/</guid>
    </item>
    <item>
      <title>See all package versions in npm cli</title>
      <link>https://seanmcp.com/articles/see-all-package-versions-in-npm-cli/</link>
      <description>&lt;p&gt;At work, I ran into an issue that may have been related to an external
dependency. I wanted to update the package to see if a future release fixed the
issue.&lt;/p&gt;
&lt;p&gt;To get the latest version of a package, you can use the &lt;code&gt;@latest&lt;/code&gt; tag:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i package-name@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But what if you need the next most recent release? Or five versions ago? I
wanted to see a list of published versions without having to leave the command
line.&lt;/p&gt;
&lt;p&gt;A quick search led me to
&lt;a href=&quot;https://willi.am/blog/2015/07/17/viewing-all-versions-of-an-npm-package-including-pre-release/&quot;&gt;&lt;em&gt;Viewing All Versions of an NPM Package (Including Pre-Release)&lt;/em&gt;&lt;/a&gt;
by Will Anderson. He pointed to the &lt;code&gt;npm view&lt;/code&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/see-all-package-versions-in-npm-cli/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; command and illustrated how
to use it:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view package-name versions&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will print all of the published versions of any package on the registry.
Pretty handy!&lt;/p&gt;
&lt;p&gt;Anderson mentions that this was undocumented in 2015, but today we have
&lt;a href=&quot;https://docs.npmjs.com/cli/v6/commands/npm-view&quot;&gt;a page dedicated to the &lt;code&gt;view&lt;/code&gt; command on npm&lt;/a&gt;.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Anderson used the alias &lt;code&gt;npm show&lt;/code&gt; &lt;a href=&quot;https://seanmcp.com/articles/see-all-package-versions-in-npm-cli/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Wed, 09 Dec 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/see-all-package-versions-in-npm-cli/</guid>
    </item>
    <item>
      <title>TypeScript without TypeScript</title>
      <link>https://seanmcp.com/articles/typescript-without-typescript/</link>
      <description>&lt;p&gt;As a superset of JavaScript, TypeScript provides a lot of useful features. It&#39;s
no mystery why it is one of the
&lt;a href=&quot;https://insights.stackoverflow.com/survey/2020#technology-most-loved-dreaded-and-wanted-languages-loved&quot;&gt;&amp;quot;most loved&amp;quot; languages in software engineering&lt;/a&gt;.
When everything is running smoothly, TypeScript can been a huge boon to
productivity.&lt;/p&gt;
&lt;p&gt;However, therein lies one of TypeScript&#39;s weaknesses: the setup. TS adds a level
of overhead that is, in my opinion, prohibitively complex for most of my
projects. So while I would love the benefits of working in TypeScript, I don&#39;t
want to go through the trouble of setting it up.&lt;/p&gt;
&lt;p&gt;In searching for a solution to my problem, I came across
&lt;a href=&quot;https://fettblog.eu/typescript-jsdoc-superpowers/&quot;&gt;&lt;em&gt;TypeScript without TypeScript -- JSDoc superpowers&lt;/em&gt;&lt;/a&gt;
by &lt;a href=&quot;https://twitter.com//ddprrt&quot;&gt;@ddprrt&lt;/a&gt; on &lt;a href=&quot;http://fettblog.eu&quot;&gt;fettblog.eu&lt;/a&gt;. In it, the author
highlights how &lt;a href=&quot;https://jsdoc.app/&quot;&gt;JSDoc&lt;/a&gt; can provide some of the features of
TypeScript without the setup.&lt;/p&gt;
&lt;p&gt;I found the
&lt;a href=&quot;https://fettblog.eu/typescript-jsdoc-superpowers/#defining-objects&quot;&gt;&amp;quot;Defining Objects&amp;quot; section&lt;/a&gt;
to be particularly helpful. The ability to create &amp;quot;interfaces&amp;quot; for functions and
objects in JavaScript is really powerful. If you combine that with
&lt;a href=&quot;https://code.visualstudio.com/docs/nodejs/working-with-javascript#_type-checking-javascript&quot;&gt;&lt;code&gt;// @ts-check&lt;/code&gt;&lt;/a&gt;
and a TS-centric IDE like VS Code, you&#39;ll find yourself in a great position when
developing.&lt;/p&gt;
&lt;p&gt;Give the article a read and &lt;a href=&quot;https://seanmcp.com/articles/typescript-without-typescript/#comment-link&quot;&gt;let me know what you think&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy typing!&lt;/p&gt;
</description>
      <pubDate>Tue, 08 Dec 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/typescript-without-typescript/</guid>
    </item>
    <item>
      <title>Emulate iPhone &amp; iPad in Safari</title>
      <link>https://seanmcp.com/articles/emulate-iphone-and-ipad-in-safari/</link>
      <description>&lt;p&gt;To emulate an iPhone or iPad in Safari, you first need to have
&lt;a href=&quot;https://support.apple.com/guide/safari/use-the-developer-tools-in-the-develop-menu-sfri20948/mac&quot;&gt;the developer tools enabled&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then when visiting a page that you want to test, select &amp;quot;Develop&amp;quot; in the
application toolbar then &amp;quot;Enter Responsive Design Mode&amp;quot;.&lt;/p&gt;
&lt;p&gt;This will open a special window where you can emulate your website on an array
of Apple devices and user agents.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/safari-responsive-design-mode.png&quot; alt=&quot;Viewing SeanMcP.com on an iPad mini in Safari&#39;s responsive design mode&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;keyboard-shortcut&quot; tabindex=&quot;-1&quot;&gt;Keyboard shortcut&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/emulate-iphone-and-ipad-in-safari/#keyboard-shortcut&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Keyboard shortcut”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;To enter Responsive Design Mode without going through the toolbar, you can use
the shortcut &lt;kbd&gt;control&lt;/kbd&gt; &lt;kbd&gt;command&lt;/kbd&gt; &lt;kbd&gt;r&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;Happy emulating!&lt;/p&gt;
</description>
      <pubDate>Wed, 02 Sep 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/emulate-iphone-and-ipad-in-safari/</guid>
    </item>
    <item>
      <title>Get started with Preact</title>
      <link>https://seanmcp.com/articles/get-started-with-preact/</link>
      <description>&lt;p&gt;Preact is a lightweight alternative to React. It provides the same&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/get-started-with-preact/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; modern
API but at only 3kB minified and gzipped. For comparison, bundlephobia has
&lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; at 38.5kB.&lt;/p&gt;
&lt;p&gt;With many of the same features at a fraction of the size, Preact might be a
better alternative for your simple client-side React app.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;sandbox&quot; tabindex=&quot;-1&quot;&gt;Sandbox&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/get-started-with-preact/#sandbox&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Sandbox”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;repl&quot; tabindex=&quot;-1&quot;&gt;REPL&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/get-started-with-preact/#repl&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “REPL”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://preactjs.com/repl&quot;&gt;Preact website has a REPL&lt;/a&gt; where you can test
drive the library. The example as of writing using state and effect hooks and
makes a request to the GitHub API with &lt;code&gt;fetch&lt;/code&gt;:&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; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setItems&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;useEffect&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;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://api.github.com/search/repositories?q=preact&quot;&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 function&quot;&gt;then&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;res&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; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&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 class=&quot;token function&quot;&gt;then&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;data&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 function&quot;&gt;setItems&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;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;)&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 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 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 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;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;codesandbox.io&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;http://CodeSandbox.io&quot;&gt;CodeSandbox.io&lt;/a&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/get-started-with-preact/#codesandbox.io&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “CodeSandbox.io”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;CodeSandbox has 54,000+ templates&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/get-started-with-preact/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; for Preact, including
&lt;a href=&quot;https://codesandbox.io/s/preact-preact&quot;&gt;a starter template from the CodeSandbox Team&lt;/a&gt;.
The code is similar to the official REPL, but with a class component:&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;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;componentDidMount&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 function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&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;https://api.github.com/search/repositories?q=preact&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 class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&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;r&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; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&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 class=&quot;token function&quot;&gt;then&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;json&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 class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&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 literal-property property&quot;&gt;results&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;json &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;)&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 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 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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; results &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 punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Example&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;list&quot;&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;results&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&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;result&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Result result&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&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 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;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 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;/code&gt;&lt;/pre&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Mostly. &lt;a href=&quot;https://seanmcp.com/articles/get-started-with-preact/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Based on a template search for &amp;quot;preact&amp;quot;. I did not verify this count. &lt;a href=&quot;https://seanmcp.com/articles/get-started-with-preact/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Tue, 18 Aug 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/get-started-with-preact/</guid>
    </item>
    <item>
      <title>Make a POST request with fetch</title>
      <link>https://seanmcp.com/articles/make-a-post-request-with-fetch/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&quot;&gt;&lt;code&gt;fetch&lt;/code&gt; is utility for making HTTP requests&lt;/a&gt;
from the browser. It is
&lt;a href=&quot;https://caniuse.com/#feat=fetch&quot;&gt;available in all modern browsers&lt;/a&gt; and has a
&lt;a href=&quot;https://github.com/github/fetch&quot;&gt;popular polyfill for older browsers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To make a &lt;code&gt;GET&lt;/code&gt; request, just pass the endpoint to the global &lt;code&gt;fetch&lt;/code&gt; method:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://mirror-api.seanmcp.repl.co/get&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;It returns a promise that will resolve with the server response. From there, you
can convert the response to JSON and read the value in your code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&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 keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://mirror-api.seanmcp.repl.co/get&quot;&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 keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&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;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;But &lt;code&gt;fetch&lt;/code&gt; also handles other request methods. After &lt;code&gt;GET&lt;/code&gt;, the first you will
reach for is &lt;code&gt;POST&lt;/code&gt;. But it isn&#39;t immediately apparent how to make a request
with a different method.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;how-to-post-with-fetch&quot; tabindex=&quot;-1&quot;&gt;How to &lt;code&gt;POST&lt;/code&gt; with &lt;code&gt;fetch&lt;/code&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/make-a-post-request-with-fetch/#how-to-post-with-fetch&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “How to POST with fetch”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The global &lt;code&gt;fetch&lt;/code&gt; function accepts a second options argument. There you can set
the method, add a body, and set headers.&lt;/p&gt;
&lt;p&gt;Here is an example POST request that sends a JSON body:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://mirror-api.seanmcp.repl.co/post&quot;&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 literal-property property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&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 literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello API!&quot;&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 literal-property property&quot;&gt;headers&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 string-property property&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&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 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;Once you have the request in order, you can handle the promises and get the
server response.&lt;/p&gt;
&lt;p&gt;For more information on the available including examples with other methods,
checkout
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch&quot;&gt;&lt;em&gt;Using Fetch&lt;/em&gt; on MDN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy fetching!&lt;/p&gt;
</description>
      <pubDate>Fri, 07 Aug 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/make-a-post-request-with-fetch/</guid>
    </item>
    <item>
      <title>Why you shouldn&#39;t mutate parameters</title>
      <link>https://seanmcp.com/articles/why-you-shouldnt-mutate-parameters/</link>
      <description>&lt;!-- https://repl.it/@SeanMcP/why-you-shouldnt-mutate-parameters --&gt;
&lt;p&gt;When you pass an argument to a function, you can then access it as a parameter
within the function.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;value-arguments&quot; tabindex=&quot;-1&quot;&gt;Value arguments&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-you-shouldnt-mutate-parameters/#value-arguments&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Value arguments”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;When you pass a value argument like &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;true&lt;/code&gt;, or &lt;code&gt;hello&lt;/code&gt;, you have access to
that value within the function. As a result, you can mutate it like so:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;number&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;
  number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; number&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 function&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&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 comment&quot;&gt;// 4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;value&lt;/strong&gt;, or&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;reference&lt;/strong&gt; to a value?&lt;/li&gt;
&lt;/ol&gt;
&lt;!-- ## Values and references

When you declare a variable in JavaScript, you are doing two things: 1) storing a value in memory, and 2) creating a reference to that value. Take the following code:

```js
let name = &#39;sean&#39;
```

With this expression, I am storing the string `&#39;sean&#39;` in memory, and then creating the variable `name` that references that value. --&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;mutating-parameters&quot; tabindex=&quot;-1&quot;&gt;Mutating parameters&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-you-shouldnt-mutate-parameters/#mutating-parameters&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Mutating parameters”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;When you pass a variable as an argument, you are handing a &lt;em&gt;reference&lt;/em&gt; to the
function – not a value. So when the function references its parameter,
JavaScript traces the reference chain to the stored value.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at an example:&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;let&lt;/span&gt; colors &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;&quot;red&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;orange&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yellow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;green&quot;&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 keyword&quot;&gt;let&lt;/span&gt; warmColors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colors&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&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 comment&quot;&gt;// &#39;green&#39; is not a warm color&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;array&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&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 comment&quot;&gt;// 🎗 Remember: [] === [] is false&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; colors&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;What would you expect the console output to be? You might be surprised to see
that the log is &lt;code&gt;true&lt;/code&gt;. That&#39;s because we passed a &lt;em&gt;reference to&lt;/em&gt; &lt;code&gt;colors&lt;/code&gt; and
&lt;strong&gt;not&lt;/strong&gt; the value.&lt;/p&gt;
&lt;p&gt;So if we go on to mutate the &lt;code&gt;array&lt;/code&gt; parameter, we will make changes to the
value that it &lt;strong&gt;and&lt;/strong&gt; &lt;code&gt;colors&lt;/code&gt; reference:&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;let&lt;/span&gt; colors &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;&quot;red&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;orange&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yellow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;green&quot;&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 keyword&quot;&gt;let&lt;/span&gt; warmColors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colors&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;array&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&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;
  array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;splice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&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 keyword&quot;&gt;return&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colors&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;warmColors&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;The log after running our &lt;code&gt;remove()&lt;/code&gt; function shows that &lt;code&gt;colors&lt;/code&gt; has been
modified:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;red&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;orange&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;yellow&#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 string&quot;&gt;&#39;red&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;orange&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;yellow&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</description>
      <pubDate>Wed, 05 Aug 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/why-you-shouldnt-mutate-parameters/</guid>
    </item>
    <item>
      <title>Remove an item at a given index in JavaScript</title>
      <link>https://seanmcp.com/articles/remove-an-item-at-a-given-index-in-javascript/</link>
      <description>&lt;p&gt;Removing an item from an array at a given index is a pretty common task in
programming. So it is a little disappointing that the Array prototype in
JavaScript doesn&#39;t have a method for that exact task.&lt;/p&gt;
&lt;p&gt;If you search for an answer, you&#39;ll find solutions that use &lt;code&gt;Array#slice&lt;/code&gt;:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;removeAtWithSlice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;array&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&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 keyword&quot;&gt;return&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&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 function&quot;&gt;contact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;And answers with &lt;code&gt;Array#splice&lt;/code&gt; (which mutates the original array):&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;removeAtWithSplice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;array&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&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 keyword&quot;&gt;const&lt;/span&gt; copy &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 operator&quot;&gt;...&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  copy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;splice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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 keyword&quot;&gt;return&lt;/span&gt; copy&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;Both of these require that you remember the difference between the two methods,
their behavior, and the arguments they require.&lt;/p&gt;
&lt;p&gt;Personally, neither of these solutions are memorable, so I often find myself
searching &amp;quot;how to remove an item at a given index JS&amp;quot;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;filtering&quot; tabindex=&quot;-1&quot;&gt;Filtering&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-an-item-at-a-given-index-in-javascript/#filtering&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Filtering”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Instead of using &lt;code&gt;slice&lt;/code&gt;/&lt;code&gt;splice&lt;/code&gt;, I came upon a new method that is my favorite
yet: &lt;code&gt;Array#filter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;filter&lt;/code&gt; method will iterate over your array and return a new one with
values that pass a certain criterion. You have probably used &lt;code&gt;Array#filter&lt;/code&gt; for
a todo-like example before:&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; done &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; todos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&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;todo&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; todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isComplete &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like other array methods, you also have access to the current index as a
parameter. So to use &lt;code&gt;Array#filter&lt;/code&gt; to remove an item from an array, check if
the indices match:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;removeAtWithFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;array&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&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 keyword&quot;&gt;return&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&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;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&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; i &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; index&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I&#39;m using the &lt;code&gt;_&lt;/code&gt; to indicate a parameter that I don&#39;t intend to
reference and a non-description &lt;code&gt;i&lt;/code&gt; variable because &lt;code&gt;index&lt;/code&gt; is already in
scope.&lt;/p&gt;
&lt;p&gt;As I mentioned above, this is now my preferred method of removing an item from
an array. I am more familiar with &lt;code&gt;Array#filter&lt;/code&gt; and the idea of &amp;quot;filtering out&amp;quot;
in the unwanted index makes sense. The other two methods are harder for me to
conceptualize.&lt;/p&gt;
&lt;p&gt;There are two objective benefits to this method as well. First, it doesn&#39;t
mutate the array (unlike &lt;code&gt;Array#splice&lt;/code&gt;), which allows for functional
programming without an additional step. And second, it only iterates the length
of the array once (vs a potential two iterations for &lt;code&gt;Array#slice&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;For all those reasons, I recommend you use &lt;code&gt;Array#filter&lt;/code&gt; for removing an item
from an array at a given index in JavaScript. Let me know if you have an even
better method; I&#39;d love to hear it!&lt;/p&gt;
&lt;p&gt;Happy filtering!&lt;/p&gt;
</description>
      <pubDate>Wed, 05 Aug 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/remove-an-item-at-a-given-index-in-javascript/</guid>
    </item>
    <item>
      <title>Tab focus not working in Safari?</title>
      <link>https://seanmcp.com/articles/tab-focus-not-working-in-safari/</link>
      <description>&lt;p&gt;While doing some cross-browser testing in Safari, I noted that &lt;kbd&gt;tab&lt;/kbd&gt;
was not working as expected. I was looking at a focus-managed component, so my
first thought was that I had done something in my JavaScript that did not work
in Safari.&lt;/p&gt;
&lt;p&gt;After trying to debug the issue for a few minutes, I finally noticed that
&lt;strong&gt;&lt;kbd&gt;tab&lt;/kbd&gt; wasn&#39;t working anywhere&lt;/strong&gt;, not just my component. This was both
a relief (it wasn&#39;t me!) and a mystery. How could tab not work?&lt;/p&gt;
&lt;p&gt;A quick Google search for &amp;quot;Safari tab order&amp;quot; lead me to this StackOverflow
question and answer:
&lt;a href=&quot;https://stackoverflow.com/questions/1848390/safari-ignoring-tabindex&quot;&gt;&lt;em&gt;Safari ignoring tabindex&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As it turns out, Safari does not enable tab highlighting by default. To turn it
on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to &amp;quot;Preferences&amp;quot;&lt;/li&gt;
&lt;li&gt;Select the &amp;quot;Advanced&amp;quot; tab&lt;/li&gt;
&lt;li&gt;Check &amp;quot;Press Tab to highlight each item on a webpage&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With that setting enabled, &lt;kbd&gt;tab&lt;/kbd&gt; worked as I expected.
&lt;a href=&quot;https://stackoverflow.com/a/1914496/8486161&quot;&gt;Thanks &lt;code&gt;graphicdivine&lt;/code&gt;&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;While you&#39;re fiddling with settings, the A11y Project has a guide for
&lt;a href=&quot;https://www.a11yproject.com/posts/2017-12-29-macos-browser-keyboard-navigation/&quot;&gt;enabling browser keyboard navigation on macOS&lt;/a&gt;
that is worth checking out.&lt;/p&gt;
&lt;p&gt;Setting you computer up for accessibility testing makes it that much easier to
ensure that you&#39;re building a product that will work for everyone.&lt;/p&gt;
&lt;p&gt;Happy tabbing!&lt;/p&gt;
</description>
      <pubDate>Tue, 04 Aug 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/tab-focus-not-working-in-safari/</guid>
    </item>
    <item>
      <title>Don&#39;t remove default focus styles, enhance them</title>
      <link>https://seanmcp.com/articles/dont-remove-default-focus-styles-enhance-them/</link>
      <description>&lt;style&gt;
.example {
    margin: 2rem 0;
}

.example &gt; * {
    margin: 0 !important;
}

.live {
    background-color: hsl(0, 0%, 95%);
    box-shadow: inset 0 0 0.5rem hsla(0, 0%, 0%, 8%);
    display: grid;
    place-items: center;
    padding: 1rem;
}

@media screen and (min-width: 640px) {
    .example {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }
}

.article input[class] {
    border: 1px solid hsla(0, 0%, 0%, 17.5%);
    border-radius: 0.5rem;
    font-family: inherit;
    font-size: inherit;
    padding: 0.5rem;
}
&lt;/style&gt;
&lt;p&gt;I can&#39;t tell you how many times over the past fifteen years that I have searched
&amp;quot;how to remove default focus styles&amp;quot;. Something about that fuzzy blue glow
around form elements has always looked terrible to me. So I, and countless other
developers like me, have stumbled upon the code to remove focus styles:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;*:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/*
        I&#39;m sharing this for an illustration;
        please don&#39;t use this code.
    */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&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;But by removing those styles, you are removing a key feature of user agent
styles: the focus outline! Let&#39;s look at why they are so important and what you
can do to make them look less ugly.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;why-focus-styles%3F&quot; tabindex=&quot;-1&quot;&gt;Why focus styles?&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/dont-remove-default-focus-styles-enhance-them/#why-focus-styles%3F&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Why focus styles?”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;That glowing blue outline lets you know when the browser is focused on a
particular element. This is as necessary as having a &lt;code&gt;:hover&lt;/code&gt; state for links;
you shouldn&#39;t deploy without it.&lt;/p&gt;
&lt;p&gt;Focus styles are also important for accessibility. Visual users who navigate by
the keyboard need those distinct focus styles to indicate where they are on the
page.&lt;/p&gt;
&lt;p&gt;Imagine trying to navigate a webpage with an invisible mouse; it would be
terrible. When you remove focus styles, you are doing the same thing to keyboard
users.&lt;/p&gt;
&lt;p&gt;Focus styles are a critical part of your website or applications design.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;better-focus-styles&quot; tabindex=&quot;-1&quot;&gt;Better focus styles&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/dont-remove-default-focus-styles-enhance-them/#better-focus-styles&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Better focus styles”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Just because these styles are necessary, doesn&#39;t mean that they have to be a
chore. By using the same kind of CSS properties that you style a site, you can
give your focus styles the love they deserve.&lt;/p&gt;
&lt;p&gt;All of the examples below are interactive, so make sure to click inside the
input box to see the focus styles illustrated.&lt;/p&gt;
&lt;p&gt;For comparison, this is what your browser&#39;s default focus styles look like:&lt;/p&gt;
&lt;div class=&quot;live&quot;&gt;
&lt;input aria-label=&quot;dummy input to illustrate focus styles&quot; placeholder=&quot;Focus here...&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;outline&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;outline&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/dont-remove-default-focus-styles-enhance-them/#outline&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “outline”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Instead of removing the outline with CSS, you can customize it. &lt;code&gt;outline&lt;/code&gt; has
accompanying &lt;code&gt;-weight&lt;/code&gt;, &lt;code&gt;-style&lt;/code&gt;, and &lt;code&gt;-color&lt;/code&gt; properties that give you the same
flexibility as &lt;code&gt;border&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The one hangup is that it looks to the bounding box and ignores things like
border radius:&lt;/p&gt;
&lt;div class=&quot;example&quot;&gt;
&lt;style&gt;
.example .outline:focus {
    outline: 2px solid red;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;input:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px solid red&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;div class=&quot;live&quot;&gt;
&lt;input aria-label=&quot;dummy input to illustrate focus styles&quot; class=&quot;outline&quot; placeholder=&quot;Focus here...&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That doesn&#39;t look great for rounded buttons, so you can use the &lt;code&gt;outline-offset&lt;/code&gt;
style to give your outline some more character:&lt;/p&gt;
&lt;div class=&quot;example&quot;&gt;
&lt;style&gt;
.example .outline--character:focus {
    outline: 4px dotted red;
    outline-offset: 4px;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;input:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px dotted red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline-offset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px&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;div class=&quot;live&quot;&gt;
&lt;input aria-label=&quot;dummy input to illustrate focus styles&quot; class=&quot;outline--character&quot; placeholder=&quot;Focus here...&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://Niche.com&quot;&gt;Niche.com&lt;/a&gt; does this for their button focus styles, and I think it looks great. I
matches the brand while clearly signifying that the button is focused.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;box-shadow&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;box-shadow&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/dont-remove-default-focus-styles-enhance-them/#box-shadow&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “box-shadow”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;For a more flexible &amp;quot;outline&amp;quot;, &lt;code&gt;box-shadow&lt;/code&gt; is your probably your best option.
You can dial down the &amp;quot;blur radius&amp;quot; and get an outline that honors the border
radius of a button:&lt;/p&gt;
&lt;div class=&quot;example&quot;&gt;
&lt;style&gt;
.box-shadow:focus {
    box-shadow: 0 0 0 2px red;
    outline: 4px solid transparent;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;input:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 0 2px red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px solid transparent&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;div class=&quot;live&quot;&gt;
&lt;input aria-label=&quot;dummy input to illustrate focus styles&quot; class=&quot;box-shadow&quot; placeholder=&quot;Focus here...&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Notice how I&#39;m not removing the &lt;code&gt;outline&lt;/code&gt; styles entirely. Some operating
systems have a &amp;quot;high-contrast mode&amp;quot; that will alter the color of the outline, so
leaving it as transparent will work for users with those settings.&lt;/p&gt;
</description>
      <pubDate>Thu, 30 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/dont-remove-default-focus-styles-enhance-them/</guid>
    </item>
    <item>
      <title>The Economics of Software Development, Part III</title>
      <link>https://seanmcp.com/articles/the-economics-of-software-development-part-iii/</link>
      <description>&lt;p&gt;&lt;em&gt;This is the third post in a series on how economics helps explain the decisions
we make when developing software. It is a response to &amp;quot;Summer School&amp;quot;, a series
from &lt;a href=&quot;https://www.npr.org/sections/money/&quot;&gt;NPR&#39;s Planet Money&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This week&#39;s
&lt;a href=&quot;https://www.npr.org/2020/07/22/894368822/summer-school-3-profit-cocaine&quot;&gt;episode from Planet Money&#39;s &amp;quot;Summer School&amp;quot;&lt;/a&gt;
was about &lt;strong&gt;elastic and inelastic demand&lt;/strong&gt;. The elasticity of demand refers to
how responsive consumers are to changes in price. If a small change in price has
a big impact on demand, then it is considered elastic. If the consumer will buy
the good no matter the price, then it is inelastic.&lt;/p&gt;
&lt;p&gt;The best example of this I can think of is sitting on the desk in front of me.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;case-study%3A-the-macbook-pro&quot; tabindex=&quot;-1&quot;&gt;Case study: the MacBook Pro&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-iii/#case-study%3A-the-macbook-pro&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Case study: the MacBook Pro”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Apple has done a fantastic job of creating inelastic demand for their laptops.
While it is by no means a monopoly, the popularity of the MacBook Pro in
developer circles cannot be overstated. If you are developer, some people argue,
you need to use a MacBook.&lt;/p&gt;
&lt;p&gt;But Apple computers are very expensive, and they seem to grow more so every
release. The
&lt;a href=&quot;https://www.businessinsider.com/apple-50000-mac-pro-price-not-for-you-2019-12&quot;&gt;latest iMac is priced between &lt;strong&gt;$6,000 and $50,000&lt;/strong&gt;&lt;/a&gt;,
depending on the features. Most people would never pay that much, but the fact
that Apple can even consider charging that much illustrates the inelasticity of
the demand.&lt;/p&gt;
&lt;p&gt;Recent releases of the MacBook, however, have received mixed reviews. Apple&#39;s
transition to the butterfly keyboard was largely unpopular, and
&lt;a href=&quot;https://www.wsj.com/graphics/apple-still-hasnt-fixed-its-macbook-keyboard-problem/&quot;&gt;seems to have resulted in hardware issues&lt;/a&gt;.
I am typing this on a MacBook Pro that regularly has issues with overheating,
visual bugs, and display inconsistencies.&lt;/p&gt;
&lt;p&gt;Despite this, many developers (myself included) are still convinced that they
need to have a MacBook to do their work. So the demand for the product remains
fixed, regardless of rising prices or falling quality.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;how-to-encourage-elasticity&quot; tabindex=&quot;-1&quot;&gt;How to encourage elasticity&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-iii/#how-to-encourage-elasticity&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “How to encourage elasticity”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Whenever possible, we as consumers need to push back against inelastic demand.
As developers, we can structure our tools and processes in a way that one could
easily be exchanged for another.&lt;/p&gt;
&lt;p&gt;If you are using a cloud provider, write a wrapper interface than can be
switched from AWS to Google Cloud Platform or Microsoft Azure without touching
any business logic. Architect your products so that they run on multiple
platforms, environments, and browsers.&lt;/p&gt;
&lt;p&gt;In JavaScript land, that can be as simple as using double quotes in Node.js
scripts so that they work on Windows.&lt;/p&gt;
&lt;p&gt;By embracing the inevitability of future change and planning for it, we will be
in a better position to stay ahead of the competition.&lt;/p&gt;
</description>
      <pubDate>Wed, 29 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/the-economics-of-software-development-part-iii/</guid>
    </item>
    <item>
      <title>Fix &#39;EMFILE: too many open files&#39; error in Jest</title>
      <link>https://seanmcp.com/articles/fix-emfile-too-many-open-files-error-in-jest/</link>
      <description>&lt;p&gt;When I tried to run &lt;code&gt;jest --watch&lt;/code&gt; in a codebase, I received the following
error:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;Error: EMFILE: too many &lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt; files, &lt;span class=&quot;token function&quot;&gt;watch&lt;/span&gt;
    at FSEvent.FSWatcher._handle.onchange &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs.js:1372:28&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
Emitted &lt;span class=&quot;token string&quot;&gt;&#39;error&#39;&lt;/span&gt; event at:
    at NodeWatcher.checkedEmitError &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/Users/seanmcp/dev/REPO/node_modules/sane/src/node_watcher.js:143:12&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at FSWatcher.emit &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events.js:182:13&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at FSEvent.FSWatcher._handle.onchange &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs.js:1378:12&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A Google search of the error message produced three (!) results.
&lt;a href=&quot;https://buildmedia.readthedocs.org/media/pdf/delivery-console/stable/delivery-console.pdf&quot;&gt;The second was a PDF from Mozilla&lt;/a&gt;
that recommended installing &lt;a href=&quot;https://facebook.github.io/watchman/&quot;&gt;&lt;code&gt;watchman&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;why-watchman%3F&quot; tabindex=&quot;-1&quot;&gt;Why &lt;code&gt;watchman&lt;/code&gt;?&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/fix-emfile-too-many-open-files-error-in-jest/#why-watchman%3F&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Why watchman?”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Watchman is a file-watching service from Facebook that will respond to changes
in your files. Jest (also by Facebook)
&lt;a href=&quot;https://jestjs.io/docs/en/cli#--watchman&quot;&gt;default to using watchman in &lt;code&gt;--watch&lt;/code&gt; mode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://facebook.github.io/watchman/docs/install.html&quot;&gt;Watchman is available on all major platforms&lt;/a&gt;.
On a Mac or Linux with Homebrew installed, you can run:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; watchman&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that installed, Jest&#39;s &lt;code&gt;--watch&lt;/code&gt; flag works as expected.&lt;/p&gt;
&lt;p&gt;I hope that works for you (and saves you some valuable time)!&lt;/p&gt;
&lt;p&gt;Happy testing!&lt;/p&gt;
</description>
      <pubDate>Wed, 29 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fix-emfile-too-many-open-files-error-in-jest/</guid>
    </item>
    <item>
      <title>Updated TypeScript mega-course on Egghead.io</title>
      <link>https://seanmcp.com/articles/updated-typescript-mega-course-on-egghead/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://seanmcp.com/articles/updated-typescript-mega-course-on-egghead/typescript-mega-course-on-egghead&quot;&gt;Last month, I embarked on a journey to learn TypeScript better&lt;/a&gt;.
I hand-crafted a TypeScript&amp;quot;mega-course&amp;quot; on &lt;a href=&quot;http://Egghead.io&quot;&gt;Egghead.io&lt;/a&gt; by cobbling multiple
courses into one.&lt;/p&gt;
&lt;p&gt;My goal was to complete the course in three weeks. Now a month and a half later,
I have completed the course and am ready to reflect on my experience.&lt;/p&gt;
&lt;p&gt;Overall, it was a beneficial experience. I learned a lot more about types and
how to write meaningful generics that will help make my code more reusable.&lt;/p&gt;
&lt;p&gt;I also gained a greater appreciation for TypeScript as a language. There were
some examples where I needed to stop the video and try to read what was
happened. I know TS is technically a super-set of JavaScript, but it is 100% a
proficiency of its own.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;updated-mega-course&quot; tabindex=&quot;-1&quot;&gt;Updated Mega-course&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/updated-typescript-mega-course-on-egghead/#updated-mega-course&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Updated Mega-course”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;After completing the course, I want to update and annotate the original
syllabus. Here is the updated course playlist:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Optional:
&lt;a href=&quot;https://egghead.io/courses/up-and-running-with-typescript&quot;&gt;&lt;em&gt;Up and Running with TypeScript&lt;/em&gt;&lt;/a&gt;
by John Lindquist (22 min)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;This was helpful, but very basic. Consider skipping this if you already know
how to set up a TypeScript project&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/use-types-effectively-in-typescript&quot;&gt;&lt;em&gt;Use Types Effectively in TypeScript&lt;/em&gt;&lt;/a&gt;
by Ari Picker (29 min)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;This was a great introduction into TypeScript on its own. I learned a lot
about generics, which are an important feature.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/practical-advanced-typescript&quot;&gt;&lt;em&gt;Practical Advanced TypeScript&lt;/em&gt;&lt;/a&gt;
by Rares Matei (62 min)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;I took more notes on this course than any of the others. This was definitely
the material I needed to introduce me to more advanced TypeScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/advanced-static-types-in-typescript&quot;&gt;&lt;em&gt;Advanced Static Types in TypeScript&lt;/em&gt;&lt;/a&gt;
by Marius Schulz (67 min)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;There was some overlap between this course and the previous, but they both did
a good job of explaining core type concepts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The updated runtime is 2 hours and 38 minutes to 3 hours, depending on whether
you include the optional introduction.&lt;/p&gt;
&lt;p&gt;It also drops both of Basarat Ali Syed&#39;s courses, &lt;em&gt;Async/Await using TypeScript&lt;/em&gt;
and &lt;em&gt;Use Typescript to develop React Applications&lt;/em&gt;. Both were fine but
ultimately weren&#39;t substantial enough to be included in the mega-course.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I hope that was helpful! Let me know if you take this TypeScript &amp;quot;mega-course&amp;quot;
on &lt;a href=&quot;http://Egghead.io&quot;&gt;Egghead.io&lt;/a&gt; and what you think about it.&lt;/p&gt;
&lt;p&gt;Happy learning!&lt;/p&gt;
</description>
      <pubDate>Mon, 27 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/updated-typescript-mega-course-on-egghead/</guid>
    </item>
    <item>
      <title>The Economics of Software Development, Part II</title>
      <link>https://seanmcp.com/articles/the-economics-of-software-development-part-ii/</link>
      <description>&lt;p&gt;&lt;em&gt;This is the second post in a series on how economics helps explain the
decisions we make when developing software. It is a response to &amp;quot;Summer School&amp;quot;,
a series from &lt;a href=&quot;https://www.npr.org/sections/money/&quot;&gt;NPR&#39;s Planet Money&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This week&#39;s
&lt;a href=&quot;https://www.npr.org/2020/07/15/891488686/summer-school-2-markets-pickles&quot;&gt;episode from Planet Money&#39;s &amp;quot;Summer School&amp;quot;&lt;/a&gt;
was about &lt;strong&gt;markets&lt;/strong&gt;: where supply and demand come together. It&#39;s where those
decisions that Economists love so much get made.&lt;/p&gt;
&lt;p&gt;Sometimes a marketplace is like a bazaar (which I tend to picture), but other
times it looks more like
&lt;a href=&quot;https://www.npmjs.com/search?q=capitalize&quot;&gt;the results page when searching for an NPM package&lt;/a&gt;.
With that in mind, it&#39;s easy to see markets everywhere.&lt;/p&gt;
&lt;p&gt;Markets can be freeing, if you&#39;re the one making the decisions. But if you
aren&#39;t in the driver&#39;s chair, you might not even know they exist.&lt;/p&gt;
&lt;p&gt;In general, we want as many decisions as possible to be made by the people who
will be most affected by the decision. I&#39;ll add the caveat: &amp;quot;provided they
affected have the information necessary to make that decision.&amp;quot;&lt;/p&gt;
&lt;p&gt;This is the ideal, but it isn&#39;t the only option.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;suffering-in-a-%22central-market%22&quot; tabindex=&quot;-1&quot;&gt;Suffering in a &amp;quot;central market&amp;quot;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-ii/#suffering-in-a-%22central-market%22&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Suffering in a &amp;quot;central market&amp;quot;”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;An alternative to individuals deciding is central planning. This is when a
higher authority makes the decisions that affect everyone else.&lt;/p&gt;
&lt;p&gt;At tech companies, you can see this in service contracts. In some organizations,
the decision of what SaaS to use comes from the top, and the repercussions
trickle their way down.&lt;/p&gt;
&lt;p&gt;I worked at a large company that had a contract with Microsoft for all of their
office software services. As a result, everyone at the company had to use Skype
and an ancient program called Lync for all of our messaging needs&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-ii/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. Both had
significant shortcomings, and neither served the needs of the tech team well.&lt;/p&gt;
&lt;p&gt;Instead of letting the market decide which messaging app to use, we were forced
to struggle with the inefficiencies of central planning.&lt;/p&gt;
&lt;p&gt;Rather than mandating a solution from the top, organizations should work hard to
give teams and individuals the freedom to gather information and agency to make
those decisions for themselves.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;encouraging-markets&quot; tabindex=&quot;-1&quot;&gt;Encouraging markets&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-ii/#encouraging-markets&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Encouraging markets”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;As developers, we want to structure as much of our infrastructure and processes
to encourage a marketplace of ideas.&lt;/p&gt;
&lt;p&gt;If you have a micro-service architecture, teams are free to choose whatever
language and framework will work best for them. By using CSS for your design
system, each project can pick the JS framework fits their needs.&lt;/p&gt;
&lt;p&gt;Once you have an open marketplace, it&#39;s now on the individuals to make rational
decisions. Now if only we would actually do that&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-ii/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;The company was rolling out Microsoft Teams when I left, which was much
better. &lt;a href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-ii/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Ariely, Dan. (2009). &lt;em&gt;The End of Rational Economics&lt;/em&gt;. Harvard Business
Review. &lt;a href=&quot;https://hbr.org/2009/07/the-end-of-rational-economics&quot;&gt;https://hbr.org/2009/07/the-end-of-rational-economics&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-ii/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Wed, 22 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/the-economics-of-software-development-part-ii/</guid>
    </item>
    <item>
      <title>Access the home directory in Deno</title>
      <link>https://seanmcp.com/articles/access-the-home-directory-in-deno/</link>
      <description>&lt;p&gt;If you want to access the home directory from you Deno program, you can use the
&lt;code&gt;Deno.env&lt;/code&gt;&#39;s &lt;code&gt;get&lt;/code&gt; method with &lt;code&gt;&amp;quot;HOME&amp;quot;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;HOME&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;In unix environments, returns a string along the lines of &lt;code&gt;&amp;quot;/home/USER&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So if you want to write a script that accesses a configuration file like
&lt;code&gt;.bashrc&lt;/code&gt;, you could read the file with:&lt;/p&gt;
&lt;!-- ```js/2 --&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;// read-bashrc.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bashConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readTextFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;HOME&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/.bashrc&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since we are accessing &lt;code&gt;Deno.env&lt;/code&gt;, we need to run the program with the &lt;code&gt;env&lt;/code&gt;
permission:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;deno run --allow-env read-bashrc.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is a Repl where you can
&lt;a href=&quot;https://repl.it/@SeanMcP/deno-access-home-directory&quot;&gt;play around with accessing the home directory in Deno&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Fri, 17 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/access-the-home-directory-in-deno/</guid>
    </item>
    <item>
      <title>The Economics of Software Development, Part I</title>
      <link>https://seanmcp.com/articles/the-economics-of-software-development-part-i/</link>
      <description>&lt;p&gt;&lt;em&gt;This is the first post in a series on how economics helps explain the decisions
we make when developing software. It is a response to &amp;quot;Summer School&amp;quot;, a series
from &lt;a href=&quot;https://www.npr.org/sections/money/&quot;&gt;NPR&#39;s Planet Money&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Let&#39;s look at two concepts from
&lt;a href=&quot;https://www.npr.org/2020/07/07/888690075/summer-school-1-choices-dating&quot;&gt;Planet Money&#39;s &amp;quot;Summer School 1: Choices &amp;amp; Dating&amp;quot;&lt;/a&gt;:
&lt;strong&gt;opportunity cost&lt;/strong&gt;, and &lt;strong&gt;sunk-cost fallacy&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;opportunity-cost&quot; tabindex=&quot;-1&quot;&gt;Opportunity cost&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-i/#opportunity-cost&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Opportunity cost”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This compilation process takes a long time. I&#39;m going to get a snack while I
wait.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Opportunity cost is the cost of doing activity A instead of activity B. For
example, watching another YouTube video before bed comes at the cost of
finishing that novel you started last year&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-i/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I am reminded of opportunity cost whenever I&#39;m waiting for webpack to rebuild a
client and server in development. Every moment I spend waiting is a moment that
I&#39;m doing any development work (and a moment that I&#39;m likely to switch to
another task).&lt;/p&gt;
&lt;p&gt;We should be attuned to these costs and advocate for the time to fix the
underlying problem. If we can reduce these opportunity costs, then we can
increase our productivity and even improve the developer experience.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;sunk-cost-fallacy&quot; tabindex=&quot;-1&quot;&gt;Sunk-cost fallacy&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-i/#sunk-cost-fallacy&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Sunk-cost fallacy”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;We&#39;ve already invested heavily in our Java architecture. Let&#39;s find a way to
make it work for this machine-learning project.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A sunk cost is one that has already been paid and cannot be recovered. The
fallacy comes from making altering future decisions based on those costs.&lt;/p&gt;
&lt;p&gt;In development, it&#39;s common to take one of two paths: a) finding your hammer and
using it for every problem, or b) chasing every new trend under the sun. Both
can illustrate the sunk-cost fallacy, but the former is more straightforward.&lt;/p&gt;
&lt;p&gt;Time spent learning a language, money spent on a subscription service, and
effort spent working on a problem are all sunk costs. Once they&#39;re gone, they&#39;re
gone. The experience can help inform future decisions, but it shouldn&#39;t limit
them with artificial constraints.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;This isn&#39;t autobiographical at all. &lt;a href=&quot;https://seanmcp.com/articles/the-economics-of-software-development-part-i/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Tue, 14 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/the-economics-of-software-development-part-i/</guid>
    </item>
    <item>
      <title>Manage analysis paralysis while programming</title>
      <link>https://seanmcp.com/articles/manage-analysis-paralysis-while-programming/</link>
      <description>&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;references&quot; tabindex=&quot;-1&quot;&gt;References&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/manage-analysis-paralysis-while-programming/#references&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “References”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Raypole, Crystal. (2020). &lt;em&gt;How to Beat ‘Analysis Paralysis’ and Make All the
Decisions&lt;/em&gt;. &lt;a href=&quot;https://www.healthline.com/health/mental-health/analysis-paralysis&quot;&gt;https://www.healthline.com/health/mental-health/analysis-paralysis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Lengsdorf, Jason. (2020, July 17). Twitter.
&lt;a href=&quot;https://twitter.com/jlengstorf/status/1284111359303659521&quot;&gt;https://twitter.com/jlengstorf/status/1284111359303659521&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Adolos. (2020). &lt;em&gt;Feeling unproductive? Maybe you should stop overthinking&lt;/em&gt;.
&lt;a href=&quot;https://adolos.substack.com/p/feeling-unproductive-maybe-you-should&quot;&gt;https://adolos.substack.com/p/feeling-unproductive-maybe-you-should&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&amp;quot;if you&#39;re a creator then you should be engaging in creative thinking on a
regular basis. The more you do it, the better your brain becomes at thinking
up ideas. This makes it easier for you to work on your projects because you
won&#39;t get stuck as often&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;I&#39;ve found that the best way to get into the habit of creative thinking is to
engage in it daily&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;engaging in multiple forms of creative thinking will also help you get
through creative blocks&amp;quot;&lt;/li&gt;
&lt;li&gt;Groth, Aimee. (2013, February 12). &lt;em&gt;SHERYL SANDBERG: Women Need To Get More
Comfortable With Power&lt;/em&gt;. Business Insider.
&lt;a href=&quot;https://www.businessinsider.com/sheryl-sandberg-lean-in-2013-2&quot;&gt;https://www.businessinsider.com/sheryl-sandberg-lean-in-2013-2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&amp;quot;done is better than perfect&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;to-reference&quot; tabindex=&quot;-1&quot;&gt;To reference&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/manage-analysis-paralysis-while-programming/#to-reference&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “To reference”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Grant, Heidi. (2014, February 14). &lt;em&gt;How to Make Yourself Work When You Just
Don’t Want To&lt;/em&gt;.
&lt;a href=&quot;https://hbr.org/2014/02/how-to-make-yourself-work-when-you-just-dont-want-to&quot;&gt;https://hbr.org/2014/02/how-to-make-yourself-work-when-you-just-dont-want-to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Tue, 14 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/manage-analysis-paralysis-while-programming/</guid>
    </item>
    <item>
      <title>Before You Debug</title>
      <link>https://seanmcp.com/articles/before-you-debug/</link>
      <description>&lt;p&gt;Debugging, &amp;quot;the process of finding and resolving defects within a computer
system&amp;quot;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/before-you-debug/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, is a critical skill for software development. It&#39;s how we figure
out what went wrong and how to fix it.&lt;/p&gt;
&lt;p&gt;This can be as informal as throwing &lt;code&gt;* { outline: 1px solid red }&lt;/code&gt; on all your
elements or sophisticated as using an IDE&#39;s debugger.&lt;/p&gt;
&lt;p&gt;But whatever your method, there are certain steps that you can follow to help
yourself debug more logically and efficiently.&lt;/p&gt;
&lt;p&gt;This garden aims to detail everything you should do &lt;em&gt;before you actually start
debugging&lt;/em&gt;. Think of this as the prep work before you start cooking.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;pre-steps&quot; tabindex=&quot;-1&quot;&gt;Pre-steps&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/before-you-debug/#pre-steps&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Pre-steps”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Relax&lt;/strong&gt;: Find a bug can be frustrating, and those emotions can impede the
debugging process. Take a deep breath and dive in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Describe what happened&lt;/strong&gt;: Did the process exit? Did the page fail to
render? Is there a message in the console? Does the UI not respond correctly?
Understanding what happened is the first step to solving a problem. The &amp;quot;how&amp;quot;
comes later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read the error message&lt;/strong&gt;: Most errors will provide you useful information
in the message. Some APIs provide a detailed explanation of what went wrong.
So take the time and read it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Follow the stack trace&lt;/strong&gt;: If the error includes a stack, walk through each
line. Where did the error originate? What it internal or external code?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pinpoint the error&lt;/strong&gt;: Try to isolate the exact point where something went
wrong. In the stack trace, this will usually have a file name and a line
number, &lt;em&gt;e.g.&lt;/em&gt; &lt;code&gt;app.js:25&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inspect the line&lt;/strong&gt;: Read the entire line where the error occurred. Is there
anything that immediately jumps out to you? Sometimes the bug could be as
simple as a typo or an unexpected type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review the block&lt;/strong&gt;: Follow the block from start to finish, making note of
the line in question. Try to get a good sense of how that code is working.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scan any higher scopes&lt;/strong&gt;: Make sure you understand at a high-level what the
code is doing and what variables are needed/expected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make a hypothesis&lt;/strong&gt;: You are a scientist, after all. Make an educated guess
about what went wrong and what will fix it. Make sure to review your
hypothesis while you&#39;re debugging.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;note&quot; tabindex=&quot;-1&quot;&gt;Note&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/before-you-debug/#note&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Note”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This is a living document and will change as my approach to debugging grows over
time. If you have any suggestions, &lt;a href=&quot;https://seanmcp.com/articles/before-you-debug/#comment-link&quot;&gt;please let me know&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;additional-reading&quot; tabindex=&quot;-1&quot;&gt;Additional reading&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/before-you-debug/#additional-reading&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Additional reading”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://jvns.ca/blog/debugging-attitude-matters/&quot;&gt;&lt;em&gt;When debugging, your attitude matters&lt;/em&gt;&lt;/a&gt;
by Julia Evans&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Debugging&quot;&gt;https://en.wikipedia.org/wiki/Debugging&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/before-you-debug/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Fri, 10 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/before-you-debug/</guid>
    </item>
    <item>
      <title>Before Debugging</title>
      <link>https://seanmcp.com/articles/before-debugging/</link>
      <description>&lt;p&gt;Debugging, &amp;quot;the process of finding and resolving defects within a computer
system&amp;quot;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/before-debugging/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, is a critical skill for software development. It&#39;s how we figure
out what went wrong and how to fix it.&lt;/p&gt;
&lt;p&gt;This can be as informal as throwing &lt;code&gt;* { outline: 1px solid red }&lt;/code&gt; on all your
elements or sophisticated as using an IDE&#39;s debugger.&lt;/p&gt;
&lt;p&gt;But whatever your method, there are certain steps that you can follow to help
yourself debug more logically and efficiently.&lt;/p&gt;
&lt;p&gt;This garden aims to detail everything you should do &lt;em&gt;before you actually start
debugging&lt;/em&gt;. Think of this as the prep work before you start cooking.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;pre-steps&quot; tabindex=&quot;-1&quot;&gt;Pre-steps&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/before-debugging/#pre-steps&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Pre-steps”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Relax&lt;/strong&gt;: Find a bug can be frustrating, and those emotions can impede the
debugging process. Take a deep breath and dive in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Describe what happened&lt;/strong&gt;: Did the process exit? Did the page fail to
render? Is there a message in the console? Does the UI not respond correctly?
Understanding what happened is the first step to solving a problem. The &amp;quot;how&amp;quot;
comes later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read the error message&lt;/strong&gt;: Most errors will provide you useful information
in the message. Some APIs provide a detailed explanation of what went wrong.
So take the time and read it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Follow the stack trace&lt;/strong&gt;: If the error includes a stack, walk through each
line. Where did the error originate? What it internal or external code?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pinpoint the error&lt;/strong&gt;: Try to isolate the exact point where something went
wrong. In the stack trace, this will usually have a file name and a line
number, &lt;em&gt;e.g.&lt;/em&gt; &lt;code&gt;app.js:25&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inspect the line&lt;/strong&gt;: Read the entire line where the error occurred. Is there
anything that immediately jumps out to you? Sometimes the bug could be as
simple as a typo or an unexpected type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review the block&lt;/strong&gt;: Follow the block from start to finish, making note of
the line in question. Try to get a good sense of how that code is working.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scan any higher scopes&lt;/strong&gt;: Make sure you understand at a high-level what the
code is doing and what variables are needed/expected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make a hypothesis&lt;/strong&gt;: You are a scientist, after all. Make an educated guess
about what went wrong and what will fix it. Make sure to review your
hypothesis while you&#39;re debugging.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;additional-reading&quot; tabindex=&quot;-1&quot;&gt;Additional reading&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/before-debugging/#additional-reading&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Additional reading”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://jvns.ca/blog/debugging-attitude-matters/&quot;&gt;&lt;em&gt;When debugging, your attitude matters&lt;/em&gt;&lt;/a&gt;
by Julia Evans&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Debugging&quot;&gt;https://en.wikipedia.org/wiki/Debugging&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/before-debugging/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Fri, 10 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/before-debugging/</guid>
    </item>
    <item>
      <title>Intro to &quot;The Economics of Software Development&quot;</title>
      <link>https://seanmcp.com/articles/intro-to-economics-of-software-development/</link>
      <description>&lt;p&gt;Economics was one of my favorite subjects in high school and college. Back
before I made the career switch to software engineers, I would imagine the sweet
&amp;quot;what ifs&amp;quot; of majoring in Econ instead of Education.&lt;/p&gt;
&lt;p&gt;I was (and am) fascinated by economics as the study of decision making. Whether
you prefer to put that in terms of &amp;quot;scarcity&amp;quot; and &amp;quot;resources&amp;quot; is up to you. For
me, it&#39;s about analyzing &amp;quot;why&amp;quot; we do the things that we do.&lt;/p&gt;
&lt;p&gt;You can take that analytical spotlight and shine it on monetary policy or
production, which is probably what most people think of when they hear
&amp;quot;economics.&amp;quot;&lt;/p&gt;
&lt;p&gt;But increasingly economists have been looking at other aspects of life, giving
birth to the field of behavioral economics.
&lt;a href=&quot;https://freakonomics.com/&quot;&gt;&lt;em&gt;Freakonomics&lt;/em&gt;, the book and radio program,&lt;/a&gt; is the
most popular example of this field of study, but you may have heard of other
examples from &lt;a href=&quot;https://en.wikipedia.org/wiki/Richard_Thaler&quot;&gt;Richard Thaler&lt;/a&gt; or
&lt;a href=&quot;https://en.wikipedia.org/wiki/Daniel_Kahneman&quot;&gt;Daniel Kahneman&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When NPR&#39;s Planet Money announced a &amp;quot;Summer School&amp;quot; series focusing on key
economics concepts, I was intrigued. Here was the perfect invitation to do a
little bit of analytical thinking of my own.&lt;/p&gt;
&lt;p&gt;With the release of each episode of &amp;quot;Planet Money Summer School,&amp;quot; I&#39;m going to
respond by taking that lens and the concepts and focus them on the practice of
software development.&lt;/p&gt;
&lt;p&gt;I&#39;m not interested in the &lt;em&gt;business of software development&lt;/em&gt;, but rather the
decisions that I and other developers make in building websites and
applications. In other words: &lt;strong&gt;why we do the things that we do (and what we can
do about it)&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;I hope you&#39;ll find this series interesting, even if it&#39;s just to crack your
knuckles and &lt;a href=&quot;https://seanmcp.com/articles/intro-to-economics-of-software-development/#comment-link&quot;&gt;tell me how I have it all wrong&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy learning!&lt;/p&gt;
</description>
      <pubDate>Thu, 09 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/intro-to-economics-of-software-development/</guid>
    </item>
    <item>
      <title>Nodemon for Deno</title>
      <link>https://seanmcp.com/articles/nodemon-for-deno/</link>
      <description>&lt;p&gt;&lt;em&gt;Update: As of Deno v1.4, there is a built in watch mode that you can enable
with the &lt;code&gt;--watch&lt;/code&gt; flag.
&lt;a href=&quot;https://deno.land/posts/v1.4#codedeno-run---watchcode&quot;&gt;Read more about that here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Nodemon, or as I read it in my head &lt;em&gt;Nodémon!&lt;/em&gt;, is a utility for restarting
Node.js processes when the source changes. It&#39;s is the first dev dependency I
add when working in Node, because it is so handy!&lt;/p&gt;
&lt;p&gt;Deno, the secure runtime for JavaScript and TypeScript, doesn&#39;t work with
Nodemon. When I first started building with Deno, I was reminded just how
annoying it is to stop and start your server whenever there is a change.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://deno.land/x/denon&quot;&gt;Enter &lt;strong&gt;denon&lt;/strong&gt;&lt;/a&gt;, the &amp;quot;replacement&amp;quot; for Nodemon in
Deno land. As long as you&#39;re using &lt;code&gt;deno@^1.0.1&lt;/code&gt;, you can get all the goodness
of automattic restarting with denon.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;install&quot; tabindex=&quot;-1&quot;&gt;Install&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/nodemon-for-deno/#install&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Install”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You can install denon with Deno&#39;s &lt;code&gt;install&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;deno &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --allow-read --allow-run --allow-write --allow-net &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--unstable&lt;/span&gt; https://deno.land/x/denon@v2.2.0/denon.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That command installs denon version &lt;code&gt;2.2.0&lt;/code&gt;, so reference
&lt;a href=&quot;https://deno.land/x/denon#install&quot;&gt;the documentation for the most up-to-date instructions&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;simple-usage&quot; tabindex=&quot;-1&quot;&gt;Simple usage&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/nodemon-for-deno/#simple-usage&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Simple usage”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Once you have denon installed, you can use it as a straight replacement for all
&lt;code&gt;deno&lt;/code&gt; commands:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# With Deno&lt;/span&gt;
deno run --allow-net https://deno.land/std/examples/echo_server.ts

&lt;span class=&quot;token comment&quot;&gt;# With denon&lt;/span&gt;
denon run --allow-net https://deno.land/std/examples/echo_server.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the same behavior as &lt;code&gt;node/nodemon&lt;/code&gt;. But running external code doesn&#39;t
really illustrate the value of denon.&lt;/p&gt;
&lt;p&gt;To see it in action, copy &lt;code&gt;echo_server.ts&lt;/code&gt; locally and then run with denon:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;denon run --allow-net echo_server.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you make any changes to the source, you will see denon automatically
restart the server.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;configuration&quot; tabindex=&quot;-1&quot;&gt;Configuration&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/nodemon-for-deno/#configuration&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Configuration”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;My favorite feature of denon is its configuration file: &lt;code&gt;denon.json&lt;/code&gt;. With it,
you get some of the &amp;quot;scripts&amp;quot; features of Node.js&#39;s &lt;code&gt;package.json&lt;/code&gt; that are
currently missing in Deno.&lt;/p&gt;
&lt;p&gt;To create a configuration file, you can call denon with the &lt;code&gt;--init&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;denon &lt;span class=&quot;token parameter variable&quot;&gt;--init&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a &lt;code&gt;denon.json&lt;/code&gt; file where you can include scripts with
commands, permissions, and environment variables.&lt;/p&gt;
&lt;p&gt;We can create a &amp;quot;start&amp;quot; script for the &lt;code&gt;echo_server.ts&lt;/code&gt; like so:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://deno.land/x/denon/schema.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&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 property&quot;&gt;&quot;start&quot;&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 property&quot;&gt;&quot;cmd&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deno run echo_server.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;desc&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Start echo_server in watch mode&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;allow&quot;&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 string&quot;&gt;&quot;env&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;net&quot;&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 property&quot;&gt;&quot;env&quot;&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 property&quot;&gt;&quot;PORT&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2525&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our &amp;quot;start&amp;quot; script has a command, description, permissions to allow, and an
environment variable &lt;code&gt;PORT&lt;/code&gt; that we are passing to our server.&lt;/p&gt;
&lt;p&gt;To run the script, type:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;denon start&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you&#39;re off to the races! I love the ability to start a Deno process without
typing all those permissions. :chefs-kiss:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;learn-more&quot; tabindex=&quot;-1&quot;&gt;Learn more&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/nodemon-for-deno/#learn-more&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Learn more”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://deno.land/x/denon&quot;&gt;denon documentation&lt;/a&gt; lists the full feature set
and includes a bunch of helpful examples. Make sure to check that out when
setting up denon on your next Deno project.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Wed, 01 Jul 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/nodemon-for-deno/</guid>
    </item>
    <item>
      <title>Adding images to my articles</title>
      <link>https://seanmcp.com/articles/adding-images-to-my-articles/</link>
      <description>&lt;p&gt;Back in February, I
&lt;a href=&quot;https://github.com/SeanMcP/seanmcp.com/commit/b33b30ea11a8fbcf4e4f1753df5ce325ee39917d&quot;&gt;set up post images to this blog&lt;/a&gt;.
Very few of my articles had images, so you may not have noticed.&lt;/p&gt;
&lt;p&gt;In the original design, an image spanned the width of the window, replacing the
blue gradient on most pages.&lt;/p&gt;
&lt;p&gt;This was problematic, because I needed to find large images for it to look
decent on big screens. But I also needed to compress those huge images so that
the file sizes weren&#39;t massive.&lt;/p&gt;
&lt;p&gt;I don&#39;t know how to do the file sizing and compression during a build step, so
instead I avoided added images to any future articles.&lt;/p&gt;
&lt;p&gt;Fast forward a few months, and I wanted to take another crack at article images.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;updating-the-design&quot; tabindex=&quot;-1&quot;&gt;Updating the design&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/adding-images-to-my-articles/#updating-the-design&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Updating the design”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I didn&#39;t realize it initially, but choosing a design with full-screen images set
me down a path of no return. There isn&#39;t a good way to find and serve images
that large.&lt;/p&gt;
&lt;p&gt;So rather than searching for an impossible solution, I changed the design. By
limitting the width of article images, I opened a world of possibilities.&lt;/p&gt;
&lt;p&gt;I now have access to more images, because the only need to be 800 pixels by 400
pixels. At that size, cropped and compressed images are super reasonable.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;how-to-find-images&quot; tabindex=&quot;-1&quot;&gt;How to find images&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/adding-images-to-my-articles/#how-to-find-images&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “How to find images”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;When I want to find an image for an article, I can search the usual places:
Unsplash, Pixabay, or an Google search. Then I can copy the image URL, and head
over to &lt;a href=&quot;http://ezgif.com&quot;&gt;ezgif.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;From there, I will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Resize the image&lt;/li&gt;
&lt;li&gt;Crop it to 2:1&lt;/li&gt;
&lt;li&gt;Compress to &amp;lt; 30 kB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once that&#39;s done, I can save the image to this repo&#39;s &lt;code&gt;img/&lt;/code&gt; directory. The
whole process takes about two minutes.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;room-for-improvement&quot; tabindex=&quot;-1&quot;&gt;Room for improvement&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/adding-images-to-my-articles/#room-for-improvement&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Room for improvement”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;It would be great to automate the resize and compression steps with a build
process, so I&#39;ll be on the lookout for solutions there.&lt;/p&gt;
&lt;p&gt;I would also like to give attribution for the images, where applicable. That
seems best served through frontmatter, but I will have to look into that too.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I hope that helps you when deciding how to add images to your articles!&lt;/p&gt;
&lt;p&gt;Happy writing!&lt;/p&gt;
</description>
      <pubDate>Tue, 30 Jun 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/adding-images-to-my-articles/</guid>
    </item>
    <item>
      <title>Parse argument aliases in Deno</title>
      <link>https://seanmcp.com/articles/parse-argument-aliases-in-deno/</link>
      <description>&lt;p&gt;You can parse command-line arguments in Deno with the
&lt;a href=&quot;https://deno.land/std/flags&quot;&gt;standard &lt;code&gt;flags&lt;/code&gt; module&lt;/a&gt;:&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;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parse &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://deno.land/std/flags/mod.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;args&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;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;deno run https://deno.land/std/examples/flags.ts &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; beep &lt;span class=&quot;token parameter variable&quot;&gt;-b&lt;/span&gt; boop

&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;, a: &lt;span class=&quot;token string&quot;&gt;&#39;beep&#39;&lt;/span&gt;, b: &lt;span class=&quot;token string&quot;&gt;&#39;boop&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;parse&lt;/code&gt; function has a second options argument where you can assign aliases.
Before we do that, let&#39;s use a more description example.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;example-scenario&quot; tabindex=&quot;-1&quot;&gt;Example scenario&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/parse-argument-aliases-in-deno/#example-scenario&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Example scenario”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Say you have a script, &lt;code&gt;log.ts&lt;/code&gt;, that takes a message to display in the
terminal. We would use our script like this:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;deno run log.ts &lt;span class=&quot;token parameter variable&quot;&gt;--message&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello world&quot;&lt;/span&gt;

Hello world&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&#39;s alias the &lt;code&gt;--message&lt;/code&gt; flag to &lt;code&gt;-M&lt;/code&gt; for ease of use.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;alias-option&quot; tabindex=&quot;-1&quot;&gt;Alias option&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/parse-argument-aliases-in-deno/#alias-option&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Alias option”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;When we call &lt;code&gt;parse&lt;/code&gt; in our script on &lt;code&gt;Deno.args&lt;/code&gt;, we want to provide an
additional options object with a key &lt;code&gt;aliases&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That will be assigned to another object that will map flags to their aliases:&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;// log.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parse &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://deno.land/std/flags/mod.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; flagToAliasMap &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 literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;M&quot;&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 keyword&quot;&gt;const&lt;/span&gt; parsedArgs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;args&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 literal-property property&quot;&gt;aliases&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; flagToAliasMap &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;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parsedArgs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&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 we can call our script with those aliases:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;deno run log.ts &lt;span class=&quot;token parameter variable&quot;&gt;-M&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;That&#39;s better&quot;&lt;/span&gt;

That&#39;s better&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, the &lt;code&gt;flags&lt;/code&gt; module does all of the hard lifting for you!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;wrap-up&quot; tabindex=&quot;-1&quot;&gt;Wrap up&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/parse-argument-aliases-in-deno/#wrap-up&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Wrap up”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Two things to note about aliases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;flags&lt;/code&gt; doesn&#39;t care whether you use one or two hyphens for aliases, so &lt;code&gt;-M&lt;/code&gt;
and &lt;code&gt;--M&lt;/code&gt; will both work.&lt;/li&gt;
&lt;li&gt;Aliases &lt;strong&gt;are&lt;/strong&gt; case sensitive, so you&#39;ll have to map &lt;code&gt;m&lt;/code&gt; and &lt;code&gt;M&lt;/code&gt; separately.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Fri, 26 Jun 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/parse-argument-aliases-in-deno/</guid>
    </item>
    <item>
      <title>Await multiple promises in JavaScript</title>
      <link>https://seanmcp.com/articles/await-multiple-promises-in-javascript/</link>
      <description>&lt;p&gt;Let&#39;s say I have two promises: the former resolves in three seconds, while the
latter resolves in five. I need to wait for both of these before moving on to
the next code block.&lt;/p&gt;
&lt;p&gt;Depending on how you structure your promises, you will either have to wait five
seconds or eight seconds before moving on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is the best way to await multiple promises?&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;await-happy&quot; tabindex=&quot;-1&quot;&gt;Await happy&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/await-multiple-promises-in-javascript/#await-happy&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Await happy”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;My first instinct in the above scenario was to use to await both promises.
&lt;code&gt;await&lt;/code&gt; is such a useful keyword in modern JS, that I tend to throw it places
without a second thought:&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;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;promiseOne&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 comment&quot;&gt;// Resolves in three seconds&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;promiseTwo&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 comment&quot;&gt;// Resolves in five seconds&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;done&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks neat and clean, but how long will it take to resolve? &lt;strong&gt;8 seconds&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Look back at the first line. We&#39;re asking the JavaScript engine to await the
resolution of &lt;code&gt;promiseOne&lt;/code&gt;. In effect, we&#39;re asking it to twiddle its thumbs for
three seconds while we wait on the promise.&lt;/p&gt;
&lt;p&gt;When the first resolves, then JavaScript quickly moves on to the next promise.
After waiting for five seconds, it moves on to the log. 3 seconds + 5 seconds =
8 seconds.&lt;/p&gt;
&lt;p&gt;Can we reduce that wait time by refactoring?&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;call-then-await&quot; tabindex=&quot;-1&quot;&gt;Call then await&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/await-multiple-promises-in-javascript/#call-then-await&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Call then await”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Instead of reaching straight for &lt;code&gt;await&lt;/code&gt;, let&#39;s think this through: we want to
&lt;strong&gt;start&lt;/strong&gt; both promises, &lt;strong&gt;then wait&lt;/strong&gt; for their resolution. So let&#39;s change it
up:&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; promiseOnePromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;promiseOne&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 keyword&quot;&gt;const&lt;/span&gt; promiseTwoPromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;promiseOne&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 keyword&quot;&gt;await&lt;/span&gt; promiseOnePromise&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; promiseTwoPromise&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;done&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;How long will it take this code to resolve? &lt;strong&gt;5 seconds&lt;/strong&gt; (roughly).&lt;/p&gt;
&lt;p&gt;By calling the functions that return promises initially, we are &amp;quot;starting the
timer&amp;quot; for both. So the three-second and five-second promises are resolving in
tandem.&lt;/p&gt;
&lt;p&gt;When we &lt;code&gt;await&lt;/code&gt; &lt;strong&gt;after calling the promises&lt;/strong&gt;, we&#39;ll only wait as long as the
slowest promise: five seconds in this case.&lt;/p&gt;
&lt;p&gt;This is great, but there is one more solution that might be easier.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;promise.all&quot; tabindex=&quot;-1&quot;&gt;Promise.all&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/await-multiple-promises-in-javascript/#promise.all&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Promise.all”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;Promise&lt;/code&gt; object has an &lt;code&gt;all&lt;/code&gt; method that accepts any number of promises and
resolves when all have been fulfilled. We just need to pass it an iterable like
an array:&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;await&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&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 function&quot;&gt;promiseOne&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 function&quot;&gt;promiseTwo&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;done&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This functions in a similar manner to the previous &amp;quot;call then await&amp;quot; example but
is more succinct. In my tests, it was fractions of a millisecond slower, but
your mileage may vary.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;live-example&quot; tabindex=&quot;-1&quot;&gt;Live example&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/await-multiple-promises-in-javascript/#live-example&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Live example”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You can view a
&lt;a href=&quot;https://repl.it/@SeanMcP/await-multiple-promises&quot;&gt;live example of this code on Repl&lt;/a&gt;:&lt;/p&gt;
&lt;iframe height=&quot;500px&quot; width=&quot;100%&quot; src=&quot;https://repl.it/@SeanMcP/await-multiple-promises?lite=true&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot; sandbox=&quot;allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-modals&quot;&gt;&lt;/iframe&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/await-multiple-promises-in-javascript/#conclusion&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Conclusion”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;There we go! Two solutions for how to await multiple promises in JavaScript.
Hope that helped!&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Wed, 17 Jun 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/await-multiple-promises-in-javascript/</guid>
    </item>
    <item>
      <title>TypeScript mega-course on Egghead.io</title>
      <link>https://seanmcp.com/articles/typescript-mega-course-on-egghead/</link>
      <description>&lt;p&gt;&lt;em&gt;This playlist has been updated.
&lt;a href=&quot;https://seanmcp.com/articles/typescript-mega-course-on-egghead/updated-typescript-mega-course-on-egghead&quot;&gt;Check out the latest version of my TypeScript &amp;quot;mega-course&amp;quot; on Egghead.io&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I would say that I have &amp;quot;functional&amp;quot; understanding of TypeScript. I can get the
job done (at least most of the time), but as soon as the types get hairy I
struggle.&lt;/p&gt;
&lt;p&gt;As TypeScript contiues grow in popularity, this will become a liability. To fix
this, I have set a professional goal to improve my understanding of TypeScript.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://Niche.com&quot;&gt;Niche.com&lt;/a&gt; provides us with a membership to &lt;a href=&quot;http://Egghead.io&quot;&gt;Egghead.io&lt;/a&gt;, so I&#39;ve cobbled together
multiple courses on TypeScript:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;mega-course&quot; tabindex=&quot;-1&quot;&gt;Mega-course&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/typescript-mega-course-on-egghead/#mega-course&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Mega-course”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/up-and-running-with-typescript&quot;&gt;&lt;em&gt;Up and Running with TypeScript&lt;/em&gt;&lt;/a&gt;
by John Lindquist (22 min)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/use-types-effectively-in-typescript&quot;&gt;&lt;em&gt;Use Types Effectively in TypeScript&lt;/em&gt;&lt;/a&gt;
by Ari Picker (29 min)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/async-await-using-typescript&quot;&gt;&lt;em&gt;Async/Await using TypeScript&lt;/em&gt;&lt;/a&gt;
by Basarat Ali Syed (17 min)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/practical-advanced-typescript&quot;&gt;&lt;em&gt;Practical Advanced TypeScript&lt;/em&gt;&lt;/a&gt;
by Rares Matei (62 min)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/advanced-static-types-in-typescript&quot;&gt;&lt;em&gt;Advanced Static Types in TypeScript&lt;/em&gt;&lt;/a&gt;
by Marius Schulz (67 min)&lt;/li&gt;
&lt;li&gt;Optional:
&lt;a href=&quot;https://egghead.io/courses/use-typescript-to-develop-react-applications&quot;&gt;&lt;em&gt;Use TypeScript to develop React Applications&lt;/em&gt;&lt;/a&gt;
by Basarat Ali Syed (27 min)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The plan is to complete these courses over the next three weeks. At the end,
I&#39;ll review the mega-course and update the order if necessary.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Mon, 15 Jun 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/typescript-mega-course-on-egghead/</guid>
    </item>
    <item>
      <title>Six video update</title>
      <link>https://seanmcp.com/articles/six-video-update/</link>
      <description>&lt;p&gt;A few weeks ago, I wrote about
&lt;a href=&quot;https://seanmcp.com/articles/six-video-update/publishing-my-first-youtube-video&quot;&gt;publishing my first YouTube video&lt;/a&gt;. In
that article, I included my reasoning and what I hoped to gain from it.&lt;/p&gt;
&lt;p&gt;My goal is to upload twelve videos, that get an average of twelve views, and get
to twelve subscribers.&lt;/p&gt;
&lt;p&gt;Last night, I uploaded my sixth video,
&lt;a href=&quot;https://www.youtube.com/watch?v=yMEpIOyoizU&quot;&gt;&lt;em&gt;Set Breakpoints In The Chrome DevTools&lt;/em&gt;&lt;/a&gt;,
which puts me at the half-way mark.&lt;/p&gt;
&lt;p&gt;&lt;progress style=&quot;width:100%&quot; min=&quot;0&quot; max=&quot;12&quot; value=&quot;6&quot;&gt;6/12 or 50%&lt;/progress&gt;&lt;/p&gt;
&lt;p&gt;I wanted to share some of the lessons that I learned making and publishing
videos 2-6.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;lessons-learned&quot; tabindex=&quot;-1&quot;&gt;Lessons learned&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/six-video-update/#lessons-learned&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Lessons learned”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;recording-gets-easier&quot; tabindex=&quot;-1&quot;&gt;Recording gets easier&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/six-video-update/#recording-gets-easier&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Recording gets easier”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;For me, it doesn&#39;t feel natural to sit and talk to myself in front of the
computer. But with practice, I found myself getting more comfortable.&lt;/p&gt;
&lt;p&gt;There is a long way to go, but it is encouraging to see growth after making six
videos.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;keep-it-rolling&quot; tabindex=&quot;-1&quot;&gt;Keep it rolling&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/six-video-update/#keep-it-rolling&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Keep it rolling”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;By keeping a continous recording, I am able to fix mistakes as they come and
edit them out later. If anything sounds weird as I say it, I just take a break,
and then try the line again.&lt;/p&gt;
&lt;p&gt;This is far easier than trying to get a good &amp;quot;take&amp;quot; of the whole video. I found
that a 3-4 minute video would come from 10-12 minutes of continous recording.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;editing-gets-easier&quot; tabindex=&quot;-1&quot;&gt;Editing gets easier&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/six-video-update/#editing-gets-easier&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Editing gets easier”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I don&#39;t think I could say that I &lt;em&gt;like&lt;/em&gt; iMovie, but I&#39;m getting used to the
interface and the steps that I need to go through to edit my videos.&lt;/p&gt;
&lt;p&gt;The biggest frustration that remains is the slider for audio volume (no input?)
and the tendency to export the source video instead of the editted version (why
is that possible?).&lt;/p&gt;
&lt;p&gt;That being said, I think iMovie will be sufficient for the time being.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;equipment-matters&quot; tabindex=&quot;-1&quot;&gt;Equipment matters&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/six-video-update/#equipment-matters&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Equipment matters”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I started off using a $50 microphone from Amazon. It worked, but the audio
quality wasn&#39;t great and it didn&#39;t provide any control over the recording.&lt;/p&gt;
&lt;p&gt;I traded that in for a Blue Yeti Nano, and the improvement has been noticeable.
I&#39;m still getting into the swing of things, but having the right tools makes a
difference.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;very-few-views&quot; tabindex=&quot;-1&quot;&gt;Very few views&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/six-video-update/#very-few-views&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Very few views”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Since that initial video, I have averaged 1.2 views per video and gained 2
subscribers (one of whom is my wife). This is lower than I expected, but good to
know.&lt;/p&gt;
&lt;p&gt;I want to stress again that &lt;strong&gt;gaining views and subscribers is not by end
game&lt;/strong&gt;. Including those two measurements in the goal was an attempt to gauge the
utility.&lt;/p&gt;
&lt;p&gt;This may be aided by more promotion on my end, but with everything going on in
the world that didn&#39;t seem the most helpful thing to do.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Once I publish my twelfth video, I want to take a deeper dive into the
performance of each video to see what I can glean going forward.&lt;/p&gt;
&lt;p&gt;Until then, &lt;a href=&quot;https://bit.ly/seanmcp-youtube&quot;&gt;happy watching&lt;/a&gt;!&lt;/p&gt;
</description>
      <pubDate>Fri, 12 Jun 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/six-video-update/</guid>
    </item>
    <item>
      <title>Read a json file in Deno</title>
      <link>https://seanmcp.com/articles/read-a-json-file-in-deno/</link>
      <description>&lt;p&gt;Deno is a &lt;strong&gt;secure&lt;/strong&gt; runtine for JavaScript, so to read a file the program needs
explicit permissions. We can enable file reading with the &lt;code&gt;--allow-read&lt;/code&gt; flag in
the command line:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;deno run --allow-read read-json.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the permissions are straight, we need to read the file. There are a few
methods on the &lt;code&gt;Deno&lt;/code&gt; module to use, so let&#39;s look at two options: &lt;code&gt;readFile&lt;/code&gt;,
and &lt;code&gt;readTextFile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s take a look at them separately.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;readfile&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;readFile&lt;/code&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/read-a-json-file-in-deno/#readfile&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “readFile”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Deno&#39;s &lt;code&gt;readFile&lt;/code&gt; method returns a Promise resolves with a Uint8Array. You can
call the method like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data.json&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;So if we run the following code in a directory with a &lt;code&gt;data.json&lt;/code&gt; file, you
would see a log like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Uint8Array(29) [
  123,  10,  32,  32,  32, 32,  34, 115,
  111, 117, 114,  99, 101, 34,  58,  32,
   34, 100,  97, 116,  97, 46, 106, 115,
  111, 110,  34,  10, 125
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to read this, we need a new text decoder decoder with and pass the
file&#39;s encoding:&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; decoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TextDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;utf-8&quot;&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 keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data.json&quot;&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;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;decoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when we run the code, we see the contents of &lt;code&gt;data.json&lt;/code&gt; logged in the
console:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;source&amp;quot;: &amp;quot;data.json&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks good, but it is just a formatted string. To consume this data, we
need to use the global &lt;code&gt;JSON&lt;/code&gt; object&#39;s &lt;code&gt;parse&lt;/code&gt; method to get a usuable json
object.&lt;/p&gt;
&lt;!-- ```js/2 --&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; decoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TextDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;utf-8&quot;&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 keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data.json&quot;&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;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;decoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run it again, and you will see whatever data you had stored in your json file
logged to the console. You have successfully read a json file in Deno with
&lt;code&gt;readFile&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;If you know that your data is utf8 encoded, you can skip a step with the next
Deno method.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;readtextfile&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;readTextFile&lt;/code&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/read-a-json-file-in-deno/#readtextfile&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “readTextFile”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Deno&#39;s &lt;code&gt;readTextFile&lt;/code&gt; method returns a Promise that resolves to a utf8 decoded
string. You can call the method like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readTextFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data.json&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once again, if we run this code in a directory with a &lt;code&gt;data.json&lt;/code&gt; file, you
would see the file&#39;s contents logged in the console:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;source&amp;quot;: &amp;quot;data.json&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks good, but just like before it is only a formatted string. To consume
this data, we need to use the global &lt;code&gt;JSON&lt;/code&gt; object&#39;s &lt;code&gt;parse&lt;/code&gt; method again to get
a usuable json object.&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; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readTextFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data.json&quot;&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;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run it again, and you will see whatever data you had stored in your json file
logged to the console. You have successfully read a json file in Deno with
&lt;code&gt;readTextFile&lt;/code&gt;!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/read-a-json-file-in-deno/#conclusion&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Conclusion”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;If you know that your file is utf8 encoded, using &lt;code&gt;Deno.readTextFile&lt;/code&gt; is an easy
way to grab the contents of a file. Otherwise, &lt;code&gt;Deno.readFile&lt;/code&gt; and a decoder can
get you on your way.&lt;/p&gt;
&lt;p&gt;Whichever method you choose, just remember to parse the json data before trying
to consume it.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Thu, 28 May 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/read-a-json-file-in-deno/</guid>
    </item>
    <item>
      <title>Read a json file in Node.js</title>
      <link>https://seanmcp.com/articles/read-a-json-file-in-node-js/</link>
      <description>&lt;p&gt;To read any file in Node.js, you need to import the &lt;code&gt;fs&lt;/code&gt; or file system module.
From there, you have two methods from which to choose: &lt;code&gt;readFile&lt;/code&gt;, and
&lt;code&gt;readFileSync&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Both are similar in that they return
&lt;a href=&quot;https://nodejs.org/dist/latest-v14.x/docs/api/buffer.html#buffer_buffer&quot;&gt;a Buffer of binary data&lt;/a&gt;
from a file. &lt;code&gt;readFile&lt;/code&gt; accepts a callback that will receive the Buffer, while
&lt;code&gt;readFileSync&lt;/code&gt; returns it synchronously.&lt;/p&gt;
&lt;p&gt;In action, they look like:&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; fs &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;&quot;fs&quot;&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;

fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./data.json&quot;&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;error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&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;
  error &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&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 keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./data.json&quot;&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 class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run this code in a directory with a &lt;code&gt;data.json&lt;/code&gt; file, boths methods will
log something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Buffer 7b 0a 20 20 22 64 61 74 61 22 3a 20 74 72 75 65 0a 7d&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both are returning the same data, but it isn&#39;t consumable. In most instances,
you will need to do one more operation in order to use the data that you have
read from a file.&lt;/p&gt;
&lt;p&gt;Since we are working with a json file, we can use the global &lt;code&gt;JSON&lt;/code&gt; object to
read the data. The &lt;code&gt;parse&lt;/code&gt; method will convert this Buffer into a usuable JSON
object:&lt;/p&gt;
&lt;!-- ```js/3,7 --&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; fs &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;&quot;fs&quot;&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;

fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./data.json&quot;&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;error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&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;
  error &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&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 class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./data.json&quot;&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run it again, and you will see whatever data you had stored in your json file
logged to the console. You have successfully read a json file in Node.js!&lt;/p&gt;
&lt;p&gt;You can see the code in action here:&lt;/p&gt;
&lt;iframe height=&quot;500px&quot; width=&quot;100%&quot; src=&quot;https://repl.it/@SeanMcP/Read-json-file-in-Nodejs?lite=true&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot; sandbox=&quot;allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-modals&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Wed, 27 May 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/read-a-json-file-in-node-js/</guid>
    </item>
    <item>
      <title>Publishing my first YouTube video</title>
      <link>https://seanmcp.com/articles/publishing-my-first-youtube-video/</link>
      <description>&lt;p&gt;Yesterday, I published my first video on YouTube:
&lt;a href=&quot;https://www.youtube.com/watch?v=1bjwBppbpxk&quot;&gt;&amp;quot;Three Ways To Open The Firefox DevTools&amp;quot;&lt;/a&gt;.
As the title suggests, it covers the three main ways of opening the DevTools in
Firefox, the browser menu, &amp;quot;Inspect Element&amp;quot;, and the keyboard shortcut.&lt;/p&gt;
&lt;p&gt;This is new territory for me, so I wanted to take a moment and reflect on why I
started a YouTube channel, what I hope to gain from it, and what I learned in
the process.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;a-new-career-goal&quot; tabindex=&quot;-1&quot;&gt;A new career goal&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/publishing-my-first-youtube-video/#a-new-career-goal&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “A new career goal”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Before working as a software engineer, I worked for three years as a teacher.
And while that specific career was right for me, I am still passionate about
education and sharing what I know with others. So I am making it a career goal
to teach on the side.&lt;/p&gt;
&lt;p&gt;This focus will complement my work as an engineer for all the wonderful ways
that teaching is helpful. It will help me be a better team member and mentor at
work. And it is an opportunity to give back to a developer community that has
already given me so much.&lt;/p&gt;
&lt;p&gt;Eventually, I would like to lead workshops and teach on-line courses, but that
is more of a 3-5 year plan.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;plans-for-youtube&quot; tabindex=&quot;-1&quot;&gt;Plans for YouTube&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/publishing-my-first-youtube-video/#plans-for-youtube&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Plans for YouTube”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I am going to produce short, informative videos in the same style as &lt;a href=&quot;http://Egghead.io&quot;&gt;Egghead.io&lt;/a&gt;.
I have really enjoyed their content and think that there is a strong formula for
laser-focused content.&lt;/p&gt;
&lt;p&gt;With that in mind, I&#39;m aiming for videos in the 3-10 minute range: long enough
to be valuable, but not long winded.&lt;/p&gt;
&lt;p&gt;My goal for YouTube is to put out twelve videos that have an average of twelve
views and gain twelve subscribers. I&#39;m hoping that this will take three months,
but I don&#39;t want to commit to a hard timeline.&lt;/p&gt;
&lt;p&gt;After hitting twelve videos, I will reassess my efforts and determine if YouTube
is the right platform for this.&lt;/p&gt;
&lt;p&gt;So this first video puts me one step on that journey.&lt;/p&gt;
&lt;p&gt;&lt;progress max=&quot;12&quot; value=&quot;1&quot; style=&quot;width:100%&quot;&gt;1/12&lt;/progress&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;lessons-learned&quot; tabindex=&quot;-1&quot;&gt;Lessons learned&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/publishing-my-first-youtube-video/#lessons-learned&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Lessons learned”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;harder-than-it-looks&quot; tabindex=&quot;-1&quot;&gt;Harder than it looks&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/publishing-my-first-youtube-video/#harder-than-it-looks&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Harder than it looks”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In a world of endless high-quality content on YouTube, it&#39;s easy to assume that
making videos is easy. At least that&#39;s what I did. But every step of the process
was more difficult that I had anticipated.&lt;/p&gt;
&lt;p&gt;Writing, recording, and editing are all skills unto themselves, and ones that I
am not very good at. All of this will require practice.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;i-need-a-script&quot; tabindex=&quot;-1&quot;&gt;I need a script&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/publishing-my-first-youtube-video/#i-need-a-script&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “I need a script”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;When I was teaching, some lesson plans had a script for the teacher to read. In
front of the classroom, I found this restricting and would avoid reading
verbatim.&lt;/p&gt;
&lt;p&gt;In front of a screen, however, the dynamic changes. You can&#39;t see faces to meter
your delivery and the lesson pace. As a result, I find myself getting further
off-topic during digital instruction than I did in-person.&lt;/p&gt;
&lt;p&gt;But with a script, it&#39;s different. I can plan my thoughts ahead of time and hit
all the intended points, only lingering when I intend. Writing this kind of
script is a skill that I&#39;m developing, but they are a valuable part of the
lesson.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;things-to-improve&quot; tabindex=&quot;-1&quot;&gt;Things to improve&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/publishing-my-first-youtube-video/#things-to-improve&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Things to improve”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;editing&quot; tabindex=&quot;-1&quot;&gt;Editing&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/publishing-my-first-youtube-video/#editing&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Editing”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I edited this video with iMovie, and it was a new experience. The &amp;quot;Project&amp;quot;
structure is a little confusing, and I had a little bit of trouble importing the
video. But the biggest hangup was the preview, which would occassionally not
follow the scrubber.&lt;/p&gt;
&lt;p&gt;As a result, I didn&#39;t edit this video as much as I would have liked. Going
forward, I want to watch a few videos on editing in iMovie and see if I can
resolve those issues.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;multiple-takes&quot; tabindex=&quot;-1&quot;&gt;Multiple takes&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/publishing-my-first-youtube-video/#multiple-takes&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Multiple takes”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Going into filming, I planned to get everything correct in one go. When
something went wrong, I stopped the recording, reset, and then started from
scratch.&lt;/p&gt;
&lt;p&gt;That is not sustainable. Instead, I want to keep the &amp;quot;film rolling.&amp;quot; If I mess
up, just wait a beat and do the line correctly. This should increase the
recordig process because I&#39;m not repeating lines that are good for the sake of
ones that need fixed.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;mouth-sounds&quot; tabindex=&quot;-1&quot;&gt;Mouth sounds&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/publishing-my-first-youtube-video/#mouth-sounds&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Mouth sounds”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Hoo boy, do I make a lot of mouth sounds. My wife had some helpful suggestions
that I am going to try in the next recording:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stay hydrated: Keep a glass of water on hand and drink when you&#39;re thirsty&lt;/li&gt;
&lt;li&gt;Record one line at a time: Whether that is a sentence or a paragraph, do it
in one go without swallowing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once I can isolate the content better, it will be easier for me to edit out
those unpleasant sounds.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I plan to post an update on my goal once I&#39;ve hit the &amp;quot;six video&amp;quot; mark. Until
then you can
&lt;a href=&quot;https://www.youtube.com/channel/UC5KVcnzcZn_zCYXGR1ErV_A&quot;&gt;watch my progress on YouTube&lt;/a&gt;
and &lt;a href=&quot;https://bit.ly/subscribe-seanmcp&quot;&gt;subscribe to stay updated&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy watching!&lt;/p&gt;
</description>
      <pubDate>Wed, 20 May 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/publishing-my-first-youtube-video/</guid>
    </item>
    <item>
      <title>How to get the browser and version in JavaScript</title>
      <link>https://seanmcp.com/articles/how-to-get-the-browser-version-in-javascript/</link>
      <description>&lt;p&gt;For a recent project, I wanted to display the browser and version on the screen.
I figured that this would be possible in JavaScript, so I went searching around.&lt;/p&gt;
&lt;p&gt;This led me to the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent&quot;&gt;User-Agent&lt;/a&gt;,
&lt;a href=&quot;https://en.wikipedia.org/wiki/User_agent#User_agent_sniffing&quot;&gt;&amp;quot;sniffing&amp;quot;&lt;/a&gt;, and
its
&lt;a href=&quot;https://www.w3.org/community/webed/wiki/Optimizing_content_for_different_browsers:_the_RIGHT_way#A_brief_history_of_browser_sniffing&quot;&gt;wild and crazy history&lt;/a&gt;.
I also read a bunch of articles on why serving different content to different
browsers is a bad idea.
&lt;a href=&quot;https://seanmcp.com/articles/how-to-get-the-browser-version-in-javascript/#additional-reading&quot;&gt;I&#39;ll link them at the bottom of this article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I just want to display the browser and version, so the referencing the user
agent is a fine enough solution
(&lt;a href=&quot;https://css-tricks.com/freezing-user-agent-strings/&quot;&gt;even if temporary&lt;/a&gt;).
&lt;strong&gt;But you should definitely think twice before you go user agent sniffing&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;accessing-the-user-agent&quot; tabindex=&quot;-1&quot;&gt;Accessing the user agent&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-get-the-browser-version-in-javascript/#accessing-the-user-agent&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Accessing the user agent”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You can access the user agent from the read-only
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Navigator&quot;&gt;global &lt;code&gt;Navigator&lt;/code&gt; interface&lt;/a&gt;.
That&#39;s as simple as writing:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userAgent&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;Depending on your browser, you should see a few different outputs:&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;// Firefox&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:77.0) Gecko/20100101 Firefox/77.0&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Chrome&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Edge (Chromium)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 Edg/81.0.416.77&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Safari&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are fascinating, but for this article we are only interested in the
differences. If we just look at the tail for each, we get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Firefox: &lt;code&gt;&amp;quot;Firefox/77.0&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Chrome: &lt;code&gt;&amp;quot;Chrome/81.0.4044.138 Safari/537.36&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Edge (Chromium): &lt;code&gt;&amp;quot;Chrome/81.0.4044.138 Safari/537.36 Edg/81.0.416.77&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Safari: &lt;code&gt;&amp;quot;Version/13.1 Safari/605.1.15&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this, we know have enough information to parse the string.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;sniffing-logic&quot; tabindex=&quot;-1&quot;&gt;Sniffing logic&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-get-the-browser-version-in-javascript/#sniffing-logic&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Sniffing logic”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Chrome, Edge, and Safari all identify as &amp;quot;Safari&amp;quot;, so let&#39;s start our logic with
the simplest user agent: Firefox. After that, it&#39;s a process of determining who
has a unique string to search for:&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; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; userAgent &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Firefox/&quot;&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 comment&quot;&gt;// Firefox&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Edg/&quot;&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 comment&quot;&gt;// Edge (Chromium)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chrome/&quot;&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 comment&quot;&gt;// Chrome&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Safari/&quot;&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 comment&quot;&gt;// Safari&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you can identify the browser from the user agent, it&#39;s just a matter of
using your favorite string parsing method to grab the version. Here&#39;s the code
for Firefox:&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Firefox/&quot;&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 comment&quot;&gt;// Firefox&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&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;Firefox v&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Firefox/&quot;&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 number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&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 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;If you know of a better way, please
&lt;a href=&quot;https://twitter.com/intent/tweet?text=.@snmcp&quot;&gt;tweet me your solution&lt;/a&gt; and I&#39;ll
give you a shoutout here.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;additional-reading&quot; tabindex=&quot;-1&quot;&gt;Additional reading&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-get-the-browser-version-in-javascript/#additional-reading&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Additional reading”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent&quot;&gt;&lt;em&gt;Browser detection using the user agent&lt;/em&gt;&lt;/a&gt;
on MDN&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/browser-detection-is-bad/&quot;&gt;&lt;em&gt;Browser Detection is Bad&lt;/em&gt;&lt;/a&gt;
by Chris Coyier on CSS-Tricks&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webaim.org/blog/user-agent-string-history/&quot;&gt;&lt;em&gt;History of the browser user-agent string&lt;/em&gt;&lt;/a&gt;
by Aaron Andersen on WebAIM&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/community/webed/wiki/Optimizing_content_for_different_browsers:_the_RIGHT_way&quot;&gt;&lt;em&gt;Optimizing content for different browsers: the RIGHT way § 2.2&lt;/em&gt;&lt;/a&gt;
on W3&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Tue, 19 May 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-to-get-the-browser-version-in-javascript/</guid>
    </item>
    <item>
      <title>Logging with Eleventy and Nunjucks</title>
      <link>https://seanmcp.com/articles/logging-with-eleventy-and-nunjucks/</link>
      <description>&lt;p&gt;This site was forked from the
&lt;a href=&quot;https://github.com/11ty/eleventy-base-blog&quot;&gt;Eleventy base blog&lt;/a&gt; and uses
&lt;a href=&quot;https://mozilla.github.io/nunjucks/&quot;&gt;Nunjucks templates&lt;/a&gt;. When everything is
working, it is as smooth as can be. But when I run into an issue, I often find
it difficult to debug.&lt;/p&gt;
&lt;p&gt;Recently I was trying to do some logical rendering based on article tags. I knew
the information was at my finger tips, but I couldn&#39;t guess the right key. &lt;strong&gt;All
I need is a simple &lt;code&gt;console.log()&lt;/code&gt;&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;I searched combinations like &amp;quot;debugging in eleventy&amp;quot; and &amp;quot;console logging in
nunjucks&amp;quot;, but none of the suggestions quite worked. But they did give me an
idea: a custom filter for logging.&lt;/p&gt;
&lt;p&gt;Opening up &lt;code&gt;.eleventy.js&lt;/code&gt;, I added the following lines:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&quot;log&quot;&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;value&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;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, I could then use the &lt;code&gt;log&lt;/code&gt; filter in my Nunjuck template:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;li class=&amp;quot;article&amp;quot;&amp;gt; ... &amp;lt;/li&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I had Eleventy logging the entire article object in the terminal! Exactly
what I needed.&lt;/p&gt;
&lt;p&gt;I tinkered around with returning a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; from the filter to log on the
client too, but that required &lt;code&gt;JSON.stringify()&lt;/code&gt; and piping the result into the
&lt;code&gt;safe&lt;/code&gt; filter. That was more than I wanted to mess with, but there might be a
solution there that works for you.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Tue, 12 May 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/logging-with-eleventy-and-nunjucks/</guid>
    </item>
    <item>
      <title>Store readable data</title>
      <link>https://seanmcp.com/articles/store-readable-data/</link>
      <description>&lt;p&gt;Imagine that you want to store data to look up the continent for every country
in the world. The best data structure for this is a object/map/dictionary to
quickly access the value without looping. I&#39;ll use the former for my examples:&lt;/p&gt;
&lt;p&gt;Your object map will look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;Algeria&amp;quot;: &amp;quot;Africa&amp;quot;,
    &amp;quot;Angola&amp;quot;: &amp;quot;Africa&amp;quot;,
    &amp;quot;Benin&amp;quot;: &amp;quot;Africa,
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is well and good, but you&#39;ll notice one bit of data is repeated: continent.
Any time you have the same bit of data repeated, you run the risk of typing it
incorrectly: &lt;code&gt;&amp;quot;Africa&amp;quot; != &amp;quot;Afrcia&amp;quot;&lt;/code&gt;. This is smell number one.&lt;/p&gt;
&lt;p&gt;But let&#39;s ignore that for now. We want to include all of the countries in the
world, so let&#39;s add a few more:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;Afghanistan&amp;quot;: &amp;quot;Asia&amp;quot;,
    &amp;quot;Algeria&amp;quot;: &amp;quot;Africa&amp;quot;,
    &amp;quot;Argentina&amp;quot;: &amp;quot;South America&amp;quot;,
    &amp;quot;Armenia&amp;quot;: &amp;quot;Asia&amp;quot;,
    &amp;quot;Azerbaijan&amp;quot;: &amp;quot;Asia&amp;quot;,
    &amp;quot;Angola&amp;quot;: &amp;quot;Africa&amp;quot;,
    &amp;quot;Benin&amp;quot;: &amp;quot;Africa,
    &amp;quot;Bolivia&amp;quot;: &amp;quot;South America&amp;quot;,
    &amp;quot;Brazil&amp;quot;: &amp;quot;South America&amp;quot;,
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As this structure grows to accommodate all of our data, it becomes less for
humans and therefore less maintainable.&lt;/p&gt;
&lt;p&gt;South America has 3 countries and/or territories that start with &amp;quot;B&amp;quot;... do we
have them all? It&#39;s possible to answer that question, but it&#39;s not easy. This is
is smell number two.&lt;/p&gt;
&lt;p&gt;Instead of continuing down this path, let&#39;s go back to the drawing board.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;best-for-whom%3F&quot; tabindex=&quot;-1&quot;&gt;Best for whom?&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/store-readable-data/#best-for-whom%3F&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Best for whom?”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Our decision in choosing a data structure was to do what is best &lt;em&gt;for the
computer&lt;/em&gt;. Objects are a quick and easy way to retrieve the information that we
need.&lt;/p&gt;
&lt;p&gt;But in making that decision, we assumed that the best option was what is best
for the computer. Instead, what if we stored data in the way that is best &lt;em&gt;for
the human&lt;/em&gt; maintaining it?&lt;/p&gt;
&lt;p&gt;We would still start with an object/map/dictionary, but countries wouldn&#39;t be at
the top level:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;Africa&amp;quot;: [
        &amp;quot;Algeria&amp;quot;,
        &amp;quot;Angola&amp;quot;,
        &amp;quot;Benin&amp;quot;,
        // ...
    ],
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead we would put continents first. Why? Because our brains like to organize
the information we encounter into useful groups. In this scenario, the most
useful arrangement for counties is by continent.&lt;/p&gt;
&lt;p&gt;With this approach, we made it easier to read and maintain, and removed
duplicated data.&lt;/p&gt;
&lt;p&gt;However, this structure is harder for the computer to read. So before we start
this loop over again, let&#39;s add one more bit of code: a transformer.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;transformer&quot; tabindex=&quot;-1&quot;&gt;Transformer&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/store-readable-data/#transformer&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Transformer”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This is a function that will take our data in a particular format and
&amp;quot;transform&amp;quot; it into another. In our example, it will turn our human readable
code and turn it into computer usable code.&lt;/p&gt;
&lt;p&gt;The implementation of this particular function isn&#39;t important, so just focus on
the input and output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;humanReadable = {
    &amp;quot;Africa&amp;quot;: [
        &amp;quot;Algeria&amp;quot;,
        &amp;quot;Angola&amp;quot;,
        &amp;quot;Benin&amp;quot;,
    ]
}

computerUsable = {
    &amp;quot;Algeria&amp;quot;: &amp;quot;Africa&amp;quot;,
    &amp;quot;Angola&amp;quot;: &amp;quot;Africa&amp;quot;,
    &amp;quot;Benin&amp;quot;: &amp;quot;Africa&amp;quot;,
}

transformer(humanReadable) = computerUsable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we run the transformer once at the beginning of our code, we now have the
readibility of the first structure &lt;em&gt;and&lt;/em&gt; the usability of the second.&lt;/p&gt;
&lt;p&gt;So store readable data. You, or the developer after you, will thank you for it.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Mon, 11 May 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/store-readable-data/</guid>
    </item>
    <item>
      <title>MagnoliaJS 2020 conference notes</title>
      <link>https://seanmcp.com/articles/magnoliajs-2020-conference-notes/</link>
      <description>&lt;p&gt;MagnoliaJS was thrown a COVID-19-sized curve ball for it&#39;s second annual
conference. But that didn&#39;t stop the organizer&#39;s from hosting Mississippi&#39;s only
technical conference: they cut the price and pivoted online with Zoom.&lt;/p&gt;
&lt;p&gt;Niche graciously &amp;quot;sent&amp;quot; me to this two-day remote conference and here are some
of my notes:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;day-1&quot; tabindex=&quot;-1&quot;&gt;Day 1&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#day-1&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Day 1”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;keynote%3A-i&#39;m-techie-and-i-know-it&quot; tabindex=&quot;-1&quot;&gt;Keynote: &lt;em&gt;I&#39;m Techie and I Know It&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#keynote%3A-i&#39;m-techie-and-i-know-it&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Keynote: I&#39;m Techie and I Know It”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Nashlie Sephus | @phenomenashlie&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find the area that you are most passionate about, and look for problems in
that space&lt;/li&gt;
&lt;li&gt;With that problem in mind, look for ways to use technology to solve it&lt;/li&gt;
&lt;li&gt;There are ethnic disparities in data samples that negatively impact the
products we create&lt;/li&gt;
&lt;li&gt;We need to make a concerted effort to gather representative data&lt;/li&gt;
&lt;li&gt;Good data in; good data out. Garbage in; garbage out.&lt;/li&gt;
&lt;li&gt;Everybody has a responsibility to give back to the community that helped
create them&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;es8%2C-es9%2C-es10%2B%E2%80%A6-let%E2%80%99s-explore-javascript%E2%80%99s-exciting-new-features&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;ES8, ES9, ES10+… Let’s explore JavaScript’s exciting new features&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#es8%2C-es9%2C-es10%2B%E2%80%A6-let%E2%80%99s-explore-javascript%E2%80%99s-exciting-new-features&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “ES8, ES9, ES10+… Let’s explore JavaScript’s exciting new features”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Tyler Clark | @iamtylerclark&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/twclark0/magnoliajs-2020/blob/master/deck.mdx&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In 2015 after a long quiet period, the largest release of JavaScript dropped
&lt;ul&gt;
&lt;li&gt;This was ES6, and it was too much&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Since then, there have been smaller, yearly releases&lt;/li&gt;
&lt;li&gt;ES2016
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Array.includes()&lt;/code&gt; replaced &lt;code&gt;[&#39;foo&#39;].indexOf(&#39;foo&#39;) &amp;gt; -1&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Array.includes()&lt;/code&gt; has an optional second argument for starting point&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;**&lt;/code&gt; operator replaced &lt;code&gt;Math.pow()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Tagged template literals allow you to pass strings and values to functions
&lt;ul&gt;
&lt;li&gt;This pattern is used by libraries like &lt;code&gt;styled-components&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ES2017
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Promise.finally()&lt;/code&gt; allows you to write one cleanup step for promises&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ES2019
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Array.flat()&lt;/code&gt; combines nested arrays into a single array
&lt;ul&gt;
&lt;li&gt;To flatten recursively, you can pass an &lt;code&gt;Infitity&lt;/code&gt; argument&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Object.fromEntries()&lt;/code&gt; convert an array into an object&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ES2020
&lt;ul&gt;
&lt;li&gt;Dynamic imports: Global &lt;code&gt;import()&lt;/code&gt; that returns a promise that resolves with
the module&lt;/li&gt;
&lt;li&gt;Nullish coalescing: &lt;code&gt;??&lt;/code&gt; checks for null or undefined
&lt;code&gt;js 0 || &#39;fallback&#39; // &#39;fallback&#39; 0 ?? &#39;fallback&#39; // 0 &lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Optional chaining: &lt;code&gt;?.&lt;/code&gt; will returned undefined if any layer of an object
doesn&#39;t exist
&lt;ul&gt;
&lt;li&gt;Works with dynamic properties: &lt;code&gt;props?.data?.[props.name]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Promise.allSettled&lt;/code&gt;: Like &lt;code&gt;Promise.all()&lt;/code&gt; but fulfills even on rejection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Future
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Promise.any()&lt;/code&gt;: First promise that is a success or when all are rejected&lt;/li&gt;
&lt;li&gt;Private class methods and fields: &lt;code&gt;#&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Top-level await: &lt;code&gt;await&lt;/code&gt; does not need to be in wrapped in an &lt;code&gt;async&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Renamed exports: &lt;code&gt;export foo as bar from &#39;./file&#39;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Logical assignments: Assign variable based on logical conditions
&lt;ul&gt;
&lt;li&gt;Examples &lt;code&gt;||=&lt;/code&gt;, &lt;code&gt;&amp;amp;&amp;amp;=&lt;/code&gt;, &lt;code&gt;??=&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;static-generated-sites-%3D%3D%3D-great-performance.-what-are-you-waiting-for%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Static generated sites === great performance. What are you waiting for?&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#static-generated-sites-%3D%3D%3D-great-performance.-what-are-you-waiting-for%3F&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Static generated sites === great performance. What are you waiting for?”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Debbie O&#39;Brien | @debs_obrien&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://noti.st/debbie/8qdhog/static-generated-sites-great-performance-what-are-you-waiting-for&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Static is the new dynamic (which was the new static)&lt;/li&gt;
&lt;li&gt;You can mix and match static sites and single-paged applications
&lt;ul&gt;
&lt;li&gt;It&#39;s not &amp;quot;all or nothing&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Next/Nuxt are great options for walking the line&lt;/li&gt;
&lt;li&gt;Performance tips:
&lt;ul&gt;
&lt;li&gt;Audit your site for performance regularly&lt;/li&gt;
&lt;li&gt;Uses a service to optimize your images
&lt;ul&gt;
&lt;li&gt;Quality and format&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reduce your CSS bundle with smart classes&lt;/li&gt;
&lt;li&gt;Send modern JavaScript to modern browsers&lt;/li&gt;
&lt;li&gt;Split your code&lt;/li&gt;
&lt;li&gt;Lazy-load as much as possible&lt;/li&gt;
&lt;li&gt;Audit your webpack build with the &lt;code&gt;-a&lt;/code&gt; flag&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Jamstack is worth it&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;design-systems-%26-css-variables%3A-2-design-2-systems&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Design Systems &amp;amp; CSS Variables: 2 Design 2 Systems&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#design-systems-%26-css-variables%3A-2-design-2-systems&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Design Systems &amp;amp; CSS Variables: 2 Design 2 Systems”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Scott Tolinski | @stolinski&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/stolinski/Design-Systems-CSS-Variables&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CSS variables are also known as CSS custom properties
&lt;ul&gt;
&lt;li&gt;Not supported in IE&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Declare a variable in a scope, and then reference it with the &lt;code&gt;var()&lt;/code&gt; function
&lt;ul&gt;
&lt;li&gt;First thought it was a liability&lt;/li&gt;
&lt;li&gt;After further reflection: scoping is a great feature&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reassign variables based on any selector: classes, media queries, &lt;em&gt;etc&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Design systems comprise: Color, Type, Spacing, Character, Elevation&lt;/li&gt;
&lt;li&gt;Pay attention to vertical spacing and font size&lt;/li&gt;
&lt;li&gt;Margins are tricky: stick with &lt;code&gt;margin-bottom&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Styled Components allow you to scope CSS when you want it and embrace the
cascade when you don&#39;t
&lt;ul&gt;
&lt;li&gt;You can reference CSS variables insided &lt;code&gt;styled&lt;/code&gt; components&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Elements should be: simple, obvious, hard to break, and extensible
&lt;ul&gt;
&lt;li&gt;Make components that can&#39;t be goofed up&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You don&#39;t need a grid system: use CSS grid or flex box&lt;/li&gt;
&lt;li&gt;Frameworks need to be taught
&lt;ul&gt;
&lt;li&gt;Write only what you need, and use variables as the backbone
&lt;ul&gt;
&lt;li&gt;Colors, typography, and spacing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;vintage-bundles%3A-modern-javascript-for-modern-browsers&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Vintage Bundles: Modern JavaScript for Modern Browsers&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#vintage-bundles%3A-modern-javascript-for-modern-browsers&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Vintage Bundles: Modern JavaScript for Modern Browsers”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Sia Karamalegos | @TheGreenGreek&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gist.github.com/siakaramalegos/3ae7bdd2398ef75b5d621ee1101bec1e&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Transpiling to older versions of JavaScript increases the side of our code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;module&amp;quot;&amp;gt;&lt;/code&gt; is an indicator whether the browser supports ES2015+&lt;/li&gt;
&lt;li&gt;You can save 24-70% in JS bundle sizes&lt;/li&gt;
&lt;li&gt;Reduce load AND parse/eval time
&lt;ul&gt;
&lt;li&gt;Translates to real business gains&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Balance speed and support with differential serving
&lt;ul&gt;
&lt;li&gt;Generate a modern bundle and a legacy bundle&lt;/li&gt;
&lt;li&gt;A main bundle that loads a polyfill on older browsers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Module scripts are supported by 90%+ of browsers by usage&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;twitter-driven-development%2C-redux&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Twitter Driven Development, Redux&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#twitter-driven-development%2C-redux&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Twitter Driven Development, Redux”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Justin Samuels | @ThugDebugger&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Three keys of asking a good question in a tweet:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Ask: Is this a relevant question? Is it clear? Is it concise?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;People have stuff to do, minimize the amount of time they need to take to
help you&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pour: Share with others the knowledge that has been shared with you&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A candle doesn&#39;t lose it&#39;s intensity by lighting another&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Offer to help other people&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Effort: This is the hardest part&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&#39;t make excuses; do the thing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Another acronym, ATL: ask the question, teach others, and learn new things&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;create-dynamic-web-apps-with-serverless-and-the-jamstack&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Create Dynamic Web Apps With Serverless and the Jamstack&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#create-dynamic-web-apps-with-serverless-and-the-jamstack&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Create Dynamic Web Apps With Serverless and the Jamstack”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Jason Lengstorf | @jlengstorf&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://magnolia-serverless.netlify.app/&quot;&gt;&lt;strong&gt;Demo&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We still need servers&lt;/li&gt;
&lt;li&gt;&amp;quot;Serverless&amp;quot; just means someone else&#39;s servers
&lt;ul&gt;
&lt;li&gt;Managed infrastructure&lt;/li&gt;
&lt;li&gt;Let the experts (AWS, Microsoft, Google) do the heavy lifting&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Serverless lets us focus on logic, not infrastructure
&lt;ul&gt;
&lt;li&gt;It&#39;s hard, because it&#39;s a lot (and it&#39;s repetitive)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Live demo of &lt;a href=&quot;https://www.netlify.com/products/functions/&quot;&gt;Netlify Functions&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;A way to add severless actions to static sites on Netlify&lt;/li&gt;
&lt;li&gt;Pretty cool&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;day-2&quot; tabindex=&quot;-1&quot;&gt;Day 2&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#day-2&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Day 2”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;which-database%3F-how-to-choose-the-best-place-for-your-json-documents&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Which Database? How to Choose the Best Place for your JSON Documents&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#which-database%3F-how-to-choose-the-best-place-for-your-json-documents&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Which Database? How to Choose the Best Place for your JSON Documents”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Suphatra Rufo | @skprufo&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Database overview&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Relational databases developed by IBM back in the 1970s&lt;/li&gt;
&lt;li&gt;Non-relational databases respond better to unexpected events
&lt;ul&gt;
&lt;li&gt;Instagram spikes at New Years handled better by non-relational dbs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JSON is the &lt;em&gt;de facto&lt;/em&gt; way to communicate data&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Key value&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Dictionary data structure
&lt;ul&gt;
&lt;li&gt;Pros: simple, ess memory, better performance, partiionable, horizontal
scaling&lt;/li&gt;
&lt;li&gt;Cons: no query language, key management is vital, freeform search impossible&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AWS DynamoDB&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Document-oriented databases&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;A better way to store JSON&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Multi-modes&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Support and mange different models&lt;/li&gt;
&lt;li&gt;Couchbase can normalize and denormalize documents&lt;/li&gt;
&lt;li&gt;SQL querying with NQL&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;thar-be-dragons%3A-rebuilding-native-uis-on-the-web&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Thar Be Dragons: Rebuilding Native UIs on the Web&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#thar-be-dragons%3A-rebuilding-native-uis-on-the-web&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Thar Be Dragons: Rebuilding Native UIs on the Web”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Chance Strickland | @chancethedev&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tender-stonebraker-e2b443.netlify.app/&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The web started as a platform for sharing documents&lt;/li&gt;
&lt;li&gt;As it grew, we started to develop more interactive elements&lt;/li&gt;
&lt;li&gt;Today, we have complex HTML elements for rich interactions
&lt;ul&gt;
&lt;li&gt;Why don&#39;t we use them?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reasons why we build custom controls: appearance, functionality, browser
differences, accessibility issues&lt;/li&gt;
&lt;li&gt;Libraries can only do so much to recreate native UI elements&lt;/li&gt;
&lt;li&gt;Takeaways
&lt;ol&gt;
&lt;li&gt;Use HTML&lt;/li&gt;
&lt;li&gt;Identify good primitives&lt;/li&gt;
&lt;li&gt;Understand the full scope&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;svelte-and-the-great-space-elevator&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Svelte and the Great Space Elevator&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#svelte-and-the-great-space-elevator&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Svelte and the Great Space Elevator”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Shawn Wang | @swyx&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1fTdTyCVdiHC7PvyYRzmessS09TfLFDXEk50aRhb-JRM/edit&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;
| &lt;a href=&quot;https://youtu.be/atOIxTHylF8&quot;&gt;&lt;strong&gt;Talk&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How do we get into space?
&lt;ul&gt;
&lt;li&gt;There is a tradeoff between the amount of fuel you need (to carry valuable
payload) and the weight from that additional fuel&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How do we make the web?
&lt;ul&gt;
&lt;li&gt;The web wasn&#39;t designed for everything that we use it for&lt;/li&gt;
&lt;li&gt;This is especially true for mobile web (low powered, slow connections)&lt;/li&gt;
&lt;li&gt;JavaScript is really heavy&lt;/li&gt;
&lt;li&gt;The framework that you choose has a big impact on the ultimate weight&lt;/li&gt;
&lt;li&gt;The tyranny of run-time frameworks:
&lt;ul&gt;
&lt;li&gt;Choose two:
&lt;ul&gt;
&lt;li&gt;Ship more features&lt;/li&gt;
&lt;li&gt;Write less code&lt;/li&gt;
&lt;li&gt;Send less JavaScript&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A way out
&lt;ul&gt;
&lt;li&gt;Put up a rope!&lt;/li&gt;
&lt;li&gt;A space elevator is a big upfront investment, but a low per use cost&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Compilers as the future
&lt;ul&gt;
&lt;li&gt;Tom Dale&#39;s &lt;em&gt;Compilers are the new Frameworks&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Frameworks are shifting towards smart compiling to reduce bundles&lt;/li&gt;
&lt;li&gt;Svelte
&lt;ul&gt;
&lt;li&gt;Write less code: learn extra syntax, but it will be worth the time
&lt;ul&gt;
&lt;li&gt;Personal experience: 20% less code, 93% smaller bundle&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;No virtual DOM: it&#39;s as close to writing vanilla JS as possible
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Compile Svelte in Your Head&lt;/em&gt; by Tan Li Hau&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Truly reactive: Reponds to changes with &lt;code&gt;$&lt;/code&gt; syntax&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Lighter tooling that only makes it to the build when used&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;progressive-enhancement-in-react&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Progressive Enhancement in React&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#progressive-enhancement-in-react&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Progressive Enhancement in React”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Kayla Sween | @_kaylasween&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/kaylasween/accessibility/blob/master/PEforReact-MagnoliaJS2020.pdf&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spectrum of delivery: HTLM &amp;gt; CSS &amp;gt; JavaScript
&lt;ul&gt;
&lt;li&gt;Structure it with HTML&lt;/li&gt;
&lt;li&gt;Make it pretty with CSS&lt;/li&gt;
&lt;li&gt;Add behavior with JavaScript&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Why Progressive Enhancement?
&lt;ul&gt;
&lt;li&gt;Enhanced accessibility and performance&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Start with core functionality and choose the simplest technologies to make it
work&lt;/li&gt;
&lt;li&gt;Then you can sprinkle on optional features&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;building-jamstack-cms%3A-a-full-stack-cms-for-the-modern-age&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Building JAMstack CMS: A Full Stack CMS for the Modern Age&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#building-jamstack-cms%3A-a-full-stack-cms-for-the-modern-age&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Building JAMstack CMS: A Full Stack CMS for the Modern Age”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Nader Dabit&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.dropbox.com/sh/bhdci4t3z4qupiv/AAC7fQO6IfwUfvIkFiv_eg6Qa?dl=0&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Wordpress alternative with all jamstack technologies&lt;/li&gt;
&lt;li&gt;More information: &lt;a href=&quot;https://github.com/jamstack-cms&quot;&gt;https://github.com/jamstack-cms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;handling-data-with-react-query&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Handling Data with React Query&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#handling-data-with-react-query&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Handling Data with React Query”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Domitrius Clark | @domitriusclark&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://slides.com/domitrius/react-query/fullscreen&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt; |
&lt;a href=&quot;https://egghead.io/playlists/use-react-query-s-hooks-to-query-and-mutate-data-in-your-react-components-64d6?af=4roogg&quot;&gt;&lt;strong&gt;Course&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I accidentally missed this!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;canvas%3A-the-final-escape-hatch&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;Canvas: The Final Escape Hatch&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#canvas%3A-the-final-escape-hatch&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Canvas: The Final Escape Hatch”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Ken Wheeler | @ken_wheeler&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://drive.google.com/open?id=1OdDF3VZwuBmRQWRPhN-sWzl7PQJw3mQd&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Canvas is a browser API to draw 2D graphics&lt;/li&gt;
&lt;li&gt;WebGL: alternative, high performance API for drawing on the &lt;code&gt;canvas&lt;/code&gt; element
with the GL language&lt;/li&gt;
&lt;li&gt;For games, data vizualization, high-performance graphics, granular control
over rendering&lt;/li&gt;
&lt;li&gt;Drawbacks: No layouts, no z-index, bad a11y, with a strange imperative API&lt;/li&gt;
&lt;li&gt;Drawing methods
&lt;ul&gt;
&lt;li&gt;Immediate: Redraws the entire canvas every time
&lt;ul&gt;
&lt;li&gt;Tough on CPU&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Patching: Only redraw pars that have changed
&lt;ul&gt;
&lt;li&gt;Hard to handle full draws&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Compositing: Layering convases&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Events: All events are listening to the parent &lt;code&gt;canvas&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Use a spacial index with a library like RBush&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Scroll: Let the browser and DOM help you
&lt;ul&gt;
&lt;li&gt;Using overflow and elements porportional to the content&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;You can use a layout solution like Yoga (&lt;code&gt;yoga-layout&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Plug: &lt;code&gt;react-blazing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Other libraries: Skia, Lottie, PixiJS&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;how-to-supercharge-teams-with-carefully-crafted-conventions&quot; tabindex=&quot;-1&quot;&gt;&lt;em&gt;How to Supercharge Teams with Carefully Crafted Conventions&lt;/em&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#how-to-supercharge-teams-with-carefully-crafted-conventions&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “How to Supercharge Teams with Carefully Crafted Conventions”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Speaker&lt;/strong&gt;: Chris Ball | @cball_&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://speakerdeck.com/cball/how-to-supercharge-teams-with-carefully-crafted-conventions&quot;&gt;&lt;strong&gt;Slides&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every good scalable system relies on process and automation
&lt;ul&gt;
&lt;li&gt;How you develop, your workflows, how you do things&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Process is built by conventions&lt;/li&gt;
&lt;li&gt;How to be your best dev:
&lt;ul&gt;
&lt;li&gt;Play to your strengths&lt;/li&gt;
&lt;li&gt;Understand what slows you down&lt;/li&gt;
&lt;li&gt;Eliminate decision fatigue&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Examples in the wild
&lt;ul&gt;
&lt;li&gt;Next.js: leans in to standardized conventions in React&lt;/li&gt;
&lt;li&gt;GraphQL: a system for requesting data and documenting&lt;/li&gt;
&lt;li&gt;Utility css frameworks&lt;/li&gt;
&lt;li&gt;Semantic versioning&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Crafting your own
&lt;ul&gt;
&lt;li&gt;Find a base and build on it
&lt;ul&gt;
&lt;li&gt;If you&#39;re disabling everything, it means that you are following the wrong
convention&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create a plugin for your team&lt;/li&gt;
&lt;li&gt;Add a PR checklist
&lt;ul&gt;
&lt;li&gt;Screenshots (GIFs)&lt;/li&gt;
&lt;li&gt;Automated tests&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Leverage generators
&lt;ul&gt;
&lt;li&gt;Hygen templates that live alongside the code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How to perfect process
&lt;ul&gt;
&lt;li&gt;Synchronization: Understand the vision with RFCs
&lt;ul&gt;
&lt;li&gt;Summary, motivation, examples, how to teach, how to enforce, drawbacks,
and questions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Repitition: Continually refining with retros&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;reflection&quot; tabindex=&quot;-1&quot;&gt;Reflection&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/magnoliajs-2020-conference-notes/#reflection&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Reflection”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I enjoyed having a single track of JS-focused talks. The remote conference had
it&#39;s own technical issues, but overall I think it was a great content delivery
method for these types of topics.&lt;/p&gt;
&lt;p&gt;Some of the talks were prerecorded, which might sound like a let down, but the
speakers were presents in the chat and willing to answer questions live. That
was a neat feature that other conferences cannot offer.&lt;/p&gt;
&lt;p&gt;Whether it&#39;s remote or in-person in 2021, I recommend checking out MagnoliaJS
Conference.&lt;/p&gt;
</description>
      <pubDate>Thu, 16 Apr 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/magnoliajs-2020-conference-notes/</guid>
    </item>
    <item>
      <title>How to open the Dev Tools</title>
      <link>https://seanmcp.com/articles/how-to-open-dev-tools/</link>
      <description>&lt;p&gt;As a web developer, one of the most powerful tools in your arsenal is the
browser&#39;s developer tools.&lt;/p&gt;
&lt;p&gt;Here is a list of all the ways to access the dev tools for your browser and
operating system.&lt;/p&gt;
&lt;hr&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;with-your-mouse&quot; tabindex=&quot;-1&quot;&gt;With your mouse&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#with-your-mouse&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “With your mouse”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;in-the-window&quot; tabindex=&quot;-1&quot;&gt;In the window&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#in-the-window&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “In the window”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You can enter the dev tools with the mouse by right clicking on any element. In
the menu that appears, select &amp;quot;Inspect&amp;quot; or &amp;quot;Inspect Element&amp;quot;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chrome&lt;/strong&gt;: &lt;code&gt;Right click &amp;gt; Inspect&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Firefox&lt;/strong&gt;: &lt;code&gt;Right click &amp;gt; Inspect Element&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;through-settings&quot; tabindex=&quot;-1&quot;&gt;Through settings&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#through-settings&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Through settings”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You can also enter the dev tools through your browser&#39;s settings menu.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chrome&lt;/strong&gt;: &lt;code&gt;Three dot menu &amp;gt; More tools &amp;gt; Developer tools&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Firefox&lt;/strong&gt;: &lt;code&gt;Three bar menu &amp;gt; Web Developer &amp;gt; Toggle Tools&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;keyboard-shortcuts&quot; tabindex=&quot;-1&quot;&gt;Keyboard shortcuts&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#keyboard-shortcuts&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Keyboard shortcuts”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Beyond the mouse, there are shortcuts that you can use to open the dev tools
without leaving your keyboard.&lt;/p&gt;
&lt;p&gt;The shortcuts differ by browser and operating system, so I&#39;ve tried to group
them logically below.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; Windows and Linux commands are the same, so I&#39;ve
comibined them into &amp;quot;Win/Lin&amp;quot;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;elements%2Finspector&quot; tabindex=&quot;-1&quot;&gt;Elements/Inspector&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#elements%2Finspector&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Elements/Inspector”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The default tab, it allows you to inspect and edit the elements in the DOM,
their properties, and styles.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h4 id=&quot;chrome-%26-firefox&quot; tabindex=&quot;-1&quot;&gt;Chrome &amp;amp; Firefox&lt;/h4&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#chrome-%26-firefox&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Chrome &amp;amp; Firefox”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt;: &lt;kbd&gt;command&lt;/kbd&gt; + &lt;kbd&gt;option&lt;/kbd&gt; + &lt;kbd&gt;c&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Win/Lin&lt;/strong&gt;: &lt;kbd&gt;control&lt;/kbd&gt; + &lt;kbd&gt;shift&lt;/kbd&gt; + &lt;kbd&gt;c&lt;/kbd&gt; &lt;strong&gt;OR&lt;/strong&gt;
&lt;kbd&gt;F12&lt;/kbd&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;console&quot; tabindex=&quot;-1&quot;&gt;Console&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#console&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Console”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This tab prints the console calls from your code and allows you to write
JavaScript that interacts with the current web page.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h4 id=&quot;chrome&quot; tabindex=&quot;-1&quot;&gt;Chrome&lt;/h4&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#chrome&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Chrome”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt;: &lt;kbd&gt;command&lt;/kbd&gt; + &lt;kbd&gt;option&lt;/kbd&gt; + &lt;kbd&gt;j&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Win/Lin&lt;/strong&gt;: &lt;kbd&gt;control&lt;/kbd&gt; + &lt;kbd&gt;shift&lt;/kbd&gt; + &lt;kbd&gt;j&lt;/kbd&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h4 id=&quot;firefox&quot; tabindex=&quot;-1&quot;&gt;Firefox&lt;/h4&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#firefox&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Firefox”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt;: &lt;kbd&gt;command&lt;/kbd&gt; + &lt;kbd&gt;option&lt;/kbd&gt; + &lt;kbd&gt;k&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Win/Lin&lt;/strong&gt;: &lt;kbd&gt;control&lt;/kbd&gt; + &lt;kbd&gt;shift&lt;/kbd&gt; + &lt;kbd&gt;k&lt;/kbd&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;debugger&quot; tabindex=&quot;-1&quot;&gt;Debugger&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#debugger&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Debugger”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This tab shows all the source code for your project and allows you to debug with
breakpoints.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h4 id=&quot;firefox-1&quot; tabindex=&quot;-1&quot;&gt;Firefox&lt;/h4&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#firefox-1&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Firefox”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt;: &lt;kbd&gt;command&lt;/kbd&gt; + &lt;kbd&gt;option&lt;/kbd&gt; + &lt;kbd&gt;z&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Win/Lin&lt;/strong&gt;: &lt;kbd&gt;control&lt;/kbd&gt; + &lt;kbd&gt;shift&lt;/kbd&gt; + &lt;kbd&gt;z&lt;/kbd&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;network&quot; tabindex=&quot;-1&quot;&gt;Network&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#network&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Network”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This tab logs all network requests and provides detailed information about the
responses.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h4 id=&quot;firefox-2&quot; tabindex=&quot;-1&quot;&gt;Firefox&lt;/h4&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#firefox-2&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Firefox”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt;: &lt;kbd&gt;command&lt;/kbd&gt; + &lt;kbd&gt;option&lt;/kbd&gt; + &lt;kbd&gt;e&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Win/Lin&lt;/strong&gt;: &lt;kbd&gt;control&lt;/kbd&gt; + &lt;kbd&gt;shift&lt;/kbd&gt; + &lt;kbd&gt;e&lt;/kbd&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;most-recent&quot; tabindex=&quot;-1&quot;&gt;Most recent&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#most-recent&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Most recent”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This will open the most recently opened tab. In my experience, it&#39;s the shortcut
that I reach for most often.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h4 id=&quot;chrome-%26-firefox-1&quot; tabindex=&quot;-1&quot;&gt;Chrome &amp;amp; Firefox&lt;/h4&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#chrome-%26-firefox-1&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Chrome &amp;amp; Firefox”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt;: &lt;kbd&gt;command&lt;/kbd&gt; + &lt;kbd&gt;option&lt;/kbd&gt; + &lt;kbd&gt;i&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Win/Lin&lt;/strong&gt;: &lt;kbd&gt;control&lt;/kbd&gt; + &lt;kbd&gt;shift&lt;/kbd&gt; + &lt;kbd&gt;i&lt;/kbd&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Phew! That was a bigger undertaking than I first imagined. If I made any
mistakes, &lt;a href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#comment-link&quot;&gt;please let me know&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I hope that helps! Happy tooling!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I spent a good deal of time debating how to outline this article. I
eventually decided to group things by dev tool tab instead of browser or
operating system. There is more in common than not, so this seemed the
simplest. &lt;a href=&quot;https://seanmcp.com/articles/how-to-open-dev-tools/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Wed, 15 Apr 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-to-open-dev-tools/</guid>
    </item>
    <item>
      <title>Tools for custom Slack emojis</title>
      <link>https://seanmcp.com/articles/tools-for-custom-slack-emojis/</link>
      <description>&lt;p&gt;Custom emojis are a fun way to customize your Slack workplace. The fun, silly,
and can provide some genuine utility for the rest of your team.&lt;/p&gt;
&lt;p&gt;Slack already
&lt;a href=&quot;https://slack.com/help/articles/206870177-Add-custom-emoji&quot;&gt;has a guide for adding custom emojis&lt;/a&gt;,
so I won&#39;t be reinventing the wheel here. But I wanted to share some of the
online tools that I use to whip out some fresh emojis for every occasion.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;tools&quot; tabindex=&quot;-1&quot;&gt;Tools&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/tools-for-custom-slack-emojis/#tools&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Tools”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;google-images&quot; tabindex=&quot;-1&quot;&gt;Google Images&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/tools-for-custom-slack-emojis/#google-images&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Google Images”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;When looking for an emoji, I usually start with
&lt;a href=&quot;https://images.google.com/&quot;&gt;a Google Images search&lt;/a&gt;. Once I get the results, I
use their tools to change the color and the usage rights.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Tools &amp;gt; Color &amp;gt; Transparent&lt;/code&gt;: Gets you those sweet transparent backgrounds&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Tools &amp;gt; Usage Rights&lt;/code&gt;: I don&#39;t often do this because most images are not
correctly labelled, but it&#39;s still good to know&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once I find the perfect image, just right-click and download the image or save
the URL to use in another tool.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;svg-to-png&quot; tabindex=&quot;-1&quot;&gt;SVG to PNG&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/tools-for-custom-slack-emojis/#svg-to-png&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “SVG to PNG”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Sometimes the perfect image from Google is an SVG, but sadly Slack does not
support SVG emojis. &lt;a href=&quot;https://svgtopng.com&quot;&gt;Enter SVG to PNG&lt;/a&gt;, which – you
guessed it – converts SVGs to usable PNGs.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;remove-bg&quot; tabindex=&quot;-1&quot;&gt;Remove BG&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/tools-for-custom-slack-emojis/#remove-bg&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Remove BG”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Other times the perfect image is a JPG or something with a solid white
background. You can use these as emojis, but they don&#39;t look as nice. Instead,
you can take that image
&lt;a href=&quot;https://www.remove.bg&quot;&gt;and easily remove the background with removebg&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;ez-gif&quot; tabindex=&quot;-1&quot;&gt;EZ Gif&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/tools-for-custom-slack-emojis/#ez-gif&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “EZ Gif”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This is probably my favorite tool on this list because it works so well. Despite
the name, &lt;a href=&quot;https://ezgif.com/&quot;&gt;EZGif.com is for editing all types of images&lt;/a&gt;
with crops, resizes, optimizations, and other effects. The neatest bit is that
you can pipe the output from one edit into another.&lt;/p&gt;
&lt;p&gt;You might start with one image from Google, then crop it, resize it, and
optimize it to make it ready for Slack.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I hope that helps! Happy reacting!&lt;/p&gt;
</description>
      <pubDate>Tue, 14 Apr 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/tools-for-custom-slack-emojis/</guid>
    </item>
    <item>
      <title>Laws I Love</title>
      <link>https://seanmcp.com/articles/laws-i-love/</link>
      <description>&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;laws&quot; tabindex=&quot;-1&quot;&gt;Laws&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/laws-i-love/#laws&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Laws”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Golden Rule&lt;/strong&gt;: Treat your neighbor as you would like to be treated&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Campbell&#39;s Law&lt;/strong&gt;: Using an social indicator for decision-making will lead to
distortion and corruption of the process it is intended to monitor
(&lt;a href=&quot;https://en.wikipedia.org/wiki/Campbell%27s_law&quot;&gt;source&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Goodhart&#39;s Law&lt;/strong&gt;: When a measure becomes a target, it ceases to be a good
measure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parkinson&#39;s Law&lt;/strong&gt;: Work expands to the time allotted&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Tue, 14 Apr 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/laws-i-love/</guid>
    </item>
    <item>
      <title>Programming reads</title>
      <link>https://seanmcp.com/articles/programming-reads/</link>
      <description>&lt;p&gt;This is an alphabetical list that has helped shape the way that I think about
software engineering.&lt;/p&gt;
&lt;p&gt;At some point in the future I may add some commentary on why I found these
insightful. Until then, you&#39;ll have to form those opinions for yourself.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;articles&quot; tabindex=&quot;-1&quot;&gt;Articles&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/programming-reads/#articles&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Articles”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://signalvnoise.com/posts/3124-give-it-five-minutes&quot;&gt;&lt;em&gt;Give it five minutes&lt;/em&gt;&lt;/a&gt;
by Jason Fried&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://overreacted.io/optimized-for-change/&quot;&gt;&lt;em&gt;Optimize for change&lt;/em&gt;&lt;/a&gt; by Dan
Abramov&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction&quot;&gt;&lt;em&gt;The wrong abstraction&lt;/em&gt;&lt;/a&gt;
by Sandi Metz&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.brandonsmith.ninja/blog/write-code-not-too-much-mostly-functions&quot;&gt;&lt;em&gt;Write code. Not too much. Mostly functions.&lt;/em&gt;&lt;/a&gt;
by Brandon Smith&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to&quot;&gt;&lt;em&gt;Write code that is easy to delete, not easy to extend.&lt;/em&gt;&lt;/a&gt;
by tef&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Fri, 20 Mar 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/programming-reads/</guid>
    </item>
    <item>
      <title>Great programming reads</title>
      <link>https://seanmcp.com/articles/great-programming-reads/</link>
      <description>&lt;p&gt;This is an alphabetical list that has helped shape the way that I think about
software engineering.&lt;/p&gt;
&lt;p&gt;At some point in the future I may add some commentary on why I found these
insightful. Until then, you&#39;ll have to form those opinions for yourself.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;articles&quot; tabindex=&quot;-1&quot;&gt;Articles&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/great-programming-reads/#articles&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Articles”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://signalvnoise.com/posts/3124-give-it-five-minutes&quot;&gt;&lt;em&gt;Give it five minutes&lt;/em&gt;&lt;/a&gt;
by Jason Fried&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://overreacted.io/optimized-for-change/&quot;&gt;&lt;em&gt;Optimize for change&lt;/em&gt;&lt;/a&gt; by Dan
Abramov&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction&quot;&gt;&lt;em&gt;The wrong abstraction&lt;/em&gt;&lt;/a&gt;
by Sandi Metz&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.brandonsmith.ninja/blog/write-code-not-too-much-mostly-functions&quot;&gt;&lt;em&gt;Write code. Not too much. Mostly functions.&lt;/em&gt;&lt;/a&gt;
by Brandon Smith&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to&quot;&gt;&lt;em&gt;Write code that is easy to delete, not easy to extend.&lt;/em&gt;&lt;/a&gt;
by tef&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Fri, 20 Mar 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/great-programming-reads/</guid>
    </item>
    <item>
      <title>Free to grow</title>
      <link>https://seanmcp.com/articles/free-to-grow-with-tests/</link>
      <description>&lt;!-- Outline

- Holds up to refactoring
- Promotes iteration
- Confidence to grow
--&gt;
&lt;p&gt;&lt;img src=&quot;https://seanmcp.com/img/redwood.min.jpg&quot; alt=&quot;a photograph of an upward shot of redwood trees&quot;&gt;&lt;/p&gt;
&lt;p&gt;I previously
&lt;a href=&quot;https://seanmcp.com/articles/snapshot_test_ui_components&quot;&gt;wrote about testing UI components&lt;/a&gt;, and why I
recommend that you don&#39;t use snapshots. I finished that article with this line:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your tests should give me the confidence to refactor any component to your
heart&#39;s content. They should fail if you change functionality, not structure.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Instead, I advocate for writing entirely
&lt;a href=&quot;https://en.wikipedia.org/wiki/Functional_testing&quot;&gt;functional tests&lt;/a&gt;, asserting
based on the required functionality of the component.&lt;/p&gt;
&lt;p&gt;This type of testing is certainly more difficult and time-consuming (especially
compared to snapshots). But I think the benefits are clear: they hold up under
&lt;strong&gt;refactoring&lt;/strong&gt;, promote &lt;strong&gt;iteration&lt;/strong&gt;, and gives you the confidence to
&lt;strong&gt;grow&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;refactoring&quot; tabindex=&quot;-1&quot;&gt;Refactoring&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/free-to-grow-with-tests/#refactoring&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Refactoring”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I have yet to come across a component or complex function I&#39;ve written that
couldn&#39;t use refactoring. Maybe it&#39;s a new technique that is perfect for these
situation, or perhaps it&#39;s logic that appeared logical at the time that isn&#39;t on
a second look.&lt;/p&gt;
&lt;p&gt;Whatever the case, refactoring code is a constant in this profession, and tests
(should) help you embrace it.&lt;/p&gt;
&lt;p&gt;When you write a functional test, you start with a statement like &amp;quot;With &lt;code&gt;A&lt;/code&gt;
input, my function should output &lt;code&gt;B&lt;/code&gt;.&amp;quot; Then you write a test that covers that
condition. Your test doesn&#39;t care how you get from &lt;code&gt;A&lt;/code&gt; to &lt;code&gt;B&lt;/code&gt;, just that you
always do.&lt;/p&gt;
&lt;p&gt;Maybe the first time through you take a long an circuitous path with lots of
conditional logic. Next time you refactor, you take a more direct route. Then
you decide that some of those conditions were necessary. The test doesn&#39;t care,
so long as &lt;code&gt;fn(A) = B&lt;/code&gt;.&lt;/p&gt;
&lt;!-- &lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Refactoring ones own code, and watching the tests continue to pass, is both a humbling and an enriching experience.&lt;/p&gt;&amp;mdash; Venkat Subramaniam (@venkat_s) &lt;a href=&quot;https://twitter.com/venkat_s/status/1176386197641412608?ref_src=twsrc%5Etfw&quot;&gt;September 24, 2019&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt; --&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;iteration&quot; tabindex=&quot;-1&quot;&gt;Iteration&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/free-to-grow-with-tests/#iteration&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Iteration”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Refactoring is looking back at code, while iterating is looking ahead. Our goal
as software engineers should be to continuously deliver incremental improvements
on our active products. This means slowing building on what we already have.&lt;/p&gt;
&lt;p&gt;Maybe you designed a function with situation A in mind, but notice that with a
minor adjustment it can easily handle B and C. Once you are done making that
change, the functional test that you wrote for situation A should be the
barometer for moving forward.&lt;/p&gt;
&lt;p&gt;If the test passes, then you&#39;re good to proceed. If not, then you&#39;ve made a
breaking change; go back and rethink your approach.&lt;/p&gt;
&lt;p&gt;When you&#39;re looking to add a new feature to your product, a passing test suite
should give you full confidence that all of your changes are backwards
compatible.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;free-to-grow&quot; tabindex=&quot;-1&quot;&gt;Free to grow&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/free-to-grow-with-tests/#free-to-grow&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Free to grow”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;A redwood tree doesn&#39;t grow to 300 feet over night. Rather, a journey of a
thousand miles begins with a single step&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/free-to-grow-with-tests/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Great software is not written in one sitting. It is fine tuned and improved
slowly over the lifespan of the product.&lt;/p&gt;
&lt;p&gt;This means a regular cycle of refactoring and iteration–improving and expanding.
The more your software is able to do this, the healthier it is and the better it
will become over time.&lt;/p&gt;
&lt;p&gt;A functional test suite enables and encourages your software to grow this way.
You can make improvements to your code and verify that it still functions. You
can add more features while being assured that there are no breaking changes.&lt;/p&gt;
&lt;p&gt;To set your team or product up on a thousand-mile journey, start with your best
foot forward. Write the kind of tests that enable you to take each successive
step with confidence.&lt;/p&gt;
&lt;p&gt;Write functional tests.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Paraphrased from Laozi&#39;s
&lt;a href=&quot;https://en.wikipedia.org/wiki/Tao_Te_Ching&quot;&gt;&lt;em&gt;Tao Te Ching&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/free-to-grow-with-tests/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Mon, 17 Feb 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/free-to-grow-with-tests/</guid>
    </item>
    <item>
      <title>How to check npm scripts in current directory</title>
      <link>https://seanmcp.com/articles/check-npm-scripts-in-current-directory/</link>
      <description>&lt;p&gt;As a front-end engineer, I spend a lot of time jumping in-between JavaScript
projects. Often times I want to run a command from those projects to start
watchmode or spin up a service.&lt;/p&gt;
&lt;p&gt;The commands that I want to use are in the &lt;code&gt;package.json&lt;/code&gt; under &lt;code&gt;&amp;quot;scripts&amp;quot;&lt;/code&gt;.
When I haven&#39;t memorized the common commands, I need to either open the file in
my editor or use a Unix utility like &lt;code&gt;cat&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# In my editor&lt;/span&gt;
code package.json

&lt;span class=&quot;token comment&quot;&gt;# In the terminal&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; package.json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both of these commands work, but they are slow. &lt;code&gt;package.json&lt;/code&gt; files can be
massive, including dozens of dependencies, dev depenendencies, and other fields.
To find the scripts, I need to sort through the cruft to find the information
that I want. This isn&#39;t very efficient.&lt;/p&gt;
&lt;p&gt;Instead, I wrote a short bash scripts to quickly do the work for me. Here is the
code in all of its glory:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&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 variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;package.json&quot;&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 keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-pe&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;JSON.parse(require(&#39;fs&#39;).readFileSync(&#39;package.json&#39;).toString()).scripts&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;There is no &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;package.json&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt; in this directory&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, I let the computer know to run this file with bash. Then I set up an &lt;code&gt;if&lt;/code&gt;
block: if there is a &lt;code&gt;package.json&lt;/code&gt; file in this directory, then run the
following code; if not, then print a &amp;quot;can&#39;t find it&amp;quot; message.&lt;/p&gt;
&lt;p&gt;The code to run, inspired by
&lt;a href=&quot;https://stackoverflow.com/questions/1955505/parsing-json-with-unix-tools#comment36088507_18469304&quot;&gt;a StackOverflow comment&lt;/a&gt;,
is a node process with two flags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-e&lt;/code&gt; tells node to evaluate the script that you pass&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p&lt;/code&gt; tells node to print the results to the console&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Within the script, I am using &lt;code&gt;JSON&lt;/code&gt; to parse the results of a
&lt;code&gt;fs.readFileSync()&lt;/code&gt; of the &lt;code&gt;package.json&lt;/code&gt;, then diving down to the &lt;code&gt;scripts&lt;/code&gt;
key.&lt;/p&gt;
&lt;p&gt;I set it up by running &lt;code&gt;chmod&lt;/code&gt; with two flags on the file in a &lt;code&gt;scripts/&lt;/code&gt;
directory:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; u+x ~/scripts/npm-scripts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I could resource my terminal and run the command with &lt;code&gt;npm-scripts&lt;/code&gt;. Bish
bash bosh.&lt;/p&gt;
&lt;p&gt;Everything worked fine, but I wanted an easier way to share my script with
others. The &lt;code&gt;chmod&lt;/code&gt; command and flags are hard to remember, so it would be
easier if the script could be compressed into a one-liner that could be aliased
in their &lt;code&gt;~/.bash_profile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To do that, I removed all the new lines and added semicolons to separate the
statements. The alias needed to be wrapped in quotes, so I escaped all the
double quotes within the script.&lt;/p&gt;
&lt;p&gt;Now I had a handy alias to share:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;if [[ -f &lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt;package.json&lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt; ]]; then node -pe &lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt;JSON.parse(require(&#39;fs&#39;).readFileSync(&#39;package.json&#39;).toString()).scripts&lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt;; else echo &lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt;There is no package.json in this directory&lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt;; fi&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Overall, I&#39;m pretty happy with this utility. Going forward, I would like to
extend it so that you can lookup and key on &lt;code&gt;package.json&lt;/code&gt; by passing an
argument.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;update-%E2%80%93-apr-4%2C-2020&quot; tabindex=&quot;-1&quot;&gt;Update – Apr 4, 2020&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/check-npm-scripts-in-current-directory/#update-%E2%80%93-apr-4%2C-2020&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Update – Apr 4, 2020”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I created a new script to which you could pass an argument to pick a key from
the &lt;code&gt;package.json&lt;/code&gt;. This script is in zsh, but I think it ports to bash:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# rpj - Read package.json&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-name function&quot;&gt;rpj&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 keyword&quot;&gt;if&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 variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;package.json&quot;&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 keyword&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-pe&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;JSON.parse(require(&#39;fs&#39;).readFileSync(&#39;package.json&#39;).toString())[&#39;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&#39; || &#39;scripts&#39;]&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;There is no &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;package.json&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt; in this directory&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note how the shell arguments need to be wrapped in &lt;code&gt;&#39;&#39;&lt;/code&gt; in order to work.
Otherwise JavaScript thinkgs that it is looking for a variable, which is
undefined.&lt;/p&gt;
&lt;p&gt;If no argument is provided, then it falls back to printing the scripts which is
probably the most common use case for me.&lt;/p&gt;
&lt;p&gt;I also named the new function &lt;code&gt;rpj&lt;/code&gt; for &amp;quot;Read &lt;code&gt;package.json&lt;/code&gt;&amp;quot; to avoid any
&lt;a href=&quot;https://en.wikipedia.org/wiki/Name_server&quot;&gt;confusion with name servers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For the next iteration, I would like to handle errors, but that is a project for
another day.&lt;/p&gt;
</description>
      <pubDate>Thu, 06 Feb 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/check-npm-scripts-in-current-directory/</guid>
    </item>
    <item>
      <title>What to include in every node module</title>
      <link>https://seanmcp.com/articles/what-to-include-in-every-node-module/</link>
      <description>&lt;p&gt;All that is required to publish a node module is a &lt;code&gt;package.json&lt;/code&gt; file with a
few fields. However, there are more components to a good package.&lt;/p&gt;
&lt;p&gt;Here are a few things to include in every node module that you publish:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://seanmcp.com/articles/what-to-include-in-every-node-module/#readmemd&quot;&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;readme.md&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/what-to-include-in-every-node-module/#readme.md&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “README.md”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;A README contains all the information necessary to use your node module. They
come in all shapes and sizes: decorated, detailed, and simple. But they all need
to include a one important detail: &lt;strong&gt;how to use the package&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There are many ways to create a good README, I like the format set out by
&lt;a href=&quot;https://www.makeareadme.com/&quot;&gt;&amp;quot;Make a README&amp;quot;&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Mon, 03 Feb 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/what-to-include-in-every-node-module/</guid>
    </item>
    <item>
      <title>How to remove duplicates from an object array?</title>
      <link>https://seanmcp.com/articles/remove-duplicates-from-object-array/</link>
      <description>&lt;p&gt;Reviewing a recent code exercise at work, we came across a situation where a
candidate wanted to ensure that there were no duplicates in an array of objects.&lt;/p&gt;
&lt;p&gt;The solution, which seemed to have been cobbled together from
&lt;a href=&quot;https://stackoverflow.com/questions/2218999/remove-duplicates-from-an-array-of-objects-in-javascript&quot;&gt;suggestions in this Stack Overflow thread&lt;/a&gt;,
included &lt;code&gt;Array.from()&lt;/code&gt;, &lt;code&gt;new Set&lt;/code&gt;, &lt;code&gt;Array.map()&lt;/code&gt;, and &lt;code&gt;Array.find()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It worked, but it definitely fulfilled the definition of
&lt;a href=&quot;https://www.urbandictionary.com/define.php?term=Frankencode&quot;&gt;&amp;quot;frankencode&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at a simpler solution to this common problem with
&lt;a href=&quot;https://en.wikipedia.org/wiki/Big_O_notation&quot;&gt;a simple &lt;em&gt;O(n)&lt;/em&gt; complexity&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;setup&quot; tabindex=&quot;-1&quot;&gt;Setup&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-duplicates-from-object-array/#setup&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Setup”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The data here could be pretty complicated, so I&#39;m going to reduce it to the
simplest form. We have an array of possibly duplicate objects, and we&#39;ll know
that they&#39;re duplicated based on the &lt;code&gt;name&lt;/code&gt; key:&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; listWithDups &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 punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&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 literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&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 literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&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 comment&quot;&gt;// &amp;lt;-- Duplicate&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;three&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want to be able to loop through this data as few times as possible in order
to remove that duplicate entry.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;steps&quot; tabindex=&quot;-1&quot;&gt;Steps&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-duplicates-from-object-array/#steps&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Steps”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;There are a few things that we are going to need in order to solve this problem.
First, we&#39;re going to need a new array to which we&#39;ll add unique items. Second,
we&#39;re going to need a way to keep track of the items that we have already seen.
And third, we&#39;re going to need a way to iterate through the data.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at those one at a time.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;new-array&quot; tabindex=&quot;-1&quot;&gt;New array&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-duplicates-from-object-array/#new-array&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “New array”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;For this process, we want this to create a new unique array without modifying
the original. This is a good practice, because it gives you the ability to
compare your data down the road.&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; uniqueList &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 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;When we are iterating through our original array and come across a unique
object, we&#39;ll add it to &lt;code&gt;uniqueList&lt;/code&gt; with &lt;code&gt;Array.push()&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;unique-key-tracker&quot; tabindex=&quot;-1&quot;&gt;Unique key tracker&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-duplicates-from-object-array/#unique-key-tracker&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Unique key tracker”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In order to know if an object is unique, we need to know all of the keys that we
have seen before.&lt;/p&gt;
&lt;p&gt;There are a few different data types that we could use, but the best option here
is an object; they&#39;re perfect for looking up information based on a known key.&lt;/p&gt;
&lt;p&gt;This object only exists to track those keys. It will not be returned and no
other process depends on its information. That makes it a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Temporary_variable&quot;&gt;temporary variable&lt;/a&gt;, and we
should name it as such:&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; temp &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 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;For every object in our list, we&#39;ll check to see if their &lt;code&gt;name&lt;/code&gt; key exists on
our temp object. If it does not, then we&#39;ll add it to the object and then add
the object to the unique list. If it does not, then we know that it is a
duplicate and can move on.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;iterate&quot; tabindex=&quot;-1&quot;&gt;Iterate&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-duplicates-from-object-array/#iterate&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Iterate”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Since we are going to be iterating through data, the easiest&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/remove-duplicates-from-object-array/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; way to do that
is with a &lt;code&gt;for&lt;/code&gt; loop. We&#39;re going to use a
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of&quot;&gt;&lt;code&gt;for...of&lt;/code&gt;&lt;/a&gt;
here because we don&#39;t care about the index number:&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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; listWithDups&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 comment&quot;&gt;// TODO: Check if item is unique.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// If so, add it to the list and remember it&#39;s name.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// If not, pass over; it&#39;s a duplicate.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can go about implementing the steps above.&lt;/p&gt;
&lt;p&gt;Since we&#39;re using our &lt;code&gt;temp&lt;/code&gt; object to track all of the items that we have
already encountered, we can check to see if each item&#39;s &lt;code&gt;name&lt;/code&gt; exists as a key
on &lt;code&gt;temp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note: We haven&#39;t written the logic to add items to &lt;code&gt;temp&lt;/code&gt; yet, but we know that
this is how we want the application to work.&lt;/p&gt;
&lt;!-- ```js/2 --&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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; listWithDups&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 comment&quot;&gt;// If we haven&#39;t seen this name before...&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;temp&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within this &lt;code&gt;if&lt;/code&gt; block, we&#39;ll do our &amp;quot;unique name&amp;quot; logic. We want to add the
item to our unique list and remember the name for next time.&lt;/p&gt;
&lt;!-- ```js/3-4 --&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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; listWithDups&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 comment&quot;&gt;// If we haven&#39;t seen this name before...&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;temp&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&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;
    uniqueItems&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&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 comment&quot;&gt;// Add to unique list&lt;/span&gt;
    temp&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Remember the name&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that loop has run, we will have built our &lt;code&gt;uniqueItems&lt;/code&gt; array without
duplicating any values. We have successfully de-duped our array with only a
single iteration!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;sample&quot; tabindex=&quot;-1&quot;&gt;Sample&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/remove-duplicates-from-object-array/#sample&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Sample”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Here is a Repl of the solution. Feel free to fork it, make improvements, and
&lt;a href=&quot;https://seanmcp.com/articles/remove-duplicates-from-object-array/#comment-link&quot;&gt;share your code with me&lt;/a&gt;.&lt;/p&gt;
&lt;iframe height=&quot;400px&quot; width=&quot;100%&quot; src=&quot;https://repl.it/@SeanMcP/Unique-items-in-array?lite=true&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot; sandbox=&quot;allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-modals&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;YMMV &lt;a href=&quot;https://seanmcp.com/articles/remove-duplicates-from-object-array/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Mon, 03 Feb 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/remove-duplicates-from-object-array/</guid>
    </item>
    <item>
      <title>Write a draft script for your SSG blog</title>
      <link>https://seanmcp.com/articles/write-a-draft-script-for-your-ssg-blog/</link>
      <description>&lt;p&gt;This site is powered by Eleventy, a simple static-site generator. All of the
pages and articles exists as markdown files that are converted into HTML files
via Nunjuck templates at build time.&lt;/p&gt;
&lt;p&gt;For a blogging platform, it&#39;s pretty neat. I enjoy being able to run the whole
site from the comfort of my text editor and the command line (with a little help
from Netlify for continual deployments).&lt;/p&gt;
&lt;p&gt;The secret sauce behind markdown-based sites is the frontmatter: specially
formatted content at the top of a markdown file that is read by the generator.
Here&#39;s what the frontmatter for a typical article looks like:&lt;/p&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@layouts/ArticleLayout.astro&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Add event listener for class change
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  How to create a custom event listener for class names using the
  MutationObserver API
&lt;span class=&quot;token key atrule&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token datetime number&quot;&gt;2019-05-10&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;tags&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; JavaScript
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; Web&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Start writing your article here --&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I want to write a new article, I need to make sure I have all of those
fields populated in a particular way in order for Eleventy to generate the page
correctly.&lt;/p&gt;
&lt;p&gt;Formatting and required fields are not something that I care to remember, so I
wanted to develop an easier method for generating a draft for this blog. This is
my journey.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;setup&quot; tabindex=&quot;-1&quot;&gt;Setup&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/write-a-draft-script-for-your-ssg-blog/#setup&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Setup”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Most static-site generators will have a directory for you to store some files
that are excluded from the build process. For Eleventy, that default directory
is &lt;code&gt;_includes/&lt;/code&gt;. I added a subdirectory for &lt;code&gt;templates/&lt;/code&gt; and added an
&lt;code&gt;article.md&lt;/code&gt;. All it contains is the bare-bones frontmatter for a new article:&lt;/p&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token datetime number&quot;&gt;2020-01-01&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;tags&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; SAMPLE_TAG&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;I hard coded the date because I have a hard time remembering the YYYY-MM-DD
format. It would be cool for the script to generate that for me.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;solution-1%3A-bash-script&quot; tabindex=&quot;-1&quot;&gt;Solution 1: Bash script&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/write-a-draft-script-for-your-ssg-blog/#solution-1%3A-bash-script&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Solution 1: Bash script”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Now that I have a template to reference, all I really need to do is write a
simple script to copy that file to the &lt;code&gt;articles/&lt;/code&gt; directory. I added the
following code to a &lt;code&gt;create-draft.sh&lt;/code&gt; file in the root directory:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;draft

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_includes/templates/article.md&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;articles/_&lt;span class=&quot;token variable&quot;&gt;$title&lt;/span&gt;.md&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script declares a variable &lt;code&gt;title&lt;/code&gt; and sets the value to the string
&lt;code&gt;&amp;quot;draft&amp;quot;&lt;/code&gt;. Then it checks to see if another argument is passed. If so, then it
reassigns &lt;code&gt;title&lt;/code&gt; to that value.&lt;/p&gt;
&lt;p&gt;Then it copies the article template to the &lt;code&gt;articles/&lt;/code&gt; directory with the
filename title.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I am prepending an underscore to the name of the file because I have
configured Eleventy to ignore markdown files that begin with an underscore from
the build.
&lt;a href=&quot;https://github.com/11ty/eleventy/issues/188#issuecomment-488350222&quot;&gt;The solution got Eleventy&#39;s stamp of approval.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I added a &lt;code&gt;draft&lt;/code&gt; script to packages.json:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&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 property&quot;&gt;&quot;draft&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sh create-draft.sh&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that was in place, I could call my script by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run draft my-new-article
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This worked, but it was pretty simple. I needed to remember to use kebab case
for the title of the article so that the file name would work. It didn&#39;t add the
title to the copied file, nor set a date.&lt;/p&gt;
&lt;p&gt;It served me well for a while, but eventually time came for a change.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;solution-2%3A-node.js-script&quot; tabindex=&quot;-1&quot;&gt;Solution 2: node.js script&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/write-a-draft-script-for-your-ssg-blog/#solution-2%3A-node.js-script&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Solution 2: node.js script”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;To be fair, the original bash script could have been improved to meet all of my
needs. But I&#39;m not as comfortable with Bash as I am with JavaScript, and I jump
at every opportunity to work in node.js.&lt;/p&gt;
</description>
      <pubDate>Fri, 31 Jan 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/write-a-draft-script-for-your-ssg-blog/</guid>
    </item>
    <item>
      <title>A new Twitter handle</title>
      <link>https://seanmcp.com/articles/a-new-twitter-handle/</link>
      <description>&lt;p&gt;I have had a difficult relationship with Twitter. When it launched, I was in
high school and still active on a number of forums and AOL instant messenger.
Around the same time, Facebook opened up to non-collegiate users, so I and a lot
of my friends signed up. Later I went all in on Google+.&lt;/p&gt;
&lt;p&gt;Twitter always seemed different. Rather than connection-based interactions,
Twitter was more simply a broadcast platform. This meant that the reach of your
message was tied not the the number of individuals with whom you could connect,
but instead the number that you could impress. It was a popularity platform.&lt;/p&gt;
&lt;p&gt;As a teenage contrarian, that was intensely unappealing to me.&lt;/p&gt;
&lt;p&gt;Fast forward a few years and I started doing freelance web design and
development while in college. By then both Twitter and I had matured, and I saw
it as an opportunity to promote my business. &lt;code&gt;@seanmcp&lt;/code&gt; was taken, so I
registered the handle &lt;code&gt;@sean_mcp&lt;/code&gt;. After a largely unsuccessful go, I shuttered
the business and deleted the account.&lt;/p&gt;
&lt;p&gt;The decision to delete that account is one that I have regretted for the past
eight years.&lt;/p&gt;
&lt;p&gt;By the mid 2010s, I was interested in getting on the platform for some soccer
journalism I was doing. Most iterations of my name were taken, so I settled on
&lt;a href=&quot;https://twitter.com/mcpsoccer&quot;&gt;&lt;code&gt;@mcpsoccer&lt;/code&gt;&lt;/a&gt;. That served me well and still
exists today.&lt;/p&gt;
&lt;p&gt;I was teaching at the time too, so I created a professional account for my
classroom: &lt;code&gt;@mrseanmcp&lt;/code&gt;. When that era ended, I changed the handle first to
&lt;code&gt;@seanthemcp&lt;/code&gt; and then &lt;code&gt;@mcpmakes&lt;/code&gt; and then &lt;code&gt;@mcpcodes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;By this time I had settled on a new career path and saw the value of Twitter as
a developer. In an effort to consolidate my online brand™, I changed the handle
to &lt;code&gt;@_seanmcp&lt;/code&gt;. But while some folks are able to own the underscore, it left me
with a sense of insecurity. Why had I given up &lt;code&gt;@sean_mcp&lt;/code&gt; all those years ago?&lt;/p&gt;
&lt;p&gt;In 2020, I sat down with my wife and brainstormed some ideas for a new handle.
Here is a short list of the options we enumerated in alphabetical order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@askseanmcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@biseanfrise&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@nabasson&lt;/code&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/a-new-twitter-handle/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@seanaroll&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@seancloudnine&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@seanmcpdotcom&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@seantheupandup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@snmcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@thierryseanry&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can probably guess, we had a good time coming up with the punnier
options.&lt;/p&gt;
&lt;p&gt;After much internal deliberation, I settled on
&lt;a href=&quot;https://twitter.com/snmcp&quot;&gt;&lt;code&gt;@snmcp&lt;/code&gt;&lt;/a&gt;. First, it&#39;s as close to my brand as
possible. Second, it doesn&#39;t add additional words, letters, or symbols. And
third, it&#39;s short and quick to type.&lt;/p&gt;
&lt;p&gt;At first I didn&#39;t like the look of the new handle, but after a few weeks it&#39;s
starting to grow on me. I&#39;m hoping that, starting this new decade, I will
finally be content on Twitter (LW).&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Like Barnabas, which means
&lt;a href=&quot;https://www.biblegateway.com/passage/?search=Acts+4%3A36-37&amp;amp;version=NKJV&quot;&gt;&amp;quot;son of encouragement&amp;quot;&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/a-new-twitter-handle/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Tue, 28 Jan 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/a-new-twitter-handle/</guid>
    </item>
    <item>
      <title>What default parameters do (and don&#39;t do)</title>
      <link>https://seanmcp.com/articles/what-default-parameters-do-and-dont-do/</link>
      <description>&lt;p&gt;Default parameters are a handy feature in JavaScript.
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters&quot;&gt;MDN has a more thorough explanation&lt;/a&gt;,
but imagine a function that greets a user:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&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 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;Hello &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we call our function and pass a name, we get the expected log:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;greetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Abby&quot;&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 comment&quot;&gt;// Hello Abby!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But what about when we don&#39;t pass a name? &lt;strong&gt;Quiz time&lt;/strong&gt;: What is going to log?&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;greetUser&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 comment&quot;&gt;// Hello undefined!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Is that what you expected? Since we passed no argument, the &lt;code&gt;name&lt;/code&gt; parameter is
&lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;JavaScript — ever the helpful friend — sees that we want to include that value
in a string, so it
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Type_coercion&quot;&gt;coerces it into the correct type&lt;/a&gt;,
and then logs it.&lt;/p&gt;
&lt;p&gt;You probably don&#39;t want your function to return &lt;code&gt;Hello undefined!&lt;/code&gt;, so let&#39;s try
a default parameter.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;what-default-parameters-do&quot; tabindex=&quot;-1&quot;&gt;What default parameters do&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/what-default-parameters-do-and-dont-do/#what-default-parameters-do&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “What default parameters do”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Add the following to the declaration line of &lt;code&gt;greetUser()&lt;/code&gt;:&lt;/p&gt;
&lt;!-- ```js/0 --&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;there&quot;&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 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;Hello &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we&#39;re saying to our function, &amp;quot;if you don&#39;t receive an argument, set the
value of the &lt;code&gt;name&lt;/code&gt; parameter to be &lt;code&gt;&amp;quot;there&amp;quot;&lt;/code&gt;.&amp;quot; When we call our function and
pass an argument, we get the same result:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;greetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Abby&quot;&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 comment&quot;&gt;// Hello Abby!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, when we call this updated function without passing an argument, the
default parameter kicks in:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;greetUser&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 comment&quot;&gt;// Hello there!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bingo! Now if our function is called without an argument, then the resulting log
still makes sense.&lt;/p&gt;
&lt;p&gt;In this situation, the default parameter is working as a fallback value. This is
probably the most common use case. But you will also find default parameters
useful to reduce existential checking.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;reduce-checks&quot; tabindex=&quot;-1&quot;&gt;Reduce checks&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/what-default-parameters-do-and-dont-do/#reduce-checks&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Reduce checks”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Imagine a user object with a &lt;code&gt;name&lt;/code&gt; key that we want to log. In order to update
our original function to handle this use case, I might do something like this:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;user&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&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;Hello &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&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 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 keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello there!&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can imagine the complexity of this dive check growing with the size of the
user object. Instead, we can save ourselves some trouble by defaulting that
parameter to something that we can use right away:&lt;/p&gt;
&lt;!-- ```js/0 --&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &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 literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;there&quot;&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;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&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;Hello &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&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 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;We set the default value of the &lt;code&gt;user&lt;/code&gt; parameter to be an object with a &lt;code&gt;name&lt;/code&gt;
key that we could access.&lt;/p&gt;
&lt;p&gt;I use this pattern often; default props make it easier to see what types and
shapes of arguments that any given function or component is expecting.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;what-default-parameters-don&#39;t-do&quot; tabindex=&quot;-1&quot;&gt;What default parameters don&#39;t do&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/what-default-parameters-do-and-dont-do/#what-default-parameters-don&#39;t-do&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “What default parameters don&#39;t do”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A parameter will only default to a given value if it the argument passed is
&lt;code&gt;undefined&lt;/code&gt;&lt;/strong&gt;. So either no argument is passed, or the function specifically
receives &lt;code&gt;undefined&lt;/code&gt; as an argument.&lt;/p&gt;
&lt;p&gt;When I first learned about default parameters, I developed the wrong mental
model — associating them with falsy values in JavaScript. My incorrect thinking
went like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When ever I pass an argument that evaluates to false, the default parameter
will kick in and save me from errors! &lt;br&gt;— Sean, being wrong&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I ran into this problem at work when I expected a function to return the default
parameter when passed a &lt;code&gt;null&lt;/code&gt; argument.&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;favoriteAnimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;animal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;wolf&quot;&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;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Favorite animal:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; animal&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 function&quot;&gt;favoriteAnimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;manatee&quot;&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 comment&quot;&gt;// Favorite animal: manatee&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;favoriteAnimal&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 comment&quot;&gt;// Favorite animal: wolf&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;favoriteAnimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&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 comment&quot;&gt;// Favorite animal: null&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s because &lt;code&gt;null&lt;/code&gt; is a deliberately passed value (even if I didn&#39;t know that
I was deliberately passing it). Default parameters will only kick in if that
argument is omitted or passed as &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To state it plainly: &lt;strong&gt;falsy values do NOT become default parameters&lt;/strong&gt;. It only
works for &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So the above code is worked as designed, even if it&#39;s not what I expected. Time
to update that mental model!&lt;/p&gt;
&lt;p&gt;Hopefully that clears up the mystery of default parameters for you. Happy
coding!&lt;/p&gt;
</description>
      <pubDate>Mon, 13 Jan 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/what-default-parameters-do-and-dont-do/</guid>
    </item>
    <item>
      <title>Reviewing 2019 goals</title>
      <link>https://seanmcp.com/articles/reviewing-2019-goals/</link>
      <description>&lt;p&gt;To kick-off 2019, I wrote an article in which I
&lt;a href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/2018-into-2019&quot;&gt;set some goals for 2019&lt;/a&gt;. I had planned to check in on those
goals more frequently:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I am going to check back on these goals throughout the year and track my
progress in a different article.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But alas, the best laid plans of mice and men!&lt;/p&gt;
&lt;p&gt;Now that it is &lt;s&gt;December of 2019&lt;/s&gt; January of 2020, I wanted to take the chance
to measure my progress towards those goals that I set.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;review&quot; tabindex=&quot;-1&quot;&gt;Review&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#review&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Review”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%91%8E-dive-deeper-into-python&quot; tabindex=&quot;-1&quot;&gt;👎 Dive deeper into Python&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%91%8E-dive-deeper-into-python&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “👎 Dive deeper into Python”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I didn&#39;t do anything more with Python this year. I even turned away from Python
for simple scripting projects in favor of Bash or node.js. I&#39;m still a fan, but
I don&#39;t see a big use case for Python in my current career path.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%91%8E-learn-machine-learning&quot; tabindex=&quot;-1&quot;&gt;👎 Learn machine learning&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%91%8E-learn-machine-learning&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “👎 Learn machine learning”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;ML sounds cool and exciting, but there weren&#39;t a lot of opportunities to utilize
it in my work as a front-end engineer.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%91%8E-demystify-module-bundlers&quot; tabindex=&quot;-1&quot;&gt;👎 Demystify module bundlers&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%91%8E-demystify-module-bundlers&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “👎 Demystify module bundlers”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This is still a good idea, but I haven&#39;t done it (yet).&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%8E%89-release-two-npm-packages&quot; tabindex=&quot;-1&quot;&gt;🎉 Release two npm packages&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%8E%89-release-two-npm-packages&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “🎉 Release two npm packages”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I published two npm packages in January of 2019 alone. This is something I want
to continue to do going forward.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%A4%B7%E2%80%8D%E2%99%82%EF%B8%8F-investigate-react-tooling&quot; tabindex=&quot;-1&quot;&gt;🤷‍♂️ Investigate React tooling&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%A4%B7%E2%80%8D%E2%99%82%EF%B8%8F-investigate-react-tooling&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “🤷‍♂️ Investigate React tooling”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I learned a lot this year about tooling in a JavaScript environment, but I think
my goal was a little misguiding in thinking that a &lt;code&gt;dotfiles&lt;/code&gt; repo would be the
answer.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%91%8E-publish-24-articles&quot; tabindex=&quot;-1&quot;&gt;👎 Publish 24 articles&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%91%8E-publish-24-articles&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “👎 Publish 24 articles”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;By my count, I published about a dozen articles in 2019. I would like to strive
for this goal in 2020 too.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%91%8E-a-partner-for-jr-devs&quot; tabindex=&quot;-1&quot;&gt;👎 A partner for Jr Devs&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%91%8E-a-partner-for-jr-devs&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “👎 A partner for Jr Devs”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I moved to Pittsburgh in the summer of 2019, but Jr Devs had stalled before
that. I never found the right fit within the Atlanta developer community, and a
partner wasn&#39;t going to change that.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%8E%89-speaking-on-tech&quot; tabindex=&quot;-1&quot;&gt;🎉 Speaking on tech&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%8E%89-speaking-on-tech&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “🎉 Speaking on tech”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;While I did not present at another Meetup in 2019, I did give a talk about
&lt;em&gt;React and Accessibility&lt;/em&gt; at the Connect.Tech conference in Atlanta. I enjoyed
the experience, but I think sharing at Meetups is a little more my speed.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%91%8E-dating-my-wife&quot; tabindex=&quot;-1&quot;&gt;👎 Dating my wife&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%91%8E-dating-my-wife&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “👎 Dating my wife”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;We didn&#39;t schedule any official dates in 2019, but I don&#39;t think either of us
missed it. I need to check with my wife again to see if this is something we
want to try in 2020.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%8E%89-being-a-good-father&quot; tabindex=&quot;-1&quot;&gt;🎉 Being a good father&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%8E%89-being-a-good-father&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “🎉 Being a good father”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;While we were living in Atlanta, I cared for our son on Saturdays while my wife
worked. We would regularly go to the park or around town on errands while she
was gone. Here in Pittsburgh, her schedule has changed and I don&#39;t have that
dedicated parenting time.&lt;/p&gt;
&lt;p&gt;Once the winter is through, I want to sieze opportunities to take my son on new
adventures in a new city.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%8E%89-leading-our-household&quot; tabindex=&quot;-1&quot;&gt;🎉 Leading our household&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%8E%89-leading-our-household&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “🎉 Leading our household”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In 2019 we continued to have regular family worship in the evenings and added
another time over the breakfast table. It has been a huge encouragement to see
my son begin to develop the skills necessary to worship God throughout his life:
sitting, listening, singing, and praying (sort of). I no longer teach Sunday
school but am open to that possibility here in Pittsburgh.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%91%8E-work-out&quot; tabindex=&quot;-1&quot;&gt;👎 Work out&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%91%8E-work-out&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “👎 Work out”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I wasn&#39;t even close. This is something to reassess in 2020.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%F0%9F%91%8E-play-soccer&quot; tabindex=&quot;-1&quot;&gt;👎 Play soccer&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#%F0%9F%91%8E-play-soccer&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “👎 Play soccer”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I was not able to attend pick-up soccer regularly in Atlanta and have yet to
find a group in Pittsburgh. This is something that I would like to try again
in 2020. Maybe twice a month, though.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;total-score%3A-4%2F12-(33.33%25)&quot; tabindex=&quot;-1&quot;&gt;Total score: 4/12 (33.33%)&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reviewing-2019-goals/#total-score%3A-4%2F12-(33.33%25)&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Total score: 4/12 (33.33%)”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;At face value, that&#39;s not a great completion rate. But it is all a learning
opportunity.&lt;/p&gt;
&lt;p&gt;You can see by going through the list the difference between goals that I
&lt;em&gt;thought&lt;/em&gt; were important vs. the ones that were actually important. I&#39;m happy
with the four successes and am looking forward to reaching towards the other
goals in this new year.&lt;/p&gt;
&lt;p&gt;Here&#39;s to new attempts in a new year!&lt;/p&gt;
</description>
      <pubDate>Wed, 08 Jan 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/reviewing-2019-goals/</guid>
    </item>
    <item>
      <title>2019 into 2020</title>
      <link>https://seanmcp.com/articles/2019-into-2020/</link>
      <description>&lt;p&gt;2019 was an exciting year, though perhaps not as
&lt;a href=&quot;https://seanmcp.com/articles/2019-into-2020/2018-into-2019&quot;&gt;eventful as 2018&lt;/a&gt;. Here are some of the major events and
accomplishments of the past year in mostly chronological order.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;2019-in-review&quot; tabindex=&quot;-1&quot;&gt;2019 in review&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/2019-into-2020/#2019-in-review&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “2019 in review”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Last year, I:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Officially hit one year in the industry&lt;/strong&gt;: My previous experience is
valuable but scattered; this is one year straight of work&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Took on new work responsibilities&lt;/strong&gt;: I began work as a UI Engineer lead at
TSYS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accepted first conference talk&lt;/strong&gt;: I got the news in June that I was accepted
to speak at Connect.Tech 2019&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attended two conferences&lt;/strong&gt;: Abstractions in Pittsburgh and Connect.Tech in
Atlanta&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interviewed at multiple companies&lt;/strong&gt;: Dusted off the ol&#39; code challenge
skills&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accepted an offer at Niche&lt;/strong&gt;: Front-end Software Engineer at an exciting
company in Pittsburgh&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Moved to Pittsburgh&lt;/strong&gt;: Said a sad &amp;quot;goodbye&amp;quot; to all our wonderful friends in
Atlanta&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Added some new features to &lt;a href=&quot;http://Niche.com&quot;&gt;Niche.com&lt;/a&gt;&lt;/strong&gt;: Most of them minor, but it&#39;s neat to
see your work in action&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spoke at a conference&lt;/strong&gt;: &lt;em&gt;React and Accessibility&lt;/em&gt; at Connect.Tech 2019&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Started a learning program at Niche&lt;/strong&gt;: Weekly tech learning sessions over
lunch with live demos&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Published eleven npm packages&lt;/strong&gt;: And learned a lot about package management
along the way&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Passed 10K downloads on a package&lt;/strong&gt;: &lt;code&gt;a11y-react-emoji&lt;/code&gt; is off to the races!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Published thirteen articles&lt;/strong&gt;: &lt;a href=&quot;https://seanmcp.com/articles/2019-into-2020/reviewing-2019-goals&quot;&gt;Off the mark&lt;/a&gt;, but
still solid&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some of these things were fulfilling goals that I had set in the previous year,
while others were completely out of the blue. I&#39;m so thankful for the
opportunity to try new things this past year.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;previewing-2020&quot; tabindex=&quot;-1&quot;&gt;Previewing 2020&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/2019-into-2020/#previewing-2020&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Previewing 2020”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Looking forward to this year, there are several things that I want to learn and
do. I am going to list some general goals, followed by measurable ways that I
can accomplish them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Read more&lt;/strong&gt;: I want to read &lt;strong&gt;four professional&lt;/strong&gt; and &lt;strong&gt;six personal&lt;/strong&gt; books&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open source&lt;/strong&gt;: I want to become a maintainer on an open-source library (that
I didn&#39;t create)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write more&lt;/strong&gt;: Last year I missed the mark, so I&#39;m going to set a goal of
publishing &lt;strong&gt;18 articles&lt;/strong&gt; in 2020&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Speaking&lt;/strong&gt;: I want to create a deck and &lt;strong&gt;present at a Meetup&lt;/strong&gt; here in
Pittsburgh&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Father&lt;/strong&gt;: I want to use the time I have with my son more productively,
&lt;strong&gt;going on father-son outings twice a month&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faith&lt;/strong&gt;: I want lead &lt;strong&gt;family worship every day&lt;/strong&gt;, and &lt;strong&gt;read/reflect on the
Bible daily&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fitness&lt;/strong&gt;: I want to exercise 5 days a week&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Soccer&lt;/strong&gt;: I want to find room in the family schedule to &lt;strong&gt;play a pick-up
game twice a month&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Wed, 08 Jan 2020 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/2019-into-2020/</guid>
    </item>
    <item>
      <title>The fastest way to look for object properties</title>
      <link>https://seanmcp.com/articles/fastest-way-to-look-for-object-properties/</link>
      <description>&lt;p&gt;I manage a library called &lt;code&gt;onkey-event-manager&lt;/code&gt; that helps users map functions
to keyboard actions. Part of the library is a validation step that looks for
properties on an object.&lt;/p&gt;
&lt;p&gt;To make this step as performant as possible, I wanted to know what the fastest
method for checking an object for a given property.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;contenders&quot; tabindex=&quot;-1&quot;&gt;Contenders&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/fastest-way-to-look-for-object-properties/#contenders&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Contenders”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;object.hasownproperty()&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;Object.hasOwnProperty()&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/fastest-way-to-look-for-object-properties/#object.hasownproperty()&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Object.hasOwnProperty()”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This is the built-in method for checking an object for properties. The only
question is whether this method is more performant than other options.&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; obj &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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;test&#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 operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;exists&#39;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;does not&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// -&gt; &#39;does not&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;object%5B%5D&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;Object[]&lt;/code&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/fastest-way-to-look-for-object-properties/#object%5B%5D&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Object[]”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;By referencing the property on the object directly, you can let JavaScript&#39;s
type coercion to force a boolean.&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; obj &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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;test &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;exists&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;does not&quot;&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 comment&quot;&gt;// -&gt; &#39;does not&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This method for checking a property will fail if the value at the
given property is false/falsey (&lt;em&gt;e.g.&lt;/em&gt; &lt;code&gt;const obj = { test: false }&lt;/code&gt;). Since I
can guarantee that case won&#39;t happen, I can still use it.&lt;/p&gt;
</description>
      <pubDate>Tue, 31 Dec 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/fastest-way-to-look-for-object-properties/</guid>
    </item>
    <item>
      <title>&quot;Stop Teaching Code&quot; in JavaScript</title>
      <link>https://seanmcp.com/articles/stop-teaching-code-resources/</link>
      <description>&lt;p&gt;At some time in the future, I would like to return to the classroom to teach
programming to middle school students. That is the age when I first starting
coding, and I would love the opportunity to inspire the next generation of
developers at an early age.&lt;/p&gt;
&lt;p&gt;So I&#39;m on the lookout for good techniques, strategies, and patterns for teaching
the basics of computer science. &lt;a href=&quot;https://seanmcp.com/articles/stop-teaching-code-resources/#comment-link&quot;&gt;Hit me up if you have any&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today I read
&lt;a href=&quot;https://blog.upperlinecode.com/stop-teaching-code-a1039983b39&quot;&gt;&amp;quot;Stop Teaching Code&amp;quot; by Jeff Olson&lt;/a&gt;
where he described a different approach to teaching programming. He advocated
for ditching the definitions, unrealistic examples, and non-sensical names and
use real-world puzzles for students to solve.&lt;/p&gt;
&lt;p&gt;It&#39;s an interesting read, and I recommend it if you have any dreams of teaching
programming.&lt;/p&gt;
&lt;p&gt;All of the examples in Olson&#39;s article are in Python, but I plan on introducing
students to code with JavaScript. So here are all of the examples from his
article rewritten in JavaScript:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;translated-examples&quot; tabindex=&quot;-1&quot;&gt;Translated Examples&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/stop-teaching-code-resources/#translated-examples&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Translated Examples”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Note: I&#39;ve linked to the nearest anchor on Medium, but they appear to be
generated and might change. Your mileage may vary.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;1.-code-for-predictions&quot; tabindex=&quot;-1&quot;&gt;1. &lt;a href=&quot;https://blog.upperlinecode.com/stop-teaching-code-a1039983b39#511e&quot;&gt;Code for Predictions&lt;/a&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/stop-teaching-code-resources/#1.-code-for-predictions&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “1. Code for Predictions”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&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;let&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tamara&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; name&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;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;2.-example-1%3A-conditionals&quot; tabindex=&quot;-1&quot;&gt;2. &lt;a href=&quot;https://blog.upperlinecode.com/stop-teaching-code-a1039983b39#0f1e&quot;&gt;Example 1: Conditionals&lt;/a&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/stop-teaching-code-resources/#2.-example-1%3A-conditionals&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “2. Example 1: Conditionals”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&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;let&lt;/span&gt; age &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&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;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;You&#39;re old enough to buy a ticket for an R-rated film.&quot;&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 keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&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;You&#39;ll be old enough in &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; age&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; years&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 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;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;3.-example-2%3A-collections&quot; tabindex=&quot;-1&quot;&gt;3. &lt;a href=&quot;https://blog.upperlinecode.com/stop-teaching-code-a1039983b39#147c&quot;&gt;Example 2: Collections&lt;/a&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/stop-teaching-code-resources/#3.-example-2%3A-collections&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “3. Example 2: Collections”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&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;let&lt;/span&gt; favorite_foods &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;&quot;Sushi&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tamales&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Mofongo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Pizza&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Chicken Tikka&quot;&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;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;favorite_foods&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&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;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;3.-example-3%3A-objects&quot; tabindex=&quot;-1&quot;&gt;3. &lt;a href=&quot;https://blog.upperlinecode.com/stop-teaching-code-a1039983b39#d814&quot;&gt;Example 3: Objects&lt;/a&gt;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/stop-teaching-code-resources/#3.-example-3%3A-objects&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “3. Example 3: Objects”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&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;let&lt;/span&gt; user1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ty&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tdog@aol.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Pa$$word&quot;&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 keyword&quot;&gt;let&lt;/span&gt; user2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sarai&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SMendes@hotmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Kangaroo!&quot;&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 keyword&quot;&gt;let&lt;/span&gt; user3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Osu&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Osu22@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;12345abcde&quot;&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;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age&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;hr&gt;
&lt;p&gt;I hope that helps. Happy coding!&lt;/p&gt;
</description>
      <pubDate>Tue, 26 Nov 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/stop-teaching-code-resources/</guid>
    </item>
    <item>
      <title>5 Lessons from 10k open-source downloads</title>
      <link>https://seanmcp.com/articles/5-lessons-from-10k-open-source-downloads/</link>
      <description>&lt;p&gt;In April of 2018, I wrote an article
&lt;a href=&quot;https://medium.com/@seanmcp/%EF%B8%8F-how-to-use-emojis-in-react-d23bbf608bf7&quot;&gt;&amp;quot;How to use emojis in React&amp;quot;&lt;/a&gt;.
At the time, the default eslint configuration for Create React App would flash
an error if you put an emoji in a rendered string.&lt;/p&gt;
&lt;p&gt;The answer was to wrap the emoji in a &lt;code&gt;span&lt;/code&gt; with some appropriate attributes to
notify screen readers that it was being used as an image. Since it was React
specific, I included a component that handles all the attribute logic for you.&lt;/p&gt;
&lt;p&gt;The article gained some traction and, thanks to Medium&#39;s SEO, remains the first
page on
&lt;a href=&quot;https://www.google.com/search?q=use+emojis+in+react&amp;amp;oq=how+to+use+emojis+in+react&quot;&gt;a Google search for &amp;quot;use emojis react&amp;quot;&lt;/a&gt;.
Later that same year, I decided to create and release a package for the included
React component. Thus, &lt;a href=&quot;https://npm.im/a11y-react-emoji&quot;&gt;&lt;code&gt;a11y-react-emoji&lt;/code&gt;&lt;/a&gt; was
born.&lt;/p&gt;
&lt;p&gt;Twelve months and 17 versions later, and my little package has
&lt;a href=&quot;https://www.npmjs.com/package/a11y-react-emoji&quot;&gt;10,000 downloads from the npm registry&lt;/a&gt;.
Beyond that, the repo has
&lt;a href=&quot;https://github.com/SeanMcP/a11y-react-emoji&quot;&gt;15 stars on GitHub&lt;/a&gt; and is used by
&lt;a href=&quot;https://github.com/SeanMcP/a11y-react-emoji/network/dependents?package_id=UGFja2FnZS0yODIxNzMzNDE%3D&quot;&gt;61 other projects&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There have been some maintainer highs and lows in that time, so here are the
five biggest lessons I learned along the way.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;1.-packages-can-be-simple&quot; tabindex=&quot;-1&quot;&gt;1. Packages can be simple&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/5-lessons-from-10k-open-source-downloads/#1.-packages-can-be-simple&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “1. Packages can be simple”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Before stepping into the world of open source, I thought of packages as a black
box. But I&#39;ve since learned that they are much simpler than they appear.&lt;/p&gt;
&lt;p&gt;In the JavaScript world, a package is just four files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;package.json&lt;/code&gt; with a few fields,&lt;/li&gt;
&lt;li&gt;an &lt;code&gt;index.js&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;README.md&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;and a &lt;code&gt;LICENSE&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And the last two are optional. Once I got over the hurdle of
&lt;a href=&quot;https://www.freecodecamp.org/news/how-to-make-a-beautiful-tiny-npm-package-and-publish-it-2881d4307f78/&quot;&gt;publishing my first package&lt;/a&gt;,
the curtain of mystery fell away.&lt;/p&gt;
&lt;p&gt;Take a moment and look through the &lt;code&gt;node_modules/&lt;/code&gt; of you next project, and
you&#39;ll see a bunch of directories with those files mentioned above. Not too
scary!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;2.-dependencies-can-get-tricky&quot; tabindex=&quot;-1&quot;&gt;2. Dependencies can get tricky&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/5-lessons-from-10k-open-source-downloads/#2.-dependencies-can-get-tricky&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “2. Dependencies can get tricky”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;If you&#39;re publishing a basic utility, you may not need to worry about
dependencies. But the more complex your library becomes, the more tricky it
becomes to manage your dependencies.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;a11y-react-emoji&lt;/code&gt; was a shared React component written in TypeScript, which
meant I needed to manage React and TypeScript dependencies. I wanted the
component to be fully tested, so add Jest and Babel to the mix. Error checking?
Add ESLint. Consistent formatting? Bring Prettier along.&lt;/p&gt;
&lt;p&gt;Before long, your dependency tree can grow really big. Keep something in mind
when creating a package: every dependency you add is another dependency that you
have to be willing to manage. That can add a maintenance work to your package.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;3.-security-is-a-hassle&quot; tabindex=&quot;-1&quot;&gt;3. Security is a hassle&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/5-lessons-from-10k-open-source-downloads/#3.-security-is-a-hassle&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “3. Security is a hassle”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I&#39;m not an expert on security. So when GitHub flags one of my packages for
potential security risks, I still feel a chill run down my spine.&lt;/p&gt;
&lt;p&gt;When I first created the &lt;code&gt;a11y-react-emoji&lt;/code&gt;, I was quick to address every
security risk highlight, merge the changes, and release a new version of the
package. Every time it felt like I was just a few clicks in front of the next
Equifax data breach.&lt;/p&gt;
&lt;p&gt;But after a few months at the helm, I stopped caring so much. Part of that is
how the project is structured: &lt;code&gt;a11y-react-emoji&lt;/code&gt; has no dependencies, which
means that no flagged security issues present a risk in production.&lt;/p&gt;
&lt;p&gt;Every potential issue is within the dev dependency tree, which isn&#39;t a big deal
to the package user.&lt;/p&gt;
&lt;p&gt;However, keeping a large-scale library with tons of dependency ahead of all
security risks would be a nightmare. That&#39;s not something I&#39;m chomping at the
bit to do in my spare time.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;4.-leverage-the-community&quot; tabindex=&quot;-1&quot;&gt;4. Leverage the community&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/5-lessons-from-10k-open-source-downloads/#4.-leverage-the-community&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “4. Leverage the community”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Earlier this year,
&lt;a href=&quot;https://github.com/SeanMcP/a11y-react-emoji/issues/3&quot;&gt;@bdbaraban filed an issue&lt;/a&gt;
with &lt;code&gt;a11y-react-emoji&lt;/code&gt; concerning its types. I was just learning TypeScript
when I wrote the library, and his suggestion was an improvement to the existing
code.&lt;/p&gt;
&lt;p&gt;I asked him if he wanted to open a pull request to make the change but quickly
got antsy and did it myself.&lt;/p&gt;
&lt;p&gt;That was a mistake. The beauty of open-source is the community; it&#39;s the product
of our collective intelligence. It didn&#39;t matter that the current types for the
package were limited. What would have been more important would be to wait and
give someone else the opportunity to contribute.&lt;/p&gt;
&lt;p&gt;To encourage more community involvement, you should include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An
&lt;a href=&quot;https://help.github.com/en/github/building-a-strong-community/manually-creating-a-single-issue-template-for-your-repository&quot;&gt;&lt;code&gt;ISSUE_TEMPLATE.md&lt;/code&gt;&lt;/a&gt;
to standardize issues,&lt;/li&gt;
&lt;li&gt;labels for
&lt;a href=&quot;https://help.github.com/en/github/building-a-strong-community/helping-new-contributors-find-your-project-with-labels&quot;&gt;&lt;code&gt;good-first-issue&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;https://www.firsttimersonly.com/&quot;&gt;&lt;code&gt;first-timers-only&lt;/code&gt;&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;and contact information in the &lt;code&gt;README.MD&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When someone reaches out or files an issue, give them a few days to respond.
Security bugs should be fixed immediately; features and small fixes can wait.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;5.-don&#39;t-lose-yourself&quot; tabindex=&quot;-1&quot;&gt;5. Don&#39;t lose yourself&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/5-lessons-from-10k-open-source-downloads/#5.-don&#39;t-lose-yourself&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “5. Don&#39;t lose yourself”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;There is an undeniable high that comes from seeing your code used by someone
else in the wild or stars the project on GitHub. I&#39;m not immune; I&#39;ve found
myself getting caught up in that ever-growing download count since releasing the
package.&lt;/p&gt;
&lt;p&gt;It&#39;s important to remember that your worth is not determined by the number or
popularity of your open-source contributions. It has nothing to do with how many
Twitter followers you have, claps you&#39;ve received on Medium, or unique traffic
to your personal website.&lt;/p&gt;
&lt;p&gt;You are infinitely valuable for just being you.&lt;/p&gt;
&lt;p&gt;If you want to publish packages because you think it&#39;s fun or you want to help
other people, then open source is a great way to contribute. But don&#39;t lose
sight of that motivation.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I hope those were helpful. If you have any questions about open source or want
to share some tips of your own, &lt;a href=&quot;https://seanmcp.com/articles/5-lessons-from-10k-open-source-downloads/#comment-link&quot;&gt;please let me know&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Mon, 25 Nov 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/5-lessons-from-10k-open-source-downloads/</guid>
    </item>
    <item>
      <title>Should I snapshot my UI components?</title>
      <link>https://seanmcp.com/articles/snapshot_test_ui_components/</link>
      <description>&lt;p&gt;If you are writing tests for React components in 2019, you will probably find
your way to Airbnb&#39;s Enzyme testing utilities. The recent industry standard,
Enzyme provides you some useful tools for rendering your components.&lt;/p&gt;
&lt;p&gt;Often times, folks will turn to snapshot testing with Enzyme because it a) is
easy, b) provides significant test coverage, and c) seems like a good idea. I
worked for a company where the default test suite for any component was a
snapshot, then any additional tests were added to bump coverage to an acceptible
level.&lt;/p&gt;
&lt;p&gt;However, the ease of use is often a double-edged sword. Here I wanted to outline
some common issues that arise from using snapshot tests for your UI components.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;issue-%231%3A-false-positives&quot; tabindex=&quot;-1&quot;&gt;Issue #1: False positives&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/snapshot_test_ui_components/#issue-%231%3A-false-positives&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Issue #1: False positives”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;If you&#39;ve every worked in a codebase that snapshots UI components, you&#39;ll know:
snapshot tests fail all the time. This is because snapshots collect a JSON-ified
version of the shallow render from Enzyme. Whenever anything changes to the
structure of the render, the output is different and therefore the snapshot
fails.&lt;/p&gt;
&lt;p&gt;Whenever you need to refactor a component, you can be assured that the test will
fail because the new snapshot doesn&#39;t match the one on record.&lt;/p&gt;
&lt;p&gt;Because snapshot tests fail all the time, they become &lt;strong&gt;false positives&lt;/strong&gt;:
results which incorrectly indicate that a problem is present. You snapshot can
fail, even though everything about the component will still function as
expected.&lt;/p&gt;
&lt;p&gt;If your test can fail when the function still works, &lt;strong&gt;what value is the
snapshot test adding&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;I have seen deployments fail because the snapshot encountered an unexpected
&lt;code&gt;div&lt;/code&gt; tag. The &amp;quot;solution&amp;quot; was simple enough: update the snapshot and then deploy
again. But that has its own problems.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;issue-%232%3A-combating-false-positives&quot; tabindex=&quot;-1&quot;&gt;Issue #2: Combating false positives&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/snapshot_test_ui_components/#issue-%232%3A-combating-false-positives&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Issue #2: Combating false positives”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Since failing snapshot tests are so common, it is common to run you rest suite
with the &lt;code&gt;-u, --update-snapshot&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run test -u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This rewrites the snapshot to match the current value instead of comparing it to
the previous one. Since the updated snapshot will always match the new value,
the test passes. Running the update snapshot flag is kind of like doing this:&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;let&lt;/span&gt; snapshot&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// npm test -u&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
snapshot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; snapshot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Passed!&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a little silly, but I think it illustrates the point. On its own,
updating a snapshot will automatically pass a test without providing you any
valuable information.&lt;/p&gt;
&lt;p&gt;Since failing tests are so common, you will be tempted to add the updated
snapshot flag to every test run. I know that I got a little too quick the the
&lt;code&gt;-u&lt;/code&gt; flag.&lt;/p&gt;
&lt;p&gt;But as illustrated above, running updated snapshot tests provides no valuable
information. So if you need to update the snapshot test every time, &lt;strong&gt;what value
is the snapshot test adding&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;If you find yourself backed into this corner, take heart in knowing that it&#39;s
not your fault. Snapshot testing forces us to focus on structure.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;issue-%233%3A-structure-over-function&quot; tabindex=&quot;-1&quot;&gt;Issue #3: Structure over function&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/snapshot_test_ui_components/#issue-%233%3A-structure-over-function&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Issue #3: Structure over function”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Snapshot tests value the structure of a component over its functionality. They
don&#39;t tell you how the component will work in your application; it only tells
you how the JSX will look. So you need to ask yourself: Do I want my test suite
to care about the structure of my components?&lt;/p&gt;
&lt;p&gt;Let&#39;s imagine an &lt;code&gt;IconButton&lt;/code&gt; component that looks something like this:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;IconButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; icon&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;props &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;props&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Icon icon&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&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 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;After reviewing with the design team, you determing that it would be better for
the button text to be inline with the &lt;code&gt;Icon&lt;/code&gt;, so you swap the &lt;code&gt;div&lt;/code&gt; for a
&lt;code&gt;span&lt;/code&gt;:&lt;/p&gt;
&lt;!-- ```js/4 --&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;IconButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; icon&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;props &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;props&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Icon icon&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&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 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;After this change, should the test for &lt;code&gt;IconButton&lt;/code&gt; fail? I&#39;d argue: no.&lt;/p&gt;
&lt;p&gt;The functionality of the IconButton hasn&#39;t changed; it still renders an &lt;code&gt;Icon&lt;/code&gt;
and passes the &lt;code&gt;onClick&lt;/code&gt; handler to the &lt;code&gt;button&lt;/code&gt; element. I and every developer
after me should be free to refactor as much as I need so long as the component
still functions in the same manner when we&#39;re done.&lt;/p&gt;
&lt;p&gt;But a snapshot test disagrees and will fail because you changed that tag. You
might anticipate the test failing and run it with the update snapshot flag,
which has all the problems listed above. So again, &lt;strong&gt;what value is the snapshot
test adding&lt;/strong&gt;?&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;snapshots-are-a-bad-fit-for-ui-testing&quot; tabindex=&quot;-1&quot;&gt;Snapshots are a bad fit for UI testing&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/snapshot_test_ui_components/#snapshots-are-a-bad-fit-for-ui-testing&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Snapshots are a bad fit for UI testing”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Shallow rendering + snapshot tests. &lt;a href=&quot;https://t.co/3TIa4VIhsc&quot;&gt;pic.twitter.com/3TIa4VIhsc&lt;/a&gt;&lt;/p&gt;&amp;mdash; Mark Dalgleish (@markdalgleish) &lt;a href=&quot;https://twitter.com/markdalgleish/status/1161616241531863045?ref_src=twsrc%5Etfw&quot;&gt;August 14, 2019&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;In most instances, snapshots provde no additional value for your UI tests. They
are easy and feel good to write, but I think they add little confidence to your
testing suite for any given component.&lt;/p&gt;
&lt;p&gt;You may stumble across scenarios with a highly specialized component, definite
structure, and specific attributes that would benefit from a snapshot. But these
are an exception to the rule. In general, &lt;strong&gt;avoid snapshot testing for UI
components&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;another-way&quot; tabindex=&quot;-1&quot;&gt;Another way&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/snapshot_test_ui_components/#another-way&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Another way”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Instead of defaulting to snapshots, ask yourself: What does this component
&lt;strong&gt;need&lt;/strong&gt; to do? Then write a test that ensures the component can do just that.&lt;/p&gt;
&lt;p&gt;If you&#39;re working with Enzyme, use
&lt;a href=&quot;https://airbnb.io/enzyme/docs/api/ReactWrapper/find.html&quot;&gt;&lt;code&gt;find()&lt;/code&gt;&lt;/a&gt; to insure
that any necessary elements render with the appropriate attributes. Use
&lt;a href=&quot;https://airbnb.io/enzyme/docs/api/ReactWrapper/simulate.html&quot;&gt;&lt;code&gt;simulate()&lt;/code&gt;&lt;/a&gt; to
interact with your components in a meaningful way, focusing on functionality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your tests should give me the confidence to refactor any component to your
heart&#39;s content&lt;/strong&gt;. They should fail if you change functionality, not structure.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;further-reading&quot; tabindex=&quot;-1&quot;&gt;Further reading&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/snapshot_test_ui_components/#further-reading&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Further reading”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kentcdodds.com/blog/testing-implementation-details&quot;&gt;&amp;quot;Testing Implementation Details&amp;quot; by Kent C. Dodds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://testing-library.com/docs/react-testing-library/intro&quot;&gt;React Testing Library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Wed, 11 Sep 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/snapshot_test_ui_components/</guid>
    </item>
    <item>
      <title>Lessons Learned at Abstractions 2019</title>
      <link>https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/</link>
      <description>&lt;p&gt;Two weeks into the job, my new employer sent me and a dozen other engineers,
qa&#39;s, and designers to &lt;a href=&quot;https://abstractions.io/&quot;&gt;Abstractions 2019&lt;/a&gt;, a
cross-discipline software conference in Pittsburgh, PA. Headlining the group of
speakers was the YouTuber and creater Simone Gertz, Chris Coyier of CodePen and
CSS-Tricks, and Guillermo Rauch of Zeit.&lt;/p&gt;
&lt;p&gt;I took down some notes for my team and me to reference after the event. If I
have time, I&#39;ll add more details including speakers, videos, and links. Until
then, enjoy this for what it is!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;wednesday&quot; tabindex=&quot;-1&quot;&gt;Wednesday&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#wednesday&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Wednesday”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;your-work-has-moral-implications&quot; tabindex=&quot;-1&quot;&gt;Your Work Has Moral Implications&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#your-work-has-moral-implications&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Your Work Has Moral Implications”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Tech has great power and great responsibility. Ensure that your team has a
policy in place and make plans to protect yourself from moral crises.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;how-to-build-a-community-from-scratch&quot; tabindex=&quot;-1&quot;&gt;How to Build a Community from Scratch&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#how-to-build-a-community-from-scratch&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “How to Build a Community from Scratch”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Find a consistent schedule to hold interesting meetings in an intimate
environment.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;deep-learning-for-developers&quot; tabindex=&quot;-1&quot;&gt;Deep Learning for Developers&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#deep-learning-for-developers&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Deep Learning for Developers”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The barrier to entry for machine learning is lower than ever. Check it out!&lt;/p&gt;
&lt;p&gt;Resources&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TensorFlow&lt;/li&gt;
&lt;li&gt;PyTorch&lt;/li&gt;
&lt;li&gt;IBM Max&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;building-accessibility-together&quot; tabindex=&quot;-1&quot;&gt;Building Accessibility Together&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#building-accessibility-together&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Building Accessibility Together”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Every team should plan and work with accessibility in mind. Stick to best
practices in design, markup, and scripting to help insure that products work
well for everyone.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;software-for-people-who-don&#39;t-want-it&quot; tabindex=&quot;-1&quot;&gt;Software for people who don&#39;t want it&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#software-for-people-who-don&#39;t-want-it&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Software for people who don&#39;t want it”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Consider the end user when making all software decisions. Strive for an
application that is as accessible and usable as possible.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;programming-productivity&quot; tabindex=&quot;-1&quot;&gt;Programming Productivity&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#programming-productivity&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Programming Productivity”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Take the time to create tooling to standardize best practices. Use the
JavaScript AST to write custom lint tools or CodeMods that will work across the
codebase.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;thursday&quot; tabindex=&quot;-1&quot;&gt;Thursday&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#thursday&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Thursday”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;practical-open-source-security&quot; tabindex=&quot;-1&quot;&gt;Practical Open Source Security&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#practical-open-source-security&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Practical Open Source Security”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Use available tools like npm audit to scan your dependencies regularly. Make it
part of the regular code reviews and automate the process as much as possible.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mitre&lt;/li&gt;
&lt;li&gt;Snyk&lt;/li&gt;
&lt;li&gt;webhint&lt;/li&gt;
&lt;li&gt;Lighthouse&lt;/li&gt;
&lt;li&gt;Dependabot&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;those-who-can-do-should-also-teach&quot; tabindex=&quot;-1&quot;&gt;Those Who Can Do Should Also Teach&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#those-who-can-do-should-also-teach&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Those Who Can Do Should Also Teach”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Teaching is a great way to reinforce your knowledge, and help people. Prepare
your students for the challenges of learning something new and how to work
through them. Teach targetted interactive lessons in 10-15 minute bursts.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;mentoring-the-way-to-a-more-inclusive-workplace&quot; tabindex=&quot;-1&quot;&gt;Mentoring the Way to a More Inclusive Workplace&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#mentoring-the-way-to-a-more-inclusive-workplace&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Mentoring the Way to a More Inclusive Workplace”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The tenants of good mentorship align with those of an inclusive workplace. List
realistic criteria on job postings and provide specific constructive feedback
throughout the hiring process. Have clear and structured onboarding, learning
groups, and formalized mentorship/sponsorship. Don&#39;t forget: mentorship is a
learned skill that must be honed.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;readability-%26-web&quot; tabindex=&quot;-1&quot;&gt;Readability &amp;amp; Web&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#readability-%26-web&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Readability &amp;amp; Web”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Reading is a complex cognitive task. Around 10% of the global population has
some degree of dyslexia. Improving readability for users with dyslexia helps
everyone. Readability is about anything communicating meaningful information,
including icons.&lt;/p&gt;
&lt;p&gt;Fours C&#39;s of readability&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Continuity: reputation of the information until the endpoint&lt;/li&gt;
&lt;li&gt;Conspicuity: beating easily seen or noticeable&lt;/li&gt;
&lt;li&gt;Consistency: keeping the same wording along the way&lt;/li&gt;
&lt;li&gt;Clarity: the message need to be clearly understandable&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;building-afghanistan-2.0-with-%23afghangirlscode&quot; tabindex=&quot;-1&quot;&gt;Building Afghanistan 2.0 with #AfghanGirlsCode&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#building-afghanistan-2.0-with-%23afghangirlscode&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Building Afghanistan 2.0 with #AfghanGirlsCode”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;an-unforgettable-developer-experience&quot; tabindex=&quot;-1&quot;&gt;An Unforgettable Developer Experience&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#an-unforgettable-developer-experience&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “An Unforgettable Developer Experience”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Everything we do is for the human being on the other side. There is a lot of
value from making things more accessible. Make the process work for both novices
and experts.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;friday&quot; tabindex=&quot;-1&quot;&gt;Friday&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#friday&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Friday”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;humane-design-patterns&quot; tabindex=&quot;-1&quot;&gt;Humane Design Patterns&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#humane-design-patterns&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Humane Design Patterns”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Considering the well-being of the user in your designs. Habit-forming products
and sticky experiences might be bad for the people using them. Instead, align
human goals and tech goals.&lt;/p&gt;
&lt;p&gt;Resources&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Abstract on Netflix&lt;/li&gt;
&lt;li&gt;Humane by Design&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;pedagogy-in-pairing&quot; tabindex=&quot;-1&quot;&gt;Pedagogy in Pairing&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#pedagogy-in-pairing&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Pedagogy in Pairing”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Scope the activity so that it lends itself to joint productive activity. Remove
all distractions during the session (e.g. phones). Point to the source of
information; don&#39;t spoon feed. Ground your activity in areas that your partner
already understands.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;accessibility-as-a-bridge-between-designers-and-engineers&quot; tabindex=&quot;-1&quot;&gt;Accessibility as a Bridge Between Designers and Engineers&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#accessibility-as-a-bridge-between-designers-and-engineers&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Accessibility as a Bridge Between Designers and Engineers”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;We can communicate effectively only by speaking the same language: developers
must understand design, and &lt;em&gt;vice versa&lt;/em&gt;. Collaboration is key to an accessible
product.&lt;/p&gt;
&lt;p&gt;Resources&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Accessibility Insights for Web&lt;/li&gt;
&lt;li&gt;Vox Accessibility Guidelines&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;teaching-high-school-computer-science&quot; tabindex=&quot;-1&quot;&gt;Teaching High School Computer Science&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#teaching-high-school-computer-science&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Teaching High School Computer Science”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;There are differences between teaching high schoolers and junior developers, but
there are also a lot of similarities. Try to direct the student towards self
discovery. Help work them through failures with learning in mind. GitHub pages
provided a great experience with quick feedback.&lt;/p&gt;
&lt;p&gt;Resources&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TEALS&lt;/li&gt;
&lt;li&gt;Pitt TLI Summer Program&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;oops-i-guess-we&#39;re-full-stack-developers-now&quot; tabindex=&quot;-1&quot;&gt;Oops I Guess We&#39;re Full-stack Developers Now&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#oops-i-guess-we&#39;re-full-stack-developers-now&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Oops I Guess We&#39;re Full-stack Developers Now”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Certain tasks have shifted from the backend of the application to the front.
There is a divide between frontend developers who focus on the front of the
frontend and those who focus on the back.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;building-ux-teams&quot; tabindex=&quot;-1&quot;&gt;Building UX Teams&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/#building-ux-teams&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Building UX Teams”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Managers empower the team to realize its full potential. People have different
skills, can work together, can learn, and can teach.&lt;/p&gt;
</description>
      <pubDate>Tue, 27 Aug 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/lessons-learned-at-abstractions-con-2019/</guid>
    </item>
    <item>
      <title>Edit a previous commit message</title>
      <link>https://seanmcp.com/articles/edit-previous-commit-message/</link>
      <description>&lt;p&gt;At my previous company, all commit messages need to begin with a ticket name and
number from Jira. If I&#39;m working on the Bananas project and am assigned ticket
5, my commit messages will look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BANANAS-5 Add user authentication
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you forget to add the ticket to the commit message, you are unable to push
your code to the project repository on BitBucket.&lt;/p&gt;
&lt;p&gt;Since I commit a lot outside of work and am the type of developer who makes
mistakes, I find myself with commit messages that are missing the required
ticket.&lt;/p&gt;
&lt;p&gt;You might not have the same policy on your project, but you may have found
yourself wanting to change a commit message. Here are two options for editing a
previous commit message.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;change-the-most-recent-commit&quot; tabindex=&quot;-1&quot;&gt;Change the most recent commit&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/edit-previous-commit-message/#change-the-most-recent-commit&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Change the most recent commit”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Sometimes you can catch the error quickly after making your commit. If you need
to change the most recent commit message, you can use the &lt;code&gt;amend&lt;/code&gt; flag.&lt;/p&gt;
&lt;p&gt;In your project directory in the terminal, enter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit --amend
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will bring the most recent commit message open in your default editor. Make
the necessary changes to the message, mind the formatting, save the file, and
exit. You should see a read out with the updated commit message.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;change-an-old-commit&quot; tabindex=&quot;-1&quot;&gt;Change an old commit&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/edit-previous-commit-message/#change-an-old-commit&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Change an old commit”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;More often than not, I don&#39;t find my commit message error until many commits
later. In those cases, the &lt;code&gt;amend&lt;/code&gt; flag won&#39;t help use. We need to &lt;code&gt;rebase&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Rebasing in git is a way to change commits from long ago by reapplying all the
commits from a point in time. When you &lt;code&gt;rebase&lt;/code&gt; you can rewrite the history of
your project; so proceed with caution.&lt;/p&gt;
&lt;p&gt;First, you need to find the commit message from which you want to start your
rebase. To do that, go to the log:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now find find the commit &lt;strong&gt;immediately prior to the one you want to change&lt;/strong&gt; and
copy the hash (long series of letters and numbers). With that point of
reference, you&#39;re ready to rebase!&lt;/p&gt;
&lt;p&gt;Enter the &lt;code&gt;rebase&lt;/code&gt; command with the &lt;code&gt;interactive&lt;/code&gt; flag (you can also use &lt;code&gt;-i&lt;/code&gt;
for short) and the commit hash you copied:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git rebase --interactive &amp;lt;prior_commit_hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will open the default editor with a list of all the commits &lt;strong&gt;after&lt;/strong&gt; the
hash you provided. There is a good bit going on here, but don&#39;t be overwhelmed.
Focus on the top of where you can see a list of the commits and their messages.&lt;/p&gt;
&lt;p&gt;It should look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pick a8fc26a BANANAS-5 This is a good commit message
pick 60ac057 Change me!

# Rebase 5923966..60ac057 onto 5923966 (2 commands)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, find the commit message you want to change, and replace the word &lt;code&gt;pick&lt;/code&gt;
with &lt;code&gt;reword&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pick a8fc26a BANANAS-5 This is a good commit message
reword 60ac057 Change me!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then save and exit the editor.&lt;/p&gt;
</description>
      <pubDate>Fri, 23 Aug 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/edit-previous-commit-message/</guid>
    </item>
    <item>
      <title>Arguments or parameters?</title>
      <link>https://seanmcp.com/articles/arguments-or-parameters/</link>
      <description>&lt;p&gt;I have a hard time remembering the difference between two common programming
terms: arguments, and parameters.&lt;/p&gt;
&lt;p&gt;This simple inability leads to real insecurity when I speak with coworkers with
more education and experience. Nothing inspires imposter syndrome in a more than
not remembering the fundamentals!&lt;/p&gt;
&lt;p&gt;Here is the difference between the two similar terms:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;arguments&quot; tabindex=&quot;-1&quot;&gt;Arguments&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/arguments-or-parameters/#arguments&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Arguments”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Arguments are the data passed to a function. Think of something simple like
&lt;code&gt;console.log()&lt;/code&gt;. In order to get a message written to the console, you need to
pass it some information.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;One&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every bit of information that you pass to a function is an argument. In the
above example, we are assigning four arguments to &lt;code&gt;console.log()&lt;/code&gt;: &lt;code&gt;&amp;quot;One&amp;quot;&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;,
&lt;code&gt;false&lt;/code&gt;, and &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;parameters&quot; tabindex=&quot;-1&quot;&gt;Parameters&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/arguments-or-parameters/#parameters&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Parameters”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Parameters are the data that a function is expecting. Let&#39;s say you have a
function that takes two numbers and returns the sum. It will look something like
this:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&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 keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&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;The variables that a function is expecting are its parameters. In our &lt;code&gt;sum()&lt;/code&gt;
function, we are expecting two parameters: &lt;code&gt;a&lt;/code&gt;, and &lt;code&gt;b&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;remembering-the-difference&quot; tabindex=&quot;-1&quot;&gt;Remembering the difference&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/arguments-or-parameters/#remembering-the-difference&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Remembering the difference”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I&#39;ve been wracking my brain for a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Mnemonic&quot;&gt;mnemonic device&lt;/a&gt; for remembering the
difference, but have yet to come up with a great one. In the meantime, this will
have to do:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Arguments&lt;/strong&gt; are data you &lt;strong&gt;assign&lt;/strong&gt; to a function, while &lt;strong&gt;parameters&lt;/strong&gt; are
variables that the function is &lt;strong&gt;prepared&lt;/strong&gt; to accept.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If I hear of or come up with a better one, I&#39;ll update this article.&lt;/p&gt;
&lt;p&gt;I hope that helps!&lt;/p&gt;
</description>
      <pubDate>Tue, 30 Jul 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/arguments-or-parameters/</guid>
    </item>
    <item>
      <title>Reasons to use prop-types</title>
      <link>https://seanmcp.com/articles/reasons-to-use-prop-types/</link>
      <description>&lt;p&gt;I work in an environment where we have dozens of developers across multiple
locations and countries working on a few central applications. This requires a
level of organization and coordination to ensure that the team is working
collaboratively and efficiently.&lt;/p&gt;
&lt;p&gt;One of the ways we can encourage efficient collaboration is by type checking in
our React applications with the &lt;code&gt;prop-types&lt;/code&gt; library.&lt;/p&gt;
&lt;p&gt;Here are two reasons why you should be using &lt;code&gt;prop-types&lt;/code&gt; in your React
applications.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;catch-errors&quot; tabindex=&quot;-1&quot;&gt;Catch errors&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reasons-to-use-prop-types/#catch-errors&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Catch errors”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The most immediate benefit to using prop-types is that you can catch and
diagnose errors quickly while working on your application. If you instantiate a
component and pass invalid props or forget to pass required props, the library
will send you a console warning that details each mistake.&lt;/p&gt;
&lt;p&gt;Let&#39;s say you have a &lt;code&gt;OrderedList&lt;/code&gt; component that renders children based on an
array of &lt;code&gt;items&lt;/code&gt; passed as a prop. It might look like this:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;OrderedList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&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;item&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 class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Item&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;meta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Item&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&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 class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&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 class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; OrderedList&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now what would happen if instantiated this component without passing it an array
of items? React will throw an error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TypeError

props.items is undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could probably resolve the issue from that message, but what would happen if
&lt;code&gt;OrderedList&lt;/code&gt; was passed an array, but the items were strings, or numbers, or
objects with different properties?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TypeError

item.meta is undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;document-components&quot; tabindex=&quot;-1&quot;&gt;Document components&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/reasons-to-use-prop-types/#document-components&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Document components”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;When you are working on a larger code base with multiple users, it is easy to
lose track of the available components. When you want to use a component that
you didn&#39;t create or haven&#39;t look at in a while, it can be difficult to
determine what props that component requires.&lt;/p&gt;
&lt;p&gt;Take for example the this &lt;code&gt;LoginForm&lt;/code&gt; component using
&lt;a href=&quot;https://npmjs.com/package/formik&quot;&gt;&lt;code&gt;Formik&lt;/code&gt;&lt;/a&gt;. Try to quickly determine what
props this component requires:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;LoginForm&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;props&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Heading &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;h&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headingLevel&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 class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Heading&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Login&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Heading&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Formik&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;initialValues&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation 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 literal-property property&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&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;span class=&quot;token attr-name&quot;&gt;onSubmit&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;values&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; actions&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 class=&quot;token keyword&quot;&gt;await&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;values&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          actions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetForm&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation 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 parameter&quot;&gt;props&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 class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onSubmit&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleSubmit&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Input&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Input&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Login&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&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;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could do it, but it is time consuming and can be a nightmare for larger
class components. Now imagine we had the same component above with the following
prop-types declared below:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;LoginForm&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;props&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 class=&quot;token comment&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;

LoginForm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;propTypes &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 literal-property property&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;func&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isRequired&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;headingLevel&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;oneOf&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 number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&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;isRequired&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now at a glance we can determine that this component needs two props, &lt;code&gt;level&lt;/code&gt;
and &lt;code&gt;login&lt;/code&gt;, and what their types and restrictions are. When I want to use
&lt;code&gt;LoginForm&lt;/code&gt;, I know I need to pass it a &lt;code&gt;headingLevel&lt;/code&gt; (from 1-6) and a &lt;code&gt;login&lt;/code&gt;
function. Nice and easy.&lt;/p&gt;
</description>
      <pubDate>Thu, 27 Jun 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/reasons-to-use-prop-types/</guid>
    </item>
    <item>
      <title>How to pronounce my name</title>
      <link>https://seanmcp.com/articles/how-to-pronounce-my-name/</link>
      <description>&lt;p&gt;My name is Sean McPherson, and I have an often-mispronounced name. I wanted to
take the opportunity for all those curious the history and pronunciation of my
first and last names, and handle.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; My name is pronounced &amp;quot;Shon Mick·Fur·sen&amp;quot; and my handle is &amp;quot;Shon
Mick·Pea&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;sean&quot; tabindex=&quot;-1&quot;&gt;Sean&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-pronounce-my-name/#sean&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Sean”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Sean is an Irish counterpart to the English name John and the French name Jean.
They all come from the Hebrew name &lt;em&gt;Yohanan&lt;/em&gt; which means &amp;quot;God is gracious.&amp;quot;&lt;/p&gt;
&lt;p&gt;Irish names in general are difficult for English-speakers to read, because the
letters represent different phonemes, or sounds. To resolve this, Sean is often
Anglicized as the more English phonetic &amp;quot;Shaun&amp;quot; or &amp;quot;Shawn.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I pronounce Sean as &amp;quot;Shon.&amp;quot;&lt;/strong&gt; This is the most common pronunciation, though
you may have the good pleasure of coming across a &amp;quot;Seen&amp;quot; in your life. I&#39;ve been
told that there is a pronunciation of Sean that is closer to the English
&amp;quot;Shane&amp;quot;, but I&#39;ve never met one.&lt;/p&gt;
&lt;p&gt;If you know a &amp;quot;Shon&amp;quot; spelled Sean in your life, please do pronounce the name
appropriately. Chances are they, like me, have had their name mispronounced many
times in their life, both accidentally and deliberately, and chafe at the sound
of &amp;quot;Seen.&amp;quot;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;mispronuncations-to-avoid&quot; tabindex=&quot;-1&quot;&gt;Mispronuncations to avoid&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-pronounce-my-name/#mispronuncations-to-avoid&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Mispronuncations to avoid”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Seen&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Say-on&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;See-an&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;mcpherson&quot; tabindex=&quot;-1&quot;&gt;McPherson&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-pronounce-my-name/#mcpherson&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “McPherson”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;McPherson is a version of the Scottish surname Macpherson. In the original Scots
Gaelic, the name is rendered &lt;em&gt;Mac a&#39; Phearsain&lt;/em&gt; and means &amp;quot;son of the parson.&amp;quot;&lt;/p&gt;
&lt;p&gt;Macpherson is the name of a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Scottish_clan&quot;&gt;clan that hails from the Scottish Highlands&lt;/a&gt;.
From there, the Macphersons spread throughout the British Isles, to the
Americas, Australia, and the rest of the world.&lt;/p&gt;
&lt;p&gt;Said with a Scottish accent, Macpherson is approximately pronounced
&amp;quot;Mack·Fair·sen.&amp;quot; I&#39;ve learned from Scots that there is no major distinction
between &amp;quot;Mac&amp;quot; and &amp;quot;Mc&amp;quot; names; it&#39;s largely a family or individual decision.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I pronounce McPherson as &amp;quot;Mick·Fur·sen.&amp;quot;&lt;/strong&gt; In my experience, this is less
common than a &amp;quot;Mick·Fear·sen&amp;quot; pronunciation. However, I feel my version is a)
closer to the Scottish pronunciation, and b) more phonetic in American English.
There are members of my own family who disagree and prefer &amp;quot;Fear&amp;quot; to &amp;quot;Fur.&amp;quot;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;mispronuncations-to-avoid-1&quot; tabindex=&quot;-1&quot;&gt;Mispronuncations to avoid&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-pronounce-my-name/#mispronuncations-to-avoid-1&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Mispronuncations to avoid”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Mick·Fear·sen&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Mick·Person&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;mcp&quot; tabindex=&quot;-1&quot;&gt;McP&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-pronounce-my-name/#mcp&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “McP”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Scottish and Irish surnames beginning with Mac/Mc are often abbreviated with the
first letter after the prefix. For example, the name &amp;quot;McDonald&amp;quot; could be
shortened to &amp;quot;McD.&amp;quot;&lt;/p&gt;
&lt;p&gt;When reading one of the abbreviated names, the pronunciation is preserved for
the prefix, but the letter is read by its name. &amp;quot;McD&amp;quot; would be read as
&amp;quot;Mick·Dee&amp;quot; and not &amp;quot;Em·See·Dee&amp;quot; nor &amp;quot;Mick·Duh.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I pronounce McP as &amp;quot;Mick·Pea.&amp;quot;&lt;/strong&gt; My surname is unique in that the first letter
after the prefix, p, is not pronounced in the full version. Despite this, I am
still a McP along with all the McPattersons, and McPartlands of the world.&lt;/p&gt;
&lt;p&gt;This article was originally posted on &lt;a href=&quot;https://seanmcp.com/&quot;&gt;SeanMcP.com&lt;/a&gt;, which you now know is
read &amp;quot;Shon Mick·Pea Dot Com.&amp;quot;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;mispronuncations-to-avoid-2&quot; tabindex=&quot;-1&quot;&gt;Mispronuncations to avoid&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-pronounce-my-name/#mispronuncations-to-avoid-2&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Mispronuncations to avoid”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Em·See·Pea&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Mick·Puh&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;to-each-their-own&quot; tabindex=&quot;-1&quot;&gt;To each their own&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/how-to-pronounce-my-name/#to-each-their-own&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “To each their own”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You may come across another Sean McPherson who pronounces his name differently
than I do; and that&#39;s okay. It is our reasonable responsibility to learn and use
the correct pronunciation of our neighbor&#39;s name to the best of our ability.&lt;/p&gt;
</description>
      <pubDate>Sat, 15 Jun 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/how-to-pronounce-my-name/</guid>
    </item>
    <item>
      <title>Add event listener for class change</title>
      <link>https://seanmcp.com/articles/event-listener-for-class-change/</link>
      <description>&lt;p&gt;🆕 &lt;strong&gt;Update:&lt;/strong&gt; I created a
&lt;a href=&quot;https://seanmcp.com/articles/listen-for-class-change-in-javascript&quot;&gt;reusable function to listen for class changes on any element&lt;/a&gt;
that is more robust that the solution here.&lt;/p&gt;
&lt;p&gt;If you&#39;re like me and spent some time searching for an event listener for class
changes, let me save you some time: &lt;strong&gt;there isn&#39;t one&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;However, you can create your own custom event listener using the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver&quot;&gt;&lt;code&gt;MutationObserver&lt;/code&gt; API&lt;/a&gt;.
Let&#39;s look at how to create a simple listener for class changes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;MutationObserver&lt;/code&gt; is a constructor that can allows you to watch any changes to
the DOM tree. The constructor takes a callback function that will receive two
arguments: a list of
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord&quot;&gt;MutationRecords&lt;/a&gt;,
and a reference to the observer.&lt;/p&gt;
&lt;p&gt;First, let&#39;s first create a callback that logs its parameters and then construct
a new &lt;code&gt;MutationObserver&lt;/code&gt;:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;mutationsList&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; observer&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;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Mutations:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mutationsList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Observer:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; observer&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 keyword&quot;&gt;const&lt;/span&gt; mutationObserver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MutationObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;callback&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;We&#39;ve constructed basic custom observer, but we need to listen for changes on
something. To add our custom event listener, we need to call the created
observer&#39;s
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe&quot;&gt;&lt;code&gt;observe()&lt;/code&gt; method&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;MutationObserver.observe()&lt;/code&gt; takes two arguments: the DOM node target, and an
optional options object. We&#39;ll call this method and pass it a node reference and
one option:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;mutationObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;main&quot;&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 literal-property property&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This set our &lt;code&gt;MutationObserver&lt;/code&gt; to look for any changes to the &lt;code&gt;main&lt;/code&gt;&#39;s
attributes. If we trigger a change, we can see the two logs from &lt;code&gt;callback()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Mutations:
[MutationRecord]

Observer:
MutationObserver
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On each &lt;code&gt;MutationRecord&lt;/code&gt; object, there is an &lt;code&gt;attributeName&lt;/code&gt; property. If that
value is equal to &#39;class&#39;, then we know that there has been a change to the
element&#39;s class name.&lt;/p&gt;
&lt;p&gt;Now we can update the &lt;code&gt;callback&lt;/code&gt; function by adding a condition:&lt;/p&gt;
&lt;!-- ```js/0-5 --&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;mutationsList&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;
  mutationsList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&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;mutation&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 class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mutation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attributeName &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&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 function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ch-ch-ch-changes!&quot;&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code will fire an alert if any item in &lt;code&gt;mutationsList&lt;/code&gt; records a change to
&lt;code&gt;main&lt;/code&gt;&#39;s class attribute.&lt;/p&gt;
&lt;p&gt;If you need to stop your custom event listener later, use &lt;code&gt;MutationObserver&lt;/code&gt;&#39;s
&lt;code&gt;disconnect()&lt;/code&gt; method to remove it like you would a normal event listener:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;mutationObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;disconnect&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;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;example&quot; tabindex=&quot;-1&quot;&gt;Example&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/event-listener-for-class-change/#example&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Example”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Checkout a working example of custom event listener for class change on CodePen:&lt;/p&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Event listener for class change&quot; src=&quot;https://codepen.io/SeanMcP/embed/preview/RmWJvV/?height=500&amp;theme-id=0&amp;default-tab=js,result&quot; frameborder=&quot;no&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot;&gt;
  See the Pen &lt;a href=&quot;https://codepen.io/SeanMcP/pen/RmWJvV/&quot;&gt;Event listener for class change&lt;/a&gt; by Sean McPherson
  (&lt;a href=&quot;https://codepen.io/SeanMcP&quot;&gt;@SeanMcP&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Fri, 10 May 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/event-listener-for-class-change/</guid>
    </item>
    <item>
      <title>Why I switched from Gatsby to Eleventy</title>
      <link>https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/</link>
      <description>&lt;p&gt;I spent the last few weeks exploring Eleventy, a simpler static-site generator.
After tinkering for a few days, I decided to transition this website from Gatsby
to Eleventy.&lt;/p&gt;
&lt;p&gt;I&#39;m going to take the opportunity explain my decision for the benefit of myself
and others who are looking for a static-site generator.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;disclaimer&quot; tabindex=&quot;-1&quot;&gt;Disclaimer&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#disclaimer&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Disclaimer”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Before I dive into my reasons for switching from Gatsby to Eleventy, I would
like to state: &lt;strong&gt;I like Gatsby&lt;/strong&gt;. I have no major criticisms of the project and
would turn to it first if I were making a rich static site for a small business
or enterprise. All of the following are not reasons why Gatsby is bad but rather
why I preferred Eleventy for this blog.&lt;/p&gt;
&lt;!--
### Gatsby

In case you haven&#39;t heard of it, Gatsby is one of the fastest-growing static site generators. The project was started by [Kyle Mathews](https://github.com/kyleamathews) way back in 2015 and has grown into a &quot;free and open source framework based on React that helps developers build blazing fast websites and apps&quot;.

For this reason (and others), Gatsby has become the first choice for React developers looking to create a static site with a growing ecosystem of powerful plugins.

### Eleventy

While some folks may have heard of Gatsby before, fewer will be familiar Eleventy. Created by [Zach Leatherman](https://github.com/zachleat) in November 2017, Eleventy bills itself as &quot;a simpler static site generator.&quot; In a directory with only a single markdown file, Eleventy can generate a static site with one command:

```bash
eleventy
```

Folks at CERN, Google, Netlify, CSS-Tricks, and [loads of others](https://www.11ty.io/docs/sites/) are using Eleventy to generate static sites. --&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;the-initial-decision&quot; tabindex=&quot;-1&quot;&gt;The initial decision&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#the-initial-decision&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “The initial decision”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;After kicking around the idea of moving off Medium to a personal blog, I decided
to take the plunge in the Fall of 2018. After a quick survey of the options, I
settled on Gatsby for the following reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;React based&lt;/strong&gt;: I am a React developer and loved the idea of using a familiar
library for creating the components of my personal website.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GraphQL powered&lt;/strong&gt;: I didn&#39;t know much about GraphQL at the time, but
everything I heard was positive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Growing popularity&lt;/strong&gt;: It seemed that everyone in the React community was
launching a new Gatsby site. All aboard the hype train!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I tinkered around with the setup for a few weeks, then launched the site late
in 2018.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;honeymoon&quot; tabindex=&quot;-1&quot;&gt;Honeymoon&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#honeymoon&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Honeymoon”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Gatsby has a bit of a learning curve, but after the first few weeks I felt
comfortable working in their environment. Whenever I wanted to tweak the layout,
design, and/or functionality of the site, I felt comfortable diving into the
JavaScript to make the change.&lt;/p&gt;
&lt;p&gt;Gatsby&#39;s integration with Netlify was seamless as well&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, which made pushing
changes to production as easy as pushing code.&lt;/p&gt;
&lt;p&gt;With my Gatsby personal site and blog, I was riding high with the cool kids.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;cracks-start-to-form&quot; tabindex=&quot;-1&quot;&gt;Cracks start to form&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#cracks-start-to-form&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Cracks start to form”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;As the months rolled by, however, I started to struggle with my Gatsby-powered
site. There were a few specific ways where those manifested themselves.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;tinker-temptation&quot; tabindex=&quot;-1&quot;&gt;Tinker temptation&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#tinker-temptation&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Tinker temptation”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I found myself spending significantly more time making minor tweaks to the site
than I did writing. This is partly a personal problem; I have a fixed mindset
about writing that can throw significant blocks. I&#39;m working on valuing
production over perfection in that error.&lt;/p&gt;
&lt;p&gt;And the fact that my site was written in React provided me ample temptation to
postpone writing in favor of tinkering. Whenever I found it difficult to write,
I would create, then refactor, then recreate, then refactor components &lt;em&gt;ad
nauseam&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It was not uncommon for me to have over a dozen commits on my blog a week, but
no article to show for it. Gatsby&#39;s strength of using React ended up a weakness
for me as a writer.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;modern-complication&quot; tabindex=&quot;-1&quot;&gt;Modern complication&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#modern-complication&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Modern complication”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Gatsby&#39;s surge in popularity at the end of 2018 drew criticism from certain
sections of the web. The criticism usually centered around Gatsby&#39;s modern
tooling, which is more advanced than some of its competitors.&lt;/p&gt;
&lt;p&gt;Mark Dalgleish, one of my funniest follows on Twitter, shared that sentiment
with this gem of a meme:&lt;/p&gt;
&lt;!-- EMBED THE MEME, DOG --&gt;
&lt;p&gt;I think this criticism is a little misguided, but there is a truth to the
implied question: &amp;quot;Why do you need to use all these fancy tools for a simple
blog?&amp;quot;&lt;/p&gt;
&lt;p&gt;As I struggled with the aforementioned temptation to tinker instead of write,
that question was left ringing in my ears. Maybe there was a simpler system for
my site?&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;the-final-straw&quot; tabindex=&quot;-1&quot;&gt;The final straw&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#the-final-straw&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “The final straw”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I have a 2010 MacBook Pro on which I occasionally do some work. Despite it&#39;s
age, the computer still does a great job keeping up with the modern development
environment. Up until recently, I used that laptop exclusively for personal
projects, and it never had a problem (besides speed).&lt;/p&gt;
&lt;p&gt;That is, until it met Gatsby.&lt;/p&gt;
&lt;p&gt;Try as I might, I could not get my Gatsby site to run on that computer. I
changed Node versions, updated &lt;code&gt;gatsby-cli&lt;/code&gt;, and cloned and re-cloned the
repository, but to no avail.&lt;/p&gt;
&lt;p&gt;I just resigned myself to not using that computer (it &lt;em&gt;is&lt;/em&gt; almost ten years
old). But then, a two months ago out of the blue, I had the same problem with my
work computer (2015 MacBook Pro). Try as I might, I couldn&#39;t get Gatsby running.&lt;/p&gt;
&lt;p&gt;Now there is almost certainly a solution to the problem on both of my computers.
But I searched for a while, tried a few workarounds, and couldn&#39;t find it. At
some point, you need to ask: is it worth it?&lt;/p&gt;
&lt;p&gt;I decided that, instead of sinking more time tracking down this problem, I would
instead investigate different generator solutions.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;investigating-alternatives&quot; tabindex=&quot;-1&quot;&gt;Investigating alternatives&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#investigating-alternatives&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Investigating alternatives”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;making-the-switch&quot; tabindex=&quot;-1&quot;&gt;Making the switch&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#making-the-switch&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Making the switch”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Simplicity&lt;/li&gt;
&lt;li&gt;Meme&lt;/li&gt;
&lt;li&gt;Diagnosing Gatsby problems&lt;/li&gt;
&lt;li&gt;Gatsby problems on older systems&lt;/li&gt;
&lt;li&gt;Not React&lt;/li&gt;
&lt;li&gt;React === reusability&lt;/li&gt;
&lt;li&gt;Felt wrong creating 90+% single-use components&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;In retrospect, I think this has more to do with Netlify&#39;s amazing service
than Gatsby. &lt;a href=&quot;https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Fri, 12 Apr 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/why-i-switched-from-gatsby-to-eleventy/</guid>
    </item>
    <item>
      <title>Storing data in state vs. class variable</title>
      <link>https://seanmcp.com/articles/storing-data-in-state-vs-class-variable/</link>
      <description>&lt;p&gt;This past week I was working with a new coworker who has previous experience
developing but is new to React. After picking up a few syntax cues, he started
writing class components in what was a familiar manner for him: storing
information as class variables in the constructor.&lt;/p&gt;
&lt;p&gt;The constructor from one of his components looked like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&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 keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;default&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Later on, when he wanted to update the value stored on the object, he did with a
simple reassignment:&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;new value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After reviewing his code, I tried to explain the &amp;quot;correct&amp;quot; pattern for storing
data in React. I said (something like):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I see that you&#39;re using class variables to store information on your React
component. That&#39;s not a common pattern. Instead, we use a state object that is
referenced with &lt;code&gt;this.state.whatever&lt;/code&gt;. When we want to update the data, we
call &lt;code&gt;this.setState&lt;/code&gt; and pass the new information.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He naturally asked why that was the case, because his code was working as is. I
went into a mealy-mouthed explanation with gem quotes like &amp;quot;classes in React
aren&#39;t &lt;em&gt;true&lt;/em&gt; classes&amp;quot;, and &amp;quot;state isn&#39;t &lt;em&gt;actually&lt;/em&gt; stored in the component&amp;quot;.
None of what I stumbled through answered his original question.&lt;/p&gt;
&lt;p&gt;Realizing that I was talking out of ignorance, I stopped and offered a more
honest: &amp;quot;Actually, I don&#39;t really know.&amp;quot; Then I turned to Google to find the
answer.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;class-variables&quot; tabindex=&quot;-1&quot;&gt;Class variables&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/storing-data-in-state-vs-class-variable/#class-variables&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Class variables”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I thought that storing class variables was almost always a bad idea. The only
use cases that I had personally encountered before were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setting and clearing timeouts&lt;/li&gt;
&lt;li&gt;Storing frequently-referenced values&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, my colleague&#39;s work showed that class variables can be used to manage
and update data.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example of using class variables to create a counter:&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VariableCounter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;render&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;The count is&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;increment&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Add one&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 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 function-variable function&quot;&gt;increment&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 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 class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the component is initiated, a class variable named &lt;code&gt;count&lt;/code&gt; is set to &lt;code&gt;0&lt;/code&gt;.
When the &lt;code&gt;increment&lt;/code&gt; method is called, it updates &lt;code&gt;count&lt;/code&gt; by adding one to the
previous value. The rest of the component is pretty standard.&lt;/p&gt;
&lt;p&gt;After writing up this simple example, I thought this code would work. But I was
wrong. You can click the button as much as you want, but the &lt;code&gt;count&lt;/code&gt; value
displayed in the component will never change.&lt;/p&gt;
&lt;p&gt;If you throw a breakpoint or add a &lt;code&gt;console.log&lt;/code&gt; to &lt;code&gt;increment()&lt;/code&gt;, you&#39;d see
that the method is indeed called and the value of &lt;code&gt;this.count&lt;/code&gt; is incremented,
but the component never reflects the change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That&#39;s because React components will only re-render when there are changes to
&lt;code&gt;props&lt;/code&gt; or &lt;code&gt;state&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Updating the class variable involves neither, so it does not trigger a
re-render. This explains why the &lt;code&gt;count&lt;/code&gt; changes in our example, but the
displayed value never updates.&lt;/p&gt;
&lt;p&gt;In order to use class variables, we need to take the responsibility of
triggering the re-render when our data changes. To do that, we need to add a
line to our example:&lt;/p&gt;
&lt;!-- ```js/14 --&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VariableCounter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;render&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;The count is&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;increment&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Add one&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 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 function-variable function&quot;&gt;increment&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 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 class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forceUpdate&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 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;React class components have a &lt;code&gt;forceUpdate&lt;/code&gt; method that triggers the &lt;code&gt;render&lt;/code&gt;
method. By calling &lt;code&gt;this.forceUpdate()&lt;/code&gt; after incrementing our class value, we
can get our displayed value to update.&lt;/p&gt;
&lt;p&gt;I had never heard of this method before, and there is a reason why: the React
team doesn&#39;t recommend using it. Here&#39;s a line from
&lt;a href=&quot;https://reactjs.org/docs/react-component.html#forceupdate&quot;&gt;the React documentation on &lt;code&gt;forceUpdate&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Normally you should try to avoid all uses of &lt;code&gt;forceUpdate()&lt;/code&gt; and only read
from &lt;code&gt;this.props&lt;/code&gt; and &lt;code&gt;this.state&lt;/code&gt; in &lt;code&gt;render()&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In some instances you may receive data from other sources, but otherwise &lt;strong&gt;try
to avoid manually managing a component&#39;s render&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The alternative to using class variables and forcing updates is leveraging
React&#39;s component state.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;component-state&quot; tabindex=&quot;-1&quot;&gt;Component State&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/storing-data-in-state-vs-class-variable/#component-state&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Component State”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Instead of setting our count as a class variable, let&#39;s instead create a state
object on our class and store the data there.&lt;/p&gt;
&lt;p&gt;We only need to make a few modifications to the &lt;code&gt;VariableCounter&lt;/code&gt; for our new
component:&lt;/p&gt;
&lt;!-- ```js/1-3,8,15-17 --&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateCounter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &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 literal-property property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&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 function&quot;&gt;render&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;The count is&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;increment&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Add one&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 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 function-variable function&quot;&gt;increment&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 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 class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token parameter&quot;&gt;prevState&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 class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; prevState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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 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;While instantiating a state object might look like the previous example using a
class variable, &lt;code&gt;state&lt;/code&gt; is a protected keyword in React that refers to stored
component data.&lt;/p&gt;
&lt;p&gt;You can access data from state with normal object dot or bracket notation, so
the current count can be referenced with &lt;code&gt;this.state.count&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The major difference between using class variables and state is updating data.
Instead of manually reassigning the variable, you call &lt;code&gt;this.setState()&lt;/code&gt; and
pass it an object or a function that returns an object.&lt;/p&gt;
&lt;p&gt;When you call &lt;code&gt;this.setState()&lt;/code&gt;, React merges the passed object into the current
state to create a new state object. Then it compares this new state to the
previous state. If there is a change, React re-renders the component, resulting
in the updated &lt;code&gt;count&lt;/code&gt; value displayed on the screen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By using &lt;code&gt;this.setState()&lt;/code&gt;, you let React handle the logic of whether to
re-render the component.&lt;/strong&gt; This is a feature of React and one of the reasons it
has become so popular.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;example&quot; tabindex=&quot;-1&quot;&gt;Example&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/storing-data-in-state-vs-class-variable/#example&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Example”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I created a CodeSandbox example of the components discussed above. There are
working state and class variable counters; with a bonus functional component
using &lt;code&gt;React.useState()&lt;/code&gt;, and a broken class variable counter without
&lt;code&gt;this.forceUpdate()&lt;/code&gt;. Check it out:&lt;/p&gt;
&lt;iframe src=&quot;https://codesandbox.io/embed/n40x05xy8l?fontsize=14&quot; title=&quot;React State vs. Class Variables&quot; style=&quot;width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;&quot; sandbox=&quot;allow-modals allow-forms allow-popups allow-scripts allow-same-origin&quot;&gt;&lt;/iframe&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;Summary&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/storing-data-in-state-vs-class-variable/#summary&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Summary”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Contrary to what I initially thought, it&#39;s okay to store component data as a
class variable. However, if you&#39;re going to render that data, you should instead
store it in state so that any changes trigger a re-render. &lt;strong&gt;Ninety-five percent
of the time, you&#39;ll store data in state.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now that you know the difference between storing data in state and class
variables, you can be more helpful than I was to your fellow developers.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Fri, 12 Apr 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/storing-data-in-state-vs-class-variable/</guid>
    </item>
    <item>
      <title>Filter out premium Medium articles</title>
      <link>https://seanmcp.com/articles/filter-premium-medium-articles/</link>
      <description>&lt;p&gt;Medium has received a lot of flack recently for their push to monetize the
platform. And while I don&#39;t fault them for moving some of their best articles
behind a pay wall, I have no interest in subscribing to their platform.&lt;/p&gt;
&lt;p&gt;There is still a lot of high-quality free content on Medium, but you have to
navigate around all of the premium content that the platform is pushing.&lt;/p&gt;
&lt;p&gt;I wanted to write a console script that would do that filtering work for me.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;reach-for-the-stars&quot; tabindex=&quot;-1&quot;&gt;Reach for the stars&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/filter-premium-medium-articles/#reach-for-the-stars&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Reach for the stars”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;On Medium, premium articles can be identified on the homepage by their ★ icon.
Using the inspector, you can see that the star is an &lt;code&gt;svg&lt;/code&gt; element nested in a
&lt;code&gt;span&lt;/code&gt; with the class &lt;code&gt;svgIcon--star&lt;/code&gt;. Bingo!&lt;/p&gt;
&lt;p&gt;Now, using the handy &lt;code&gt;querySelectorAll&lt;/code&gt;-esque selector &lt;code&gt;$$&lt;/code&gt;, we can find all the
stars on the page. Our console script starts out like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;span.svgIcon--star&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we&#39;ve selected all stars, we need to iterate over them. Since we need
to do something for every item in our iterable, we&#39;ll use &lt;code&gt;forEach&lt;/code&gt;:&lt;/p&gt;
&lt;!-- ```js/0,2 --&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;span.svgIcon--star&quot;&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 function&quot;&gt;forEach&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;star&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 class=&quot;token comment&quot;&gt;// Hide parent element&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we&#39;re iterating over the stars, we need to find and hide the
appropriate parent element. To do this, we&#39;ll use a neat &lt;code&gt;Element&lt;/code&gt; method called
&lt;code&gt;closest&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;finding-a-parent-with-closest&quot; tabindex=&quot;-1&quot;&gt;Finding a parent with &lt;code&gt;closest&lt;/code&gt;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/filter-premium-medium-articles/#finding-a-parent-with-closest&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Finding a parent with closest”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You may be familiar with the &lt;code&gt;querySelector&lt;/code&gt; method, which starts with a given
element and then searches down the DOM tree and returns the first match.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;closest&lt;/code&gt; works the same way, but in reverse. It starts with an element, then
travels &lt;strong&gt;up&lt;/strong&gt; the DOM tree. This makes it the perfect method for starting with
an element and looking for a specific parent.&lt;/p&gt;
&lt;p&gt;Using the inspector again, we can see that articles outside the featured section
are &lt;code&gt;div&lt;/code&gt;s with the class &lt;code&gt;streamItem&lt;/code&gt;. Using &lt;code&gt;closest&lt;/code&gt;, we&#39;ll grab those
elements:&lt;/p&gt;
&lt;!-- ```js/1 --&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;span.svgIcon--star&quot;&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 function&quot;&gt;forEach&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;star&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;
  star&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.streamItem&quot;&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 comment&quot;&gt;// Hide element&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had never used the &lt;code&gt;closest&lt;/code&gt; method before and was glad to find it. As always,
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/closest&quot;&gt;I read up about it on MDN&lt;/a&gt;
before using it.&lt;/p&gt;
&lt;p&gt;We&#39;ve selected the correct parent element; now we need to hide it.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;hiding-an-element&quot; tabindex=&quot;-1&quot;&gt;Hiding an element&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/filter-premium-medium-articles/#hiding-an-element&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Hiding an element”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Removing an element in JavaScript is more difficult than it should be. There is
no &lt;code&gt;remove&lt;/code&gt; method on the global &lt;code&gt;Element&lt;/code&gt;. Instead, you need to jump up to the
parent element and then use &lt;code&gt;removeChild&lt;/code&gt;. The normal strategy looks like this:&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; element &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.remove-me&quot;&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;

element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element&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;I think this pattern is annoying and try to avoid it whenever possible. But we
could use it in our instance by first creating a variable to reference the
element to be removed:&lt;/p&gt;
&lt;!-- ```js/2,3 --&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;// Option 1 - Remove element&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;span.svgIcon--star&quot;&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 function&quot;&gt;forEach&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;star&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; article &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; star&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.streamItem&quot;&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;
  article&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is your best option if you need to remove the element from the DOM.&lt;/p&gt;
&lt;p&gt;My preferred method leaves the element in the DOM and applies CSS styles to hide
it from view. We&#39;ll set the article element&#39;s &lt;code&gt;display&lt;/code&gt; style to &lt;code&gt;&#39;none&#39;&lt;/code&gt;:&lt;/p&gt;
&lt;!-- ```js/2 --&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;// Option 2 - Hide element&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;span.svgIcon--star&quot;&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 function&quot;&gt;forEach&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;star&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;
  star&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.streamItem&quot;&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;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unless you &lt;strong&gt;need&lt;/strong&gt; to remove and element from the DOM, the simpler solution is
to just hide the element.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;save-for-later&quot; tabindex=&quot;-1&quot;&gt;Save for later&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/filter-premium-medium-articles/#save-for-later&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Save for later”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Chrome has a handy feature in the &amp;quot;Sources&amp;quot; tab called snippets. Here you can
save bits of code for use later.&lt;/p&gt;
&lt;p&gt;I created a snippet title &amp;quot;Medium&amp;quot; and saved the above code. Now when I want to
filter out premium articles, I just need to run that snippet with
&lt;kbd&gt;command&lt;/kbd&gt; + &lt;kbd&gt;enter&lt;/kbd&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;read-on&quot; tabindex=&quot;-1&quot;&gt;Read on&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/filter-premium-medium-articles/#read-on&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Read on”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Now the next time I want to find an article to read on Medium, I can run the
snippet and filter out premium articles.&lt;/p&gt;
&lt;p&gt;For a more reusable solution, we could turn this snippet into a browser
extension, but that&#39;s the topic of another article.&lt;/p&gt;
</description>
      <pubDate>Mon, 08 Apr 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/filter-premium-medium-articles/</guid>
    </item>
    <item>
      <title>Adding accessibility checks to your React app</title>
      <link>https://seanmcp.com/articles/adding-accessibility-checks-to-react/</link>
      <description>&lt;p&gt;&lt;strong&gt;Updated - 3/9/19&lt;/strong&gt; This article was initially released with a reference to a
third tool for React accessibility checking: &lt;code&gt;react-a11y&lt;/code&gt;. Since publication,
that library has been deprecated. The article has been updated to reflect that
change.&lt;/p&gt;
&lt;p&gt;Whether you are working for a startup, non-profit, or Fortune 500 company,
accessibility is a critical component of your product. Not only it is a legal
requirement in many countries&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/adding-accessibility-checks-to-react/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, but creating accessible applications
maximizes the number of potential users. And it often has a positive impact on
all users
(&lt;a href=&quot;https://ssir.org/articles/entry/the_curb_cut_effect&quot;&gt;read about the &amp;quot;Curb Cut Effect&amp;quot;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;As React developers, we have a responsibility to avoid hiding in the virtual DOM
and work to ensure that the components we craft are going to work for everyone.
Fortunately for us, React has
&lt;a href=&quot;https://reactjs.org/docs/accessibility.html&quot;&gt;React has accessibility features built into the platform&lt;/a&gt;
to make our jobs easier.&lt;/p&gt;
&lt;p&gt;In this article, we&#39;re going to look at two additional tools than can help React
developers create awesome and accessible applications for all.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;setup&quot; tabindex=&quot;-1&quot;&gt;Setup&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/adding-accessibility-checks-to-react/#setup&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Setup”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;To demonstrate these features, I&#39;m going to bootstrap a new project with React&#39;s
&lt;a href=&quot;https://facebook.github.io/create-react-app/&quot;&gt;Create React App&lt;/a&gt;. It&#39;s a great
way to spin up a new project and works nicely with the accessibility checks
we&#39;re going to implement.&lt;/p&gt;
&lt;p&gt;If you&#39;re looking to add these checks to a current project, the only dependency
is ESLint, which we&#39;ll look at later in this article. Now, let&#39;s jump in!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;react-axe&quot; tabindex=&quot;-1&quot;&gt;React-axe&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/adding-accessibility-checks-to-react/#react-axe&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “React-axe”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Create React App already has wonderfully helpful console messages, and react-axe
is an out-of-the-box solution that adds accessibility checking to your console.&lt;/p&gt;
&lt;p&gt;React-axe is developed by Deque, the makers of the fantastic accessibility tools
like the &lt;a href=&quot;https://www.deque.com/axe/&quot;&gt;Axe browser extension&lt;/a&gt; (which I highly
recommend). Install it in the usual way:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn add --dev react-axe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once installed, we&#39;ll check for a development environment before requiring and
calling react-axe&#39;s &lt;code&gt;axe&lt;/code&gt; function and passing certain parameters.&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;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; ReactDOM &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react-dom&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; App &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./components/App&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;evn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;development&quot;&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 keyword&quot;&gt;const&lt;/span&gt; axe &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;&quot;react-axe&quot;&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 function&quot;&gt;axe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ReactDOM&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&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;

ReactDOM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;App &lt;span class=&quot;token operator&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; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final parameter for &lt;code&gt;axe&lt;/code&gt; is &amp;quot;a timing delay in milliseconds that will be
observed between each component change and the time the analysis starts&amp;quot;
(&lt;a href=&quot;https://github.com/dequelabs/react-axe/#initialize-the-module&quot;&gt;source&lt;/a&gt;). We&#39;ll
set it to 1000ms, or one second.&lt;/p&gt;
&lt;p&gt;Now if I give an element an invalid role (&lt;em&gt;e.g.&lt;/em&gt; &lt;code&gt;role=&amp;quot;Test&amp;quot;&lt;/code&gt;), I&#39;m met with
the following console message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;▸ critical: ARIA roles used must conform to valid values
https://dequeuniversity.com/rules/axe/3.1/aria-roles?application=axeAPI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Expanding the message provides a reference to the original document, and a file
and line reference to fix the issue. The console errors are helpful without
being overwhelming and provide all the information and resources you need to fix
the issue.&lt;/p&gt;
&lt;p&gt;Console warnings are great, but let&#39;s add another tool to help catch mistakes
before they make it to the browser.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;jsx-a11y&quot; tabindex=&quot;-1&quot;&gt;Jsx-a11y&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/adding-accessibility-checks-to-react/#jsx-a11y&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Jsx-a11y”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Jsx-a11y is an ESLint plugin that looks over your code for some key
accessibility considerations. If you don&#39;t have ESLint added to your project
yet, follow their
&lt;a href=&quot;https://eslint.org/docs/user-guide/getting-started&quot;&gt;get started guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you bootstrapped your React app with &lt;code&gt;create-react-app&lt;/code&gt; like I did, it
already has ESLint and some of jsx-a11y&#39;s accessibility checking included. For
the sake of this article, we&#39;re going to add the full set of rules, so we&#39;ll
need to install the plugin library:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn add --dev eslint-plugin-jsx-a11y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then modify the ESLint configs in your &lt;code&gt;package.json&lt;/code&gt; to match the following:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;eslintConfig&quot;&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 property&quot;&gt;&quot;plugins&quot;&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 string&quot;&gt;&quot;jsx-a11y&quot;&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 property&quot;&gt;&quot;extends&quot;&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 string&quot;&gt;&quot;react-app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;plugin:jsx-a11y/recommended&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now if we make a change that reduces our app&#39;s accessibility, like removing the
&lt;code&gt;alt&lt;/code&gt; attribute for an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, we&#39;re met with the following lint warning:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./src/components/App/App.js
    Line 10:  img elements must have an alt prop, either with meaningful text, or an empty string for decorative images  jsx-a11y/alt-text
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more customization over which rules jsx-a11y uses, refer to
&lt;a href=&quot;https://github.com/evcohen/eslint-plugin-jsx-a11y#eslint-plugin-jsx-a11y&quot;&gt;the official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are using a text editor like VS Code that features in-line linting, you
can identify and fix potential accessibility issues before you even save.&lt;/p&gt;
&lt;p&gt;With these tools in place, you&#39;re well on your way to creating more accessible
React applications!&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;a11y-react-starter&quot; tabindex=&quot;-1&quot;&gt;A11y-react-starter&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/adding-accessibility-checks-to-react/#a11y-react-starter&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “A11y-react-starter”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I created &lt;a href=&quot;https://github.com/seanmcp/a11y-react-starter&quot;&gt;&lt;code&gt;a11y-react-starter&lt;/code&gt;&lt;/a&gt;,
a lightly-configured create-react-app with both of the above accessibility tools
baked in. Check it out for a reference or use it as a starting point for your
next app.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/adding-accessibility-checks-to-react/#conclusion&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Conclusion”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;By enabling these accessibility features, you&#39;re adding the wisdom and knowledge
of the many developers who have gone before you. With their help, and a little
bit of learning, anyone can create accessible applications that work for all
users.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Web_accessibility#Web_accessibility_legislation&quot;&gt;Web accessibility: Web accessibility legislation&lt;/a&gt;
on Wikipedia &lt;a href=&quot;https://seanmcp.com/articles/adding-accessibility-checks-to-react/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Mon, 04 Mar 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/adding-accessibility-checks-to-react/</guid>
    </item>
    <item>
      <title>Three reasons to learn Python</title>
      <link>https://seanmcp.com/articles/three-reasons-to-learn-python/</link>
      <description>&lt;p&gt;There are tons of reasons to learn a programming language. Maybe you are new to
coding and are looking for a good place to start. Maybe you’re interested in new
technologies like machine learning and artificial intelligence and want to learn
more. Or maybe you’re a seasoned developer who is looking to add a new language
to his/her skill set.&lt;/p&gt;
&lt;p&gt;But which to choose? There are
&lt;a href=&quot;https://www.quora.com/How-many-programming-languages-are-there-in-the-world&quot;&gt;hundreds of programming languages out there&lt;/a&gt;,
each one vying for your attention. What should you do?&lt;/p&gt;
&lt;p&gt;Never fear, faithful reader! Here are three reasons why you (yes, you!) should
learn the &lt;strong&gt;Python&lt;/strong&gt; programming language.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;1---versatile&quot; tabindex=&quot;-1&quot;&gt;1-- Versatile&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/three-reasons-to-learn-python/#1---versatile&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “1-- Versatile”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Python’s creator, Guido van Rossum, designed Python to be a general purpose
programming language. As a result, Python can be used in a wide variety of
cases: machine learning, web development, GUI development, scientific and
numeric applications, software development, system administration, and more.
Whatever type of programming sparks your interest, there is probably
&lt;a href=&quot;https://www.python.org/about/apps/&quot;&gt;a Python application or framework for it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Major companies like Google, Instagram, Dropbox, Spotify, Reddit, Duolingo, and
BitBucket; government organizations; educational institutions; and non-profits
use Python for its versatility.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;2---beginner-friendly&quot; tabindex=&quot;-1&quot;&gt;2-- Beginner friendly&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/three-reasons-to-learn-python/#2---beginner-friendly&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “2-- Beginner friendly”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In addition to being a general purpose programming language, Python advertises
itself as being
“&lt;a href=&quot;https://docs.python.org/3.6/tutorial/appetite.html&quot;&gt;easy to use&lt;/a&gt;”. It achieves
this by utilizing a simple and intuitive syntax and offering high-level data
types like dictionaries.&lt;/p&gt;
&lt;p&gt;Since 1999, Python has been guided by a series of 19 principles called the “Zen
of Python.” Among these are specific guidelines to keep things simple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Simple is better than complex.
Readability counts.
If the implementation is hard to explain, it&#39;s a bad idea.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a result of this, Python is often used as the language in introductory
computer science courses (notably
&lt;a href=&quot;https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/index.htm&quot;&gt;MIT&lt;/a&gt;).
If you are looking to learn your first language or add to a growing toolbelt,
Python is a great choice.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;3---highly-rated&quot; tabindex=&quot;-1&quot;&gt;3-- Highly rated&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/three-reasons-to-learn-python/#3---highly-rated&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “3-- Highly rated”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In my experience, every Python developer I have met loves working in Python. And
it turns out that my anecdotal observation may be right!&lt;/p&gt;
&lt;p&gt;In
&lt;a href=&quot;https://insights.stackoverflow.com/survey/2018/&quot;&gt;StackOverlow’s Developer Survey&lt;/a&gt;-- which
compiles user responses to demographic, technology, and work questions--shows
that Python is a desirable language. It has been listed among the
“&lt;a href=&quot;https://insights.stackoverflow.com/survey/2018/##most-loved-dreaded-and-wanted&quot;&gt;Most Loved&lt;/a&gt;”
languages for a while and has topped the charts in the past two years as the
“&lt;a href=&quot;https://insights.stackoverflow.com/survey/2018/&quot;&gt;Most Desired&lt;/a&gt;” language. If a
lot of other developers like using Python, or want to learn Python, then it is
probably going to be a good experience.&lt;/p&gt;
&lt;p&gt;[Sidebar: If you haven’t before, take the time to look over the results of the
&lt;a href=&quot;https://insights.stackoverflow.com/survey/2018/&quot;&gt;2018 Developer Survey&lt;/a&gt;. It
isn’t perfect, suffering from both
&lt;a href=&quot;https://en.wikipedia.org/wiki/Participation_bias&quot;&gt;participation&lt;/a&gt; and
&lt;a href=&quot;https://en.wikipedia.org/wiki/Response_bias&quot;&gt;response bias&lt;/a&gt;, but does open a
window into the world of development.]&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;bonus---python-jokes!&quot; tabindex=&quot;-1&quot;&gt;Bonus-- Python jokes!&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/three-reasons-to-learn-python/#bonus---python-jokes!&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Bonus-- Python jokes!”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Van Rossum named the Python programming language not after the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Python_%28genus%29&quot;&gt;serpent genus&lt;/a&gt; (as I
previously assumed) but after the British TV comedy “Monty Python’s Flying
Circus.” According to the
&lt;a href=&quot;https://docs.python.org/3.6/tutorial/appetite.html&quot;&gt;Python Software Foundation&lt;/a&gt;,
“Making references to Monty Python skits in documentation is not only allowed,
it is encouraged!”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.giphy.com/media/RBEUIVI6ey5Ta/giphy.webp&quot; alt=&quot;The laughing French knight from Monty Python&#39;s Holy Grail&quot;&gt;&lt;/p&gt;
&lt;p&gt;There you go: three (and a half) reasons why you should learn Python.&lt;/p&gt;
&lt;p&gt;If you are looking for a good place to get started, I recommend the
&lt;a href=&quot;https://docs.python.org/3/&quot;&gt;Python Docs&lt;/a&gt;: they are well-written and -paced for
a beginner diving into the language.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Thu, 21 Feb 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/three-reasons-to-learn-python/</guid>
    </item>
    <item>
      <title>JS Basics: Generate a random number</title>
      <link>https://seanmcp.com/articles/js-generate-random-number/</link>
      <description>&lt;p&gt;Whether you are simulating a die roll or pulling any index from an array,
computers can be used to generate a random number. In JavaScript, random numbers
require a combination of methods on the &lt;code&gt;Math&lt;/code&gt; object that can be a little
tricky to understand your first time through.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;random-die-roll&quot; tabindex=&quot;-1&quot;&gt;Random die roll&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/js-generate-random-number/#random-die-roll&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Random die roll”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Let’s say you want to create a digital six-sided die, which will give you a
random number between one and six when called. Our function to simulate a die
roll will look like this:&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; &lt;span class=&quot;token function-variable function&quot;&gt;rollDie&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;sides&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; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&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;*&lt;/span&gt; sides&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;rollDie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&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 comment&quot;&gt;// 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every time we call the function, it will return any number from one to six,
inclusive. But why? To answer that question, we need to know more about the two
methods on the &lt;code&gt;Math&lt;/code&gt; object we called.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;math-methods&quot; tabindex=&quot;-1&quot;&gt;Math methods&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/js-generate-random-number/#math-methods&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Math methods”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Math.random()&lt;/code&gt; is a method that generates an integer (number with a decimal)
from zero to one, including zero but excluding one. The output of
&lt;code&gt;Math.random()&lt;/code&gt; could be 0, 0.1, 0.2, 0.3343424587866554652345, or 0.99.
Anything from zero to nearly one is a possibility.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Math.floor&lt;/code&gt; is a method that rounds an integer down to the nearest whole
number. Rounding down, 0.1 becomes 0, 3.6 becomes 3, and 5.4 becomes 5. Think of
it as rounding all the way down to the floor. (Note: there is also a &lt;code&gt;Math.ceil&lt;/code&gt;
function for rounding up that I&#39;ll mention later).&lt;/p&gt;
&lt;p&gt;By combining these two methods, we can generate the random number for our die.
Starting on the inside of our function and working outward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Math.random()&lt;/code&gt; returns an integer &lt;strong&gt;between&lt;/strong&gt; zero and nearly one: &lt;code&gt;0.16&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Next we &lt;strong&gt;multiply&lt;/strong&gt; that integer by the number of sides: &lt;code&gt;0.16 * 6 = 0.96&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Then we &lt;strong&gt;round&lt;/strong&gt; that product up with &lt;code&gt;Math.floor&lt;/code&gt;: &lt;code&gt;0.96 -&amp;gt; 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Finally, we &lt;strong&gt;add&lt;/strong&gt; one to get our final random number: &lt;code&gt;0 + 1 = 1&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The result of our &lt;code&gt;rollDie(6)&lt;/code&gt; function is &lt;code&gt;1&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Our function allows for a varying number of sides, so you would only need to
pass a different argument if you were rolling a different die (much to the
satisfaction of all
&lt;a href=&quot;https://en.wikipedia.org/wiki/Dungeons_%26_Dragons#Game_mechanics&quot;&gt;D&amp;amp;D players&lt;/a&gt;
out there).&lt;/p&gt;
&lt;p&gt;You might even create some help functions to simplify calling different sided
dice:&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; &lt;span class=&quot;token function-variable function&quot;&gt;rollD6&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rollDie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&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 keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;rollD12&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rollDie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&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 keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;rollD20&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rollDie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&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 comment&quot;&gt;// etc...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I used functions like these in Dice Roller, a handy dice rolling app for
table-top gaming. You can
&lt;a href=&quot;https://github.com/SeanMcP/dice-roller&quot;&gt;view the project&#39;s source code here&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;common-questions&quot; tabindex=&quot;-1&quot;&gt;Common questions&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/js-generate-random-number/#common-questions&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Common questions”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Here are some questions you may have (I know I did!) about our &lt;code&gt;rollDie&lt;/code&gt;
function:&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%E2%80%9Cwhy-did-we-need-to-add-one-to-our-number%3F%E2%80%9D&quot; tabindex=&quot;-1&quot;&gt;“Why did we need to add one to our number?”&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/js-generate-random-number/#%E2%80%9Cwhy-did-we-need-to-add-one-to-our-number%3F%E2%80%9D&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to ““Why did we need to add one to our number?””&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Math.floor(Math.random() * 6)&lt;/code&gt; alone will generate a random number between 0
and 5. That is six options, but I&#39;ve never seen a die numbered from zero to
five. We add one to the output so that it makes sense for our situation.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%22wouldn&#39;t-using-math.ceil-make-the-%2B-1-unnecessary%3F%22&quot; tabindex=&quot;-1&quot;&gt;&amp;quot;Wouldn&#39;t using &lt;code&gt;Math.ceil&lt;/code&gt; make the &lt;code&gt;+ 1&lt;/code&gt; unnecessary?&amp;quot;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/js-generate-random-number/#%22wouldn&#39;t-using-math.ceil-make-the-%2B-1-unnecessary%3F%22&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “&amp;quot;Wouldn&#39;t using Math.ceil make the + 1 unnecessary?&amp;quot;”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In the same manner that &lt;code&gt;Math.floor&lt;/code&gt; rounded our integer down to the nearest
whole number, &lt;code&gt;Math.ceil&lt;/code&gt;--short for &amp;quot;ceiling&amp;quot;--rounds an integer up. At face
value, this would simplify our solution--but for one issue:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ceil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&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 comment&quot;&gt;// 0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since &lt;code&gt;Math.random()&lt;/code&gt; &lt;strong&gt;can&lt;/strong&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/js-generate-random-number/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; return a &lt;code&gt;0&lt;/code&gt;, using &lt;code&gt;Math.ceil&lt;/code&gt; would on a
rare instance return an unexpected value. So it&#39;s safer to use &lt;code&gt;Math.floor&lt;/code&gt; and
add one.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;%22does-the-math-always-work-out%3F%22&quot; tabindex=&quot;-1&quot;&gt;&amp;quot;Does the math always work out?&amp;quot;&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/js-generate-random-number/#%22does-the-math-always-work-out%3F%22&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “&amp;quot;Does the math always work out?&amp;quot;”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I’ve included a few additional examples at the bottom of this post&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/js-generate-random-number/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. Try it
out for yourself and see!
&lt;span role=&quot;img&quot; aria-label=&quot;smiling face with squinting eyes emoji&quot;&gt;😊&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;random-item-from-an-array&quot; tabindex=&quot;-1&quot;&gt;Random item from an array&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/js-generate-random-number/#random-item-from-an-array&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Random item from an array”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;rollDie&lt;/code&gt; works well for our dice example, but what if you want to select a
random item from an array? Remember, that
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#Accessing_array_elements&quot;&gt;arrays are zero-indexed (MDN)&lt;/a&gt;,
which means that the first item of an array is accessed with a zero (&lt;code&gt;0&lt;/code&gt;):&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; myArray &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;&quot;first&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;second&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;third&quot;&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;

myArray&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&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 comment&quot;&gt;// &#39;first&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our &lt;code&gt;rollDie&lt;/code&gt; function will never return a &lt;code&gt;0&lt;/code&gt; because we&#39;re always adding one.
By removing that addition, we can create a new function that will work well with
arrays. Let&#39;s use the code from the previous block and add:&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; &lt;span class=&quot;token function-variable function&quot;&gt;randomIndex&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;arrayLength&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; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&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;*&lt;/span&gt; arrayLength&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 function&quot;&gt;randomIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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 comment&quot;&gt;// 0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By calling our function and passing the length of our array, we can pull a
random item from our array:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;myArray&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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 comment&quot;&gt;// &#39;second&#39;&lt;/span&gt;
myArray&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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 comment&quot;&gt;// &#39;first&#39;&lt;/span&gt;
myArray&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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 comment&quot;&gt;// &#39;second&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using our function in this manner is a little repetitive, so let&#39;s create
another function that will simplify the process for us:&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; &lt;span class=&quot;token function-variable function&quot;&gt;randomItemFrom&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;array&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; array&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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 function&quot;&gt;randomItemFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray&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 comment&quot;&gt;// &#39;third&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;randomItemFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray&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 comment&quot;&gt;// &#39;second&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have a few handy utility functions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;rollDie&lt;/code&gt; for simulating a die roll with a given number of sides,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;randomIndex&lt;/code&gt; for getting a valid index from an array, and&lt;/li&gt;
&lt;li&gt;&lt;code&gt;randomItemFrom&lt;/code&gt; for returning a random item from an array&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While building those, we learned about JavaScript&#39;s &lt;code&gt;Math&lt;/code&gt; object and some of
its methods: &lt;code&gt;random&lt;/code&gt;, &lt;code&gt;floor&lt;/code&gt;, and &lt;code&gt;ceil&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I emphasize &amp;quot;can&amp;quot; because it is an
&lt;a href=&quot;https://en.wikipedia.org/wiki/Edge_case&quot;&gt;edge case (Wikipedia)&lt;/a&gt;: a
statistically unlikely but possible event. Programmers need to be on the
lookout and account for edge cases when possible. &lt;a href=&quot;https://seanmcp.com/articles/js-generate-random-number/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Additional examples stepping through the &lt;code&gt;rollDie&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. Math.random()    // 0.4
2. Multiply by 6    // 2.4
3. Math.floor       // 2
4. Add one          // 3
Random number: 3

1. Math.random()    // 0.9
2. Multiply by 6    // 5.4
3. Math.floor       // 5
4. Add one          // 6
Random number: 6

1. Math.random()    // 0.642
2. Multiply by 6    // 3.8520000000000003
3. Math.floor       // 3
4. Add one          // 4
Random number: 4
&lt;/code&gt;&lt;/pre&gt;
 &lt;a href=&quot;https://seanmcp.com/articles/js-generate-random-number/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Mon, 11 Feb 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/js-generate-random-number/</guid>
    </item>
    <item>
      <title>JS Basics: Ternary Operator</title>
      <link>https://seanmcp.com/articles/js-ternary-operator/</link>
      <description>&lt;p&gt;A ternary or conditional operator is like an inline if statement. In JavaScript,
you can identify them by their unique use of a question mark, &lt;code&gt;?&lt;/code&gt;, and colon,
&lt;code&gt;:&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When I first started coding, I found ternary operators more than a little
confusing, so let&#39;s try to unpack them. Look at this common example:&lt;/p&gt;
&lt;!-- ```js/1 --&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;span className&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 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;Todo &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDone &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Todo--done&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&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 class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we have a snippet from a React to-do app with a ternary operator. The class
( &lt;code&gt;className&lt;/code&gt; in JSX) is being set based on a condition in our &lt;code&gt;props&lt;/code&gt; object.
Here’s how to read a ternary operator:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If &lt;code&gt;props.isDone&lt;/code&gt; is true/truthy, then return the string &amp;quot;Todo--done&amp;quot;;
otherwise return an empty string.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Written out, a ternary operator looks like an &lt;code&gt;if&lt;/code&gt; statement. We could express
the same code like this:&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDone &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Todo--done&quot;&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 keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;That looks more familiar! Ternary operators are useful because they allow you to
add inline conditions to your code. Compare the original code to our final if
statement. The ternary operator is shorter, cleaner, and more efficient.&lt;/p&gt;
&lt;p&gt;MDN has a great list of other examples for you to reference. You can check them
out here:
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator&quot;&gt;Conditional (ternary) operators&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</description>
      <pubDate>Fri, 08 Feb 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/js-ternary-operator/</guid>
    </item>
    <item>
      <title>Convert class components to functions with React Hooks</title>
      <link>https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/</link>
      <description>&lt;p&gt;Last year, the React team announced hooks, a feature to more easily abstract
logic into reusable components. On the day of the announcement,
&lt;a href=&quot;https://medium.com/@seanmcp/refactoring-a-render-prop-with-hooks-fc22ed537199&quot;&gt;I wrote and article&lt;/a&gt;
explaining how to refactor a render prop component with hook.&lt;/p&gt;
&lt;p&gt;Starting this week with release of
&lt;a href=&quot;https://reactjs.org/blog/2019/02/06/react-v16.8.0.html&quot;&gt;React v16.8&lt;/a&gt;, &lt;strong&gt;you can
now add hooks in your projects&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;Today, I want to look at a typical class component and demonstrate how you can
covert it into a functional component with React hooks. Let&#39;s jump in.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;terminology&quot; tabindex=&quot;-1&quot;&gt;Terminology&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#terminology&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Terminology”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: Skip this section if you&#39;re familiar with class and functional React
components.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m going to use the terms &amp;quot;class&amp;quot; and &amp;quot;functional&amp;quot; components to refer to the
modern options for creating components. A class component looks something like
this:&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClassExample&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &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 literal-property property&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello&quot;&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 function&quot;&gt;render&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;span&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 class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;greeting&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Class components have access to internal state; default, custom, and lifecycle
methods; and a &lt;code&gt;this&lt;/code&gt; to reference. These components have been the powerhouses
of React development throughout its history.&lt;/p&gt;
&lt;p&gt;The aptly named functional components are functions that return JSX. Here&#39;s a
typical example:&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; &lt;span class=&quot;token function-variable function&quot;&gt;FunctionalExample&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;props&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; greeting &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Howdy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;greeting&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;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Functional components lack an internal state, methods, and &lt;code&gt;this&lt;/code&gt;. In order to
access passed props, we refer to the &lt;code&gt;props&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;Because they contain less internal logic, functional components are sometimes
referred to as &amp;quot;dumb&amp;quot; or &amp;quot;presentational&amp;quot; components.&lt;/p&gt;
&lt;p&gt;Although different in composition, class and functional components are used in
the same manner. Let&#39;s reference both examples from above in a hypothetical
parent component:&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;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ClassExample name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sean&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;FunctionalExample name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;partner&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;section&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the parent&#39;s perspective, the two types of components are identical. To
understand how React internally understands and processes these two different
types of components, I recommend Dan Abramov&#39;s
&lt;a href=&quot;https://overreacted.io/how-does-react-tell-a-class-from-a-function/&quot;&gt;&amp;quot;How Does React Tell a Class from a Function?&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These are simplified definitions and examples, but they will provide us with a
good basis for the rest of the article.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;class-component&quot; tabindex=&quot;-1&quot;&gt;Class component&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#class-component&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Class component”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I have a component that makes an API request on mount, stores the response in
state, then renders some JSX based on the data. Here&#39;s the code:&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClassComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &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 literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&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 function&quot;&gt;componentDidMount&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 function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://random.dog/woof.json&quot;&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 function&quot;&gt;then&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;raw&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; raw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&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 class=&quot;token function&quot;&gt;then&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;res&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&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 class=&quot;token function&quot;&gt;render&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Doggo&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&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 class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &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 operator&quot;&gt;&amp;lt;&lt;/span&gt;img src&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; alt&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A cute dog&quot;&lt;/span&gt; &lt;span class=&quot;token operator&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 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 operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Fetching&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;section&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: To create this example, I looked through Todd Motto&#39;s awesome list of
&lt;a href=&quot;https://github.com/toddmotto/public-apis&quot;&gt;&amp;quot;Public APIs&amp;quot;&lt;/a&gt;. Check it out and give
it a star if you find it useful.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I used the fetch library to keep the example straight forward (and it seemed
fitting for an app that displays dog photos), but I recommend using a more
robust option like &lt;a href=&quot;https://github.com/axios/axios&quot;&gt;Axios&lt;/a&gt; with better error
handling for your applications.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;functional-component&quot; tabindex=&quot;-1&quot;&gt;Functional Component&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#functional-component&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Functional Component”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Using React Hooks, we can create the same example from above using a functional
component.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;setup&quot; tabindex=&quot;-1&quot;&gt;Setup&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#setup&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Setup”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;First, we&#39;ll create a new file for our functional code and set up a basic
function. Then copy the &lt;code&gt;return&lt;/code&gt; from our class component and remove all
references to &lt;code&gt;this.state&lt;/code&gt;. We need to declare a variable to prevent reference
errors, so let&#39;s call it &lt;code&gt;url&lt;/code&gt;.&lt;/p&gt;
&lt;!-- ```js/1,3-6 --&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; &lt;span class=&quot;token function-variable function&quot;&gt;FunctionalComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;props&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token comment&quot;&gt;// We&#39;ll change this later&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Doggo&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&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;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; alt&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A cute dog&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Fetching&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;section&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;useeffect&quot; tabindex=&quot;-1&quot;&gt;useEffect&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#useeffect&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “useEffect”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In our class component, we make an AJAX request when the component mounted and
stored that data in state. &lt;code&gt;componentDidMount&lt;/code&gt; and the other lifecycle methods
are limited to classes. However, the React team was able to implement those
features with the &lt;code&gt;useEffect&lt;/code&gt; hook.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;useEffect&lt;/code&gt; is a method that takes two parameters: 1) a function to call on
mount, and 2) an array of variables to watch for updating.&lt;/p&gt;
&lt;p&gt;For the first parameter, we&#39;ll pass an anonymous function that calls fetch,
processes the data, and logs the response. The second argument just be an empty
array for now.&lt;/p&gt;
&lt;!-- ```js/2-6 --&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; &lt;span class=&quot;token function-variable function&quot;&gt;FunctionalComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;props&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url
  React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useEffect&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;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://random.dog/woof.json&#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 function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;raw&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; raw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&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 class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&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 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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Doggo&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&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;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; alt&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A cute dog&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Fetching&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;section&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are coding along, you should see something like the following in your
console when the component mounts:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{ &amp;quot;url&amp;quot;: &amp;quot;https://random.dog/867580b3d005.jpg&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This shows us that our API call is working, but logging the response isn&#39;t our
goal; we need to store the data for later. Let&#39;s revisit the &lt;code&gt;url&lt;/code&gt; variable that
we created.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;usestate&quot; tabindex=&quot;-1&quot;&gt;useState&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#usestate&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “useState”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Now that we have the response from our API, we need to be able to capture and
reference that value. In our class component, we used its state and the
&lt;code&gt;setState&lt;/code&gt; method to remember that data.&lt;/p&gt;
&lt;p&gt;React&#39;s &lt;code&gt;useState&lt;/code&gt; hook gives us access to a similar API without using a class.
When calling the function and optionally passing a default value, we receive two
returned variables: 1) a reference to the value, and 2) a function to update
that value, respectively.&lt;/p&gt;
&lt;p&gt;Using array destructuring, we can grab those variables and assign a name
according to the convention: &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;setX&lt;/code&gt;. For our example, we&#39;ll call the
value &lt;code&gt;url&lt;/code&gt; and the setter function &lt;code&gt;setUrl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We&#39;ll replace our old &lt;code&gt;url&lt;/code&gt; with the results from &lt;code&gt;useState&lt;/code&gt;:&lt;/p&gt;
&lt;!-- ```js/1 --&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; &lt;span class=&quot;token function-variable function&quot;&gt;FunctionalComponent&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;props&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setUrl&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&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;
  React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useEffect&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;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://random.dog/woof.json&quot;&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 function&quot;&gt;then&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;raw&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; raw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&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 class=&quot;token function&quot;&gt;then&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;res&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; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&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 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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Doggo&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&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;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; alt&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A cute dog&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Fetching&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;section&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;code&gt;useState&lt;/code&gt;, we can quickly add basic state to our functional component.&lt;/p&gt;
&lt;p&gt;In the code above, we&#39;re just logging the response from our API. Now we want to
use the &lt;code&gt;setUrl&lt;/code&gt; function to set the value or url in our component&#39;s state.&lt;/p&gt;
&lt;!-- ```js/5 --&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; &lt;span class=&quot;token function-variable function&quot;&gt;FunctionalComponent&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;props&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setUrl&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&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;
  React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useEffect&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;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://random.dog/woof.json&quot;&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 function&quot;&gt;then&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;raw&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; raw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&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 class=&quot;token function&quot;&gt;then&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;res&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 function&quot;&gt;setUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&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 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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Doggo&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&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;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; alt&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A cute dog&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Fetching&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;section&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&#39;s take a second to revisit the second parameter passed to &lt;code&gt;useEffect&lt;/code&gt;.
Remember, it contains a list of variables that will trigger the effect to run
again on change.&lt;/p&gt;
&lt;p&gt;Our effect depends on one outside variable: &lt;code&gt;setUrl&lt;/code&gt;. This function is generated
once by &lt;code&gt;useState&lt;/code&gt;, and adding it to the array will ensure that our fetch call
isn&#39;t fired repeatedly.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;!-- ```js/6 --&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; &lt;span class=&quot;token function-variable function&quot;&gt;FunctionalComponent&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;props&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setUrl&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&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;
  React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useEffect&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;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://random.dog/woof.json&quot;&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 function&quot;&gt;then&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;raw&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; raw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&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 class=&quot;token function&quot;&gt;then&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;res&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 function&quot;&gt;setUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&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 class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;setUrl&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Doggo&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&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;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; alt&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A cute dog&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Fetching&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;section&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 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s it! Checkout the &lt;a href=&quot;https://codesandbox.io/s/wwvmq1v407&quot;&gt;live example&lt;/a&gt; on
CodeSandbox to see the two components side-by-side.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;comparison&quot; tabindex=&quot;-1&quot;&gt;Comparison&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#comparison&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Comparison”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Let&#39;s take a moment and consider our two components. The new functional
component is a little shorter than the original class component, and the
variables are shorter and easier to reference.&lt;/p&gt;
&lt;p&gt;Those are nice benefits, but I don&#39;t find either a convincing reason to go back
and convert all my class components.&lt;/p&gt;
&lt;p&gt;More important to me is the fact that &lt;strong&gt;you can now use a functional component
for nearly everything in your application&lt;/strong&gt;. Since every component is
functional, it makes the process of bringing in or removing state and lifecycle
effects much easier.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;embrace-the-class-less-future&quot; tabindex=&quot;-1&quot;&gt;Embrace the class-less future&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#embrace-the-class-less-future&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Embrace the class-less future”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;For those reasons and others, the future of React is class-less. And that&#39;s a
good thing. Hooks provide more flexibility when creating and managing
components, to say nothing of
&lt;a href=&quot;https://reactjs.org/docs/hooks-custom.html&quot;&gt;custom hooks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But don&#39;t panic and convert all your class components right now;
&lt;a href=&quot;https://twitter.com/dan_abramov/status/1026839679197437953&quot;&gt;&amp;quot;they&#39;ll be supported for a looooong time&amp;quot;&lt;/a&gt;.
However, when you have the opportunity to refactor a class component going
forward, consider converting it into a functional component with hooks.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;A big thanks to &lt;a href=&quot;https://medium.com/@hungtrn75&quot;&gt;Hung Tran Van&lt;/a&gt; on Medium for
&lt;a href=&quot;https://medium.com/@hungtrn75/an-infinite-loop-when-u-set-url-state-in-useeffect-efc4426ef4ce&quot;&gt;highlighting a previous issue with this article&lt;/a&gt;.
While fixing my mistake, I found
&lt;a href=&quot;https://daveceddia.com/useeffect-hook-examples/&quot;&gt;&lt;em&gt;How the useEffect Hook Works&lt;/em&gt;&lt;/a&gt;
by David Ceddia to be super helpful. &lt;a href=&quot;https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Wed, 06 Feb 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/convert-class-component-to-function-with-hooks/</guid>
    </item>
    <item>
      <title>Accessibility Wednesday</title>
      <link>https://seanmcp.com/articles/accessibility-wednesday/</link>
      <description>&lt;p&gt;In the fall of 2018, I had the good pleasure of attending the
&lt;a href=&quot;https://connect.tech&quot;&gt;Connect.Tech&lt;/a&gt; conference in Atlanta, GA. There were great
talks about React, user experience design, and exciting new features for
JavaScript.&lt;/p&gt;
&lt;p&gt;But the sessions that had the greatest impact on me were all focused on
accessibility.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;accessibility-is-important&quot; tabindex=&quot;-1&quot;&gt;Accessibility is important&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/accessibility-wednesday/#accessibility-is-important&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Accessibility is important”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;It might seem obvious to you, but I needed to hear that loudly proclaimed from
the keynote stage.&lt;/p&gt;
&lt;p&gt;Accessibility isn&#39;t just about our friends and neighbors with disabilities; it&#39;s
about making applications that work well for everyone.
&lt;a href=&quot;https://ssir.org/articles/entry/the_curb_cut_effect&quot;&gt;The Curb Cut Effect&lt;/a&gt; is a
perfect example about how accommodations make everyone&#39;s lives easier.&lt;/p&gt;
&lt;p&gt;I was treating accessibility as if it was icing on a cake--something that was
nice to have but not essential. Unfortunately, it was that type of thinking that
allows inaccessible apps to flourish.&lt;/p&gt;
&lt;p&gt;Accessibility is not a feature; it&#39;s part of the product. If our app isn&#39;t
accessible, then it isn&#39;t done.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;%22whoa%2C-what-was-i-doing-before%3F%22&quot; tabindex=&quot;-1&quot;&gt;&amp;quot;Whoa, what was I doing before?&amp;quot;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/accessibility-wednesday/#%22whoa%2C-what-was-i-doing-before%3F%22&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “&amp;quot;Whoa, what was I doing before?&amp;quot;”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Hearing all that was like a light switch being flipped in my mind--and with that
came loads of guilt and regret for all the neighbors I had neglected for the
decade I had been developing. I felt especially convicted by my experiences in
my previous career.&lt;/p&gt;
&lt;p&gt;Before becoming a developer, I studied education in college and spent three
years working as a teacher. The importance of creating an inclusive environment
and the value of accommodations was drilled into us early on, and we studied how
great accommodations increase learning outcomes for all students.&lt;/p&gt;
&lt;p&gt;Further yet, I had seen those benefits first hand while working in over a dozen
schools across Pennsylvania and Georgia.&lt;/p&gt;
&lt;p&gt;If accessibility was so important to my previous career, why wouldn&#39;t it be the
same in my new one as a developer?&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;%22okay...-now-what%3F%22&quot; tabindex=&quot;-1&quot;&gt;&amp;quot;Okay... now what?&amp;quot;&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/accessibility-wednesday/#%22okay...-now-what%3F%22&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “&amp;quot;Okay... now what?&amp;quot;”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Impressed with a sudden desire to make all the things accessible, I left the
conference and looked online for resources to help.&lt;/p&gt;
&lt;p&gt;I first tried to read the
&lt;a href=&quot;https://www.w3.org/TR/WCAG21/&quot;&gt;Web Content Accessibility Guidelines 2.1&lt;/a&gt;, which
weren&#39;t as difficult to grasp as I had feared. Even so, the content was dense
and a tough slog for a newbie.&lt;/p&gt;
&lt;p&gt;Seeking more digestible information, I found other resources on W3C to be
helpful, especially
&lt;a href=&quot;https://www.w3.org/WAI/roles/developers/&quot;&gt;those specifically for developers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was working with React Native at the time, so I created
&lt;a href=&quot;https://github.com/SeanMcP/awesome-react-native-accessibility&quot;&gt;Awesome React Native Accessibility&lt;/a&gt;,
a repo with a list of mobile-specific resources. Progress on that halted when I
switched to a React project at work.&lt;/p&gt;
&lt;p&gt;At this point, I was starting to feel a little overwhelmed. Everywhere I looked
were categories, rules, laws, practices, and oodles of examples of companies
doing it wrong. It all seemed like too much.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;accessibility-is-hard&quot; tabindex=&quot;-1&quot;&gt;Accessibility is hard&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/accessibility-wednesday/#accessibility-is-hard&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Accessibility is hard”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I was beginning to think that maybe accessibility was too difficult, too
technical, too intricate for me. And I&#39;m sure that I am not alone in having
thought that.&lt;/p&gt;
&lt;p&gt;But the reality is that developing for accessibility is a skill. And just like
any other skill, it requires little bites and regular practice. Over time you
will improve, and eventually what seemed difficult will become second nature.&lt;/p&gt;
&lt;p&gt;There is a reason we don&#39;t learn to walk by running. Our brains weren&#39;t designed
to work that way. As the father of a now-cruising baby, I feel particularly
qualified to say that learning to walk is hard work. It&#39;s filled with failures,
bumps, and bruises. Same with riding a bike, learning to read, or adjusting to a
AAC device.&lt;/p&gt;
&lt;p&gt;You will fail at first (and second and third), but as long as you persevere, you
can be successful. You can learn. You can grow.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;let&#39;s-learn-about-accessibility&quot; tabindex=&quot;-1&quot;&gt;Let&#39;s learn about accessibility&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/accessibility-wednesday/#let&#39;s-learn-about-accessibility&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Let&#39;s learn about accessibility”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I am determined: I want to become an accessible developer. I want to bake
accessibility in for as many neighbors as possible into every application on
which I work.&lt;/p&gt;
&lt;p&gt;In order to meet that goal, I need to learn more about accessibility. And that&#39;s
where this article comes in. This is the kick-off to a series called
&amp;quot;Accessibility Wednesday&amp;quot; where I will share accessibility tid-bits for
developers.&lt;/p&gt;
&lt;p&gt;These won&#39;t be full articles--neither you nor I have time for that. Rather, I&#39;ll
share images, links, and code samples on Twitter with the hashtag
&lt;a href=&quot;https://twitter.com/hashtag/a11ywednesday&quot;&gt;#a11ywednesday&lt;/a&gt;. Everything will be
available in &lt;a href=&quot;https://github.com/SeanMcP/accessibility-wednesday&quot;&gt;a GitHub repo&lt;/a&gt;
for easy reference.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;remember%3A-i&#39;m-no-expert-(yet)&quot; tabindex=&quot;-1&quot;&gt;Remember: I&#39;m no expert (yet)&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/accessibility-wednesday/#remember%3A-i&#39;m-no-expert-(yet)&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Remember: I&#39;m no expert (yet)”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Since I am not an authoritative resource on accessibility, I will make sure to
include references and citations to every applicable tidbit. Is MLA formatting
still a thing? (s/o PurdueOWL)&lt;/p&gt;
&lt;p&gt;This is a learning exercise for me as much as it is for everyone else. With that
in mind, I will almost certainly make mistakes. All of us fall short of the
target. That&#39;s why I encourage you to help me correct my mistakes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/SeanMcP/accessibility-wednesday/issues/new&quot;&gt;File an issue&lt;/a&gt;
on GitHub referencing the tip in question, or--better yet--make a pull request
with the fix. I happily welcome any and all contributions the the project.&lt;/p&gt;
&lt;p&gt;If you notice the mistake &lt;a href=&quot;https://twitter.com/snmcp&quot;&gt;on Twitter&lt;/a&gt;, please
respond or direct message to let me know. I will remove the tweet, make the
correction, and share the updated information later.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;stronger-together&quot; tabindex=&quot;-1&quot;&gt;Stronger together&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/accessibility-wednesday/#stronger-together&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Stronger together”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;As time goes on, I&#39;m hoping that Accessibility Wednesday grows into a catalog of
useful information for developers of all levels to make more accessible
applications. We know that there is power in our collective knowledge.&lt;/p&gt;
&lt;p&gt;As a wise man once penned, &amp;quot;with great power there must also come--great
responsibility!&amp;quot;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/accessibility-wednesday/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; Let&#39;s use our power for good and make the web more
accessible for everyone.&lt;/p&gt;
&lt;p&gt;Join the discussion &lt;a href=&quot;https://twitter.com/hashtag/a11ywednesday&quot;&gt;on Twitter&lt;/a&gt; or
&lt;a href=&quot;https://github.com/SeanMcP/accessibility-wednesday&quot;&gt;GitHub&lt;/a&gt;. I look forward to
learning and sharing with you all!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I learned while writing this article that the original phrase from
Spider-Man is slightly different than the modern variation.
&lt;a href=&quot;http://www.quotecounterquote.com/2012/07/with-great-power-comes-great.html&quot;&gt;(source)&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/accessibility-wednesday/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Wed, 06 Feb 2019 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/accessibility-wednesday/</guid>
    </item>
    <item>
      <title>2018 into 2019</title>
      <link>https://seanmcp.com/articles/2018-into-2019/</link>
      <description>&lt;p&gt;As 2018 comes to a close, I was inspired by
&lt;a href=&quot;https://www.taniarascia.com/2018-into-2019/&quot;&gt;Tania Rascia&lt;/a&gt; to write a year in
review. I&#39;m hoping to turn this into an annual opportunity to reflect on the
successes, failures, and challenges from the previous year, and set goals for
the next.&lt;/p&gt;
&lt;p&gt;2018 was probably the most eventful year of my life, edging 2014 in which I
graduated from college, married, and received my first teaching position. I&#39;ve
attempted to list all of the major events and accomplishments of the past year
in mostly chronological order.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;2018-in-review&quot; tabindex=&quot;-1&quot;&gt;2018 in review&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/2018-into-2019/#2018-in-review&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “2018 in review”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Last year, I:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Received three tech job offers&lt;/strong&gt;: Node developer with a crypto company, QA
with CNN Politics, and React Developer with TSYS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accepted my first software developer position&lt;/strong&gt;: TSYS offered a
contract-to-hire position that worked well for my family&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployed two apps&lt;/strong&gt;: &lt;a href=&quot;https://seanmcp.github.io/dice-roller/&quot;&gt;Dice Roller&lt;/a&gt;
and &lt;a href=&quot;https://seanmcp.github.io/contraction-tracker/&quot;&gt;Contraction Tracker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attended two conferences&lt;/strong&gt;: NG ATL, and Connect Tech&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Became a father&lt;/strong&gt;: My first child was born in March, changing our lives
forever&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Converted from contract to salaried employee&lt;/strong&gt;: After a three-month trial,
TSYS was happy to offer me an official position with the company&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Organized&lt;/strong&gt; &lt;a href=&quot;https://www.meetup.com/jrdevsatl/&quot;&gt;&lt;strong&gt;Jr Devs&lt;/strong&gt;&lt;/a&gt;: a Meetup for
learners with over 200 members&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gave a lightning talk at a Meetup&lt;/strong&gt;:
&lt;a href=&quot;https://github.com/jr-devs/meetup/blob/master/events/june-2018.md&quot;&gt;Intro to CSS Animations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learned some Python&lt;/strong&gt;: created a few scripts, a Flask app for gaming, and a
text-based adventure game &lt;a href=&quot;https://github.com/SeanMcP/rimoria&quot;&gt;Rimoria&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Created two NPM packages&lt;/strong&gt;:
&lt;a href=&quot;https://www.npmjs.com/package/vanilla-include&quot;&gt;vanilla-include&lt;/a&gt; and
&lt;a href=&quot;https://www.npmjs.com/package/nodify-string&quot;&gt;nodify-string&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Represented TSYS&lt;/strong&gt; at a job fair&lt;/li&gt;
&lt;li&gt;Worked on an application at work in &lt;strong&gt;React Native&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Launched a Chrome extension&lt;/strong&gt;:
&lt;a href=&quot;https://chrome.google.com/webstore/detail/popsicle-sticks-random-st/lnckbgaeagindapiodcmccfokcmnfecm&quot;&gt;Popsicle Sticks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Helped TSYS create an
&lt;a href=&quot;https://medium.com/tsys-engineering&quot;&gt;&lt;strong&gt;engineering blog&lt;/strong&gt;&lt;/a&gt; and
&lt;a href=&quot;https://github.com/tsys&quot;&gt;&lt;strong&gt;GitHub organization&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Published &lt;strong&gt;12 articles&lt;/strong&gt; (including two on this website)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After sitting down and writing it all out, I am impressed with the things that I
have accomplished in 2018. Especially considering how uncertain I was about
continuing my search for work in the tech industry by the end of 2017.&lt;/p&gt;
&lt;p&gt;Last year was a long stretch of giving from God: blessed be the name of the LORD
(&lt;a href=&quot;https://www.biblegateway.com/passage/?search=Job+1&amp;amp;version=NKJV&quot;&gt;read Job 1&lt;/a&gt;).&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;previewing-2019&quot; tabindex=&quot;-1&quot;&gt;Previewing 2019&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/2018-into-2019/#previewing-2019&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Previewing 2019”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Looking forward to next year, there are several things that I want to learn and
do. I am going to list some general goals, followed by measurable ways that I
can accomplish them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt;: I want to dive deeper in Python applications, including
environments and dependencies, by &lt;strong&gt;deploying a Python app&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Machine learning&lt;/strong&gt;: TSYS is interested in integrating ML into their
applications, so I will &lt;strong&gt;complete a course&lt;/strong&gt; in either Udacity or Udemy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Module bundlers&lt;/strong&gt;: I am lost (and confused) when it comes to module
bundling, so I am going to de-mystify bundlers by publishing React &lt;strong&gt;setup
guides for Webpack, Parcel, and Rollup&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NPM Packages&lt;/strong&gt;: I want to learn about creating and maintaining open-source
software by publishing &lt;strong&gt;two new packages&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling&lt;/strong&gt;: I want to investigate tooling options for React development that
streamline the process and &lt;strong&gt;create a personalized dot files repo&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Publish more articles&lt;/strong&gt;: It is my mission to help people, and one way to do
that is to publish &lt;strong&gt;24 articles (bi-monthly)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jr Devs Meetup&lt;/strong&gt;: I want to partner with a coding bootcamp to provide
&lt;strong&gt;instructors at every meeting&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Speaking&lt;/strong&gt;: I want to create a deck and &lt;strong&gt;present at another Meetup&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Husband&lt;/strong&gt;: I want to take dating my wife seriously and &lt;strong&gt;schedule a
babysitter four times a year&lt;/strong&gt; for real dates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Father&lt;/strong&gt;: I want to use the time I have with my son more productively,
&lt;strong&gt;going on father-son outings twice a month&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faith&lt;/strong&gt;: I want to &lt;strong&gt;continue teaching Sunday school&lt;/strong&gt;, lead &lt;strong&gt;family
worship every evening&lt;/strong&gt;, and &lt;strong&gt;read/reflect on the Bible daily&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Personal&lt;/strong&gt;: Dad bod is a real thing and is only around the corner, so I want
to &lt;strong&gt;exercise 6 days a week&lt;/strong&gt; (don&#39;t forget a
&lt;a href=&quot;https://www.biblegateway.com/passage/?search=Ex+20%3A8-11&amp;amp;version=NKJV&quot;&gt;day of rest&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Soccer&lt;/strong&gt;: I want to find room in the family schedule to &lt;strong&gt;play a pick-up
game every week&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I could go on longer, but twelve seems like a fitting number. I am going to
check back on these goals throughout the year and track my progress in a
different article.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;how-was-your-year%3F&quot; tabindex=&quot;-1&quot;&gt;How was your year?&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/2018-into-2019/#how-was-your-year%3F&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “How was your year?”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;As I mentioned above, this is not a new or unique idea. But I think it&#39;s a good
one, and I encourage you to give it a try. You might be pleasantly surprise
(like I was), at how much you&#39;ve accomplished.&lt;/p&gt;
&lt;p&gt;If not, then ask yourself: What can I do to help myself accomplish my goals next
year? You don&#39;t need to be a super hero; just try to be a little better than you
were the day before.&lt;/p&gt;
&lt;p&gt;I hope this was half as inspiring to you as it was helpful to me. Happy new
year! 🎉&lt;/p&gt;
</description>
      <pubDate>Mon, 31 Dec 2018 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/2018-into-2019/</guid>
    </item>
    <item>
      <title>Why a blog?</title>
      <link>https://seanmcp.com/articles/why-a-blog/</link>
      <description>&lt;p&gt;For over a year and half I have been happily blogging on Medium. I was quick to
sign up when the platform first launched (and snag that elusive
&lt;a href=&quot;https://medium.com/@seanmcp&quot;&gt;@seanmcp&lt;/a&gt; handle) but was slow to start writing.&lt;/p&gt;
&lt;p&gt;Then, in the summer of 2017, I enrolled in the Iron Yard and was encouraged to
blog my process. After looking at Blogger for a minute (seriously), I settled on
Medium. I appreciated the design esthetic and attention to typographical detail.
Composing was simple and I found code blocks quite useful.&lt;/p&gt;
&lt;p&gt;I never really settled into a consistent writing schedule but did find some
success writing about JavaScript and React. My most popular article,
&lt;a href=&quot;https://medium.com/@seanmcp/%EF%B8%8F-how-to-use-emojis-in-react-d23bbf608bf7&quot;&gt;&amp;quot;How to use emojis in React&amp;quot;&lt;/a&gt;,
continues to receive &amp;quot;applause&amp;quot; six months after I published.&lt;/p&gt;
&lt;p&gt;All that is to say, Medium was a perfectly suitable platform for what I needed.
And it still is a good platform, but my needs have changed.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@nikitonsky/medium-is-a-poor-choice-for-blogging-bb0048d19133&quot;&gt;Recent&lt;/a&gt;
&lt;a href=&quot;https://twitter.com/dan_abramov/status/1068903909119483904&quot;&gt;criticism&lt;/a&gt;
&lt;a href=&quot;https://twitter.com/snookca/status/1068866664257732608&quot;&gt;of Medium&lt;/a&gt; aside,
ownership of my content was the biggest catalyst for looking elsewhere. Medium
is popular now, but will it be in two years? Five years? Ten years? Will we be
reading Medium posts about terraforming when we colonize the moon? Or will
Medium&#39;s popularity wane and be shuttered before we get there? No one can know.&lt;/p&gt;
&lt;p&gt;Regardless of the future of the platform, I&#39;d like my content to live on. My
goal is to help people (help people), so my words--as feeble as they are--need
to persist long enough to do that.&lt;/p&gt;
&lt;p&gt;&amp;quot;The grass withers and the flowers fade&amp;quot;; nothing is permanent in this life. But
having more direct ownership and control of my content puts me in a better
position to continue helping people for the long-run.&lt;/p&gt;
&lt;p&gt;That&#39;s why I&#39;m starting this blog. For the immediate future, I will be
cross-posting everything I write on Medium and linking back here. After then?
We&#39;ll see what the web looks like.&lt;/p&gt;
&lt;p&gt;In the meantime, thank you for stopping by. Check back weekly or subscribe with
your favorite RSS reader.&lt;/p&gt;
</description>
      <pubDate>Tue, 04 Dec 2018 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/why-a-blog/</guid>
    </item>
    <item>
      <title>How to create a render prop component</title>
      <link>https://seanmcp.com/articles/create-a-render-prop-component/</link>
      <description>&lt;p&gt;It&#39;s been a year since Michael Jackson published &amp;quot;Use a Render Prop!&amp;quot;, which
encouraged using render props instead of higher order components (HOCs). Since
then, &amp;quot;render props&amp;quot; has grown into a bigger and bigger buzzword in the React
community.&lt;/p&gt;
&lt;p&gt;If you are new to React or just new to the concept, the look of a render prop
can be a little daunting.
&lt;a href=&quot;https://www.react-spring.io/docs/props/spring&quot;&gt;Checkout this example from &lt;code&gt;react-spring&lt;/code&gt;&lt;/a&gt;&#39;s
documentation:&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;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Spring &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react-spring&#39;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Spring from&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 punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&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; to&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 punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;span class=&quot;token parameter&quot;&gt;styles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;i will fade &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 class=&quot;token comment&quot;&gt;// &amp;lt;-- Here&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Spring&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If that makes perfect sense to you, awesome 🎉! Skip this article and get back
to coding. If not, don&#39;t worry; I didn&#39;t get it at first either. Stick around,
and I&#39;ll attempt to break down render props and show you how to make your own.&lt;/p&gt;
&lt;hr&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;background&quot; tabindex=&quot;-1&quot;&gt;Background&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#background&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Background”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;what-is-a-render-prop%3F&quot; tabindex=&quot;-1&quot;&gt;What is a render prop?&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#what-is-a-render-prop%3F&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “What is a render prop?”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;At its core, render prop is &amp;quot;a simple technique for sharing code between React
components using a prop whose value is a function.&amp;quot;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; Instead of passing a
string or object, you pass a function as a prop.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Name renderProp&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 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 string&quot;&gt;&quot;Passing a function as a prop!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &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;The code above won&#39;t accomplish much - the arrow function just returns a
string - but it introduces a new paradigm. What if, within &lt;code&gt;Name&lt;/code&gt; we pass some
data to our &lt;code&gt;props.renderProp&lt;/code&gt; function?&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; &lt;span class=&quot;token function-variable function&quot;&gt;Name&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;props&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 class=&quot;token keyword&quot;&gt;const&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Sean&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;renderProp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;When &lt;code&gt;Name&lt;/code&gt; renders, it calls &lt;code&gt;props.renderProp&lt;/code&gt; and passes some data; in this
case, a string with my name.&lt;/p&gt;
&lt;p&gt;We have designed our &lt;code&gt;Name&lt;/code&gt; component with the sole purpose of storing the
&lt;code&gt;name&lt;/code&gt; data and passing it on to the &lt;code&gt;props.renderProps&lt;/code&gt; function. The is the
secret sauce that makes render props so powerful.&lt;/p&gt;
&lt;p&gt;Going back to the implementation of &lt;code&gt;Name&lt;/code&gt;, the function I pass to &lt;code&gt;renderProp&lt;/code&gt;
can look for this data as a parameter:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Name renderProp&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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&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 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;Hello, my name is &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&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 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;When &lt;code&gt;Nam&lt;/code&gt;e is called, it creates variable &lt;code&gt;name&lt;/code&gt; with the value &#39;Sean&#39;, then
calls the &lt;code&gt;props.renderProp&lt;/code&gt; function and passes &lt;code&gt;name&lt;/code&gt;. Finally, it returns a
string that reads &lt;code&gt;Hello, my name is Sean!&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But we aren&#39;t just limited to strings; this is React! You can return JSX in your
function to render new elements in the VirtualDOM:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Name renderProp&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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Hello&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; my name is &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&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 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;That&#39;s it! Render props provide us a method of passing props from a parent
component to a child via function props. Using these same principles, we can
create useful and reusable &amp;quot;logic&amp;quot; components to implement in any application.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: I called our prop &lt;code&gt;renderProp&lt;/code&gt;, but there is nothing special about that
name; you can use whatever name you want. I&#39;ll give you my recommendation for
passing function props later on.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;what-does-render-props-replace%3F&quot; tabindex=&quot;-1&quot;&gt;What does render props replace?&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#what-does-render-props-replace%3F&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “What does render props replace?”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In most use cases, render props will replace HOCs. If we wanted to accomplish
the above example with an HOC, we might use something like this:&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; &lt;span class=&quot;token function-variable function&quot;&gt;withName&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;Component&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Component name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sean&quot;&lt;/span&gt; &lt;span class=&quot;token operator&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then our parent component would be passed to &lt;code&gt;withName&lt;/code&gt;, usually during the
export. Once that is done, we could access &lt;code&gt;props.name&lt;/code&gt; and render our greeting:&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; &lt;span class=&quot;token function-variable function&quot;&gt;App&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;props&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Hello&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; my name is &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&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 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 function&quot;&gt;withName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;App&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;This works, and certainly has
&lt;a href=&quot;https://medium.com/r/?url=https%3A%2F%2Freact-redux.js.org%2Fdocs%2Fconnect-extracting-data-with-mapstatetoprops&quot;&gt;its use cases&lt;/a&gt;,
but can introduce issues such as naming collisions. I recommend
&lt;a href=&quot;https://medium.com/r/?url=https%3A%2F%2Fcdb.reacttraining.com%2Fuse-a-render-prop-50de598f11ce&quot;&gt;Jackson&#39;s article for a more detailed dive into render props and HOCs&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;how-to&quot; tabindex=&quot;-1&quot;&gt;How To&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#how-to&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “How To”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Note: Facebook provides an
&lt;a href=&quot;https://medium.com/r/?url=https%3A%2F%2Freactjs.org%2Fdocs%2Frender-props.html&quot;&gt;introduction to render props in their React documentation&lt;/a&gt;.
It is a useful resource, but their example was contrived and therefore difficult
to reapply to new scenarios. Here I will provide a tried-and-true example that I
have utilized in multiple projects.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;setting-up-our-app&quot; tabindex=&quot;-1&quot;&gt;Setting up our app&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#setting-up-our-app&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Setting up our app”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Let&#39;s imagine you&#39;re creating an application with drawer menu. Most of the time
the menu is hidden, but when you hit a toggle button, the menu opens. Within the
menu, you want to have an accordion that reveals additional links when open. In
order to accomplish this, you might do something like this:&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;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &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 literal-property property&quot;&gt;menuIsOpen&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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 function&quot;&gt;render&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;app&quot;&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 class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;menuIsOpen &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Menu toggle&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggle&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggle&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;button&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token constant&quot;&gt;MENU&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;main&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 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 function-variable function&quot;&gt;toggle&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&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 literal-property property&quot;&gt;menuIsOpen&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;menuIsOpen &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;/code&gt;&lt;/pre&gt;
&lt;p&gt;We made &lt;code&gt;App&lt;/code&gt; a stateful component and keep track of a boolean for the menu.
Additionally, we added a &lt;code&gt;toggle&lt;/code&gt; method to open and close the menu.&lt;/p&gt;
&lt;p&gt;Now we need to create Menu:&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;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Menu&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &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 literal-property property&quot;&gt;socialsIsOpen&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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 function&quot;&gt;render&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;menu&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggle&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;button&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token constant&quot;&gt;CLOSE&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Menu&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggle&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;button&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token constant&quot;&gt;SOCIALS&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socialsIsOpen &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▴&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▾&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&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 class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socialsIsOpen &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Socials &lt;span class=&quot;token operator&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 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 function-variable function&quot;&gt;toggle&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&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 literal-property property&quot;&gt;socialsIsOpen&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socialsIsOpen &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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look familiar? We &lt;strong&gt;reused the state from &lt;code&gt;App&lt;/code&gt;&lt;/strong&gt; (🚩 red flag no 1) so that we
could keep track of whether or not to display our &lt;code&gt;Socials&lt;/code&gt; component (just an
unordered list of links). &lt;code&gt;Menu&lt;/code&gt; needed to be toggled too, so we &lt;strong&gt;copy and
pasted the method from &lt;code&gt;App&lt;/code&gt;&lt;/strong&gt; (🚩 red flag no. 2).&lt;/p&gt;
&lt;p&gt;This works, but there are two main problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;👎 &lt;strong&gt;Not DRY&lt;/strong&gt;: In order to achieve the same function in two different
components, I needed to repeat myself by copying and pasting the state and
toggle method.&lt;/li&gt;
&lt;li&gt;👎 &lt;strong&gt;Not extensible&lt;/strong&gt;: Imagine I want to add another toggle feature to my
app. What do I need to do? Copy and paste the necessary state and method
again, which compounds my problem.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Fortunately, we can solve both of these problems easily with a simple render
prop component.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;creating-a-render-prop&quot; tabindex=&quot;-1&quot;&gt;Creating a render prop&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#creating-a-render-prop&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Creating a render prop”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The purpose of our render prop component will be to manage an open and closed
state. It will also need a method to toggle the state like the examples above.
Then it will take the state and method and pass it as arguments to a prop.&lt;/p&gt;
&lt;p&gt;My preference for render props is to use a Component&#39;s &lt;code&gt;children&lt;/code&gt; prop. This
allows us to utilize opening and closing tags - a familiar convention in
React - and makes indentation a little more manageable.&lt;/p&gt;
&lt;p&gt;Our render prop component looks like this:&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;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&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 keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Open&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &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 literal-property property&quot;&gt;isOpen&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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 function-variable function&quot;&gt;toggle&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&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 literal-property property&quot;&gt;isOpen&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOpen &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 function&quot;&gt;render&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 keyword&quot;&gt;const&lt;/span&gt; renderProps &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 literal-property property&quot;&gt;isOpen&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggle&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;function&quot;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;renderProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like the previous examples, I am keeping track of an &amp;quot;open&amp;quot; state and
toggling it with a method. Here I simplified the name of the boolean in the
state to &lt;code&gt;isOpen&lt;/code&gt; because our component doesn&#39;t care what is and is not open.
The only thing &lt;code&gt;Open&lt;/code&gt; cares about is whether or not &lt;code&gt;isOpen&lt;/code&gt; is true or false.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Render props components do not care about presentational logic. Their sole
purpose is to keep track of simple bits of data and pass it on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;toggle&lt;/code&gt; method is the same as previous examples.&lt;/p&gt;
&lt;p&gt;Within the &lt;code&gt;render&lt;/code&gt; method, I create a &lt;code&gt;renderProps&lt;/code&gt; object with which I can
pass on the current value of &lt;code&gt;isOpen&lt;/code&gt; and my &lt;code&gt;toggle&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Finally I do a quick &lt;code&gt;typeof&lt;/code&gt; check while returning. If &lt;code&gt;this.props.children&lt;/code&gt; is
a function, as I expect, then it will pass &lt;code&gt;renderProps&lt;/code&gt; as an argument. If not,
then it will fail softly by simply returning &lt;code&gt;this.props.children&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can optionally do some type-checking to ensure that &lt;code&gt;children&lt;/code&gt; is a
function, but I chose not to add an additional library here.&lt;/p&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h3 id=&quot;implementing-our-render-prop&quot; tabindex=&quot;-1&quot;&gt;Implementing our render prop&lt;/h3&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#implementing-our-render-prop&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Implementing our render prop”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Now that we have abstracted the open/close logic to &lt;code&gt;Open&lt;/code&gt;, we can clean up our
App component:&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;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&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 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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Open&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 class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;openProps&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;app&quot;&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;openProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOpen &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;NewMenu toggle&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;openProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggle&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;openProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggle&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;button&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token constant&quot;&gt;MENU&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Welcome &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;main&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Open&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We pass &lt;code&gt;Open&lt;/code&gt; a child function that looks for our render props. Now I just need
to switch out all references to &lt;code&gt;this.state.menuIsOpen&lt;/code&gt; and &lt;code&gt;this.toggle&lt;/code&gt; to
refer to &lt;code&gt;openProps&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Notice that &lt;code&gt;App&lt;/code&gt; no longer needs a state; we can convert it to a functional
component and let &lt;code&gt;Open&lt;/code&gt; do all the work.&lt;/p&gt;
&lt;p&gt;Next, let&#39;s do the same thing with &lt;code&gt;Menu&lt;/code&gt;:&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;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Menu&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;props&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Open&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 class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;openProps&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;menu&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggle&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;button&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token constant&quot;&gt;CLOSE&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Menu&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;openProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggle&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;button&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token constant&quot;&gt;SOCIALS&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;openProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOpen &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▴&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▾&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;openProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOpen &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Socials &lt;span class=&quot;token operator&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 class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Open&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Likewise, we were able to simplify Menu by converting it to a functional
component as well. This may not seem like a big deal now, but it will be
increasingly important as an application scales.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The fewer states and methods that you need to manage, the better.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can see our render prop live, in all its glory, here on CodeSandbox:&lt;/p&gt;
&lt;iframe src=&quot;https://codesandbox.io/embed/x5j1pl90o?fontsize=14&amp;hidenavigation=1&amp;theme=dark&amp;view=preview&quot; style=&quot;width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;&quot; title=&quot;How to create a render prop component&quot; allow=&quot;geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb&quot; sandbox=&quot;allow-modals allow-forms allow-popups allow-scripts allow-same-origin&quot;&gt;&lt;/iframe&gt;
&lt;div class=&quot;heading-container&quot;&gt;
&lt;h2 id=&quot;review&quot; tabindex=&quot;-1&quot;&gt;Review&lt;/h2&gt;
&lt;a class=&quot;header-anchor&quot; href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#review&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Permalink to “Review”&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The render props technique is a great way to abstract reusable logic into
specialized components. These components pass their logic/data/methods as
arguments to functions passed as props.&lt;/p&gt;
&lt;p&gt;Utilizing render props components solves the two problems above:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;👍 &lt;strong&gt;DRY&lt;/strong&gt;: The only code repeated is &lt;code&gt;&amp;lt;Open&amp;gt;&lt;/code&gt;. You&#39;ll never have to write
another &lt;code&gt;toggle&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;👍 &lt;strong&gt;Extensible&lt;/strong&gt;: Next time you want to add opening and closing to your app,
all you need to add is &lt;code&gt;Open&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that we&#39;ve reviewed, let&#39;s take a look back at that original example from
&lt;code&gt;react-spring&lt;/code&gt;:&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;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Spring &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react-spring&#39;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Spring from&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 punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&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; to&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 punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;span class=&quot;token parameter&quot;&gt;styles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;i will fade &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&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 class=&quot;token comment&quot;&gt;// &amp;lt;-- Here&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Spring&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see that this &lt;code&gt;Spring&lt;/code&gt; component is passed a &lt;code&gt;this.props.children&lt;/code&gt;
function. It then gives some data, in this case a style object, to that function
as an argument. Finally, those &lt;code&gt;styles&lt;/code&gt; are applied to the &lt;code&gt;div&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Not bad, right? Now that you&#39;ve mastered render props, make sure to check out
this curated list of awesome render props. In the words of Captain Planet, &amp;quot;The
power is yours!&amp;quot;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I hope this article helped; I know I learned a lot while writing it. Let me know
if you have any &lt;a href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#comment-link&quot;&gt;questions about or problems&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://reactjs.org/docs/render-props.html&quot;&gt;https://reactjs.org/docs/render-props.html&lt;/a&gt; &lt;a href=&quot;https://seanmcp.com/articles/create-a-render-prop-component/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <pubDate>Tue, 09 Oct 2018 16:00:00 GMT</pubDate>
      <dc:creator>Sean McPherson</dc:creator>
      <guid>https://seanmcp.com/articles/create-a-render-prop-component/</guid>
    </item>
  </channel>
</rss>