<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Saul Hardman</title>
    <description>The latest articles on DEV Community by Saul Hardman (@saul).</description>
    <link>https://dev.to/saul</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F40442%2F013bc392-8373-4a9d-aab1-da936d1f3a79.jpg</url>
      <title>DEV Community: Saul Hardman</title>
      <link>https://dev.to/saul</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saul"/>
    <language>en</language>
    <item>
      <title>A Word of Caution Regarding Cross-Posting To DEV</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Fri, 28 Aug 2020 12:39:57 +0000</pubDate>
      <link>https://dev.to/saul/a-word-of-caution-regarding-cross-posting-to-dev-3e8g</link>
      <guid>https://dev.to/saul/a-word-of-caution-regarding-cross-posting-to-dev-3e8g</guid>
      <description>&lt;p&gt;I cross-post all of &lt;a href="https://viewsource.io/"&gt;my articles&lt;/a&gt; to DEV (soon to other platforms such as &lt;a href="https://medium.com/@saulhardman"&gt;Medium&lt;/a&gt; and &lt;a href="https://viewsource.hashnode.dev/"&gt;Hashnode&lt;/a&gt; as well) and am very happy to do so.&lt;/p&gt;

&lt;p&gt;DEV provides the ability to set a &lt;code&gt;&amp;lt;link rel="canonical"&amp;gt;&lt;/code&gt; pointing to the original article's permalink on the &lt;code&gt;viewsource.io&lt;/code&gt; domain. Up until now this appears to have worked just fine with Google ranking my domain higher in the results than &lt;code&gt;dev.to&lt;/code&gt; (if DEV is there at all).&lt;/p&gt;

&lt;p&gt;However, my &lt;a href="https://viewsource.io/managing-access-tokens-with-github-actions-and-encrypted-secrets/"&gt;latest article&lt;/a&gt; seems to have fallen between the gaps somewhere as the DEV version of the article is the only one which displays in the search results for the search term &lt;a href="https://www.google.com/search?hl=en&amp;amp;q=managing%20access%20tokens%20with%20github%20actions"&gt;&lt;code&gt;"Managing access tokens with GitHub Actions"&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E4P6Nmjh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/b1ueqe1fhgx79jqwh3pn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E4P6Nmjh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/b1ueqe1fhgx79jqwh3pn.png" alt="A screenshot of Google search results showing only DEV article" width="800" height="740"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The original article only appears in the search results if I navigate to the final page of results and click the 'search with the omitted results included' link:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YD9spy0B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/3yb9aj4m5ud66uwjclxi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YD9spy0B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/3yb9aj4m5ud66uwjclxi.png" alt="A screenshot of Google with some copy stating that some 'very similar' results have been omitted" width="800" height="740"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9XKDzjrL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/gx8nfssr5n02cn7fqx5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9XKDzjrL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/gx8nfssr5n02cn7fqx5p.png" alt="A screenshot of Google search results showing both the DEV article and the original article" width="800" height="740"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite both pages containing a &lt;code&gt;rel="canonical"&lt;/code&gt; link pointing to the same page, the original article appears to be falling foul of Google's duplicate content filters.&lt;/p&gt;

&lt;p&gt;This is either an anomaly or (as I suspect) this has happened due to the DEV article being crawled and indexed &lt;em&gt;before&lt;/em&gt; my site, meaning that the &lt;code&gt;href&lt;/code&gt; attribute of the &lt;code&gt;&amp;lt;link rel="canonical"&amp;gt;&lt;/code&gt; was to a page that was not indexed.&lt;/p&gt;

&lt;p&gt;Given the drastic differences in the size of the 2 sites it's not unreasonable to presume DEV simply has a larger crawl budget and is indexed far faster than my one-person publication.&lt;/p&gt;

&lt;p&gt;In order to avoid this outcome in the future I'm planning to adopt the policy of waiting at least a few days before cross-posting my articles.&lt;/p&gt;

&lt;p&gt;What do you think DEV community? Is my theory sound? Those of you who cross-post: have you been bitten by this before? Do you cross-post immediately or wait some time?&lt;/p&gt;

</description>
      <category>meta</category>
      <category>seo</category>
      <category>webdev</category>
      <category>indieweb</category>
    </item>
    <item>
      <title>Managing Access Tokens with GitHub Actions and Encrypted Secrets</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Wed, 12 Aug 2020 16:15:06 +0000</pubDate>
      <link>https://dev.to/saul/managing-access-tokens-with-github-actions-and-encrypted-secrets-19cf</link>
      <guid>https://dev.to/saul/managing-access-tokens-with-github-actions-and-encrypted-secrets-19cf</guid>
      <description>&lt;p&gt;Integrating 3rd-party APIs into &lt;a href="https://jamstack.org/"&gt;Jamstack&lt;/a&gt; apps and websites becomes a &lt;em&gt;bit&lt;/em&gt; tricky when the access tokens for these services need refreshing recurrently. Manually refreshing these access tokens and updating the corresponding secrets is one option, but automating the process via a scheduled &lt;a href="https://docs.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow"&gt;GitHub Workflow&lt;/a&gt;—though adding complexity—gives you one less thing to worry about. Using the &lt;a href="https://developers.facebook.com/docs/instagram-basic-display-api/"&gt;Instagram Basic Display API&lt;/a&gt; as a basis, I'm going to walk-through an example of automating this process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an Encrypted Secret
&lt;/h2&gt;

&lt;p&gt;The first step is to add an existing (valid) access token to your repository as an &lt;a href="https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository"&gt;encrypted secret&lt;/a&gt;. It appears to be a convention to use &lt;code&gt;UPPER_CASE_SNAKE_CASE&lt;/code&gt; when naming these secrets, so I've assigned my access token for the Instagram Basic Display API to a secret named &lt;code&gt;INSTAGRAM_ACCESS_TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pass a Secret as an Environment Variable
&lt;/h2&gt;

&lt;p&gt;The context in which I'll be using this access token within my Jamstack website is an &lt;a href="https://github.com/axios/axios"&gt;axios&lt;/a&gt; request querying the &lt;a href="https://developers.facebook.com/docs/instagram-basic-display-api/reference/user/media/"&gt;Instagram User Media endpoint&lt;/a&gt;, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://graph.instagram.com/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;instagramUserId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/media`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;INSTAGRAM_ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;process.env.INSTAGRAM_ACCESS_TOKEN&lt;/code&gt; to be referencing the correct value at runtime the environment variable &lt;code&gt;INSTAGRAM_ACCESS_TOKEN&lt;/code&gt; must be present at build time. As part of a Continuous Deployment workflow, I assign the encrypted secret &lt;code&gt;INSTAGRAM_ACCESS_TOKEN&lt;/code&gt; as an environment variable (of the same name) to the build step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/deploy.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Continuous Deployment&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bahmutov/npm-install@v1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn build&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
          &lt;span class="na"&gt;INSTAGRAM_ACCESS_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.INSTAGRAM_ACCESS_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a Scheduled Workflow
