<?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: Paul Walker</title>
    <description>The latest articles on DEV Community by Paul Walker (@arafel).</description>
    <link>https://dev.to/arafel</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%2F3471%2F154439.png</url>
      <title>DEV Community: Paul Walker</title>
      <link>https://dev.to/arafel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arafel"/>
    <language>en</language>
    <item>
      <title>Making a subset of Font Awesome</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Tue, 17 May 2022 21:07:00 +0000</pubDate>
      <link>https://dev.to/arafel/making-a-subset-of-font-awesome-30jo</link>
      <guid>https://dev.to/arafel/making-a-subset-of-font-awesome-30jo</guid>
      <description>&lt;p&gt;&lt;a href="https://fontawesome.com/"&gt;Font Awesome&lt;/a&gt; is, well, awesome - but there's no getting away from the fact that if you only want a few of the icons then it's not a lightweight package.&lt;/p&gt;

&lt;p&gt;Looking around for something to solve this with I found &lt;a href="https://github.com/omacranger/"&gt;Logan Graham&lt;/a&gt; has written a nice JavaScript package to solve this, called - appropriately enough - &lt;a href="https://github.com/omacranger/fontawesome-subset"&gt;fontawesome-subset&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That will do the work of extracting the icons you want and repackaging them back into font files, but you still need the CSS files to use them.&lt;/p&gt;

&lt;p&gt;It only works with Font Awesome 5.x - if you want to use 6 then it looks like you need to use the &lt;a href="https://fontawesome.com/plans"&gt;Font Awesome Pro kits&lt;/a&gt;, or &lt;a href="https://github.com/omacranger/fontawesome-subset/issues/16"&gt;pull up the issue on Github&lt;/a&gt; and have a crack at it. :-)&lt;/p&gt;

&lt;p&gt;After a couple of times trying to deploy this on a website and forgetting to copy or run &lt;em&gt;something&lt;/em&gt; I decided that it was probably time to invest in setting up a task runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which runner?
&lt;/h2&gt;

&lt;p&gt;I’m sure there are more, but the ones I’ve seen are &lt;a href="https://gulpjs.com"&gt;Gulp&lt;/a&gt; and &lt;a href="https://gruntjs.com"&gt;Grunt&lt;/a&gt;. I needed some way to decide which to go for, and the GitHub repos actually made that fairly simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gulp
&lt;/h3&gt;

&lt;p&gt;Gulp hasn’t been updated since mid-2021, the CI test is failing, and it &lt;a href="https://github.com/gulpjs/gulp/issues/2654"&gt;doesn’t support Node &amp;gt;= 14&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grunt
&lt;/h3&gt;

&lt;p&gt;Grunt was last updated 3 days ago (at the time of writing), and has passing CI tests.&lt;/p&gt;

&lt;p&gt;So, Grunt it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo webpage
&lt;/h2&gt;

&lt;p&gt;Just to note this post isn’t going to walk you through actually using FA - that’s not the focus here. So if the classes below don’t make sense to you, head over to the FA docs.&lt;/p&gt;

&lt;p&gt;Here’s the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;form&amp;gt;
      &amp;lt;label for="email"&amp;gt;
        &amp;lt;i class="fas fa-envelope"&amp;gt;&amp;lt;/i&amp;gt;
        Email
      &amp;lt;/label&amp;gt;
      &amp;lt;input type="email" id="email"/&amp;gt;
      &amp;lt;label for="password"&amp;gt;
        &amp;lt;i class="fas fa-lock"&amp;gt;&amp;lt;/i&amp;gt;
        Password
      &amp;lt;/label&amp;gt;
      &amp;lt;input type="password" id="password"/&amp;gt;
      &amp;lt;button type="submit"&amp;gt;Click me&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right now we don’t see any icons - we haven’t done the subsetting yet, and we don’t have any CSS files included.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0ubkTqSy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2022/05/no-icons.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0ubkTqSy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2022/05/no-icons.png" alt="Email and Password field from a webpage, no icons visible." width="880" height="64"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before we load any CSS, no icons to be seen&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We’ll use the &lt;code&gt;livereloadx&lt;/code&gt; package for serving the content, since we don’t have to write any code to use it. We’ll add the command to the scripts section in &lt;code&gt;package.json&lt;/code&gt;, to make it easy to run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
    "serve": "node ./node_modules/.bin/livereloadx -s ."
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to subset the files
&lt;/h2&gt;

&lt;p&gt;After all, if we can't work this out there's not much point in doing the rest. Luckily, &lt;a href="https://github.com/omacranger/fontawesome-subset#usage"&gt;the usage section of the README&lt;/a&gt; makes it fairly easy to work out.&lt;/p&gt;

&lt;p&gt;Import the &lt;code&gt;fontawesomeSubset&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { fontawesomeSubset } = require("fontawesome-subset");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we call it with two arguments. The first is a name, or array of names, for the icons that we want (without the &lt;code&gt;fa-&lt;/code&gt; prefix). The second is where it should write the font files it's going to produce.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fontawesomeSubset([
  'envelope',
  'lock',
],
'static/webfonts');

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And indeed, if we run that we can see it works, or at least produces some output. The output files are a fraction of the size of the source files, which is encouraging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node fa_subset.js
$ fd . -t f static/
static/webfonts/fa-solid-900.eot
static/webfonts/fa-solid-900.svg
static/webfonts/fa-solid-900.ttf
static/webfonts/fa-solid-900.woff
static/webfonts/fa-solid-900.woff2
$ du -hs static/webfonts/
 20K    static/webfonts/

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Getting Grunt setup
&lt;/h2&gt;

&lt;p&gt;First let's get Grunt to the point where it runs successfully, even if it doesn’t do anything.&lt;/p&gt;

&lt;p&gt;Grunt config can live in various places, but I’m choosing to put it in &lt;code&gt;Gruntfile.js&lt;/code&gt; to keep it separated. The simplest config is as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = function (grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json")
  })

  // We'll load any plugins here.

  // 'default' is run if you run Grunt without arguments.
  grunt.registerTask('default', []);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the ‘default’ task being registered even though it doesn’t do anything. As well as being a good placeholder, it also means if you just run &lt;code&gt;grunt&lt;/code&gt; with no arguments it won’t produce an error. We’ll come back to that later.&lt;/p&gt;

&lt;p&gt;And with that, it's alive - though admittedly it doesn’t say much.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ grunt

Done.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Copying the files
&lt;/h3&gt;

&lt;p&gt;To use the web fonts, we’ll use the CSS files from the Font Awesome package. They should automatically load the webfont files that we’ve extracted. Just a reminder - this only works with Font Awesome 5.x, not 6.x.&lt;/p&gt;

&lt;p&gt;There’s a &lt;code&gt;grunt-contrib-copy&lt;/code&gt; package which teaches Grunt how to copy files. The first thing after installing it is to load it in the Grunt setup function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grunt.loadNpmTasks('grunt-contrib-copy');

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From &lt;a href="https://gruntjs.com/configuring-tasks#building-the-files-object-dynamically"&gt;the Grunt docs&lt;/a&gt; we can see that we need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;expand&lt;/code&gt; option, to enable the other options&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;flatten&lt;/code&gt; option, to flatten the destination results to a single level.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src&lt;/code&gt;, to list the source files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dest&lt;/code&gt;, to tell Grunt where to put the copied files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From that we end up with a config object like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grunt.initConfig({
  pkg: grunt.file.readJSON("package.json"),
  copy: {
    main: {
      files: [
        {
          expand: true,
          flatten: true,
          src: [
            'node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css',
            'node_modules/@fortawesome/fontawesome-free/css/solid.min.css'
          ],
          dest: 'static/css/'
        },
      ]
    }
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run that, and it says it's copied two files. Let's just check it's the right two files, and they're where we expect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ grunt copy
Running "copy:main" (copy) task
Copied 2 files

Done.
$ fd . -t f static
static/css/fontawesome.min.css
static/css/solid.min.css

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yep, that looks good.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the subset task
&lt;/h3&gt;

&lt;p&gt;Now let's get Grunt to actually do the subsetting. For this we'll use the &lt;a href="https://gruntjs.com/creating-tasks#basic-tasks"&gt;registerTask()&lt;/a&gt; function, which provides a general purpose “run this function” task. That has the following prototype:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grunt.registerTask(name, description, function)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, let’s try just plugging in the function we wrote earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grunt.registerTask('fasubset', 'Subset the FA icons.', function() {
  fontawesomeSubset([
      'envelope',
      'lock'
    ],
    'static/webfonts');
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And … it works. That was almost too easy!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rm -rf static
$ grunt fasubset
Running "fasubset" task

Done.
$ fd . -t f static
static/webfonts/fa-solid-900.eot
static/webfonts/fa-solid-900.svg
static/webfonts/fa-solid-900.ttf
static/webfonts/fa-solid-900.woff
static/webfonts/fa-solid-900.woff2

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running tasks by default
&lt;/h3&gt;

&lt;p&gt;For the final bit of Grunt config, let's change the default task to run the copy and subset, so we can just run &lt;code&gt;grunt&lt;/code&gt; in future without any arguments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grunt.registerTask('default', ['fasubset', 'copy']);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add the CSS files to the webpage
&lt;/h2&gt;

&lt;p&gt;Now we need to start using the CSS files, otherwise nothing will happen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;link rel="stylesheet" href="/static/css/fontawesome.min.css"&amp;gt;
    &amp;lt;link rel="stylesheet" href="/static/css/solid.min.css"&amp;gt;
  &amp;lt;/head&amp;gt;
[...]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Check the results
&lt;/h2&gt;

&lt;p&gt;Finally, let's check the webpage again - we should see that our icons have popped into existence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bKQC5VfE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2022/05/with-icons.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bKQC5VfE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2022/05/with-icons.png" alt="Email and Password field from a webpage, with envelope and padlock icons visible." width="880" height="63"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After loading CSS, with icons visible&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's all there is to it! I have to admit I was expecting more difficulty when I started, but actually Grunt is designed nicely enough to make sense, and &lt;code&gt;fontawesome-subset&lt;/code&gt; Just Works. I like packages like that.&lt;/p&gt;

&lt;p&gt;How about you? Got any useful packages you'd like to share in the comments?&lt;/p&gt;




</description>
      <category>javascript</category>
      <category>grunt</category>
      <category>automation</category>
      <category>web</category>
    </item>
    <item>
      <title>mbsync, SASL and Gmail</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Thu, 17 Mar 2022 16:30:06 +0000</pubDate>
      <link>https://dev.to/arafel/mbsync-sasl-and-gmail-24na</link>
      <guid>https://dev.to/arafel/mbsync-sasl-and-gmail-24na</guid>
      <description>&lt;p&gt;Slightly more esoteric than usual; I'm posting this for my benefit as much as anyone else's!&lt;/p&gt;

&lt;p&gt;I was just trying to set up &lt;a href="https://isync.sourceforge.io"&gt;mbsync&lt;/a&gt; to work with Gmail. I'd got most of the config copied from &lt;a href="https://fastmail.com/"&gt;another provider&lt;/a&gt;, but kept getting an error about an invalid parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error performing SASL authentication step: SASL(-7): invalid parameter supplied: Parameter Error in /System/Volumes/Data/SWE/macOS/BuildRoots/533514bb11/Library/Caches/com.apple.xbs/Sources/passwordserver_saslplugins/passwordserver_saslplugins-194/plain_clienttoken.c near line 195
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I eventually figured out (from reading &lt;a href="https://frostyx.cz/posts/synchronize-your-2fa-gmail-with-mbsync"&gt;Jakub Kadlčík's clear post&lt;/a&gt;) that you need to tell mbsync which authentication method it should use - for some reason it doesn't correctly autodetect. I added in the &lt;code&gt;AuthMechs&lt;/code&gt; line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AuthMechs LOGIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's able to log in fine.&lt;/p&gt;

</description>
      <category>mail</category>
      <category>sasl</category>
      <category>cli</category>
    </item>
    <item>
      <title>One Small Step...</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Sat, 26 Feb 2022 16:24:03 +0000</pubDate>
      <link>https://dev.to/arafel/one-small-step-n5d</link>
      <guid>https://dev.to/arafel/one-small-step-n5d</guid>
      <description>

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HILLbGmh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1778/1%2AbTrlqXgDAXLPz7OBYhk_bA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HILLbGmh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1778/1%2AbTrlqXgDAXLPz7OBYhk_bA.jpeg" alt="" width="640" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If there is anyone following me, you might have noticed that I haven’t really posted anything lately.&lt;/p&gt;

&lt;p&gt;The reason is simple — there’s been so much going on at home that I haven’t had any brain space to write. There have been days when I could manage to watch a music video and that was about it.&lt;/p&gt;

&lt;p&gt;And yet things like emptying the rubbish, cleaning the bathroom, they all still need to happen.&lt;/p&gt;

&lt;p&gt;The best way I’ve found for this is to kind of sneak up on things. Rather than tell myself I’m going to clean the bathroom, I’m just getting this brush and cleaning these tiles. Rather than clearing all the stuff out of the kitchen, I’m just bringing in the bags that I’ll put the rubbish in, that’s all.&lt;/p&gt;

&lt;p&gt;Somehow, when you do it like that, things seem to still get done. The bags are in, so you might as well put some of the stuff in them. These tiles are done, might as well do those ones.&lt;/p&gt;

&lt;p&gt;The craziness isn’t showing any signs of going anyway just yet. But I know I still want to keep writing. So I’m going to keep doing it the same way as everything else at the moment — one step at a time.&lt;/p&gt;

&lt;p&gt;This post? It's one small step.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@seibelhayley?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Hayley Seibel&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/small-step?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>personal</category>
      <category>keepgoing</category>
    </item>
    <item>
      <title>My Apple purchase regrets</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Thu, 16 Dec 2021 23:02:03 +0000</pubDate>
      <link>https://dev.to/arafel/my-apple-purchase-regrets-3h7l</link>
      <guid>https://dev.to/arafel/my-apple-purchase-regrets-3h7l</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--01OWOCWC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/12/danie-franco-Zi8-E3qJ_RM-unsplash.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--01OWOCWC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/12/danie-franco-Zi8-E3qJ_RM-unsplash.jpg" alt="" width="880" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I read &lt;a href="https://markellisreviews.com/the-apple-purchase-i-regret-the-most/"&gt;an interesting article by Mark Ellis&lt;/a&gt; the other day which got me thinking about my biggest Apple purchasing regret.&lt;/p&gt;

&lt;p&gt;To be clear up-front, I’m very happy with most of their devices, and quite snug in their ecosystem. While they do have their faults I’m happier to have them making my phone than Google or any of the interchangeable Android OEMs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runner-up
&lt;/h2&gt;

&lt;p&gt;The runner-up has to be my previous laptop, the MacBook Pro 2012.&lt;/p&gt;

&lt;p&gt;To be clear, the MacBook itself didn’t really do anything wrong. What really let it down was the keyboard, and the freaking Touch Bar.&lt;/p&gt;

&lt;p&gt;The keyboard had no travel to speak of. It was horrible to type on. I really should have trusted my instincts when I was in the shop, rather than thinking I’d get used to it. It was never good for typing - and as someone who writes code as well as starting to dabble in writing text, that was a major drawback.&lt;/p&gt;

&lt;p&gt;The Touch Bar also was not a good thing. It &lt;em&gt;looked&lt;/em&gt; good, all fancy and stuff, but having things move around on you destroys muscle memory. It’s much better now I can rely on shift-F6 being in the same place the whole time, rather than having to think “what’s the Touch Bar showing at this moment? Am I going to refactor my code, or launch iTunes?”, and it's much more productive.&lt;/p&gt;

&lt;p&gt;For reference - I’m currently happily typing away on an M1 MacBook Air - light, responsive, great battery life, &lt;em&gt;and&lt;/em&gt; the keyboard is a pleasure to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Winner
&lt;/h2&gt;

&lt;p&gt;However — the winner has to be the Apple Watch.&lt;/p&gt;

&lt;p&gt;I got hooked on the idea of it, and it looked all nice and sleek when I got it. It was only after a while that I realised that the UI was clunky, and it was really useful for only a couple of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;subtly letting me know of notifications&lt;/li&gt;
&lt;li&gt;measuring your heartbeat, and maybe your steps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Fitbit can do the second two much, much more cheaply. Setting my phone to vibrate on the notifications I cared about approximated the first one.&lt;/p&gt;

&lt;p&gt;I’ve also never really liked big watches on my wrist - I thought I’d get used to the Apple Watch, but I never did. (It has that in common with the MacBook - I really must learn to listen to my instincts more.)&lt;/p&gt;

&lt;p&gt;It’s spent pretty much the whole of lockdown on the charger, and honestly, I haven’t missed it.&lt;/p&gt;

&lt;p&gt;What has taken its place is the &lt;a href="https://www.withings.com/uk/en/pulse-hr"&gt;Pulse HR from Withings&lt;/a&gt;. Great battery life, light, and does what I need much more cheaply.&lt;/p&gt;




&lt;p&gt;How about you folks out there? Any tales of “buyer’s remorse” to share, be it Apple gadgets or otherwise?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@dani_franco?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Danie Franco&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/regret?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>apple</category>
      <category>purchases</category>
    </item>
    <item>
      <title>Things I wish I’d known sooner about… JavaScript modules</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Wed, 10 Nov 2021 22:57:58 +0000</pubDate>
      <link>https://dev.to/arafel/things-i-wish-id-known-sooner-about-javascript-modules-24gd</link>
      <guid>https://dev.to/arafel/things-i-wish-id-known-sooner-about-javascript-modules-24gd</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZI6NPwUV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/10/things-javascript-modules-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZI6NPwUV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/10/things-javascript-modules-.png" alt="Things I wish I knew about… JavaScript modules" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have to confess it took me a while to get my head around JavaScript modules. This is at least partly because there are two different popular module systems, &lt;a href="http://www.commonjs.org/"&gt;CommonJS&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/ECMAScript"&gt;ECMAScript 2015&lt;/a&gt; (usually shortened to ES2015).&lt;/p&gt;

&lt;p&gt;The ECMAScript 2015 standard is newer than CommonJS. Personally I find it more straightforward, and I use it in preference where I can. Quite a lot of the modules on &lt;a href="https://www.npmjs.com/"&gt;NPM&lt;/a&gt; still use the old style, however.&lt;/p&gt;

&lt;p&gt;One important note - browsers only support ES2015, not CommonJS.&lt;/p&gt;

&lt;h2&gt;
  
  
  CommonJS
&lt;/h2&gt;

&lt;p&gt;CommonJS was started in 2009 by a member of the &lt;a href="https://www.mozilla.org/"&gt;Mozilla&lt;/a&gt; team, as an attempt to unify JavaScript functionality. &lt;a href="http://www.commonjs.org/"&gt;CommonJS&lt;/a&gt; covers more than just modules - check the website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exports
&lt;/h3&gt;

&lt;p&gt;In CommonJS modules, symbols are exported in an object (understandably named &lt;code&gt;exports&lt;/code&gt;). Each property of the object is an exported symbol.&lt;/p&gt;

&lt;p&gt;The usual object assignment rules apply. This means you can easily export a symbol with a different name than it has internal to the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function sayHello(name) {
  console.log("Hello", name);
}

module.exports = {
  sayHello,
  anotherSayHello: sayHello
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Require
&lt;/h3&gt;

&lt;p&gt;You can then &lt;code&gt;require&lt;/code&gt; either the whole module or some symbols from that module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const testModule = require("./module_name");
const { sayHello } = require("./module_name");

testModule.sayHello("world");
sayHello("second world");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NB&lt;/strong&gt; : importing named symbols from a CJS-style module was only introduced in Node 14, and doesn't work perfectly - it relies on some heuristics within Node.&lt;/p&gt;

&lt;h2&gt;
  
  
  ES2015
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Export
&lt;/h3&gt;

&lt;p&gt;Exporting is easier in ES2015; an item (function, constant, variable etc) can be exported simply by adding the &lt;code&gt;export&lt;/code&gt; keyword.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function sayHello(name) {
  console.log("Hello", name);
}

export const myName = "Rumpelstiltskin";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can export the items en-masse at the end of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export { sayHello, myName };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Import
&lt;/h3&gt;

&lt;p&gt;Like many other languages - Java, Python - you then import the parts of the module that you want to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { sayHello } from "./moduleName";

sayHello("world");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you want to keep them namespaced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as moduleStuff from "./moduleName";

moduleStuff.sayHello(moduleStuff.myName);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default exports&lt;/p&gt;

&lt;p&gt;In most of the examples above, we've imported a specific named symbol from module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { sayHello } from "./module_name";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A module can also have a default export, which is what you then get if you don't specify what you want to import from the module.&lt;/p&gt;

&lt;h3&gt;
  
  
  CommonJS
&lt;/h3&gt;

&lt;p&gt;CJS modules actually only export one single item - in that sense, they only have a default export. It's why the items we want to export need to be properties of an object.&lt;/p&gt;

&lt;h3&gt;
  
  
  ES2015
&lt;/h3&gt;

&lt;p&gt;With this system you do get more flexibility. More than one thing can be exported, and you're able to mark which one you want to be the default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default const theAnswer = 42;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  User beware
&lt;/h3&gt;

&lt;p&gt;This isn't always a good thing; you're never totally sure what you're importing, and it can be imported with a completely different (and misleading) name to the original.&lt;/p&gt;

&lt;p&gt;For example, take the trivial module &lt;code&gt;basicMaths&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default sum(x, y) { return x + y; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That default export can be imported as anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import divide from "./basicMaths";

// This will not do what you expect.
divide(2, 4);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advantage of named exports is that you import exactly what you want, no surprises.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { divide } from "./basicMaths";

// divide is empty, so this crashes (or doesn't build, depending on how
// good your tools are). Either way it's better than using the wrong 
// function.
divide(2, 4);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Mozilla Developer Network has a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules"&gt;page covering "native JavaScript modules"&lt;/a&gt; in more depth, with a tutorial.&lt;/li&gt;
&lt;li&gt;Simon Plenderleith &lt;a href="https://simonplend.com/node-js-now-supports-named-imports-from-commonjs-modules-but-what-does-that-mean/"&gt;has a good in-depth look&lt;/a&gt; at Node's support for CommonJS-style modules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Credits&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image: &lt;a href="https://www.deviantart.com/laura-c-f/"&gt;Laura C-F&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.deviantart.com/laura-c-f/art/Confused-Dog-286725830"&gt;https://www.deviantart.com/laura-c-f/art/Confused-Dog-286725830&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>modules</category>
      <category>thingsiwishiknew</category>
    </item>
    <item>
      <title>JavaScript pattern - the “singleton file”</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Sun, 10 Oct 2021 21:26:40 +0000</pubDate>
      <link>https://dev.to/arafel/javascript-pattern-the-singleton-file-ag4</link>
      <guid>https://dev.to/arafel/javascript-pattern-the-singleton-file-ag4</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d8YxyCil--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/10/3A93144E-34C3-44C8-8FB5-0D0809DF4C6F.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d8YxyCil--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/10/3A93144E-34C3-44C8-8FB5-0D0809DF4C6F.jpeg" alt="JavaScript pattern - the “singleton file”" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The other day I came across the solution to a number of problems I’d had in the past.&lt;/p&gt;

&lt;p&gt;I imagine it’s probably obvious to anyone who’s been doing JavaScript for a while, but since it took a while to dawn on me I thought I’d try and save someone else the wait, even if it’s only one. And it’ll provide the rest of you with a good laugh too.&lt;/p&gt;

&lt;p&gt;I don’t know what this pattern is officially called, so I’ve dubbed it the ‘singleton file’ - you can import it everywhere, but it only creates a single object which everything ends up using.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;The problem I was having was with services like logging, which need to be available before most of the modules are started. At first I tried doing this with an &lt;code&gt;initLogging&lt;/code&gt; function, similar to most services. However, when the system was starting up and all the modules were being imported some of the module code was being run before the logging setup was done.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;The solution isn’t really that cunning, but it works very well (for me at least): you make the actual logger ‘object’ an export from the module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the logging module
&lt;/h3&gt;

&lt;p&gt;I’m using the Winston logging system in most of my things. I like the look of the Serilog/structured logging stuff but it’s been too much hassle to get working for now. The logging module I use currently looks a bit like the code below. Warning - it’s a bit long.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&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="s1"&gt;winston&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Allow the logging level to be controlled at start time&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;level&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;LOG_LEVEL&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;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;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;test&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Logging at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; level.`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// This is the format for lines to be printed to console. The format varies&lt;/span&gt;
&lt;span class="c1"&gt;// depending on the environment.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;consoleLineFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timestamp&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="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;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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;label&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;level&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;message&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;timestamp&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;label&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;level&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;message&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;consoleFormat&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;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;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="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Heroku captures info like timestamp anyway, don't duplicate it.&lt;/span&gt;
  &lt;span class="nx"&gt;consoleFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;consoleLineFormat&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Put more information in the dev logs&lt;/span&gt;
  &lt;span class="nx"&gt;consoleFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorize&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;consoleLineFormat&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// For file logs, use JSON&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nx"&gt;format&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="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Create the base logger; everything else comes from this.&lt;/span&gt;
&lt;span class="c1"&gt;// We always log to console; exceptions are always logged, regardless&lt;/span&gt;
&lt;span class="c1"&gt;// of the level that's set.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base_logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                              &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                              &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;level&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="nx"&gt;consoleFormat&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
                              &lt;span class="p"&gt;],&lt;/span&gt;
                              &lt;span class="na"&gt;exceptionHandlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Console&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="nx"&gt;consoleFormat&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="c1"&gt;// For development, also log to files. No point in that for Heroku.&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;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;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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;base_logger&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;combined.log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;debug&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="nx"&gt;fileFormat&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;errors.log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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="nx"&gt;fileFormat&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 'parentLogger' is exported to the other modules. They use the 'child'&lt;/span&gt;
&lt;span class="c1"&gt;// function to create a local logger, tagged with the module name.&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;parentLogger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;childOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;parentLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;childOptions&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="nx"&gt;base_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;module&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;Apologies for the length of that - I did try to take bits out, but all of it’s there for a reason and I thought it might be useful to people.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the logging module
&lt;/h3&gt;

&lt;p&gt;Luckily, actually using it is simple.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parentLogger&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;./logger&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;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parentLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;events&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;That’s it. Now the local code simply uses &lt;code&gt;logger.info&lt;/code&gt;, or whatever level you want. It’s &lt;code&gt;logger&lt;/code&gt; in every module, so I don’t have to remember any other name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it works
&lt;/h2&gt;

&lt;p&gt;If I’m honest, I haven’t investigated this in detail. I imagine it’s because Node doesn’t let modules start running until the modules they’re importing have completed the initial run-through - otherwise it would be a nightmare to use dependencies. If anyone knows for sure, or knows different, please do let me know.&lt;/p&gt;

&lt;p&gt;That’s it. Hope that helps someone!&lt;/p&gt;

&lt;p&gt;Oh - and in case you're wondering, the image is totally unrelated to the story. I couldn't find a suitable post image, so I went with "cute puppy" instead.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Live reload in Hapi</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Sat, 17 Jul 2021 16:00:47 +0000</pubDate>
      <link>https://dev.to/arafel/live-reload-in-hapi-434m</link>
      <guid>https://dev.to/arafel/live-reload-in-hapi-434m</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lqlAEJ0L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/07/pablo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lqlAEJ0L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/07/pablo.png" alt="Live reload in Hapi"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Something I’ve been meaning to address for a while is the lack of a &lt;a href="https://github.com/livereload/livereload-js"&gt;livereload&lt;/a&gt; plugin for &lt;a href="https://hapi.dev/"&gt;Hapi&lt;/a&gt;. Template changes are visible on manual reload if you turn off caching, but I’ve become used to automatic reloading when developing with &lt;a href="https://vuejs.org/"&gt;Vue&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  livereload architecture
&lt;/h2&gt;

&lt;p&gt;The full livereload protocol is &lt;a href="http://livereload.com/api/protocol/"&gt;available for everyone to view&lt;/a&gt;. In brief, it requires two parts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server
&lt;/h3&gt;

&lt;p&gt;The livereload server accepts HTTP and WebSocket connections (on the same port). It’s responsible for serving the livereload client code, and for monitoring the filesystem for changes. It also listens for messages from the client indicating URL changes.&lt;/p&gt;

&lt;p&gt;When changes happen, it sends a message to the client (over WebSocket) indicating which file has changed so it can be reloaded if needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client
&lt;/h3&gt;

&lt;p&gt;The client runs in the browser - it makes a connection to the server and waits for a message. When it receives a message indicating a reload is required, it does it.&lt;/p&gt;

&lt;p&gt;(This is a rather abbreviated explanation…)&lt;/p&gt;

&lt;p&gt;I had no intention of actually re-implementing the client - I just wanted to automatically insert the fragment which causes the client code to be loaded and start listening.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugin structure
&lt;/h2&gt;

&lt;p&gt;A Hapi plugin seemed to be the best place to start. I knew from previous work that the Hapi server has “extension points” in the &lt;a href="https://hapi.dev/api/?v=20.1.4#request-lifecycle"&gt;request/response lifecycle&lt;/a&gt;, which can be hooked into and used to monitor or modify the request or response.&lt;/p&gt;

&lt;p&gt;The basic structure of a Hapi plugin is simple - it’s an object which exports a structure named &lt;code&gt;plugin&lt;/code&gt; containing a function named &lt;code&gt;register()&lt;/code&gt; (and a bit of metadata). The register function receives a &lt;code&gt;server&lt;/code&gt; object and an (optional) &lt;code&gt;options&lt;/code&gt; object. It does what it needs to do, and then returns or throws an exception.&lt;/p&gt;

&lt;p&gt;A very basic plugin is below. It exports the structure Hapi is looking for in a plugin. The register function creates a route which says hello to the user, or the whole world if the user doesn’t specify who to greet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';

async function register(server, options = {}) {
  server.route({
    path: "/hello",
    method: "GET",
    handler: function () {
      const name = options.name || "world";
      return `Hello ${name}\n`;
    }
  });
}

module.exports = {
  name: 'hapi-reload',
  version: '0.1.0',
  register: register
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are multiple ways of declaring your plugin - &lt;a href="https://hapi.dev/tutorials/plugins/?lang=en_US"&gt;the Hapi docs&lt;/a&gt; cover it all well. For now we’ll keep it simple, and put our plugin code in a file named &lt;code&gt;plugin.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We also need to get a server up and running, so we have something to plug in to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Hapi = require("@hapi/hapi");
const hapiLivereload = require("./plugin");

async function run() {
  const serverOpts = {
    port: process.env.PORT || 4000,
    host: '0.0.0.0'
  };
  const server = Hapi.server(serverOpts);

  await server.register({
    plugin: hapiLivereload
  });
  return server.start();
}

run()
  .then(() =&amp;gt; console.log("Server up"))
  .catch(err =&amp;gt; console.error("Couldn't run", err));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And - it works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node dev/server.js
Server up


$ curl http://localhost:4000/hello
Hello world

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can add the &lt;code&gt;name&lt;/code&gt; option to customise the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  await server.register({
    plugin: hapiLivereload,
    options: {
      name: "Paul"
    }
  });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now it knows me!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl http://localhost:4000/hello
Hello Paul

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Which hook to use?
&lt;/h2&gt;

&lt;p&gt;So that’s a basic plugin, but it doesn’t do anything particularly useful - you can probably think of easier ways to say “hello world”.&lt;/p&gt;

&lt;p&gt;Let’s start looking at the request lifecycle again. Looking through the list of steps the &lt;code&gt;onPreResponse&lt;/code&gt; looks good - the response has been generated but not yet transmitted, and better still:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the response contained in request.response may be modified (but not assigned a new value). To return a different response type (for example, replace an error with an HTML response), return a new response value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s try a new approach - rather than a specific route to greet the user, let’s just make &lt;em&gt;all&lt;/em&gt; the responses do that. Much more friendly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function register(server, options = {}) {
  server.ext({
    type: 'onPreResponse',
    method: function(request, h) {
      const name = options.name || "world";
      console.log(`Replacing "${request.response.source}"`);
      return `Hello ${name}\n`;
    }
  });
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;console.log&lt;/code&gt; shows us what we’re replacing (useful for debugging); and we just return the new response text we want, which the docs say should work.&lt;/p&gt;

&lt;p&gt;To test it, we’re going to need a route which &lt;em&gt;doesn’t&lt;/em&gt; return &lt;code&gt;Hello world&lt;/code&gt;. We can add that to &lt;code&gt;run()&lt;/code&gt; in our server code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  server.route({
    path: "/",
    method: "GET",
    handler: () =&amp;gt; "I should be an index page\n"
  })

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we try it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server up
Replacing "I should be an index page
"


$ curl http://localhost:4000/
Hello Paul

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excellent!&lt;/p&gt;

&lt;h3&gt;
  
  
  livereload register
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;register&lt;/code&gt; function for the livereload plugin is basically the same. The differences are specific to the plugin, working out the location of the client code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const logger = require("util").debuglog("hapi-livereload");

let host, port, src;

async function register(server, options = {}) {
  host = options.host || "localhost";
  port = options.port || 35729;
  src = options.src || '//' + host + ':' + port + '/livereload.js?snipver=1';

  server.ext({
    type: 'onPreResponse',
    method: addSnippet
  });
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bit we haven’t covered yet is that &lt;code&gt;addSnippet&lt;/code&gt; function, which is where things got a little more involved - actually modifying the webpage being returned.&lt;/p&gt;

&lt;p&gt;We also add a &lt;code&gt;logger&lt;/code&gt; function using Node’s standard &lt;code&gt;util.debuglog&lt;/code&gt; functionality, so we can report back to the user if they want us to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying the HTML
&lt;/h2&gt;

&lt;p&gt;I spent some time looking for an HTML parser. It was complicated by the fact that the plugin not only needs to parse the outgoing HTML page, but we also have to be able to modify it and emit it back out as text.&lt;/p&gt;

&lt;p&gt;Luckily, I eventually stumbled upon &lt;a href="https://github.com/andrejewski/himalaya"&gt;Himalaya&lt;/a&gt;, which is perfect for the job. It converts the HTML document to a JSON object - so this document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class='post post-featured'&amp;gt;
  &amp;lt;p&amp;gt;Himalaya parsed me...&amp;lt;/p&amp;gt;
  &amp;lt;!-- ...and I liked it. --&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes this JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[{
  type: 'element',
  tagName: 'div',
  attributes: [{
    key: 'class',
    value: 'post post-featured'
  }],
  children: [{
    type: 'element',
    tagName: 'p',
    attributes: [],
    children: [{
      type: 'text',
      content: 'Himalaya parsed me...'
    }]
  }, {
    type: 'comment',
    content: ' ...and I liked it. '
  }]
}]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Example taken from the Github page.)&lt;/p&gt;

&lt;p&gt;As well as &lt;code&gt;parse&lt;/code&gt; there’s also a &lt;code&gt;stringify&lt;/code&gt; method, which converts the JSON &lt;em&gt;back&lt;/em&gt; to HTML. Perfect.&lt;/p&gt;

&lt;p&gt;This is a bigger problem than I realised when I started on it. The best thing I’ve found for big problems is to break them down, like below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function addSnippet(request, h) {
  // Is it HTML? If not, we can't work with it.
  // Can we parse it?
  // If we can parse it, does it have a HTML tag as it should?
  // And is there a 'head' element we can attach our script to?
  // Did someone already add a livereload script to it?
  // Add the script to the 'head' element
  // Convert back to HTML and return it
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks a manageable breakdown, and all the individual pieces don’t seem as challenging. Let’s work through them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can we work with the response?
&lt;/h3&gt;

&lt;p&gt;We’ll go with the simple approach here - we’ll assume &lt;a href="https://webplatform.github.io/docs/concepts/Internet_and_Web/mime_types/"&gt;the MIME type&lt;/a&gt; is correct and check for &lt;code&gt;text/html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function isHtml(response) {
  return response.contentType?.indexOf("text/html") &amp;gt;= 0;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Can we parse it, and does it have the right nodes?
&lt;/h3&gt;

&lt;p&gt;We end up searching nodes quite a bit, so let’s have a convenience function for that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function findNode(nodes, name) {
  return nodes.find(node =&amp;gt; node.tagName === name);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, our parsing/checking code can be written as below. We try to parse it; if we can parse it we look for the &lt;code&gt;html&lt;/code&gt; element. If that exists, we look for a child of &lt;em&gt;that&lt;/em&gt; which is a &lt;code&gt;head&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;Note - we’re using &lt;code&gt;h.continue&lt;/code&gt; here. That indicates to Hapi that we don’t have anything to say about this response, move it on to the next hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const doc = parse(response.source);
  if (!doc || doc.length == 0) {
    logger("Couldn't parse", response.source);
    return h.continue;
  }

  const htmlNode = findNode(doc, "html")
  if (!htmlNode) {
    logger("Couldn't find HTML tag", response.source);
    return h.continue;
  }

  const headNode = findNode(htmlNode.children, "head");
  if (!headNode) {
    logger("Couldn't find HEAD tag", response.source);
    return h.continue;
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Is there a script already?
&lt;/h3&gt;

&lt;p&gt;Technically, the script could be anywhere in the page. We don’t want to just search the whole body of the webpage - there could be a large number of nodes in that. So we’ll simplify our problem and only check for the script in the &lt;code&gt;head&lt;/code&gt;, which is where it’s most likely to be anyway.&lt;/p&gt;

&lt;p&gt;To do this, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;find all the children of the &lt;code&gt;head&lt;/code&gt; node,&lt;/li&gt;
&lt;li&gt;which are script nodes,&lt;/li&gt;
&lt;li&gt;with a &lt;code&gt;src&lt;/code&gt; attribute&lt;/li&gt;
&lt;li&gt;and check the &lt;code&gt;src&lt;/code&gt; value for &lt;code&gt;livereload.js&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 1 is simple - &lt;code&gt;head.children&lt;/code&gt;. The remaining 3 steps can be rewritten as - for each node, if it’s a script node, check the &lt;code&gt;src&lt;/code&gt; attribute and &lt;code&gt;src&lt;/code&gt; value. If the &lt;code&gt;src&lt;/code&gt; value contains &lt;code&gt;livereload.js&lt;/code&gt;, then the script was already added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function reloadExists(nodes) {
  let found = false;

  nodes.forEach(node =&amp;gt; {
    if ((node.type === 'element') &amp;amp;&amp;amp; (node.tagName === 'script')) {
      const reloadAttribs =
        node.attributes
            .filter(node =&amp;gt; node.key === 'src')
            .filter(attr =&amp;gt; attr.value?.indexOf('/livereload.js') &amp;gt;= 0);
      found = reloadAttribs.length &amp;gt; 0;
    }
  })

  return found;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the script
&lt;/h3&gt;

&lt;p&gt;We need to add a &lt;code&gt;script&lt;/code&gt; node which loads the client script. We’ll make it &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attributes"&gt;&lt;code&gt;async&lt;/code&gt;&lt;/a&gt; so it doesn’t slow the rest of the site loading.&lt;/p&gt;

&lt;p&gt;Since we’re working with JSON, this is now relatively easy - we push a new node onto the existing children of &lt;code&gt;head&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  headNode.children.push({
    type: 'element',
    tagName: 'script',
    attributes: [{ key: 'src', value: src }, { key: "async", value: null }],
    children: []
  })

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Convert back to HTML
&lt;/h3&gt;

&lt;p&gt;As the last step, we return the new HTML from the hook so that Hapi will use it as the response. Himalaya makes this easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  return stringify(doc);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Put it all together
&lt;/h3&gt;

&lt;p&gt;We can now flesh out our &lt;code&gt;addSnippet&lt;/code&gt; skeleton from above, replacing the comments with actual code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function addSnippet(request, h) {
  logger("Processing response for", request.info.id);

  // Less typing.
  const response = request.response;

  if (!isHtml(response)) {
    logger("Response isn't HTML, skip it.");
    return h.continue;
  }

  const doc = parse(response.source);
  if (!doc || doc.length == 0) {
    logger("Couldn't parse", response.source);
    return h.continue;
  }

  const htmlNode = findNode(doc, "html")
  if (!htmlNode) {
    logger("Couldn't find HTML tag", response.source);
    return h.continue;
  }

  const headNode = findNode(htmlNode.children, "head");
  if (!headNode) {
    logger("Couldn't find HEAD tag", response.source);
    return h.continue;
  }

  if (reloadExists(headNode.children)) {
    logger("Snippet already exists, skip.");
    return h.continue;
  }

  headNode.children.push({
    type: 'element',
    tagName: 'script',
    attributes: [{ key: 'src', value: src }, { key: "async", value: null }, { key: "defer", value: null }],
    children: []
  })

  logger("Processing done for", request.info.id);
  return stringify(doc);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Trying it out
&lt;/h2&gt;

&lt;p&gt;To test it out, we need a route which actually serves an HTML file - nothing fancy, so we can easily see if the code has been added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Home Page&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;header&amp;gt;&amp;lt;h1&amp;gt;Hello from Hapi!&amp;lt;/h1&amp;gt;&amp;lt;/header&amp;gt;
&amp;lt;main&amp;gt;
    &amp;lt;p&amp;gt;This is a paragraph&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;This is still also a paragraph.&amp;lt;/p&amp;gt;&amp;lt;/main&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
index.html





&lt;p&gt;We’ll change our &lt;code&gt;/&lt;/code&gt; route to serve that. Assuming the &lt;code&gt;html&lt;/code&gt; variable contains the HTML page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  server.route({
    path: "/",
    method: "GET",
    handler: (request, h) =&amp;gt; h.response(html)
  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try it out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang='en'&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Home Page&amp;lt;/title&amp;gt;
&amp;lt;script src='//localhost:35729/livereload.js?snipver=1' async defer&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;header&amp;gt;&amp;lt;h1&amp;gt;Hello from Hapi!&amp;lt;/h1&amp;gt;&amp;lt;/header&amp;gt;
&amp;lt;main&amp;gt;
    &amp;lt;p&amp;gt;This is a paragraph&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;This is still also a paragraph.&amp;lt;/p&amp;gt;&amp;lt;/main&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there it is! You can see the HTML doesn’t look &lt;em&gt;totally&lt;/em&gt; the same as before - the case of &lt;code&gt;doctype&lt;/code&gt; has been changed, for example - but it has the same semantic meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The good…
&lt;/h3&gt;

&lt;p&gt;I haven’t heavily tested it but this approach seems to work for serving plain HTML files.&lt;/p&gt;

&lt;p&gt;It wasn’t hard to implement - Hapi makes available everything we need for integrating ourselves into the request/response lifecycle, and the Himalaya library provided exactly what we needed for the HTML processing (it would have been a lot harder without that).&lt;/p&gt;

&lt;h3&gt;
  
  
  … and the ‘oops’
&lt;/h3&gt;

&lt;p&gt;However… when I went to integrate it with the project I’m actually working on, I found it doesn’t work with Vision. The &lt;code&gt;source&lt;/code&gt; element isn’t a rendered page, it’s a data structure, presumably one that’s meaningful to Vision.&lt;/p&gt;

&lt;p&gt;At this point I did what should have occurred to me before - I put the JavaScript in the header partial being rendered by Vision. Once I’d done that, I had livereload working without the need for the plugin.&lt;/p&gt;

&lt;p&gt;So from the “getting livereload working” perspective, this was actually a fairly pointless job. It was still a useful experience, though, and I learned from it. So it’s not wasted as such, just a bit of a “doh!” moment.&lt;/p&gt;

</description>
      <category>hapi</category>
      <category>hapijs</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>My 11 favourite Vim plugins</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Thu, 03 Jun 2021 21:11:49 +0000</pubDate>
      <link>https://dev.to/arafel/my-11-favourite-vim-plugins-7og</link>
      <guid>https://dev.to/arafel/my-11-favourite-vim-plugins-7og</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OaXo0zkk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/My-11-favourite-Vim-plugins.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OaXo0zkk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/My-11-favourite-Vim-plugins.png" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While I’m spending most of my JavaScript writing time in WebStorm these days, I still use Vim a lot. Over time I’ve found some great plugins, extending Vim to cope with an amazing amount of stuff.&lt;/p&gt;

&lt;p&gt;I should note that these are all essentially general purpose plugins. Some of the language-specific ones are great to use, but unless you’re working with that particular language they’re not going to be that useful.&lt;/p&gt;

&lt;p&gt;Any screenshots are taken from the plugin website, rather than grabbed by me, unless explicitly noted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plug
&lt;/h2&gt;

&lt;p&gt;This is where it all starts. It’s very simple to use - I don’t think I’ve ever had to change a setting.&lt;/p&gt;

&lt;p&gt;Once you’ve added the plugin section to your &lt;code&gt;.vimrc&lt;/code&gt; adding a new plugin is simple, especially if it’s on Github:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;call plug#begin("~/.vim/plugged")
    Plug 'foo/bar'
call plug#end()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(It will handle arbitrary Git servers as well.)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Plug&lt;/code&gt; takes care of downloading and installing the plugin. If a plugin needs a few options then that’s easily taken care of too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you’ve got it set up just &lt;code&gt;:PlugInstall&lt;/code&gt; to install plugins, and &lt;code&gt;:PlugUpgrade&lt;/code&gt; to upgrade. Simples, just the way I like it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/junegunn/vim-plug"&gt;Get Plug here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  General functionality.
&lt;/h2&gt;

&lt;p&gt;These plugins cover Vim “as a whole” - they’ll improve your whole Vim experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  startify
&lt;/h3&gt;

&lt;p&gt;Startify has a simple goal - on startup it presents a list of most-recently-used files and sessions.&lt;/p&gt;

&lt;p&gt;I started using this fairly recently, and it’s surprisingly useful - I’d probably use it for the &lt;code&gt;SSave&lt;/code&gt; and &lt;code&gt;SLoad&lt;/code&gt; functions alone (storing Vim sessions in a dedicated session directory). As an added bonus it even recognises if there’s an existing &lt;code&gt;Session.vim&lt;/code&gt; file in the directory you’ve launched it from and offers that as an extra option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7n1ZAl3i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/startify-menu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7n1ZAl3i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/startify-menu.png" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mhinz/vim-startify"&gt;Get Startify here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  NERDTree
&lt;/h3&gt;

&lt;p&gt;NERDTree is a better file sidebar. It makes it easy to delete/add/rename files and directories, change the Vim working directory (useful for launching tools such as &lt;code&gt;grep&lt;/code&gt;), and easy to navigate around the file structure. If you rename a file or directory and you’ve got buffers open with the old name it offers to reopen the buffers with the new name, which is a very nice touch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tB4EfaUs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/nerdtree.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tB4EfaUs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/nerdtree.png" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It offers integrated help - I’ve not had to resort to reading the fine manual. The only niggle I have is that the help is accessed via &lt;code&gt;?&lt;/code&gt;, which is entirely reasonable but breaks backward search in the tree.&lt;/p&gt;

&lt;p&gt;(It is quite possible the manual gives a way to change that, of course. Maybe I should read it…)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/preservim/nerdtree"&gt;Get NerdTree here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Obsession
&lt;/h3&gt;

&lt;p&gt;One of Tim Pope’s plugins, which should be all you need to know about the quality of it. Once you start it in an editing session &lt;code&gt;Obsession&lt;/code&gt; will automatically save any window/buffer/layout changes to the session file.&lt;/p&gt;

&lt;p&gt;When used together with Startify it makes for a very smooth flow. Save the session file once and turn on Obsession - after that you can load it via the Startify menu buffer and everything’s set up right the way you left it last time you exited.&lt;/p&gt;

&lt;p&gt;(And no more problems forgetting to save the session before that automatic &lt;code&gt;:qa&lt;/code&gt;…)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tpope/vim-obsession"&gt;Get Obsession here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  markdown-preview.nvim
&lt;/h3&gt;

&lt;p&gt;To be honest - this is way better than I expected a preview in Vim to be. Once you’ve started it opens a browser window, then automatically reloads the contents as you edit the document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XehsWVEo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.solarwinter.net/content/images/2021/06/47603494-28e90000-da1f-11e8-9079-30646e551e7a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XehsWVEo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.solarwinter.net/content/images/2021/06/47603494-28e90000-da1f-11e8-9079-30646e551e7a.gif" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/iamcco/markdown-preview.nvim"&gt;Get markdown-preview.nvim&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding
&lt;/h2&gt;

&lt;p&gt;These plugins help with coding, rather than appearance or general text editing.&lt;/p&gt;

&lt;h3&gt;
  
  
  CoC
&lt;/h3&gt;

&lt;p&gt;Integrates support for &lt;a href="https://microsoft.github.io/language-server-protocol/"&gt;Language Servers&lt;/a&gt;, as used by VS Code, IntelliJ, Sublime Text and co. Language servers are what let the editors quickly and accurately auto-complete, suggesting type-compatible completions.&lt;/p&gt;

&lt;p&gt;Basically - if you write code with Vim, you need this plugin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dE8g_Npy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.solarwinter.net/content/images/2021/06/coc-animation.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dE8g_Npy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.solarwinter.net/content/images/2021/06/coc-animation.gif" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/neoclide/coc.nvim"&gt;Get CoC&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vimagit
&lt;/h3&gt;

&lt;p&gt;This is a port of (or ‘rewrite inspired by’) the Emacs &lt;code&gt;magit&lt;/code&gt; extension. While it only does a subset of &lt;code&gt;magit&lt;/code&gt; - it doesn’t do remote operations like push/pull/fetch, for example - it makes adding and committing to the repo really easy. I particularly like the fact you can stage whole files or individual hunks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vLBX8n6q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.solarwinter.net/content/images/2021/06/vimagit-animation.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vLBX8n6q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.solarwinter.net/content/images/2021/06/vimagit-animation.gif" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jreybert/vimagit"&gt;Get Vimagit here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ALE
&lt;/h3&gt;

&lt;p&gt;ALE is the Asynchronous Lint Engine. It provides background linting, syntax checking and semantic errors in recent versions of Vim, so the errors are flagged while you edit. This lets you fix them while the code is still (really!) fresh in your mind, rather than waiting for the next time you run &lt;code&gt;lint&lt;/code&gt; or the static checker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XnvTY0Rb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.solarwinter.net/content/images/2021/06/ale-animation.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XnvTY0Rb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.solarwinter.net/content/images/2021/06/ale-animation.gif" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dense-analysis/ale"&gt;Get ALE here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appearance
&lt;/h2&gt;

&lt;p&gt;Finally, we’re on to the plugins which change how Vim looks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Airline
&lt;/h3&gt;

&lt;p&gt;Airline adds a status line to the bottom of each Vim window. Without any tweaking it’ll display:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mode (normal/visual, that kind of thing)&lt;/li&gt;
&lt;li&gt;VCS info&lt;/li&gt;
&lt;li&gt;filename, filetype&lt;/li&gt;
&lt;li&gt;file encoding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s very configurable - you can probably get it to display just about anything that you’re interested in. It also plays well with others - for example, ALE above can be integrated into Airline to flag up warnings/errors in the status line.&lt;/p&gt;

&lt;p&gt;Give it a try - just don’t blame me if you then spend the next day tweaking it to your exact satisfaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/vim-airline/vim-airline"&gt;Get Airline here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Themes
&lt;/h3&gt;

&lt;p&gt;I find that I switch between themes depending on my mood, what I’m doing, and what the environment around me is like - for example, often light-background Papercolor is more readable than Solarized.&lt;/p&gt;

&lt;p&gt;Luckily, this being Vim, it’s easy to write a couple of macros to switch between them and to set the background between light and dark.&lt;/p&gt;

&lt;p&gt;For the themes I’m just going to add a screenshot from their homepage - there’s really not much I can say about these, other than I find them a good balance of colours.&lt;/p&gt;

&lt;h4&gt;
  
  
  Papercolor
&lt;/h4&gt;

&lt;p&gt;Papercolor in light mode:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ERTXh7x---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/papercolor-light.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ERTXh7x---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/papercolor-light.png" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/NLKNguyen/papercolor-theme"&gt;Get Papercolor here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Solarized
&lt;/h4&gt;

&lt;p&gt;Solarized8 in light mode:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bCBv83pq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/solarized8-light-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bCBv83pq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/solarized8-light-1.png" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lifepillar/vim-solarized8"&gt;Get Solarized8 here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dracula
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yno9-N7p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yno9-N7p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.solarwinter.net/content/images/2021/06/screenshot.png" alt="My 11 favourite Vim plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dracula/vim"&gt;Get Dracula here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful key mappings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Themes and background
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nnoremap &amp;lt;leader&amp;gt;tp :colorscheme PaperColor&amp;lt;CR&amp;gt;
nnoremap &amp;lt;leader&amp;gt;ts :colorscheme solarized8&amp;lt;CR&amp;gt;
nnoremap &amp;lt;leader&amp;gt;td :colorscheme dracula&amp;lt;CR&amp;gt;

nnoremap &amp;lt;leader&amp;gt;bd :set background=dark&amp;lt;CR&amp;gt;
nnoremap &amp;lt;leader&amp;gt;bl :set background=light&amp;lt;CR&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other stuff
&lt;/h3&gt;

&lt;p&gt;Toggle NERDTree on and off; I have to admit I mostly just turn it on these days, but it’s nice to have the easy option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nnoremap &amp;lt;leader&amp;gt;n :NERDTreeToggle&amp;lt;cr&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These I use more than I should, since I keep fiddling with my &lt;code&gt;.vimrc&lt;/code&gt;. (Faux productivity for the win!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nnoremap ,v :edit $MYVIMRC&amp;lt;CR&amp;gt;
nnoremap ,u :source $MYVIMRC&amp;lt;CR&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tools</category>
      <category>vim</category>
    </item>
    <item>
      <title>Using Closures with Axios</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Sun, 30 May 2021 01:00:00 +0000</pubDate>
      <link>https://dev.to/arafel/using-closures-with-axios-3a13</link>
      <guid>https://dev.to/arafel/using-closures-with-axios-3a13</guid>
      <description>&lt;p&gt;Recently I’ve been working on integrating with a subscription/payment gateway. (It hasn’t been straightforward, but that’s a whole other post…)&lt;/p&gt;

&lt;p&gt;I wanted to be able to test my web-hook code without repeatedly triggering events from the gateway. I stored the incoming events in JSON format, which was fine - but then of course I needed to take the stored events and do something with them.&lt;/p&gt;

&lt;p&gt;I thought it might be interesting to make a note of where I started from and how I got to the end. I’ve included the mistakes I made along the way, so if you read a bit and think “that won’t work!” - I probably found that out in the next paragraph. :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting out
&lt;/h2&gt;

&lt;p&gt;Starting simple - read the file into an array of objects, and then loop through printing a couple of details from each event, so we know it loaded okay.&lt;/p&gt;

&lt;p&gt;As this is test code I’m going to use &lt;a href="https://nodejs.org/api/fs.html#fs_fs_readfilesync_path_options"&gt;the Sync version of readFile&lt;/a&gt; to keep the code simple - no callbacks, and we can feed the result of &lt;code&gt;readFileSync&lt;/code&gt; straight into &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse"&gt;&lt;code&gt;JSON.parse&lt;/code&gt;&lt;/a&gt;, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs');

function run() {
    const json = JSON.parse(fs.readFileSync(__dirname + "/events.json"))

    for (const event of json) {
        console.log("event: ", event.id, event.event_type);
    }
}

run()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sure enough, we get what we expect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node post-events.js
event: 1 Hello
event: 2 World

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works, but the loop is going to post the events really quickly. I’d prefer to space them out - it makes it easier to watch the receiving code that way, and I’m not trying to stress test it at this point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending them gradually
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nodejs.org/dist/latest-v14.x/docs/api/timers.html#timers_settimeout_callback_delay_args"&gt;&lt;code&gt;setTimeout&lt;/code&gt;&lt;/a&gt; works nicely for queuing a function to be executed in the future. The simplest thing for the waiting time is to use the position in the array. The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of"&gt;&lt;code&gt;for...of&lt;/code&gt;&lt;/a&gt; construct doesn’t give us the index, so we’ll have to use a different method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach"&gt;&lt;code&gt;forEach&lt;/code&gt;&lt;/a&gt; can give us both the item and index, so let’s use that. It’s just the loop which changes - the file-reading and JSON-parsing stays the same, so I won’t repeat it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;json.forEach((event, index) =&amp;gt; {
    console.log(`Event ${event.id}: ${event.event_type}`);
    console.log(`Will delay ${(index + 1) * 1000} ms`);
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And yep, we’re good:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node post-events.js
Event 1: Hello
Would delay 1000 ms
Event 2: World
Would delay 2000 ms

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scheduling
&lt;/h2&gt;

&lt;p&gt;Now we just need something to schedule. Let’s try &lt;a href="https://ronjeffries.com/xprog/articles/practices/pracsimplest/"&gt;the simplest thing&lt;/a&gt; first - for each event, queue a function taking the &lt;code&gt;event&lt;/code&gt; as a parameter to print out the event id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;json.forEach((event, index) =&amp;gt; {
    const timeout = (index + 1) * 1000;
    console.log(`Event ${event.id}: ${event.event_type}`);
    console.log(`Will delay ${timeout} ms`);
    setTimeout(event =&amp;gt; console.log("Posting", event.id), timeout);
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node post-events.js
Event 1: Hello
Will delay 1000 ms
Event 2: World
Will delay 2000 ms
post-events.js:10
        setTimeout(event =&amp;gt; console.log("Posting", event.id), timeout);
                                                         ^
TypeError: Cannot read property 'id' of undefined
    at Timeout._onTimeout (post-events.js:10:52)
    at listOnTimeout (node:internal/timers:557:17)
    at processTimers (node:internal/timers:500:7)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After thinking about it that makes sense, and I really should have known better.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;event&lt;/code&gt; parameter is read &lt;strong&gt;when the function runs&lt;/strong&gt;. Because of the timeouts the functions run after the loop has finished - at which point &lt;code&gt;event&lt;/code&gt; is no longer defined, which is what we’re seeing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closure
&lt;/h2&gt;

&lt;p&gt;What we can do is create what’s known as a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures"&gt;closure&lt;/a&gt;. A closure is essentially a function together with the environment present when it was created. Luckily JavaScript makes that easy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function makeFunc(event) {
    console.log("Making func for", event);
    return async function() {
        console.log("Posting", event.event_type);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yet another version of our loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;json.forEach((event, index) =&amp;gt; {
    const timeout = (index + 1) * 1000;
    console.log(`Setting timeout for Event ${event.id}; delay ${timeout} ms.`);
    setTimeout(event =&amp;gt; makeFunc(event), timeout);
})


$ node post-events.js
Setting timeout for Event 1; delay 1000 ms.
Setting timeout for Event 2; delay 2000 ms.
Making func for undefined
Making func for undefined

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well … something has gone wrong there. What’s happened is that because we wrote &lt;code&gt;event =&amp;gt; makeFunc(event)&lt;/code&gt;, the call to &lt;code&gt;makeFunc&lt;/code&gt; hasn’t happened straight away, but has been delayed - which gives us the same problem as before. Let’s make it an immediate call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;json.forEach((event, index) =&amp;gt; {
    const timeout = (index + 1) * 1000;
    console.log(`Setting timeout for Event ${event.id}; delay ${timeout} ms.`);
    setTimeout(makeFunc(event), timeout);
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And see how that does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node post-events.js
Setting timeout for Event 1; delay 1000 ms.
Making func for { id: 1, event_type: 'Hello' }
Setting timeout for Event 2; delay 2000 ms.
Making func for { id: 2, event_type: 'World' }
Posting Hello
Posting World

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The POST request
&lt;/h2&gt;

&lt;p&gt;That’s more like it. We’ll use axios for doing the POST to the HTTP endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs');
const axios = require("axios");

const client = axios.create()

function makeFunc(event) {
    return async function() {
        console.log("Posting", event.event_type);
        const res = await client.post("http://localhost:8000/", event);
        if (res.isAxiosError) {
            console.error("Error posting");
        }
    }
}

function run() {
    const json = JSON.parse(fs.readFileSync(__dirname + "/events.json"))

    json.forEach((event, index) =&amp;gt; {
        const timeout = (index + 1) * 1000;
        console.log(`Setting timeout for Event ${event.id}; delay ${timeout} ms.`);
        setTimeout(makeFunc(event), timeout);
    })
}

run()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Looking at the output
&lt;/h2&gt;

&lt;p&gt;You can use a service like &lt;a href="https://requestbin.io/"&gt;requestbin&lt;/a&gt; as an easy way to check what POSTs look like. For this I’ve decided to use &lt;a href="https://github.com/fiatjaf/requestbin"&gt;fiatjaf’s requestbin&lt;/a&gt; - it’s small and simple.&lt;/p&gt;

&lt;p&gt;And here we are - correct data, and spaced a second apart as we expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./requestbin -port 8000
Listening for requests at 0.0.0.0:8000

=== 18:00:00 ===
POST / HTTP/1.1
Host: localhost:8000
User-Agent: axios/0.21.1
Content-Length: 29
Accept: application/json, text/plain, */*
Connection: close
Content-Type: application/json;charset=utf-8

{"id":1,"event_type":"Hello"}

=== 18:00:01 ===
POST / HTTP/1.1
Host: localhost:8000
User-Agent: axios/0.21.1
Content-Length: 29
Accept: application/json, text/plain, */*
Connection: close
Content-Type: application/json;charset=utf-8

{"id":2,"event_type":"World"}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope that helps someone, even if it’s just that we ran into the same ‘oops’ and we made mistakes together. :-)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>web</category>
      <category>programming</category>
    </item>
    <item>
      <title>Things I wish I knew about… JavaScript functions</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Sat, 29 May 2021 23:00:28 +0000</pubDate>
      <link>https://dev.to/arafel/things-i-wish-i-knew-about-javascript-functions-5hbm</link>
      <guid>https://dev.to/arafel/things-i-wish-i-knew-about-javascript-functions-5hbm</guid>
      <description>&lt;p&gt;Especially coming from a C/Python/Elixir background, there were some things about JavaScript functions that I really didn’t get to start with. I thought I’d write them down in the hope they’ll help someone else on their journey.&lt;/p&gt;

&lt;p&gt;I should note this is probably part one - there’s bound to be more things I learn about JavaScript functions as I keep using the language.&lt;/p&gt;

&lt;h2&gt;
  
  
  When one is async, all are async
&lt;/h2&gt;

&lt;p&gt;I didn’t really understand how JavaScript does async when I started using it, so I spent quite some time trying to work out how a function could get a result from an asynchronous call and return it without the function caller having to be async itself.&lt;/p&gt;

&lt;p&gt;If you’re aiming for the same thing, I’ll save you the bother - you can’t do it. I initially had high hopes for something like the construction below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function iAmAsync(num) {
  return num * 2;
}

function iUseThen(num) {
  return iAmAsync(num).then(res =&amp;gt; res + 1);
}

console.log("iUseThen(3) =&amp;gt;", iUseThen(3));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I didn’t realise was that &lt;code&gt;iAmAsync(3).then(...)&lt;/code&gt; will implicitly return a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;Promise&lt;/a&gt;, meaning the whole of &lt;code&gt;iUseThen&lt;/code&gt; will return a Promise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iUseThen(3) =&amp;gt; Promise { &amp;lt;pending&amp;gt; }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One approach I did find for using async functions in short scripts is to declare an anonymous async function and immediately invoke it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(async function() {
    const result = await somethingNetwork();
    console.log("Result", result);
}) ()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What’s the difference between &lt;code&gt;function&lt;/code&gt; and &lt;code&gt;=&amp;gt;&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;In JavaScript, &lt;code&gt;=&amp;gt;&lt;/code&gt; is called a ‘fat arrow’. Fat arrows are a shorthand way to create functions (with some restrictions as below):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function anonymous(name) {
  console.log("Hello", name);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name =&amp;gt; console.log("Hello", name);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apart from anything it saves coming up with lots of different names for anonymous functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations of &lt;code&gt;=&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;As handy as this is, there are some limitations of the fat arrow form.&lt;/p&gt;

&lt;h4&gt;
  
  
  No &lt;strong&gt;this&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A function defined with &lt;code&gt;=&amp;gt;&lt;/code&gt; doesn’t have a &lt;code&gt;this&lt;/code&gt; to reference. A (somewhat contrived) example - &lt;a href="%5Bhttps://codepen.io/arafel/pen/VwpeXQx%5D"&gt;this works&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;withFunction = {
  answer: 42,
  ask: function () {
    console.log("The answer is:", this.answer);
  }
};
withFunction.ask();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Producing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The answer is: 42

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://codepen.io/arafel/pen/gOmPzgO"&gt;This one doesn’t&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;withArrow = {
  answer: 42,
  ask: () =&amp;gt; {
    console.log("The answer is:", this.answer)
  }
}
withArrow.ask();


The answer is: undefined

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A more real-world example of this can be seen with Vuex - if you’re defining a mutation or an action and you use a fat arrow function, it probably won’t work as you expect.&lt;/p&gt;

&lt;p&gt;As an implication of this — because there’s no &lt;code&gt;this&lt;/code&gt;, you can’t use &lt;code&gt;super&lt;/code&gt; either.&lt;/p&gt;

&lt;h4&gt;
  
  
  Can’t be used as constructors.
&lt;/h4&gt;

&lt;p&gt;If you’re defining a class, you must use the full &lt;code&gt;function foo(bar) {}&lt;/code&gt; form.&lt;/p&gt;

&lt;h4&gt;
  
  
  Can’t use &lt;strong&gt;yield&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;I have to admit this hasn’t been a problem for me, I haven’t yet had a cause to use generators.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use &lt;code&gt;(foo) =&amp;gt;&lt;/code&gt; and when to use &lt;code&gt;foo =&amp;gt;&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;foo =&amp;gt; ...&lt;/code&gt; form accepts one &lt;em&gt;and only one&lt;/em&gt; parameter, and even then only if it’s a simple form.&lt;/p&gt;

&lt;p&gt;If you need to indicate no parameters, bracket are required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;() =&amp;gt; console.log("I'm not listening to you");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to pass two, &lt;code&gt;(foo, bar) =&amp;gt; ...&lt;/code&gt; then it needs brackets. So this is fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo =&amp;gt; console.log("I'm a valid construction");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(foo, bar) =&amp;gt; console.log("You gave me", foo, "and", bar);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this is not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo, bar =&amp;gt; console.log("While I won't run!");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Important note
&lt;/h4&gt;

&lt;p&gt;If you need to do anything at all with that single parameter, you need brackets. For example - need to add a TypeScript type? Brackets. Need to destructure? Brackets. Want to supply a default parameter? Brackets. And so on.&lt;/p&gt;

&lt;p&gt;What this boils down to - just because you &lt;em&gt;can_do something, doesn’t mean you _should&lt;/em&gt;. For reference, see &lt;a href="https://raw.githubusercontent.com/getify/You-Dont-Know-JS/1st-ed/es6%20%26%20beyond/fig1.png"&gt;Kyle Simpson’s wonderful flowchart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ilgEcQJg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/getify/You-Dont-Know-JS/1st-ed/es6%2520%2526%2520beyond/fig1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ilgEcQJg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/getify/You-Dont-Know-JS/1st-ed/es6%2520%2526%2520beyond/fig1.png" alt="Things I wish I knew about… JavaScript functions"&gt;&lt;/a&gt;Brackets flowchart&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use &lt;code&gt;foo =&amp;gt; {bar; return baz}&lt;/code&gt; and when &lt;code&gt;foo =&amp;gt; bar&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;If the function body is a single statement, you can omit the braces. Otherwise, the braces are required.&lt;/p&gt;

&lt;p&gt;One statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo =&amp;gt; foo + 1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More than one statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo =&amp;gt; {
    console.log("You gave me", foo);
    return foo + 1;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Closures
&lt;/h2&gt;

&lt;p&gt;Kind of a hybrid - part data, part function. I’ve come across closures before, but JavaScript makes them easier to use than the other languages I’ve spent much time with.&lt;/p&gt;

&lt;p&gt;A closure is essentially a function together with the environment present when it was created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function makeClosure(outerArgument) {
  // Return a function which adds 'outerArgument' to
  // whatever argument it's given.
  return function(innerArgument) {
    return outerArgument + innerArgument;
  }
}

addOne = makeClosure(1)
console.log("Created addOne", addOne);
console.log("Two plus one is", addOne(2));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which outputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node closure.js
Created addOne [Function (anonymous)]
Two plus one is 3

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the function returned is anonymous, simply because we didn’t name It in &lt;code&gt;makeClosure&lt;/code&gt;. It’s quite possible to name it, though I haven’t found it to serve much purpose (so far).&lt;/p&gt;

&lt;p&gt;That’s a trivia example of a closure - for a more useful one see &lt;a href="https://www.solarwinter.net/using-closures-with-axios/"&gt;my other blog post on using Axios&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope that’s been a useful introduction for someone — wish I’d known these when I started with JavaScript!&lt;/p&gt;

</description>
      <category>thingsiwishiknew</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building 'delta' on Alpine Linux</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Fri, 14 May 2021 15:24:07 +0000</pubDate>
      <link>https://dev.to/arafel/building-delta-on-alpine-5277</link>
      <guid>https://dev.to/arafel/building-delta-on-alpine-5277</guid>
      <description>&lt;p&gt;Short post for a change this time. :-)&lt;/p&gt;

&lt;p&gt;I've just built &lt;a href="https://github.com/dandavison/delta"&gt;delta&lt;/a&gt; on an Alpine Linux image. There were a couple of dependencies I had to install before it would build, and one of them puzzled me for a bit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/cargo/"&gt;Cargo&lt;/a&gt; was building and failed on &lt;code&gt;crti.o&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; = note: /usr/lib/gcc/x86_64-alpine-linux-musl/9.3.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find crti.o: No such file or directory
collect2: error: ld returned 1 exit status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;apk search&lt;/code&gt; didn't show anything useful. So, for myself in the future (or anyone else who happens to be searching for this) - the package that provides &lt;code&gt;crti.o&lt;/code&gt; for Alpine is &lt;code&gt;musl-dev&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>alpinelinux</category>
    </item>
    <item>
      <title>How to force TypeScript’s tsc to rebuild your files</title>
      <dc:creator>Paul Walker</dc:creator>
      <pubDate>Sun, 09 May 2021 12:21:32 +0000</pubDate>
      <link>https://dev.to/arafel/how-to-force-typescript-s-tsc-to-rebuild-your-files-fai</link>
      <guid>https://dev.to/arafel/how-to-force-typescript-s-tsc-to-rebuild-your-files-fai</guid>
      <description>&lt;p&gt;I’ve noticed when using &lt;code&gt;tsc&lt;/code&gt; in watch mode that sometimes - especially if a file has been renamed - it doesn’t realise the file needs recompiling. This state persists across reboots, restarting the watcher, all the usual stuff.&lt;/p&gt;

&lt;p&gt;The last time I hit this was when I needed to re-order the seed files I’m using for this project. A file that had been last needed to be first, everything had to move around as I use numbers in the filenames to ensure the seeding order is clear. Mostly it works really well.&lt;/p&gt;

&lt;p&gt;Renaming the files wasn’t a problem, but suddenly the &lt;code&gt;lib/seeds&lt;/code&gt; directory has two sets of seeds in. The results from that - as you’d expect - were not good.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rm lib/seeds/*&lt;/code&gt;, re-run and … nothing. Hmm.&lt;/p&gt;

&lt;p&gt;Maybe it needs prompting to recompile them? &lt;code&gt;touch src/seeds/*&lt;/code&gt; and still nothing.&lt;/p&gt;

&lt;p&gt;Last time this happened I rebooted the laptop and everything. This time, thankfully, I remembered before I got that far.&lt;/p&gt;

&lt;p&gt;The only solution I’ve found? Delete &lt;code&gt;tsconfig.tsbuildinfo&lt;/code&gt; and rebuild. From the filename I assume it seems caches the build info in that file, which is entirely reasonable. It just doesn’t always figure out when the cache is out of date.&lt;/p&gt;

&lt;p&gt;Hope that saves someone a few reboots and some time.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>programming</category>
      <category>computerfrustration</category>
    </item>
  </channel>
</rss>