&lt;/h2&gt;

&lt;p&gt;Long-lived Instagram access tokens will last 3 months, but must be refreshing within 2 months of the date they're issued. If we forget to update the &lt;code&gt;INSTAGRAM_ACCESS_TOKEN&lt;/code&gt; secret on this repository within that time the &lt;code&gt;yarn build&lt;/code&gt; command will fail. To prevent this from happening we can create an additional GitHub Workflow that refreshes the Instagram access token and updates the corresponding repository secret on a schedule.&lt;/p&gt;

&lt;p&gt;In a "here's one I made earlier" fashion, I've already created GitHub Actions for &lt;a href="https://github.com/saulhardman/github-actions/tree/master/packages/refresh-instagram-access-token"&gt;refreshing Instagram access tokens&lt;/a&gt; and &lt;a href="https://github.com/saulhardman/github-actions/tree/master/packages/update-github-secret"&gt;updating GitHub secrets&lt;/a&gt;. Be sure to follow the installation instructions in those packages' respective READMEs. One thing to note is that a &lt;a href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token"&gt;Personal Access Token&lt;/a&gt; (PAT) with the 'repo' scope is required to create or update GitHub Secrets via the GitHub API.&lt;/p&gt;

&lt;p&gt;With the action packages installed and a PAT set to a secret named &lt;code&gt;PERSONAL_ACCESS_TOKEN&lt;/code&gt; we can now setup a scheduled GitHub Workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/instagram.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Refresh Instagram Access Token &amp;amp; Update GitHub Secret&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# https://crontab.guru/#0_0_1_*_*&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;instagram&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bahmutov/npm-install@v1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Refresh Instagram Access Token&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;instagram&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./node_modules/@saulhardman/refresh-instagram-access-token&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.INSTAGRAM_ACCESS_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update GitHub Secret&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./node_modules/@saulhardman/update-github-secret&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;secret_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;INSTAGRAM_ACCESS_TOKEN&lt;/span&gt;
          &lt;span class="na"&gt;secret_value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.instagram.outputs.access_token }}&lt;/span&gt;
          &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PERSONAL_ACCESS_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An bonus step that I often include in workflows like these is to send a &lt;a href="https://pushover.net/"&gt;Pushover&lt;/a&gt; notification on success or failure. Passing the access token as part of the success payload gives me the opportunity to update my local development &lt;code&gt;.env&lt;/code&gt; file, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on Netlify
&lt;/h2&gt;

&lt;p&gt;As far as I'm aware, Netlify doesn't have an API for managing environment variables (in the free tier, at least). If your project is private and you live life fast and dangerous you &lt;em&gt;could&lt;/em&gt; read and write an access token from and to a &lt;code&gt;.env&lt;/code&gt; file that you then commit back to the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;falti/dotenv-action@v0.2.4&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotenv&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Refresh Instagram Access Token&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;instagram&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./node_modules/@saulhardman/refresh-instagram-access-token&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.dotenv.outputs.instagram_access_token }}&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TickX/var-to-dotenv@v1.1.1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;INSTAGRAM_ACCESS_TOKEN&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.instagram.outputs.access_token }}&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.dotenv.outputs.instagram_access_token }}&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Commit Updated DotEnv&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EndBug/add-and-commit@v4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chore:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;refresh&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;instagram&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;access&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;token"&lt;/span&gt;
    &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.token }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Local Testing
&lt;/h2&gt;

&lt;p&gt;I often develop new Workflows on a feature branch and configure them to run on &lt;code&gt;push&lt;/code&gt; until they're ready to be merged into the primary branch. A less cumbersome option is to use &lt;a href="https://github.com/nektos/act"&gt;&lt;code&gt;act&lt;/code&gt;&lt;/a&gt; to debug Workflows locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Automation can be a double-edged sword, but for processes like this GitHub Workflows are a blessing for a front-end web developer working with the Jamstack. How do you manage access tokens? How are you using GitHub Actions to extend the Jamstack?&lt;/p&gt;

</description>
      <category>jamstack</category>
      <category>github</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Publishing and Installing Private GitHub Packages using Yarn and Lerna</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Tue, 19 May 2020 14:44:42 +0000</pubDate>
      <link>https://dev.to/saul/publishing-and-installing-private-github-packages-using-yarn-and-lerna-3802</link>
      <guid>https://dev.to/saul/publishing-and-installing-private-github-packages-using-yarn-and-lerna-3802</guid>
      <description>&lt;p&gt;I have a collection of snippets and utilities that I frequently reach for when building web stuff. Up until now this code has been managed in a very adhoc fashion – copied and pasted between codebases, un-versioned, and free from the burden of tests 😉&lt;/p&gt;

&lt;p&gt;The temptation is to publish these utilities, collectively or individually, on a package registry such as &lt;a href="https://npmjs.com"&gt;NPM&lt;/a&gt;. But, as rewarding and exhilarating as it can be to open source code, it does &lt;a href="https://news.ycombinator.com/item?id=23208779"&gt;have its downsides&lt;/a&gt;. In particular, publicly publishing a package can signal to other developers that it's production-ready and bring with it the apparent obligation of supporting its use. Alternatively, sometimes the code is sensitive in nature or is not yet mature enough to see the light of day.&lt;/p&gt;

&lt;p&gt;Publishing these packages privately is a good solution so long as it's economical and has an efficient enough workflow. To keep the organisational overhead low I'll keep them all in a single repository, following the monolithic repository pattern. (I can't help but feel "minilithic" would be a more appropriate name here.)&lt;/p&gt;

&lt;p&gt;NPM doesn't allow users to publish private packages for free, but the GitHub Package Registry does (with strings attached). Given &lt;a href="https://github.blog/2020-03-16-npm-is-joining-github/"&gt;GitHub's recent acquisition of NPM&lt;/a&gt; this might well change in the future 🤷‍♂️&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup the Mono-Repository
&lt;/h2&gt;

&lt;p&gt;I'll use my &lt;code&gt;nuxt-modules&lt;/code&gt; private GitHub repository, and the private packages within, as a working example.&lt;/p&gt;

&lt;p&gt;Let's get started... In a terminal of your choice create a new project directory and initialise Git and Yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;nuxt-modules
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;nuxt-modules
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git init
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable &lt;a href="https://classic.yarnpkg.com/en/docs/workspaces/"&gt;Yarn Workspaces&lt;/a&gt; by configuring the &lt;code&gt;"workspaces"&lt;/code&gt; property in &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nuxt-modules"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"packages/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialise &lt;a href="https://lerna.js.org/"&gt;Lerna&lt;/a&gt; with independent versioning enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; lerna init &lt;span class="nt"&gt;--independent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure Lerna to play-nice with Yarn Workspaces and target the GitHub Package Registry in &lt;code&gt;lerna.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"packages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"packages/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"independent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"npmClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"useWorkspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"publish"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"conventionalCommits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chore(release): publish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"registry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://npm.pkg.github.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowBranch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"master"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel free to customise the other properties, these are just my preferences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the Packages
&lt;/h2&gt;

&lt;p&gt;Populate the &lt;code&gt;packages/&lt;/code&gt; directory with a sub-directory for each package. The directory names shouldn't be prefixed with the scope, but the &lt;code&gt;name&lt;/code&gt; field in the &lt;code&gt;package.json&lt;/code&gt; should, e.g. &lt;code&gt;packages/nuxt-html-validate&lt;/code&gt; will contain a &lt;code&gt;package.json&lt;/code&gt; with the &lt;code&gt;name&lt;/code&gt; field set to &lt;code&gt;@saulhardman/nuxt-html-validate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can create packages using Lerna's &lt;code&gt;lerna create&lt;/code&gt; command or by hand. The bare-minimum for an NPM package is a JavaScript entry-point (e.g. &lt;code&gt;index.js&lt;/code&gt;) and a &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Development dependencies that are common to all of the packages should be installed in the mono-repository root. As an example, here's the command to install &lt;a href="https://eslint.org/"&gt;ESLint&lt;/a&gt;, passing the &lt;code&gt;-W&lt;/code&gt; argument to the &lt;code&gt;add&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; eslint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A critical step in this process is to run &lt;code&gt;yarn init&lt;/code&gt; within each of the directories. It's then necessary to make a minor adjustment to the resulting &lt;code&gt;package.json&lt;/code&gt; files to set the &lt;code&gt;repository.directory&lt;/code&gt; and &lt;code&gt;publishConfig.registry&lt;/code&gt; fields. Here is an example of the &lt;code&gt;@saulhardman/nuxt-html-validate&lt;/code&gt; package which is located in the &lt;code&gt;packages/nuxt-html-validate/&lt;/code&gt; sub-directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ssh://git@github.com/saulhardman/nuxt-modules.git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"directory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"packages/nuxt-html-validate"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"publishConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"registry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://npm.pkg.github.com/"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final result should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── .gitignore
├── LICENSE.md
├── lerna.json
├── package.json
├── packages
│   ├── nuxt-html-validate
│   │   ├── README.md
│   │   ├── index.js
│   │   └── package.json
│   ├── nuxt-release
│   │   ├── README.md
│   │   ├── index.js
│   │   └── package.json
│   ├── nuxt-robotize
│   │   ├── README.md
│   │   ├── index.js
│   │   └── package.json
│   └── nuxt-rss
│       ├── README.md
│       ├── index.js
│       └── package.json
└── yarn.lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authenticate with the GitHub Package Registry
&lt;/h2&gt;

&lt;p&gt;The next step is to authenticate with the Github Package Registry (replace &lt;code&gt;@saulhardman&lt;/code&gt; with &lt;strong&gt;your&lt;/strong&gt; GitHub username):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm login &lt;span class="nt"&gt;--registry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://npm.pkg.github.com &lt;span class="nt"&gt;--scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@saulhardman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To interact with the package repository API, GitHub requires you to create a &lt;a href="https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line"&gt;Personal Access Token&lt;/a&gt; (PAT) which you will use in-lieu of your password. Make sure that the 'repo', 'write:packages', 'read:packages', and 'delete:packages' options are selected:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4EGbommp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/viewsource-io/image/upload/w_1400%2Ch_564%2Cf_auto%2Cq_80/production/assets/images/github-personal-access-token-permissions.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4EGbommp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/viewsource-io/image/upload/w_1400%2Ch_564%2Cf_auto%2Cq_80/production/assets/images/github-personal-access-token-permissions.png" alt="Screenshot of GitHub generate Personal Access Token page with relevant options selected" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that in-hand the &lt;code&gt;.npmrc&lt;/code&gt; is configured to point requests for &lt;code&gt;@saulhardman&lt;/code&gt;-scoped packages to GitHub (rather than NPM) and provide the PAT as an &lt;code&gt;authToken&lt;/code&gt; (replace &lt;code&gt;TOKEN&lt;/code&gt; and &lt;code&gt;@saulhardman&lt;/code&gt; with &lt;strong&gt;your&lt;/strong&gt; respective values):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;//npm.pkg.github.com/:_authToken&lt;span class="o"&gt;=&lt;/span&gt;TOKEN
@saulhardman:registry&lt;span class="o"&gt;=&lt;/span&gt;https://npm.pkg.github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though this Git repository will be private it's good practice not to commit keys and tokens. Accordingly, be sure to amend the &lt;code&gt;.gitignore&lt;/code&gt; config to include the &lt;code&gt;.npmrc&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publish the Packages
&lt;/h2&gt;

&lt;p&gt;Create your private GitHub repository and push your initial commit containing your packages. It's my preference to set the &lt;code&gt;package.version&lt;/code&gt; fields to &lt;code&gt;0.0.0&lt;/code&gt; to begin with. At publish-time you can pass &lt;code&gt;minor&lt;/code&gt; or &lt;code&gt;major&lt;/code&gt; to have &lt;code&gt;0.1.0&lt;/code&gt; or &lt;code&gt;1.0.0&lt;/code&gt; be the initial release version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn lerna publish minor &lt;span class="c"&gt;# initial release 0.1.0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn lerna publish major &lt;span class="c"&gt;# initial release 1.0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've received a "Package published" response, you will be able to view your packages on the GitHub repository page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6LkeyTlh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/viewsource-io/image/upload/w_1400%2Ch_109%2Cf_auto%2Cq_80/production/assets/images/4-packages-published.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6LkeyTlh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/viewsource-io/image/upload/w_1400%2Ch_109%2Cf_auto%2Cq_80/production/assets/images/4-packages-published.png" alt="Screenshot of the GitHub repository page showing '4 packages' published" width="800" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Private GitHub Packages
&lt;/h2&gt;

&lt;p&gt;The permissions workflow surrounding private packages is... not great. There is, as far as I'm aware, no way to scope PATs to organisations, repositories, or packages. The method outlined here will allow you to install all private packages that your GitHub account has access to.&lt;/p&gt;

&lt;p&gt;To install a private package all that's required is an &lt;code&gt;.npmrc&lt;/code&gt; to assign an access token and configure the scopes. The PAT could be the same one used above or a different PAT with read-only permissions (replace &lt;code&gt;TOKEN&lt;/code&gt; with your PAT and &lt;code&gt;@saulhardman&lt;/code&gt; with your GitHub username):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;//npm.pkg.github.com/:_authToken&lt;span class="o"&gt;=&lt;/span&gt;TOKEN
@saulhardman:registry&lt;span class="o"&gt;=&lt;/span&gt;https://npm.pkg.github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only packages in the scope &lt;code&gt;@saulhardman&lt;/code&gt; will be installed from the GitHub Package Registry – all others will default to NPM. The &lt;code&gt;yarn add&lt;/code&gt; command can be used as usual, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn add @saulhardman/nuxt-html-validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing Private GitHub Packages from GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Setting the &lt;code&gt;NODE_AUTH_TOKEN&lt;/code&gt; environment variable on the &lt;code&gt;yarn install&lt;/code&gt; step &lt;em&gt;should&lt;/em&gt; be enough, but in my experience it is not. There is a thread on the &lt;a href="https://github.community/t5/GitHub-Actions/Installing-npm-packages-from-the-GitHub-package-registry/td-p/30559"&gt;GitHub Community Forum&lt;/a&gt; documenting a number of people's struggles.&lt;/p&gt;

&lt;p&gt;An alternative – whether you're running &lt;code&gt;yarn install&lt;/code&gt; directly or using a third-party action such as &lt;a href="https://github.com/bahmutov/npm-install"&gt;&lt;code&gt;bahmutov/npm-install&lt;/code&gt;&lt;/a&gt; – is to construct an &lt;code&gt;.npmrc&lt;/code&gt; dynamically using a PAT stored as an &lt;a href="https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets"&gt;encrypted secret&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure NPM&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;echo "//npm.pkg.github.com/:_authToken=$NODE_AUTH_TOKEN" &amp;gt; .npmrc&lt;/span&gt;
      &lt;span class="s"&gt;echo '@saulhardman:registry=https://npm.pkg.github.com' &amp;gt;&amp;gt; .npmrc&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_AUTH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NODE_AUTH_TOKEN }}&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Yarn Dependencies&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bahmutov/npm-install@v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;I've created a number of private packages over the last few months – ranging from the Nuxt modules outlined above to Vue components and JavaScript utilities. I've thoroughly enjoyed it so far and I feel the initial overhead will be well worth the reward in the long term.&lt;/p&gt;

&lt;p&gt;Discovering a bug in one usage context, fixing it, adding a test case if necessary, and then having that update trickle-down to other projects with very little friction is both satisfying and refreshing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-npm-for-use-with-github-packages#authenticating-with-a-personal-access-token"&gt;Configuring npm for use with GitHub Packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://charliejwalter.net/github-actions-with-private-package-github-package-registry-or-npm/"&gt;Github Actions with private package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jgierer12/how-to-publish-packages-to-the-github-package-repository-4bai"&gt;How to publish packages to the GitHub Package Registry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>lerna</category>
      <category>yarn</category>
      <category>npm</category>
    </item>
    <item>
      <title>Generate a Critical JavaScript Bundle Using Rollup</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Fri, 21 Feb 2020 15:52:00 +0000</pubDate>
      <link>https://dev.to/saul/generate-a-critical-javascript-bundle-using-rollup-16om</link>
      <guid>https://dev.to/saul/generate-a-critical-javascript-bundle-using-rollup-16om</guid>
      <description>&lt;p&gt;For performance and user experience reasons there are certain tasks that we want to complete before DOM content, stylesheets, or scripts have loaded. Critical JavaScript, inlined into &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; blocks in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of an HTML document, is a pattern for achieving this:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I am critical!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples of such tasks include running CSS and JavaScript feature tests, loading polyfills, and performing conditional logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Things Simple
&lt;/h2&gt;

&lt;p&gt;Given that Critical JavaScript is inlined directly into the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of &lt;em&gt;every&lt;/em&gt; HTML response, it's essential that we keep the bundle size small.&lt;/p&gt;

&lt;p&gt;A step towards this is keeping the code simple with respect to the JavaScript and DOM APIs used and in-turn disabling transpilation tools from doing anything other than syntax transformation (see below for instructions on how to configure &lt;a href="https://babeljs.io/"&gt;Babel&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Configuring &lt;a href="https://github.com/browserslist/browserslist"&gt;Browserslist&lt;/a&gt; to reflect your support matrix will allow for the use of complimentary tools such as &lt;a href="https://github.com/amilajack/eslint-plugin-compat"&gt;&lt;code&gt;eslint-plugin-compat&lt;/code&gt;&lt;/a&gt; to ensure that you're not using unsupported features in production.&lt;/p&gt;

&lt;p&gt;All of this means you can leverage modern ES6 syntax such as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions"&gt;Arrow Functions&lt;/a&gt;, but omit JavaScript APIs such as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;&lt;code&gt;Promise&lt;/code&gt;s&lt;/a&gt; and DOM APIs like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList"&gt;&lt;code&gt;Element.classList&lt;/code&gt;&lt;/a&gt;. However, the syntax and features available to you are entirely dependent on your own browser support matrix.&lt;/p&gt;

&lt;p&gt;If polyfilling a feature is unavoidable then I would recommend doing so by directly &lt;code&gt;import&lt;/code&gt;ing specific modules from &lt;a href="https://github.com/zloirock/core-js/tree/master/packages/core-js/modules"&gt;&lt;code&gt;core-js&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rollup
&lt;/h2&gt;

&lt;p&gt;There’s no 2-ways about it: &lt;a href="https://rollupjs.com"&gt;Rollup&lt;/a&gt; creates bundles that are simpler and smaller than those created by alternatives such as &lt;a href="https://webpack.js.org"&gt;Webpack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following is a starting point for how to best configure Rollup to output &lt;code&gt;critical.js&lt;/code&gt; bundles with the smallest footprint:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// rollup.config.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rollup/plugin-node-resolve&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;commonJs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rollup/plugin-commonjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;replace&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rollup/plugin-replace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;babel&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rollup-plugin-babel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;terser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rollup-plugin-terser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;filesize&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rollup-plugin-filesize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isProduction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ANALYZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ANALYZE&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ANALYZE&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;critical.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;critical.min.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;iife&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sourcemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isProduction&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;process.env.NODE_ENV&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;

    &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;

    &lt;span class="nf"&gt;commonJs&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node_modules/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;

    &lt;span class="nf"&gt;babel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node_modules/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;babelrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@babel/preset-env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ANALYZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;useBuiltIns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;

    &lt;span class="nx"&gt;isProduction&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;terser&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="nx"&gt;isProduction&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;filesize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is just an example of some of the tasks that you might perform via &lt;code&gt;critical.js&lt;/code&gt;. It's by no means comprehensive, but it's a good example of the purpose of this category of JavaScript bundle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// critical.js&lt;/span&gt;
&lt;span class="c1"&gt;// polyfill asynchronously loading CSS from `&amp;lt;link rel="preload" /&amp;gt;` stylesheets&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fg-loadcss/src/cssrelpreload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// find out more about `font-display` here:&lt;/span&gt;
&lt;span class="c1"&gt;// https://css-tricks.com/font-display-masses/#article-header-id-3&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;supportsFontDisplay&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/utilities/supports/font-display&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;htmlClassNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supportsFontDisplay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;htmlClassNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;supports-font-display&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// overwrites any existing classes on the `&amp;lt;html&amp;gt;` element (e.g. `"no-js"`)&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;htmlClassNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern is especially useful when used in conjunction with custom web fonts in order to control &lt;a href="https://css-tricks.com/fout-foit-foft/"&gt;FOUT, FOIT, and FOFT&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inlining the Script
&lt;/h2&gt;

&lt;p&gt;The final step, and the one that I can write the least about, is inlining the compiled &lt;code&gt;critical.min.js&lt;/code&gt; into your HTML. The mechanism for this will vary drastically depending on your technology stack and deployment processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Field Notes
&lt;/h2&gt;

&lt;p&gt;Are you currently using this approach in your sites and applications? If so, how has your experience been so far? Do you plan to implement Critical JavaScript in the future? What kinds of tasks do you think this is suited to?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webperf</category>
      <category>javascript</category>
      <category>rollup</category>
    </item>
    <item>
      <title>Better Focus Indicators Using the `:focus-visible` Pseudo-Class</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Thu, 13 Feb 2020 06:51:52 +0000</pubDate>
      <link>https://dev.to/saul/better-focus-indicators-using-the-focus-visible-pseudo-class-44k4</link>
      <guid>https://dev.to/saul/better-focus-indicators-using-the-focus-visible-pseudo-class-44k4</guid>
      <description>&lt;p&gt;It used to be common practice in web design to "nuke from orbit" the default (and admittedly somewhat jarring) browser styles of the &lt;code&gt;:focus&lt;/code&gt; pseudo-class selector.&lt;/p&gt;

&lt;p&gt;Even popular CSS styling resets included rules such as this:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* remember to define focus styles! */&lt;/span&gt;
&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Speaking from experience, it was often the case that when designers were conducting a review they would express distaste when clicking on links and buttons left lingering dotted outlines and glowing gradients.&lt;/p&gt;

&lt;p&gt;Don't get me wrong, I'm not trying to pass the buck here. None of us were aware of how important these focus indicators were to assistive technologies, but it's less forgivable in Today's burgeoning culture of inclusivity and accessibility – and rightly so.&lt;/p&gt;

&lt;p&gt;Despite changing our ways – ensuring that &lt;code&gt;:focus&lt;/code&gt; styles got the same attention as &lt;code&gt;:hover&lt;/code&gt; and its siblings – the question of whether a tap or a click should &lt;code&gt;.focus()&lt;/code&gt; an element still remained. There was no practical way to differentiate between this focus scenario and that of an assistive tool or keyboard navigation, until now...&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:focus-visible&lt;/code&gt; is a new &lt;a href="https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo"&gt;CSS pseudo-class&lt;/a&gt; selector that's currently in the draft stages.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;:focus-visible&lt;/code&gt; pseudo-class applies while an element matches the &lt;code&gt;:focus&lt;/code&gt; pseudo-class and the user agent determines via heuristics that the focus should be made evident on the element.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;gold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;outline-offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;:focus:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As &lt;code&gt;:focus-visible&lt;/code&gt; is only in the draft stages, and support is currently &lt;a href="https://caniuse.com/#search=focus-visible"&gt;particularly poor&lt;/a&gt;, it's necessary to polyfill the behaviour using JavaScript.&lt;/p&gt;

&lt;p&gt;Ideally, we'd author styles as if the feature was supported and have a &lt;a href="https://github.com/WICG/focus-visible"&gt;JavaScript module&lt;/a&gt; and &lt;a href="https://github.com/jonathantneal/postcss-focus-visible"&gt;PostCSS plugin&lt;/a&gt; polyfill the behaviour and transpile the syntax respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; focus-visible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// an entry point in your application&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;focus-visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; postcss-focus-visible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// postcss.config.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postcss-focus-visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I'd recommend configuring this plugin in the context of &lt;a href="https://preset-env.cssdb.org/"&gt;PostCSS Preset Env&lt;/a&gt; to tie this transformation to a &lt;a href="https://github.com/browserslist/browserslist"&gt;Browserslist&lt;/a&gt; support configuration.&lt;/p&gt;

&lt;p&gt;There is one thing to keep in mind when using the &lt;code&gt;:focus-visible&lt;/code&gt; pseudo-class selector; the &lt;a href="https://stackoverflow.com/a/38873378/3081147"&gt;CSS specification&lt;/a&gt; dictates that browsers nullify entire rules that contain unknown or invalid selectors. Accordingly, we must create separate rules for all &lt;code&gt;:focus-visible&lt;/code&gt; styles (at least until browser support improves):-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* invalid */&lt;/span&gt;
&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* valid */&lt;/span&gt;
&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another issue to be aware of is how these styles are handled by CSS compression tools. &lt;a href="https://cssnano.co/"&gt;&lt;code&gt;cssnano&lt;/code&gt;&lt;/a&gt;, in particular, is not yet aware that merging rules with invalid selectors is not a safe transformation to make (see &lt;a href="https://github.com/cssnano/cssnano/issues/642"&gt;this GitHub issue&lt;/a&gt; for updates). Until this issue is resolved it's possible to disable the &lt;code&gt;mergeRules&lt;/code&gt; option:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// postcss.config.js&lt;/span&gt;
&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cssnano&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;
      &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;mergeRules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, if &lt;a href="https://purgecss.com/"&gt;PurgeCSS&lt;/a&gt; is part of your build pipeline, it's necessary to whitelist selectors that include the class name that's dynamically added by the polyfill:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// purgecss.config.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;whitelistPatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;focus-visible/&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;whitelistPatternsChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;focus-visible/&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>html</category>
      <category>css</category>
      <category>a11y</category>
      <category>postcss</category>
    </item>
    <item>
      <title>Authenticate with IndieAuth.com using PGP and Keybase</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Thu, 09 Jan 2020 12:01:01 +0000</pubDate>
      <link>https://dev.to/saul/authenticate-with-indieauth-com-using-pgp-and-keybase-29jg</link>
      <guid>https://dev.to/saul/authenticate-with-indieauth-com-using-pgp-and-keybase-29jg</guid>
      <description>&lt;p&gt;&lt;a href="https://indieauth.com/"&gt;IndieAuth.com&lt;/a&gt; is a public instance of &lt;a href="https://indieauth.net"&gt;IndieAuth&lt;/a&gt;, the decentralised identity protocol built on top of OAuth 2.0. In this article I'm going to explain how to authenticate yourself using your domain and a set of PGP keys handled by &lt;a href="https://keybase.io"&gt;Keybase&lt;/a&gt;. Once you've successfully logged into IndieAuth.com you'll be able to use the same method to authenticate with &lt;em&gt;any&lt;/em&gt; IndieAuth service.&lt;/p&gt;

&lt;p&gt;There are many methods of authenticating with IndieAuth, but PGP feels the most "Indie". I manage my keys using &lt;a href="https://keybase.io/saulhardman"&gt;Keybase&lt;/a&gt; which conveniently hosts your &lt;a href="https://keybase.io/saulhardman/pgp_keys.asc"&gt;public PGP keys in ASCII format&lt;/a&gt;. I'd initially presumed that IndieAuth would require keys to be hosted on the domain being used to login, but thankfully that's not the case and so keys hosted remotely on services such as Keybase can be used directly too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating a PGP Key Using Keybase
&lt;/h2&gt;

&lt;p&gt;Once you have &lt;a href="https://keybase.io/download"&gt;Keybase Desktop&lt;/a&gt; installed, open up a terminal of your choice and run the following command and follow the steps to generate a PGP key:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; keybase pgp gen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've chosen to generate a new key, alongside an existing key, then you'll need to include the &lt;code&gt;--multi&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;Once that process is complete, you should now be able to access a hosted copy of your public PGP key at the following URL:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# NOTE: replace `${YOUR_USERNAME}` with *your* Keybase username&lt;/span&gt;
&lt;span class="s"&gt;https://keybase.io/${YOUR_USERNAME}/pgp_keys.asc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have multiple PGP keys associated with your Keybase account, you can select an individual key by setting the &lt;code&gt;fingerprint&lt;/code&gt; parameter:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# NOTE: run `keybase pgp list` to reveal additional information about existing keys&lt;/span&gt;
&lt;span class="s"&gt;https://keybase.io/${YOUR_USERNAME}/pgp_keys.asc?fingerprint=${YOUR_KEY_FINGERPRINT}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Link to the Keys
&lt;/h2&gt;

&lt;p&gt;Add the following &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your HTML document:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- NOTE: replace `${YOUR_USERNAME}` with *your* Keybase username --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
  &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"pgpkey"&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/pgp-keys"&lt;/span&gt;
  &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://keybase.io/${YOUR_USERNAME}/pgp_keys.asc"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify that your configuration is working, and on each subsequent IndieAuth login, you will be provided with a snippet of text that you're required to sign using your PGP key. Navigate to the &lt;a href="https://indieauth.com"&gt;IndieAuth.com&lt;/a&gt; site and enter and submit your domain name to the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; in the "Try it!" section:-&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aOxYkD-D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/viewsource-io/image/upload/w_1060%2Ch_610%2Cf_auto%2Cq_80/production/assets/images/indieauth-com-try-it.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aOxYkD-D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/viewsource-io/image/upload/w_1060%2Ch_610%2Cf_auto%2Cq_80/production/assets/images/indieauth-com-try-it.png" alt="A Screenshot of the 'Try It!' Form on IndieAuth.com" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To do that with Keybase, open the command line and (after ensuring that you have &lt;a href="https://keybase.io/download"&gt;Keybase Desktop&lt;/a&gt; installed) run the following command, replacing the placeholder text within the single quotes with the challenge text:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; keybase pgp sign &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'INSERT CHALLENGE TEXT HERE'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're on macOS, you can pipe the output to &lt;code&gt;pbcopy&lt;/code&gt; so that you're ready to paste the signed text back into the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; and submit right away, like so:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; keybase pgp sign &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'INSERT CHALLENGE TEXT HERE'&lt;/span&gt; | pbcopy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So there you have it, you should now able to authenticate with IndieAuth services using your very own domain and a set of PGP keys.&lt;/p&gt;

</description>
      <category>indieauth</category>
      <category>indieweb</category>
      <category>pgp</category>
      <category>keybase</category>
    </item>
    <item>
      <title>Lazy Loading Images in Nuxt</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Wed, 08 Jan 2020 06:42:03 +0000</pubDate>
      <link>https://dev.to/saul/lazy-loading-images-in-nuxt-25bi</link>
      <guid>https://dev.to/saul/lazy-loading-images-in-nuxt-25bi</guid>
      <description>&lt;p&gt;The lazy loading of images via the &lt;a href="https://web.dev/native-lazy-loading/"&gt;&lt;code&gt;loading&lt;/code&gt; attribute&lt;/a&gt; has landed in Chrome, and other browser-vendors are sure to follow suit. Deferring to the browser when support is available and otherwise loading a polyfill such as &lt;a href="https://github.com/aFarkas/lazysizes"&gt;Lazysizes&lt;/a&gt; is a solid approach to performant, responsive images.&lt;/p&gt;

&lt;p&gt;Checking the &lt;code&gt;HTMLImageElement&lt;/code&gt; for the &lt;code&gt;loading&lt;/code&gt; property is a reliable way to test for native lazy loading support:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supportsLoadingAttribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;HTMLImageElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the browser supports native image loading we do nothing, or else we &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports"&gt;dynamically &lt;code&gt;import()&lt;/code&gt;&lt;/a&gt; the Lazysizes module. Authoring this code within a client-side only &lt;a href="https://nuxtjs.org/guide/plugins/"&gt;Nuxt plugin&lt;/a&gt; means the polyfill loads and initialises only once and within the context of the entire application:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ~/plugins/lazysizes.client.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;HTMLImageElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lazysizes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is a loosely outlined &lt;code&gt;ResponsiveImage&lt;/code&gt; component which follows the pattern that I want to demonstrate.&lt;/p&gt;

&lt;p&gt;The server-side rendered HTML contains an image with the &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;srcset&lt;/code&gt; values assigned to &lt;code&gt;data-*&lt;/code&gt; attributes – the actual attributes contain placeholders. On &lt;code&gt;mount()&lt;/code&gt; (a client-side only &lt;a href="https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks"&gt;Vue lifecycle hook&lt;/a&gt;) if the browser supports the &lt;code&gt;loading&lt;/code&gt; attribute the placeholders are replaced by the true &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;srcset&lt;/code&gt; values. If support is absent then the class &lt;code&gt;'lazyload'&lt;/code&gt; is added to the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; and Lazysizes takes over from there:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ~/components/ResponsiveImage.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
    &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ lazyload: loading === 'lazy' &amp;amp;&amp;amp; !supportsLoadingAttribute }"&lt;/span&gt;
    &lt;span class="na"&gt;:loading=&lt;/span&gt;&lt;span class="s"&gt;"loading"&lt;/span&gt;
    &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"{ ...sources }"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// base64-encoded transparent GIF&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;placeholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// the props required to compute `srcset` should go here&lt;/span&gt;

      &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lazy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;supportsLoadingAttribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;src&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// `return` a fallback image for browsers&lt;/span&gt;
        &lt;span class="c1"&gt;// that don't support `srcset` and `sizes`&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="nf"&gt;srcset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// responsive images can be handled in all sorts of&lt;/span&gt;
        &lt;span class="c1"&gt;// ways and I won't go into any further detail here&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="nf"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lazy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supportsLoadingAttribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data-src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data-srcset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

            &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;srcset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 1w`&lt;/span&gt;
          &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;srcset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcset&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="nf"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supportsLoadingAttribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;HTMLImageElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's are many different approaches to lazy-loading images on the web. Each has its advantages and disadvantages and the one you choose will ultimately depend on your priorities. Are you more concerned about SEO, page speed, data footprint, or browser compatibility?&lt;/p&gt;

&lt;p&gt;The pattern outlined above, for example, would need to provide a &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; fallback in the case of JavaScript being disabled.&lt;/p&gt;

&lt;p&gt;Either way, hopefully this has started you off in the right direction. Check out the links below for some more in-depth explanations of the &lt;code&gt;loading&lt;/code&gt; attribute and lazy-loading markup patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aFarkas/lazysizes#recommendedpossible-markup-patterns"&gt;Lazysizes Recommended Markup Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://addyosmani.com/blog/lazy-loading/"&gt;Native Image Lazy-Loading for the Web!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vue</category>
      <category>nuxt</category>
      <category>webperf</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Verifying a Nuxt Site on Flattr</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Mon, 06 Jan 2020 15:51:29 +0000</pubDate>
      <link>https://dev.to/saul/verifying-a-nuxt-site-on-flattr-b66</link>
      <guid>https://dev.to/saul/verifying-a-nuxt-site-on-flattr-b66</guid>
      <description>&lt;p&gt;Out of the box, Nuxt sites cannot be used to verify domains on &lt;a href="https://flattr.com/"&gt;Flattr&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The only way to verify a domain with the service is to include a &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of &lt;em&gt;all&lt;/em&gt; the pages on your site. The Flattr verification service expects this tag to have 2 attributes and 2 attributes &lt;strong&gt;only&lt;/strong&gt;:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"flattr:id"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"abc123"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason that this doesn't play nice with Nuxt is due to &lt;code&gt;data-n-head&lt;/code&gt; attributes being added to all &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags. The solution is to hook into specific Nuxt events and modify the HTML appropriately:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cheerio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeDataAttributeFromFlattrMetaTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meta[name="flattr:id"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;removeAttr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data-n-head&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flattr:id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;removeDataAttributeFromFlattrMetaTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;removeDataAttributeFromFlattrMetaTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stripping the &lt;code&gt;data-n-head&lt;/code&gt; attributes from the &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags in this way is enough to appease the Flattr verification service. I have spoken with the support team at Flattr and brought this issue to their attention, but this workaround will suffice for now.&lt;/p&gt;

&lt;p&gt;This attribute serves some purpose in the handover between Nuxt SSR and client-side hydration and originates from the &lt;a href="https://github.com/nuxt/vue-meta"&gt;Vue Meta plugin&lt;/a&gt;. I presume that removing the attribute in the manner outlined above doesn't cause any issues – especially since this attribute won't be updated elsewhere. But, if you're looking for an alternative, you can add the &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag to the &lt;a href="https://nuxtjs.org/guide/views#app-template"&gt;app template&lt;/a&gt; instead:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ~/app.html --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;HTML_ATTRS&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;HEAD_ATTRS&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"flattr:id"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"abc123"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    {{ HEAD }}
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;BODY_ATTRS&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {{ APP }}
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: After getting to know &lt;a href="https://vue-meta.nuxtjs.org/"&gt;Vue Meta&lt;/a&gt; a little better, I discovered the &lt;a href="https://vue-meta.nuxtjs.org/api/#once"&gt;&lt;code&gt;once&lt;/code&gt;&lt;/a&gt; special &lt;code&gt;metaInfo&lt;/code&gt; attribute. This disables reactivity and, in the case of Nuxt, only outputs the tag during server-side rendering (without &lt;code&gt;data-*&lt;/code&gt; attributes):-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flattr:id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;once&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>nuxt</category>
      <category>flattr</category>
      <category>verification</category>
    </item>
    <item>
      <title>Configuring PurgeCSS for Use With Nuxt</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Mon, 06 Jan 2020 11:32:56 +0000</pubDate>
      <link>https://dev.to/saul/configuring-purgecss-for-use-with-nuxt-448l</link>
      <guid>https://dev.to/saul/configuring-purgecss-for-use-with-nuxt-448l</guid>
      <description>&lt;p&gt;&lt;a href="https://www.purgecss.com/"&gt;PurgeCSS&lt;/a&gt; is a tool for removing unused CSS. It achieves this by cross-checking the compiled CSS with a list of selectors that are extracted from the content files (e.g. HTML files, JavaScript modules, and Vue components).&lt;/p&gt;

&lt;p&gt;Using PurgeCSS out of the box with Nuxt resulted in styles included via &lt;code&gt;@import&lt;/code&gt; statements being correctly purged – both from 1st- and 3rd-party sources. However, due to the configuration of the default &lt;a href="https://www.purgecss.com/extractors"&gt;extractors&lt;/a&gt;, styles authored within Vue component &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks were incorrectly preserved.&lt;/p&gt;

&lt;p&gt;Configuring the &lt;a href="https://github.com/FullHuman/postcss-purgecss"&gt;PurgeCSS PostCSS plugin&lt;/a&gt; as follows remedies the problem:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;postcss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@fullhuman/postcss-purgecss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;extractors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;extractor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;content&lt;/span&gt;
                  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;style&lt;/span&gt;&lt;span class="se"&gt;[\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;[\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;style&amp;gt;/gi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\w&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;:&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;

              &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally this extraction behaviour would be consolidated within a tested and maintained package such as &lt;code&gt;purge-from-vue&lt;/code&gt;. That way it could be used in all Vue-based projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt; (2020-03-21): Reflect change to extractor format in &lt;a href="https://github.com/FullHuman/purgecss/releases/tag/v2.0.5"&gt;PurgeCSS &lt;code&gt;v2.0&lt;/code&gt; release&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>purgecss</category>
      <category>nuxt</category>
      <category>css</category>
      <category>performance</category>
    </item>
    <item>
      <title>How to Configure Yarn on Netlify</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Mon, 22 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/saul/how-to-configure-yarn-on-netlify-14bl</link>
      <guid>https://dev.to/saul/how-to-configure-yarn-on-netlify-14bl</guid>
      <description>&lt;p&gt;&lt;a href="https://www.netlify.com/products/build/"&gt;Netlify Build&lt;/a&gt; is a great platform for running static site builds in conjunction with your Git workflow and Headless CMS content updates (via Webhooks). It makes running cloud-based builds of JavaScript Static Site Generators very slick, but it's not without its pitfalls.&lt;/p&gt;

&lt;p&gt;Many of the popular generators use Yarn as a package manager which can be configured via Netlify's &lt;a href="https://www.netlify.com/docs/netlify-toml-reference/"&gt;&lt;code&gt;netlify.toml&lt;/code&gt;&lt;/a&gt; configuration file, like so:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# netlify.toml&lt;/span&gt;

&lt;span class="nn"&gt;[build.environment]&lt;/span&gt;
  &lt;span class="py"&gt;YARN_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.17.3"&lt;/span&gt;
  &lt;span class="py"&gt;NODE_ENV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"production"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: the "Yarn approved" method of specifying the required version is via the &lt;a href="https://yarnpkg.com/lang/en/docs/cli/policies/"&gt;&lt;code&gt;yarn policies set-version&lt;/code&gt; command&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Running the build step in an environment other than local development had me wondering whether to install packages as &lt;code&gt;dependencies&lt;/code&gt; or &lt;code&gt;devDependencies&lt;/code&gt;. With the environment variable &lt;code&gt;NODE_ENV&lt;/code&gt; set to &lt;code&gt;"production"&lt;/code&gt; Yarn will &lt;em&gt;only&lt;/em&gt; install dependencies specified in the &lt;code&gt;dependencies&lt;/code&gt; section of the &lt;code&gt;package.json&lt;/code&gt; file, and not those within &lt;code&gt;devDependencies&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Perhaps &lt;code&gt;NODE_ENV&lt;/code&gt; could be set to &lt;code&gt;"production"&lt;/code&gt; within the scope of the build command? For example:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NODE_ENV=production ..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cleanest solution (in my humble opinion) is to set the &lt;a href="https://yarnpkg.com/lang/en/docs/cli/install/#toc-yarn-install-production-true-false"&gt;&lt;code&gt;--production&lt;/code&gt; flag&lt;/a&gt; to &lt;code&gt;false&lt;/code&gt;, like so:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# netlify.toml&lt;/span&gt;

&lt;span class="nn"&gt;[build.environment]&lt;/span&gt;
  &lt;span class="py"&gt;YARN_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.17.3"&lt;/span&gt;
  &lt;span class="py"&gt;YARN_FLAGS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"--production&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  &lt;span class="py"&gt;NODE_ENV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"production"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find out more about configuring build environment variables within the &lt;a href="https://www.netlify.com/docs/build-settings/#build-environment-variables"&gt;Netlify documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>yarn</category>
      <category>netlify</category>
    </item>
    <item>
      <title>How the Netlify Build Queue Works</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Mon, 20 Nov 2017 00:00:00 +0000</pubDate>
      <link>https://dev.to/saul/how-the-netlify-build-queue-works-4m1a</link>
      <guid>https://dev.to/saul/how-the-netlify-build-queue-works-4m1a</guid>
      <description>&lt;p&gt;I’m using &lt;a href="https://netlify.com"&gt;Netlify&lt;/a&gt; in conjunction with &lt;a href="https://contentful.com"&gt;Contentful&lt;/a&gt; and have configured a &lt;a href="https://www.contentful.com/developers/docs/concepts/webhooks/"&gt;Contentful Outgoing Webhook&lt;/a&gt; to trigger a &lt;a href="https://docs.netlify.com/configure-builds/build-hooks/"&gt;Netlify Build Hook&lt;/a&gt; when content is updated. This got me wondering whether or not Netlify operates a build queue and, if so, how it works.&lt;/p&gt;

&lt;p&gt;There is (at the time of writing) no formal documentation on this topic, so I hopped onto the &lt;a href="https://gitter.im/netlify/community"&gt;Netlify Gitter conversation&lt;/a&gt; to see what I could find out. Thankfully &lt;a href="https://github.com/biilmann"&gt;Mathias Biilmann&lt;/a&gt; (the founder of Netlify, no less) was on hand to enlighten me.&lt;/p&gt;

&lt;p&gt;It turns out they does use a build queue and this is how it operates:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We’ll start the first one, and then once that’s done, our system will mark all the intermediary builds as “Skipped” and then run the last one&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thankfully this is the logic that I was hoping for 🎉&lt;/p&gt;

&lt;p&gt;In the case of the &lt;a href="https://www.contentful.com/developers/docs/references/content-preview-api/"&gt;Contentful Content Preview API&lt;/a&gt;, the webhooks can be triggered fairly frequently (up to 3 times a minute) whilst content is being edited. My worry was that this was creating a race condition that &lt;em&gt;could&lt;/em&gt; result in stale content winning out over fresh content. Thankfully these worries were unfounded.&lt;/p&gt;

</description>
      <category>netlify</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Using `vuex-router-sync` with Vuex Modules and Nuxt</title>
      <dc:creator>Saul Hardman</dc:creator>
      <pubDate>Tue, 14 Nov 2017 00:00:00 +0000</pubDate>
      <link>https://dev.to/saul/using-vuex-router-sync-with-vuex-modules-and-nuxt-3obn</link>
      <guid>https://dev.to/saul/using-vuex-router-sync-with-vuex-modules-and-nuxt-3obn</guid>
      <description>&lt;p&gt;The scenario is relatively straight-forward: we have a Vuex module located in &lt;code&gt;~/store/projects.js&lt;/code&gt; and we'd like to define a getter called &lt;code&gt;currentProject&lt;/code&gt; which &lt;code&gt;.find()&lt;/code&gt;s a project based on the current route params.&lt;/p&gt;

&lt;p&gt;We'll start by installing the &lt;a href="https://github.com/vuejs/vuex-router-sync"&gt;&lt;code&gt;vuex-router-sync&lt;/code&gt;&lt;/a&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn add vuex-router-sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then define a Nuxt.js &lt;a href="https://nuxtjs.org/guide/plugins"&gt;Plugin&lt;/a&gt; to &lt;code&gt;sync()&lt;/code&gt; the store and the router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ~/plugins/vuex-router-sync.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vuex-router-sync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Sourced from this &lt;a href="https://github.com/nuxt/nuxt.js/issues/213"&gt;GitHub issue&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;For this to be run during initialisation we need to declare it in our Nuxt.js config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/plugins/vuex-router-sync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module 'route' is now available in the store and is kept in sync with the &lt;code&gt;vue-router&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;Returning to our 'projects' Vuex module, we are now able to define a getter that &lt;code&gt;.find()&lt;/code&gt;s a project based on the current route parameter &lt;code&gt;id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ~/store/projects.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;currentProject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;rootState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>nuxt</category>
      <category>vuex</category>
      <category>modules</category>
      <category>vuexroutersync</category>
    </item>
  </channel>
</rss>
