<?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>Forem: Ross Angus</title>
    <description>The latest articles on Forem by Ross Angus (@rossangus).</description>
    <link>https://forem.com/rossangus</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%2F144743%2Fe15f00f0-ae1f-4547-ab72-70503c1cc3cc.jpg</url>
      <title>Forem: Ross Angus</title>
      <link>https://forem.com/rossangus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rossangus"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Tue, 07 Apr 2026 10:29:47 +0000</pubDate>
      <link>https://forem.com/rossangus/-1of6</link>
      <guid>https://forem.com/rossangus/-1of6</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/moopet/how-to-talk-to-people-5b82" class="crayons-story__hidden-navigation-link"&gt;How to talk to people&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/moopet" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F31518%2Ffda7f57e-7bcf-4a04-b7bf-529373e2a1cd.jpg" alt="moopet profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/moopet" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Ben Sinclair
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Ben Sinclair
                
              
              &lt;div id="story-author-preview-content-39098" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/moopet" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F31518%2Ffda7f57e-7bcf-4a04-b7bf-529373e2a1cd.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Ben Sinclair&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/moopet/how-to-talk-to-people-5b82" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/moopet/how-to-talk-to-people-5b82" id="article-link-39098"&gt;
          How to talk to people
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ux"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ux&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/language"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;language&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/relations"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;relations&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/moopet/how-to-talk-to-people-5b82" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;6&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/moopet/how-to-talk-to-people-5b82#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              1&lt;span class="hidden s:inline"&gt; comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Mobile tables</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Tue, 07 Oct 2025 14:51:10 +0000</pubDate>
      <link>https://forem.com/rossangus/mobile-tables-fj8</link>
      <guid>https://forem.com/rossangus/mobile-tables-fj8</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://www.istockphoto.com/portfolio/sanlad" rel="noopener noreferrer"&gt;Tolga deniz Aran&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;This is some CSS and JavaScript which allows tables of any size to render on mobile as a simple two-column table, eliminating horizontal scroll.&lt;/p&gt;

&lt;p&gt;Note that I didn't invent (or rediscover) this technique - it's an adaptation from &lt;a href="https://www.nationalgalleries.org/visit" rel="noopener noreferrer"&gt;where I first encountered it&lt;/a&gt;, to take into account edge cases and awkward content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't bore us, get to the chorus:
&lt;/h2&gt;

&lt;p&gt;

&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/QwypaZJ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  How this works
&lt;/h2&gt;

&lt;p&gt;Your table doesn't need any specific markup for this to work, in most cases. The JavaScript makes the assumption that the first row describes the rest of the table and attempts to connect every following table cell with its corresponding header cell.&lt;/p&gt;

&lt;p&gt;It adds a &lt;code&gt;data-label&lt;/code&gt; attribute to the cells, the content of which is a plane text version of whatever appears in the corresponding header.&lt;/p&gt;

&lt;p&gt;When it's time for the table to switch to mobile mode, the first row is hidden and the table and its elements are all switched to &lt;code&gt;display: block&lt;/code&gt;. This creates a veeeeery long table with just table cells, all the way down.&lt;/p&gt;

&lt;p&gt;Inside each table cell are three elements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;before&lt;/code&gt; element, the contents of which is the text within the &lt;code&gt;data-label&lt;/code&gt; attribute&lt;/li&gt;
&lt;li&gt;One or more elements which actually live inside the table cell. This could be a single text node or a heading and a list: I'm not going to tell you how to fill your table cells&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;after&lt;/code&gt; element which has your Grandfather's clearfix on it, like it's 2005&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Using floats for layout? In the 21st century?
&lt;/h2&gt;

&lt;p&gt;Yeah, I tried a few other methods first. Here's how it went:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexbox&lt;/strong&gt;: worked well until I had multiple paragraphs within the same table cell. Then it started forming multiple columns. Go down that road and you've got yourself another table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Positioning&lt;/strong&gt;: worked until the header text wrapped. Then it started getting illegible fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grid&lt;/strong&gt;: no, I didn't try grid. I'll leave that for the professionals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Floating allows two columns of text to be as long as you like and to sit side by side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this breaks down
&lt;/h2&gt;

&lt;p&gt;As you might have seen from the notes inside the table cells, certain &lt;code&gt;colspan&lt;/code&gt; and &lt;code&gt;rowspan&lt;/code&gt; combinations are not handled well by the JavaScript. It would be perfectly possible to make them work but ... I couldn't be bothered. It wouldn't be &lt;em&gt;easy&lt;/em&gt; to get them to work, which is why I've left it as an exercise for the reader (this is what people say, right?).&lt;/p&gt;

&lt;p&gt;Also, as the text which represents the header cell has all the HTML stripped out of it, any markup there will not appear on the mobile version of the table.&lt;/p&gt;

&lt;p&gt;Perhaps there's a version of this where instead of &lt;code&gt;data-label&lt;/code&gt; attributes, the contents of each cell are wrapped in an element, the contents of each corresponding header cell is placed in a preceding element and these two elements are laid out using flexbox. Seems like a faff, if you ask me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workarounds
&lt;/h2&gt;

&lt;p&gt;When I said &lt;q&gt;in most cases&lt;/q&gt; above, you can fix the issue above with some added markup. In HTML, it's possible to explicitly associate a table cell with another cell, to show they have a relationship. We do this using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/td#headers" rel="noopener noreferrer"&gt;the &lt;code&gt;headers&lt;/code&gt; attribute&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When the JavaScript is attempting to pull in the correct header data, it checks for a &lt;code&gt;headers&lt;/code&gt; attribute first, and uses that in preference to its best guess.&lt;/p&gt;

&lt;p&gt;What this means for tables with a lot of &lt;code&gt;rowspan&lt;/code&gt; and &lt;code&gt;colspan&lt;/code&gt; attributes is that you can ensure the correct content appears on mobile by using the &lt;code&gt;headers&lt;/code&gt; attribute. But you only need to do it on cells which appear &lt;em&gt;after&lt;/em&gt; the cells which won't stay in their lane. The ones before should work fine.&lt;/p&gt;

</description>
      <category>tables</category>
      <category>mobile</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Native CSS carousels and positioning controls</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Mon, 08 Sep 2025 12:02:30 +0000</pubDate>
      <link>https://forem.com/rossangus/native-css-carousels-and-positioning-controls-6e0</link>
      <guid>https://forem.com/rossangus/native-css-carousels-and-positioning-controls-6e0</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://pixabay.com/users/jessicachristian-2845652/" rel="noopener noreferrer"&gt;JessicaChristian&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;As part of a silly academic exercise I'm working on, I was digging into pure CSS carousels. Like all lovers of plane English, I started with the implementation &lt;a href="https://css-tricks.com/css-carousels/" rel="noopener noreferrer"&gt;documented on CSS Tricks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;(Through gritted teeth, at the time of writing this only works on Chrome)&lt;/p&gt;

&lt;p&gt;This uses some CSS properties I'd not encountered before - namely &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/anchor-name" rel="noopener noreferrer"&gt;&lt;code&gt;anchor-name&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/position-anchor" rel="noopener noreferrer"&gt;&lt;code&gt;position-anchor&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/position-area" rel="noopener noreferrer"&gt;&lt;code&gt;position-area&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let me be candid for a second: this is a &lt;em&gt;small subset&lt;/em&gt; of the CSS properties from the article which I'm unfamiliar with. But these three are pertinent to my use-case.&lt;/p&gt;

&lt;p&gt;Naturally, I started thinking like a QA engineer and added multiple carousels to my page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxnkf2zaut4abgkrkgom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxnkf2zaut4abgkrkgom.png" alt="The characters Merry and Pippin from the film The Lord of the Rings: The Fellowship of the Ring. Merry looks on as Pippin asks what about second breakfast?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which is where it stopped working:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/OPyqvZe?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It turns out the &lt;code&gt;anchor-name&lt;/code&gt; works in a similar way to an &lt;code&gt;ID&lt;/code&gt; in that there can only be one per page. Except it's specified in the CSS, not the HTML. And just like with non-validating pages which include multiple &lt;code&gt;ID&lt;/code&gt;s of the same value, the browser treats the last one on the page as the &lt;em&gt;real&lt;/em&gt; one.&lt;/p&gt;




&lt;p&gt;
  Edit: a solution to this issue
  &lt;p&gt;Since writing this post, I've read the documentation at &lt;a href="https://web.dev/learn/css/anchor-positioning#scoping_the_potential_anchors" rel="noopener noreferrer"&gt;web.dev&lt;/a&gt; which describes how to use &lt;code&gt;anchor-scope&lt;/code&gt; to address exactly this issue.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;Looking at the DOM of the carousel, the answer seemed simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;aria-roledescription=&lt;/span&gt;&lt;span class="s"&gt;"carousel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  ::scroll-button(inline-start)
  ::scroll-button(inline-end)
  ::scroll-marker-group
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"group"&lt;/span&gt; &lt;span class="na"&gt;aria-roledescription=&lt;/span&gt;&lt;span class="s"&gt;"slide"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give &lt;code&gt;[aria-roledescription="carousel']&lt;/code&gt; a &lt;code&gt;position&lt;/code&gt; of &lt;code&gt;relative&lt;/code&gt; and position the two &lt;code&gt;::scroll-button&lt;/code&gt;s using &lt;code&gt;absolute&lt;/code&gt;. But that &lt;q&gt;absolutely&lt;/q&gt; did not work:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/vENPRRQ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I went around the houses for a bit with this and &lt;a href="https://cursor.com/agents" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; (I know, I know...) pointed me at a bunch of new CSS which sort of replicates positioning, but ... not?&lt;/p&gt;

&lt;p&gt;This seemed a really dumb problem. The buttons were &lt;em&gt;right there&lt;/em&gt; in the markup. Having them render in the correct spot seemed trivial. Then I remembered how my old colleague &lt;a href="https://github.com/jameshaddrillvs" rel="noopener noreferrer"&gt;James&lt;/a&gt; described me once: &lt;q&gt;You do interesting things with old technology&lt;/q&gt;. So I got rid of all that modern CSS and floated the right button:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/VYvRQgM?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It's possible that I've encountered a bug in Chrome's rendering engine here - the DOM and the layout engine disagree with each other. Cursor's only useful contribution to this question was that this happens with bleeding-edge features such as this.&lt;/p&gt;

</description>
      <category>css</category>
      <category>carousel</category>
      <category>chrome</category>
    </item>
    <item>
      <title>Chapter 8: HTML part three</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Thu, 13 Mar 2025 11:21:14 +0000</pubDate>
      <link>https://forem.com/rossangus/chapter-8-html-part-three-56jk</link>
      <guid>https://forem.com/rossangus/chapter-8-html-part-three-56jk</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://www.instagram.com/cottonbro/" rel="noopener noreferrer"&gt;cottonbro&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Last time, we continued working with &lt;em&gt;PostHTML&lt;/em&gt; and did the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learned how to use the &lt;code&gt;picture&lt;/code&gt; tag in HTML5&lt;/li&gt;
&lt;li&gt;Created a new fragment using the &lt;code&gt;picture&lt;/code&gt; tag and passed it a couple of arguments&lt;/li&gt;
&lt;li&gt;Wrote some code to rebuild pages in the &lt;code&gt;dist&lt;/code&gt; directory when we update them in the &lt;code&gt;src&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Put the code which calls &lt;em&gt;PostHTML&lt;/em&gt; into its own module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We ended last time with some tasks still to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handling changes to fragments&lt;/li&gt;
&lt;li&gt;Getting the &lt;code&gt;dist&lt;/code&gt; directory set up correctly for Our Hypothetical Second Developer&lt;/li&gt;
&lt;li&gt;Adding content inside of modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end of the last chapter, we wrote a &lt;code&gt;watch-html&lt;/code&gt; task which used the &lt;code&gt;onchange&lt;/code&gt; package to call a script. That script (&lt;code&gt;html-update.js&lt;/code&gt;) attempted to call &lt;em&gt;PostHTML&lt;/em&gt; and pass it the input and output paths for our HTML file.&lt;/p&gt;

&lt;p&gt;This worked well, if the file being updated represented a HTML page. But what if we edit a HTML fragment when the task is watching?&lt;/p&gt;

&lt;h3&gt;
  
  
  Watching fragments
&lt;/h3&gt;

&lt;p&gt;We don't know how often a particular fragment appears in different HTML pages, so the brute force approach is to rebuild all HTML pages when any single fragment is updated. This isn't ideal for a couple of reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We'll be rebuilding some pages which don't need to be rebuilt&lt;/li&gt;
&lt;li&gt;This operation requires a lot of writing to the hard drive of individual files and will be both slow and won't scale well&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is unfortunate and I can't see an easy fix. If we could use &lt;em&gt;browserSync&lt;/em&gt; to tell us which page was currently open in the browser, we could just update that page, but that would still leave all the other pages out of date.&lt;/p&gt;




&lt;p&gt;
  Static site builders
  &lt;p&gt;While this course will teach you how to build a functional static site using &lt;em&gt;Node.js&lt;/em&gt; and other technologies, if you need to build a large, complicated site or application, &lt;a href="https://jamstack.org/generators/" rel="noopener noreferrer"&gt;other technologies might be a better fit&lt;/a&gt;. However, the benefit of using this course is that you'll understand every part of the code you put together and you should be able to bolt other technology onto it and upgrade parts of it in the future.&lt;/p&gt;



&lt;/p&gt;




&lt;p&gt;This means we need to write a task which rebuilds &lt;em&gt;all&lt;/em&gt; HTML pages. Hmm. This is reminding me a lot of what we did with &lt;em&gt;Sharp&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating all HTML
&lt;/h3&gt;

&lt;p&gt;Create a new file called &lt;code&gt;generate-all-html.js&lt;/code&gt; inside the &lt;code&gt;tools&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;It should look like this:&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="nx"&gt;getFiles&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;./get-files.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;callPostHTML&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;./call-posthtml.js&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;allHTMLPaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;allHTMLPaths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// callPostHTML(something, somethingElse);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is pretty similar to &lt;code&gt;compress-all-images.js&lt;/code&gt;, but instead of importing &lt;code&gt;write-images.js&lt;/code&gt;, we're importing our new function &lt;code&gt;callPostHTML&lt;/code&gt;. Let's see what that console logs when we point it at something.&lt;/p&gt;

&lt;p&gt;In a terminal, cancel any currently running commands and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node tools/generate-all-html.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll probably see something like this in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./src/views/index.html
./src/views/about/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;path&lt;/code&gt; is needs a couple of tweaks, to get it in the format which &lt;code&gt;callPostHTML()&lt;/code&gt; expects. Create two new variables inside the &lt;code&gt;map()&lt;/code&gt; function and change the &lt;code&gt;console.log()&lt;/code&gt; so it shows them to us:&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="nx"&gt;allHTMLPaths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inputPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;views/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist/&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// callPostHTML(something, somethingElse);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;
  Chaining the replace function
  &lt;p&gt;Note that with the &lt;code&gt;outputPath&lt;/code&gt; variable we've chained together two &lt;code&gt;replace()&lt;/code&gt; functions, so we get to first remove &lt;code&gt;views&lt;/code&gt; then replace &lt;code&gt;src/&lt;/code&gt; with &lt;code&gt;dist/&lt;/code&gt;.&lt;/p&gt;



&lt;/p&gt;




&lt;p&gt;Run the file using &lt;code&gt;node tools/generate-all-html.js&lt;/code&gt; and you should see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src/views/about/index.html dist/about/index.html
src/views/index.html dist/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect! Let's plug those variables into &lt;code&gt;callPostHTML()&lt;/code&gt; and try it out. Update the inside of your &lt;code&gt;map()&lt;/code&gt; function to read:&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="nx"&gt;allHTMLPaths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inputPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;views/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;callPostHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputPath&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;Now delete the contents of your &lt;code&gt;dist&lt;/code&gt; folder and run &lt;code&gt;node tools/generate-all-html.js&lt;/code&gt; (remember to try the up arrow trick to save yourself some time). It won't generate out the images, javascript or CSS, but you should (eventually) see all your HTML files appear inside the &lt;code&gt;dist&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing how much HTML to generate
&lt;/h3&gt;

&lt;p&gt;We have two different scripts which do similar tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;html-update.js&lt;/code&gt; runs when the user makes a change to any HTML file inside either &lt;code&gt;views&lt;/code&gt; or &lt;code&gt;fragments&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;generate-all-html.js&lt;/code&gt; can be manually called in order to generate all the HTML files inside &lt;code&gt;views&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We already worked out that if we edit a file inside the &lt;code&gt;fragments&lt;/code&gt; directory, we need to generate all HTML view files from scratch. How about we treat &lt;code&gt;generate-all-html.js&lt;/code&gt; as a module, rather than a script?&lt;/p&gt;

&lt;p&gt;This means we can import it into &lt;code&gt;html-update.js&lt;/code&gt; and trigger a build if we notice a change to a fragment.&lt;/p&gt;

&lt;p&gt;Changing &lt;code&gt;generate-all-html.js&lt;/code&gt; into a module means assigning it to a variable, then exporting that variable as the default. Like this:&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="nx"&gt;getFiles&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;./get-files.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;callPostHTML&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;./call-posthtml.js&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;generateAllHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allHTMLPaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;allHTMLPaths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inputPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;views/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;callPostHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;generateAllHTML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we try and run it directly, it won't do anything. But we can call it from a different file instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Back to fragments again
&lt;/h3&gt;

&lt;p&gt;Let's add a new variable to &lt;code&gt;html-update.js&lt;/code&gt;, just after where we instantiate &lt;code&gt;editedDistPath&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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;triggerEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileEvent&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HTML change detected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDistPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&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;editedSrcPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&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;editedDistPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Do we need to rebuild all HTML files?&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rebuildAll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;editedSrcPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fragments&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new line looks a bit weird, doesn't it? This stores the result of a JavaScript comparison into a variable. What this means is that the comparison will either be &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;. Rather than storing the comparison itself, the variable will instead store the &lt;em&gt;boolean value&lt;/em&gt; of the comparison. The comparison itself looks for the string &lt;code&gt;fragments&lt;/code&gt; inside the &lt;code&gt;editedSrcPath&lt;/code&gt; variable. If this string is &lt;em&gt;not&lt;/em&gt; present, the &lt;code&gt;indexOf()&lt;/code&gt; function will return &lt;code&gt;-1&lt;/code&gt;. If it's present then the function will returns the index within the string where the fragment appears. So by checking if it is &lt;em&gt;greater&lt;/em&gt; than minus one, we can determine that the string appears &lt;em&gt;somewhere&lt;/em&gt; inside &lt;code&gt;editedSrcPath&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;rebuildAll&lt;/code&gt; to decide what to do
&lt;/h3&gt;

&lt;p&gt;Put an &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; statement around the call to &lt;code&gt;callPostHTML()&lt;/code&gt;, like this:&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rebuildAll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// rebuild all HTML&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;// Pass `callPostHTML()` our paths&lt;/span&gt;
   &lt;span class="nf"&gt;callPostHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;editedSrcPath&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;editedDistPath&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;fileName&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;extName&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's import our new &lt;code&gt;generateAllHTML()&lt;/code&gt; function into this file to take care of that first part.&lt;/p&gt;

&lt;p&gt;At the top of &lt;code&gt;html-update.js&lt;/code&gt;, import the function like this:&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="nx"&gt;generateAllHTML&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;./generate-all-html.js&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;Now call it from inside the &lt;code&gt;if&lt;/code&gt; statement like this:&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rebuildAll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;generateAllHTML&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Fragment &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileEvent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;added&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;changed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; successfully`&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="nf"&gt;callPostHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;editedSrcPath&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;editedDistPath&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;fileName&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;extName&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`HTML page &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileEvent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;added&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;changed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; successfully`&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;
  Console logs
  &lt;p&gt;We're reusing &lt;code&gt;fileEvent&lt;/code&gt; (which we use before to determine if we needed to do any work) so that the terminal will either report &lt;code&gt;HTML page added successfully&lt;/code&gt; or &lt;code&gt;HTML page changed successfully&lt;/code&gt;. At this point, we're just showing off.&lt;/p&gt;

&lt;p&gt;It's considered bad form to leave &lt;code&gt;console.log()&lt;/code&gt; commands in live code but here, we're using them to update future developers on what's going on. These logs will never appear on the live site, they will only appear in the terminal when we're using &lt;em&gt;Node&lt;/em&gt;. I'm sure you know how frustrating it is to watch a blinking cursor and wonder if the code has crashed or it's doing something but just keeping you in the dark.&lt;/p&gt;

&lt;p&gt;As an exception to this rule, &lt;a href="https://medium.com/" rel="noopener noreferrer"&gt;clever tech companies&lt;/a&gt; use &lt;code&gt;console.log()&lt;/code&gt; to reach out to developers who are snooping around there site, curious about how it works.&lt;/p&gt;



&lt;/p&gt;




&lt;h3&gt;
  
  
  Testing it out
&lt;/h3&gt;

&lt;p&gt;Let's test this out! Start the watch task with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run watch-html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;src/fragments/picture.html&lt;/code&gt; and add an extra line break at the end of the file, then hit save.&lt;/p&gt;

&lt;p&gt;You should see &lt;code&gt;Fragment changed successfully&lt;/code&gt; in the terminal and your &lt;code&gt;dist&lt;/code&gt; directory should add in an &lt;code&gt;about&lt;/code&gt; directory with a html file inside it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building all HTML files on first run
&lt;/h3&gt;

&lt;p&gt;There's still another use-case we haven't handled yet: what happens if our hypothetical second developer installs the site for the first time? We need to generate all the HTML files for them at the same time.&lt;/p&gt;

&lt;p&gt;We can't just run &lt;code&gt;generate-all-html.js&lt;/code&gt; because that does nothing but export the function &lt;code&gt;generateAllHTML()&lt;/code&gt; now. Let's account for this inside &lt;code&gt;html-update.js&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;How about if we call &lt;code&gt;html-update.js&lt;/code&gt; from the command line with no arguments, it should generate all the HTML files? Just before the &lt;code&gt;triggerEvents.includes(fileEvent)&lt;/code&gt; &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; statement, add this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// If this script is called with no arguments, rebuild all HTML files&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;generateAllHTML&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;Or if we want to use a slightly more modern way to express this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// If this script is called with no arguments, rebuild all HTML files&lt;/span&gt;
&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;generateAllHTML&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Remember that the JavaScript parser goes from left to right and keeps going until it finds a statement which equates as &lt;code&gt;false&lt;/code&gt;. This means if the first two statements are both &lt;code&gt;true&lt;/code&gt;, then the function &lt;code&gt;generateAllHTML()&lt;/code&gt; will be called.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating &lt;code&gt;package.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Edit your &lt;code&gt;html-build&lt;/code&gt; command in &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"html-build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node tools/html-update.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will call &lt;code&gt;html-update.js&lt;/code&gt; but without arguments. Let's add this new command into our &lt;code&gt;prepare&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-prod&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;node tools/compress-all-images.js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npx webpack --config webpack.prod.mjs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run html-build&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;
  Why not call the script twice?
  &lt;p&gt;You might notice that we edited the &lt;code&gt;html-build&lt;/code&gt; command so that it called &lt;code&gt;node tools/html-update.js&lt;/code&gt;. Then we called &lt;em&gt;that&lt;/em&gt; command from a different command (as part of &lt;code&gt;prepare&lt;/code&gt;). Here's what they look like next to each other:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run html-build
node tools/html-update.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why not call &lt;code&gt;node tools/html-update.js&lt;/code&gt; twice?&lt;/p&gt;

&lt;p&gt;By calling &lt;code&gt;html-update.js&lt;/code&gt; only once in the file, it's similar to assigning it to a variable. This means should we ever rename the file or point it at a new script, there's just a single reference to it and it helps to reduce confusing errors.&lt;/p&gt;



&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;To test the rebuild command out, delete the contents of your &lt;code&gt;dist&lt;/code&gt; directory, cancel the watch task in your terminal and run either &lt;code&gt;npm run prepare&lt;/code&gt; or &lt;code&gt;npm install&lt;/code&gt; from the terminal (both run the same command). This should regenerate all of the contents of the &lt;code&gt;dist&lt;/code&gt; directory from scratch, including the HTML files, images, CSS and JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing complicated markup to a HTML module
&lt;/h2&gt;

&lt;p&gt;I want to return briefly to the first fragment we wrote - &lt;code&gt;head.html&lt;/code&gt; to show you a trick. We currently call this module like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello Worm&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;module&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"src/fragments/head.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets us specify a &lt;code&gt;title&lt;/code&gt; tag which is custom to the page and also add in any additional CSS and JavaScript we want. But there's another way to do the same thing. Remove the old &lt;code&gt;head&lt;/code&gt; tag from the HTML page entirely and change the rest of the markup so it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
&lt;span class="nt"&gt;&amp;lt;module&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"src/fragments/head.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello Worm&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;posthtml-modules&lt;/em&gt; lets us pass content rich with HTML tags between the opening and closing &lt;code&gt;module&lt;/code&gt; tags. But this won't work until we tell PostHTML we want to pass data this way. So update &lt;code&gt;posthtml.json&lt;/code&gt; to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"posthtml-modules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/views"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"locals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"htmlnano"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(the new bit is &lt;code&gt;"locals": true&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Now change the &lt;code&gt;head.html&lt;/code&gt; fragment so it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/main.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;content&amp;gt;&amp;lt;/content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that anything inside the &lt;code&gt;module&lt;/code&gt; tag on individual HTML pages will be output where that &lt;code&gt;&amp;lt;content&amp;gt;&amp;lt;/content&amp;gt;&lt;/code&gt; tag sits.&lt;/p&gt;

&lt;p&gt;Now regenerate the HTML by running this from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run html-build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The markup should be the same (although the order of the tags within the &lt;code&gt;head&lt;/code&gt; is slightly different). This technique lets us simplify the tags on each page in the &lt;code&gt;src/views&lt;/code&gt; directory while still giving us freedom to add tags within the &lt;code&gt;head&lt;/code&gt; section of the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nesting modules
&lt;/h2&gt;

&lt;p&gt;Let's create a new fragment which contains a legal disclaimer. Create a file called &lt;code&gt;disclaimer.html&lt;/code&gt; inside &lt;code&gt;fragments&lt;/code&gt;. It should read as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;small&amp;gt;&lt;/span&gt;The following site is intended for educational purposes only and should not be taken as legal advice. Your home may be at risk if you do not keep up repayments. Other websites are available. This website is presented "as is" and should not be printed on a t-shirt. Yea, though I walk through the valley of the shadow of death, I will fear no evil: for thou art with me; thy rod and thy staff they comfort me. The views presented on this site do not necessarily reflect those of Tim Berners-Lee, inventor of Alexander Graham Bell's Electric Internet.&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create a second fragment called &lt;code&gt;footer.html&lt;/code&gt;. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;footer&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;content&amp;gt;&amp;lt;/content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can import these into the home page (&lt;code&gt;src/views/index.html&lt;/code&gt;) just before the &lt;code&gt;script&lt;/code&gt; tag at the bottom of the &lt;code&gt;body&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;module&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"src/fragments/footer.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;module&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"src/fragments/disclaimer.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might seem a little pointless - why not put this into one fragment? Well, at some point you might have more than one style of footer or you might want to re-use your legal disclaimer elsewhere on the site (such as on a sign up form).&lt;/p&gt;

&lt;p&gt;Wait! That's not all we can do!&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;src/views/index.html&lt;/code&gt; change your call to the module to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;module&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"src/fragments/footer.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Home page legal disclaimer&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... then in &lt;code&gt;src/fragments/footer.html&lt;/code&gt;, update it to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;footer&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;content&amp;gt;&amp;lt;/content&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;module&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"./disclaimer.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can pass custom markup to the footer when we call it, but the legal disclaimer can be the same on every page. Perhaps some pages require other kinds of legal disclaimers, I don't know. I've no idea who your site is for.&lt;/p&gt;

&lt;p&gt;Note that the path to &lt;code&gt;disclaimer.html&lt;/code&gt; is different in the above example because it's looking for it in the same directory as where the &lt;code&gt;footer.html&lt;/code&gt; lives.&lt;/p&gt;

&lt;p&gt;Look, not all of my examples can be good, OK?&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We started the chapter by working out how we should handle what happens when a developer edits a fragment&lt;/li&gt;
&lt;li&gt;What we need to do is very similar to what we did with images, so we reused some code from that&lt;/li&gt;
&lt;li&gt;We changed &lt;code&gt;generate-all-html.js&lt;/code&gt; into a module and started treating it like a function, rather than a script to be run&lt;/li&gt;
&lt;li&gt;This means &lt;code&gt;html-update.js&lt;/code&gt; can either update one file or all the files, depending on what arguments are used&lt;/li&gt;
&lt;li&gt;We changed the way fragments worked so we could call them, but add large chunks of markup inside&lt;/li&gt;
&lt;li&gt;Then we tried a couple of other ways of nesting fragments where content appears on more than one page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/Ross-Angus/node-js-for-developers/tree/chapter-8" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;View Chapter 8 code snapshot on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Quiz
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/vEYWJOL?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The end
&lt;/h2&gt;

&lt;p&gt;Thanks for making it all the way to the end of this course. I don't care what anyone else says, I think you're a genius. I want to thank Ewan Burns for inspiring this course and helping me develop it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stretch goals
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Auto-resizing images
&lt;/h4&gt;

&lt;p&gt;For certain kinds of module - for example hero panels - we need images which are a specific width and height. What if you had different rules for different directories inside the &lt;code&gt;src/img&lt;/code&gt; directory? For example, if a developer dropped a large image inside a directory called &lt;code&gt;src/img/hero&lt;/code&gt;, &lt;code&gt;image-compress.js&lt;/code&gt; could notice this and call a module which &lt;a href="https://sharp.pixelplumbing.com/api-resize" rel="noopener noreferrer"&gt;resized the image&lt;/a&gt; automatically, before generating out the different formats.&lt;/p&gt;

&lt;p&gt;What if we took this further? Why force mobile users to download the desktop images? Expand out the &lt;code&gt;picture&lt;/code&gt; tag so that it uses CSS media queries to show different images at different browser widths. That way, each hero panel might generate nine different images, which would only display at certain breakpoints.&lt;/p&gt;

&lt;h4&gt;
  
  
  Linting
&lt;/h4&gt;

&lt;p&gt;What if we checked our code before we checked it in? Using so-called &lt;a href="https://eslint.org/docs/latest/use/getting-started" rel="noopener noreferrer"&gt;linting&lt;/a&gt; packages will warn us if we add obvious bugs or bad practice?&lt;/p&gt;

&lt;h4&gt;
  
  
  TypeScript
&lt;/h4&gt;

&lt;p&gt;What if we wanted to write a version of JavaScript which is more robust and can show us errors as we type? &lt;a href="https://www.npmjs.com/package/typescript" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt; was originally developed to enforce data types more strictly in JavaScript but has since become somewhat of an industry standard for large web applications.&lt;/p&gt;

</description>
      <category>node</category>
      <category>npm</category>
      <category>posthtml</category>
      <category>posthtmlmodules</category>
    </item>
    <item>
      <title>Chapter 7: HTML part two</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Tue, 11 Mar 2025 10:49:06 +0000</pubDate>
      <link>https://forem.com/rossangus/chapter-7-html-part-two-15k</link>
      <guid>https://forem.com/rossangus/chapter-7-html-part-two-15k</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://www.pexels.com/@blue-bird/" rel="noopener noreferrer"&gt;Blue Bird&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Last time, we installed &lt;em&gt;PostHTML&lt;/em&gt; plus a few of its close, personal friends. Then we started cutting up our web page into modules. We also learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Currently, MVC is the dominant paradigm for application development on the web&lt;/li&gt;
&lt;li&gt;JAMSTACK is MVC but with an added static website&lt;/li&gt;
&lt;li&gt;How to handle &lt;em&gt;Node&lt;/em&gt; security concerns&lt;/li&gt;
&lt;li&gt;How to configure &lt;em&gt;PostHTML&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Splitting HTML into modules can save us effort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's improve how images are rendered next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Images in HTML5
&lt;/h2&gt;

&lt;p&gt;When we build web pages as a developer, we're used to the idea of making the same content fit wildly different devices. From phones to wide-screens. But what about images? Images have an inherent width and height and while modern browsers are good at downscaling and even stretching images to fit, this feels less than ideal. What other options do we have?&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;srcset&lt;/code&gt; attribute
&lt;/h3&gt;

&lt;p&gt;HTML5 brough &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images" rel="noopener noreferrer"&gt;some additional attributes for the &lt;code&gt;img&lt;/code&gt; tag&lt;/a&gt; which maintain backward compatibility but also allow us to smuggle in additional paths to larger or smaller images in the same tag. Here's an example of the markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"example-image-default.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px) 480px, 800px"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"example-image-480w.jpg 480w, example-image-default.jpg 800w"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"This is an example image."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go through those attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src&lt;/code&gt; - this is just your common-or-garden path to the default image&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sizes&lt;/code&gt; - this borrows the syntax from &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/width" rel="noopener noreferrer"&gt;CSS media queries&lt;/a&gt; to define thresholds for switching between images. Specifically, the width of the viewport of the browser. In this example, there are two:

&lt;ul&gt;
&lt;li&gt;The size between zero and 600px (&lt;code&gt;max-width: 600px&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The size above 600px&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;sizes&lt;/code&gt; (again) So what does the &lt;code&gt;480px, 800px&lt;/code&gt; mean? This is how much space is left for the image, once all the padding and so on around the image is taken into account.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;srcset&lt;/code&gt; - this echoes a lot of the above but gives us paths to images. Each image and width is separated from the next with a comma. So we have two here, &lt;code&gt;example-image-480w.jpg&lt;/code&gt; which has a width of &lt;code&gt;480&lt;/code&gt; pixels and &lt;code&gt;example-image-default.jpg&lt;/code&gt; which has a width of &lt;code&gt;800&lt;/code&gt; pixels. Note that browsers know nothing about an image's width before they load it in, so this is useful for the browser's layout engine to know so it can allocate space while it's rendering the page.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;alt&lt;/code&gt; - the alternative text which represents the image before it loads in&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;I ... &lt;em&gt;do not like&lt;/em&gt; this syntax. It's complicated and ugly and very difficult for humans to type out. However, we have an alternative!&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;picture&lt;/code&gt; tag
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;picture&lt;/code&gt; tag in HTML5 split this out into mutliple &lt;code&gt;source&lt;/code&gt; tags, inside a wrapper tag of &lt;code&gt;picture&lt;/code&gt;. It looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 799px)"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"example-image-small.jpg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 800px)"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"example-image-large.jpg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"example-image-small.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Just imagine a really cool image."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I find this much easier to read. But that's not all it can do! It can also give us a top-to-bottom way to check if a browser supports a particular image format. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- JPEG XL - poor support currently,
   so sharp support is experimental --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"example-image.jxl"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/jxl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"example-image.avif"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"example-image.webp"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"example-image.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Just imagine a really cool image."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the web browser parses this picture tag, it starts from the top and loads the first image which it has support for. Then, like an &lt;em&gt;if/else&lt;/em&gt; statement, it skips the rest of the code and continues.&lt;/p&gt;

&lt;p&gt;Legacy browsers will render the &lt;code&gt;img&lt;/code&gt; tag. But modern browsers will still honour its &lt;code&gt;alt&lt;/code&gt; text in the final markup.&lt;/p&gt;

&lt;p&gt;This finally gives us a use for all the additional Sharp image pipelines we created in chapter 4 and never did anything with the output files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Revisiting our markup
&lt;/h3&gt;

&lt;p&gt;Currently, inside &lt;code&gt;index.html&lt;/code&gt;, we reference a single image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/img/example-01.webp"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"An animal, yesterday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's use a &lt;code&gt;picture&lt;/code&gt; tag, but let's store it in a new fragment. This is cool, I promise!&lt;/p&gt;

&lt;p&gt;Create a new file in &lt;code&gt;src/fragments&lt;/code&gt; called &lt;code&gt;picture.html&lt;/code&gt;. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"{{ path }}.avif"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"{{ path }}.webp"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ path }}.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"{{ alt_text }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's with all the moustaches? The double curly braces are a little like escape characters. But instead of telling the parser to ignore the contents, it indicates that the language has changed.&lt;/p&gt;

&lt;p&gt;Now go back to your &lt;code&gt;index.html&lt;/code&gt; page and replace the paragraph with the image tag inside it with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;module&lt;/span&gt;
  &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"src/fragments/picture.html"&lt;/span&gt;
  &lt;span class="na"&gt;locals=&lt;/span&gt;&lt;span class="s"&gt;'{
    "path": "/img/example-01",
    "alt_text": "An animal, yesterday"
  }'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works exactly like the method we used to import the fragment into the &lt;code&gt;head&lt;/code&gt; tag, but this time, we're calling the &lt;code&gt;picture.html&lt;/code&gt; module and passing data along with the request.&lt;/p&gt;

&lt;p&gt;Specifically, we're passing some JSON via the &lt;code&gt;locals&lt;/code&gt; attribute. Note that because JSON &lt;em&gt;has&lt;/em&gt; to have both the names and the values surrounded by double-quotation marks (and HTML isn't fussy if we use single or double quotes), the &lt;code&gt;locals&lt;/code&gt; attribute has a single quote mark surrounding the JSON data.&lt;/p&gt;

&lt;p&gt;Once these variables reach the &lt;code&gt;picture.html&lt;/code&gt; fragment, we extract them and add them to the markup.&lt;/p&gt;

&lt;p&gt;We can't use hyphens to separate words for these variable names ("kebab case"), so we've used underscores (this is known as "snake case").&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations of this approach
&lt;/h3&gt;

&lt;p&gt;While this allows us to put our markup into components and pass data between them, it doesn't allow us to use logic to change the markup depending upon what input data is fed.&lt;/p&gt;

&lt;p&gt;For a more robust approach, we'd probably need to install a templating language of some kind, such as &lt;a href="https://www.npmjs.com/package/twig" rel="noopener noreferrer"&gt;Twig&lt;/a&gt;, &lt;a href="https://ejs.co/" rel="noopener noreferrer"&gt;EJS&lt;/a&gt;, &lt;a href="https://handlebarsjs.com/" rel="noopener noreferrer"&gt;Handlebars&lt;/a&gt;, &lt;a href="https://pugjs.org/" rel="noopener noreferrer"&gt;Pug&lt;/a&gt; or &lt;a href="https://mustache.github.io/" rel="noopener noreferrer"&gt;Mustache&lt;/a&gt; (this is not a complete list!). Reading &lt;a href="https://github.com/posthtml/posthtml-modules" rel="noopener noreferrer"&gt;the documentation for posthtml-modules&lt;/a&gt;, you'll notice it doesn't mention &lt;code&gt;package.json&lt;/code&gt; or any of the approaches we've used in this guide. Instead, the examples are in JavaScript and we've advised to add this to our &lt;em&gt;Node&lt;/em&gt; application.&lt;/p&gt;




&lt;p&gt;
  Node apps
  &lt;p&gt;The approach we've used up until now is to avoid task runners as much as possible and string together commands until we have a site which meets our needs. And this works! But another approach is to &lt;a href="https://dev.to/kartiknair/how-to-build-a-simple-static-site-generator-using-node-js-4l01"&gt;write code in JavaScript to be run by Node which produces our site&lt;/a&gt;. This works in a similar way to how we created &lt;code&gt;image-compress.js&lt;/code&gt; and ran that in &lt;code&gt;package.json&lt;/code&gt; using the command &lt;code&gt;node tools/image-compress.js&lt;/code&gt; rather than &lt;code&gt;npm run ...&lt;/code&gt;, like we did for the other commands.&lt;/p&gt;

&lt;p&gt;We could also use a dedicated static site generator, &lt;a href="https://jamstack.org/generators/" rel="noopener noreferrer"&gt;of which there is no shortage&lt;/a&gt;. But this course was intended to give you an introduction to some of the tools which are used to make these packages and how you might string them together.&lt;/p&gt;



&lt;/p&gt;




&lt;h3&gt;
  
  
  Adding a second page
&lt;/h3&gt;

&lt;p&gt;Sadly, &lt;em&gt;PostHTML&lt;/em&gt; and its pals don't work in the same way as the &lt;em&gt;sass&lt;/em&gt; package does: we call an instance of &lt;em&gt;PostHTML&lt;/em&gt; and it processes one file at a time. This means we need to add a couple of new requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The watch task needs to know when any source page has changed, and to update the corresponding distribution page&lt;/li&gt;
&lt;li&gt;We need a task which rebuilds &lt;em&gt;all&lt;/em&gt; of the HTML pages, for Our Hypothetical Second Developer, on first-run&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hey! This feels familiar. Isn't this what we had to do with &lt;em&gt;sharp&lt;/em&gt; too? Perhaps we can reuse code!&lt;/p&gt;

&lt;h3&gt;
  
  
  Calling &lt;em&gt;PostHTML&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;There's two different ways to call &lt;em&gt;PostHTML&lt;/em&gt; (strictly speaking, we're calling &lt;em&gt;posthtml-cli&lt;/em&gt;, but whatever):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calling it and pointing it at a configuration file (&lt;code&gt;posthtml.json&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Calling it and pointing it at a configuration file, but specifying the input and output file at the same time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently, the input and output files are hard-coded into &lt;code&gt;posthtml.json&lt;/code&gt;, here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/views/**/*.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"posthtml-modules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/views"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"htmlnano"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's get rid of the &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;output&lt;/code&gt; nodes from this file, so all it does is establishes the defaults for the plugins. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"posthtml-modules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/views"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"htmlnano"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's write a script which calls &lt;em&gt;PostHTML&lt;/em&gt; with the right paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  The page has updated
&lt;/h3&gt;

&lt;p&gt;The first requirement we discovered was to update a file in the &lt;code&gt;dist&lt;/code&gt; directory once the corresponding &lt;code&gt;src/views&lt;/code&gt; file changes.&lt;/p&gt;

&lt;p&gt;Inside your &lt;code&gt;tools&lt;/code&gt; directory, create a new file called &lt;code&gt;html-update.js&lt;/code&gt;. It should look like this:&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;argv&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;node:process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Destructuring the Array from Node which includes data we need&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// White-list of events which should cause PostHTML to rebuild pages&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;triggerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// If the wrong kind of event triggers this script, do nothing&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;triggerEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileEvent&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HTML change detected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&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;This is basically &lt;code&gt;image-compress.js&lt;/code&gt;, but with some of the guts removed.&lt;/p&gt;

&lt;p&gt;Edit your &lt;code&gt;package.json&lt;/code&gt; &lt;code&gt;watch-html&lt;/code&gt; task so it now reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"watch-html"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onchange &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/views&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/fragments&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -- node tools/html-update.js {{file}} {{event}}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should ring a bell as well - this is very similar to the &lt;code&gt;watch-images&lt;/code&gt; command, even down to the &lt;code&gt;{{file}}&lt;/code&gt; and &lt;code&gt;{{event}}&lt;/code&gt; arguments. Now run this task from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run watch-html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It won't open a browser window (because &lt;code&gt;serve&lt;/code&gt; isn't involved) but we can fiddle around with files and see what happens.&lt;/p&gt;




&lt;p&gt;
  Renaming variables and arguments
  &lt;p&gt;You might notice that we're passing an argument called &lt;code&gt;{{file}}&lt;/code&gt; via &lt;code&gt;package.json&lt;/code&gt; and it's being called &lt;code&gt;srcPath&lt;/code&gt; inside &lt;code&gt;html-update.js&lt;/code&gt;. This is normal - as we're destructing &lt;code&gt;argv&lt;/code&gt; into different variables, they are given names which are valid within the scope of this file. The JavaScript doesn't care what they were called before they arrived, it'll use whatever name you want.&lt;/p&gt;

&lt;p&gt;Because we're dealing with input (source) and output (distribution) filenames here, I've tweaked the variable names to reflect this.&lt;/p&gt;



&lt;/p&gt;




&lt;h3&gt;
  
  
  Adding a new folder
&lt;/h3&gt;

&lt;p&gt;Add a new folder inside &lt;code&gt;src/views&lt;/code&gt; called &lt;code&gt;about&lt;/code&gt;. Nothing will appear in the terminal because it's looking out for new &lt;code&gt;html&lt;/code&gt; files. Take a copy of your &lt;code&gt;index.html&lt;/code&gt; and put it inside the &lt;code&gt;about&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;The terminal should report this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTML change detected src&lt;span class="se"&gt;\v&lt;/span&gt;iews&lt;span class="se"&gt;\a&lt;/span&gt;bout&lt;span class="se"&gt;\i&lt;/span&gt;ndex.html add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;
  Default files and directories
  &lt;p&gt;Perhaps you're wondering why we created a new folder, rather than just a file called &lt;code&gt;about.html&lt;/code&gt;. Here's why: directories on web servers can contain multiple different files inside but the server can be configured to look for a &lt;em&gt;default&lt;/em&gt; file. &lt;code&gt;index.html&lt;/code&gt; is often one of these default file names. This means that you can specify the directory, but you don't have to specify the file name inside it.&lt;/p&gt;

&lt;p&gt;In terms of URLs, this is the difference between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://www.mycoolsite.com/about.html
&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;http://www.mycoolsite.com/about/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second one looks better, is easier to say if someone is talking about your site at a party and (to some degree) disguises the technology you used to create the site.&lt;/p&gt;

&lt;p&gt;It also means that if you change the technology in the future, the URLs can stay the same, which will save you a lot of headaches because you won't need to set up redirects.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;The &lt;code&gt;srcPath&lt;/code&gt; part of the &lt;code&gt;console.log()&lt;/code&gt; looks wrong, as usual. Hey - we can reuse &lt;code&gt;get-dist-path.js&lt;/code&gt; and use that to sort it! Import it at the top of &lt;code&gt;html-update.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getDistPath&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;./get-dist-path.js&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;Now let's use it like we did before. Replace your &lt;code&gt;console.log()&lt;/code&gt; with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDistPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HTML change detected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember how we &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment" rel="noopener noreferrer"&gt;destructed&lt;/a&gt; the object before? Now we'll see what we get back from &lt;code&gt;getDistPath()&lt;/code&gt;. Try renaming &lt;code&gt;src/views/about/index.html&lt;/code&gt; to &lt;code&gt;src/views/about/index2.html&lt;/code&gt;. You should see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTML change detected src&lt;span class="se"&gt;\v&lt;/span&gt;iews&lt;span class="se"&gt;\a&lt;/span&gt;bout&lt;span class="se"&gt;\i&lt;/span&gt;ndex2.html ./dist/views/about index2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have most of the information here, but not the file extension. This wasn't important when we were dealing with images, but let's change &lt;code&gt;/tools/get-dist-path.js&lt;/code&gt; so it sends it though. Luckily, it already exists as a variable in &lt;code&gt;getDistPath()&lt;/code&gt;, so we just need to update the &lt;code&gt;return&lt;/code&gt; statement from:&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fileName&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...to:&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;extName&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;
  Ignoring passed data
  &lt;p&gt;Even though we've changed what &lt;code&gt;getDistPath()&lt;/code&gt; returns, we don't need to alter &lt;code&gt;image-compress.js&lt;/code&gt;. It's cherry-picking data from the object which &lt;code&gt;getDistPath()&lt;/code&gt; returns and doesn't care that we've stuffed even more data inside.&lt;/p&gt;



&lt;/p&gt;




&lt;p&gt;Back in &lt;code&gt;html-update.js&lt;/code&gt;, update the code inside your &lt;code&gt;if&lt;/code&gt; statement to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDistPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HTML change detected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now rename &lt;code&gt;src/about/index2.html&lt;/code&gt; back to &lt;code&gt;src/about/index.html&lt;/code&gt; and look in your terminal. You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTML change detected src&lt;span class="se"&gt;\v&lt;/span&gt;iews&lt;span class="se"&gt;\a&lt;/span&gt;bout&lt;span class="se"&gt;\i&lt;/span&gt;ndex.html ./dist/views/about index .html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Couple of fixes to those paths:&lt;/p&gt;

&lt;p&gt;1) The source path needs &lt;code&gt;\&lt;/code&gt; replaced with &lt;code&gt;/&lt;/code&gt;&lt;br&gt;
2) The distribution path needs &lt;code&gt;views/&lt;/code&gt; removed from it (this directory was useful within &lt;code&gt;src&lt;/code&gt; so we could keep all the HTML files in one place, but we need to mix things up on the live site)&lt;/p&gt;

&lt;p&gt;Let's create new variables with those alterations. After your destructing of &lt;code&gt;getDistPath()&lt;/code&gt;, add a couple of new variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDistPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&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;editedSrcPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&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;editedDistPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;
  Why are we creating new variables?
  &lt;p&gt;You might reasonably look at the above code and wonder why we don't just change the original variables, rather than creating a new variable. You know, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDistPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;srcPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;distPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(we need to use &lt;code&gt;let&lt;/code&gt; rather than &lt;code&gt;const&lt;/code&gt; because the value is changing)&lt;/p&gt;

&lt;p&gt;This code is frowned upon in &lt;a href="https://sapegin.me/blog/avoid-reassigning-variables/" rel="noopener noreferrer"&gt;some corners of The Internet&lt;/a&gt; because we're never quite sure what the value of &lt;code&gt;srcPath&lt;/code&gt; might be at any particular moment. &lt;code&gt;srcPath&lt;/code&gt; is initialised right at the top of &lt;code&gt;html-update.js&lt;/code&gt; and here, more than half way down, becomes something different.&lt;/p&gt;



&lt;br&gt;
&lt;/p&gt;




&lt;h3&gt;
  
  
  Call PostHTML
&lt;/h3&gt;

&lt;p&gt;We need a new script which does for HTML what &lt;code&gt;write-images.js&lt;/code&gt; does for images (reminder: &lt;code&gt;write-image.js&lt;/code&gt; calls &lt;em&gt;Sharp&lt;/em&gt; multiple times and outputs different images). Create a new file called &lt;code&gt;call-posthtml.js&lt;/code&gt; inside the &lt;code&gt;tools&lt;/code&gt; directory. It should look like this:&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;exec&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;child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callPostHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputFilePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputFilePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`npx posthtml &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inputFilePath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -o &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputFilePath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -c posthtml.json`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;err&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="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`exec error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;exec&lt;/code&gt; function allows us to run commands in the terminal from within JavaScript. It's like reaching outside of the script and interacting directly with the terminal. Needless to say, JavaScript running outside of &lt;em&gt;Node&lt;/em&gt; can't do this.&lt;/p&gt;

&lt;p&gt;This is the line which runs in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx posthtml &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;inputFilePath&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;outputFilePath&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; posthtml.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quick reminder of what this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npx&lt;/code&gt; - gives us access to functions within &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;posthtml&lt;/code&gt; - calls &lt;em&gt;PostHTML&lt;/em&gt; from within &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;${inputFilePath}&lt;/code&gt; - the JavaScript within &lt;code&gt;call-posthtml.js&lt;/code&gt; will replace this with a string we pass it, which represents the path to the &lt;em&gt;input&lt;/em&gt; file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-o&lt;/code&gt; this flag means that the following path represents the &lt;em&gt;output&lt;/em&gt; file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;${outputFilePath}&lt;/code&gt; - the JavaScript within &lt;code&gt;call-posthtml.js&lt;/code&gt; will replace this with a string we pass it, which represents the path to the &lt;em&gt;output&lt;/em&gt; file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c&lt;/code&gt; this flag means the next value represents the configuration file we'd like to use with &lt;em&gt;PostHTML&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;posthtml.json&lt;/code&gt; - this is the configuration file we've added already, which sets up the plugins used by &lt;em&gt;PostHTML&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;
  Lowering a flag
  &lt;p&gt;&lt;a href="https://github.com/posthtml/posthtml-cli" rel="noopener noreferrer"&gt;The documentation for &lt;em&gt;posthtml-cli&lt;/em&gt;&lt;/a&gt; tells us that we can pass a path to &lt;em&gt;posthtml&lt;/em&gt; and it will be assumed to be the input. An output needs a &lt;code&gt;-o&lt;/code&gt; flag before it. And a &lt;code&gt;-c&lt;/code&gt; flag points to a configuration file.&lt;/p&gt;

&lt;p&gt;Perhaps you're wondering why I'm spelling this out. Actually, I don't care if you're wondering or not. Because I spent a couple of hours baffled as to why my input wasn't being found when I used a &lt;code&gt;-i&lt;/code&gt; flag, &lt;a href="https://github.com/posthtml/posthtml?tab=readme-ov-file#cli" rel="noopener noreferrer"&gt;as specified in the documentation&lt;/a&gt;. And it turns out I just needed to omit the &lt;code&gt;-i&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;Thanks for listening. I needed to get that off my chest. Real talk: sometimes documentation isn't accurate.&lt;/p&gt;



&lt;/p&gt;




&lt;p&gt;The &lt;code&gt;exec&lt;/code&gt; function has a callback function which is run if there's an error. But let's not dwell on past mistakes. We're just going to log the error to the terminal and move on with our lives:&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;err&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="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`exec error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we export &lt;code&gt;callPostHTML&lt;/code&gt; so we can use it elsewhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calling &lt;code&gt;callPostHTML&lt;/code&gt; from inside &lt;code&gt;html-update.js&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;html-update.js&lt;/code&gt; so that it looks like this:&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;argv&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;node:process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getDistPath&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;./get-dist-path.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;callPostHTML&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;./call-posthtml.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Destructuring the Array from Node which includes data we need&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// White-list of events which should cause PostHTML to rebuild pages&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;triggerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// If the wrong kind of event triggers this script, do nothing&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;triggerEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDistPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&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;editedSrcPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&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;editedDistPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Pass `callPostHTML()` all our paths&lt;/span&gt;
  &lt;span class="nf"&gt;callPostHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;editedSrcPath&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;editedDistPath&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;fileName&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;extName&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new bit is we import &lt;code&gt;callPostHTML&lt;/code&gt; then call it with the correct paths, rather than just logging them to the console.&lt;/p&gt;

&lt;p&gt;Cancel, then re-run the &lt;code&gt;npm run watch-html&lt;/code&gt; task in the terminal. Now delete the contents of &lt;code&gt;dist&lt;/code&gt; and rename &lt;code&gt;src/views/index.html&lt;/code&gt; to &lt;code&gt;src/views/index2.html&lt;/code&gt;. You should see &lt;code&gt;index2.html&lt;/code&gt; appear inside your &lt;code&gt;dist&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Rename it back to &lt;code&gt;index.html&lt;/code&gt;. You should now see both &lt;code&gt;index.html&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; &lt;code&gt;index2.html&lt;/code&gt;. This is working as expected!&lt;/p&gt;

&lt;p&gt;There's still a little bit more to do here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handling changes to fragments&lt;/li&gt;
&lt;li&gt;Getting the &lt;code&gt;dist&lt;/code&gt; directory set up correctly for Our Hypothetical Second Developer&lt;/li&gt;
&lt;li&gt;Adding content inside of modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still, we've covered quite a lot this chapter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to make images in HTML more dynamic&lt;/li&gt;
&lt;li&gt;How to pass arguments to &lt;em&gt;PostHTML&lt;/em&gt; modules&lt;/li&gt;
&lt;li&gt;Replicating our methods with images to apply them to HTML files&lt;/li&gt;
&lt;li&gt;Calling &lt;em&gt;PostHTML&lt;/em&gt; from the terminal with inputs and outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/Ross-Angus/node-js-for-developers/tree/chapter-7" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;View Chapter 7 code snapshot on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Quiz
&lt;/h1&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/dPyVOve?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>node</category>
      <category>npm</category>
      <category>posthtml</category>
      <category>posthtmlmodules</category>
    </item>
    <item>
      <title>Chapter 6 HTML part one</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Tue, 11 Mar 2025 09:09:08 +0000</pubDate>
      <link>https://forem.com/rossangus/nodejs-for-developers-course-chapter-6-html-part-one-8eo</link>
      <guid>https://forem.com/rossangus/nodejs-for-developers-course-chapter-6-html-part-one-8eo</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://www.linkedin.com/in/shkrabaanthony/" rel="noopener noreferrer"&gt;Antoni Shkraba&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Last time, we installed &lt;em&gt;Webpack&lt;/em&gt; so we could process our JavaScript. With more JavaScript. We learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Webpack&lt;/em&gt; does for JavaScript what SASS does for CSS (and SASS)&lt;/li&gt;
&lt;li&gt;How to configure &lt;em&gt;Webpack&lt;/em&gt; for development&lt;/li&gt;
&lt;li&gt;Two different types of module imports in JavaScript&lt;/li&gt;
&lt;li&gt;Why we might use &lt;em&gt;NPX&lt;/em&gt; as well as &lt;em&gt;NPM&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;How to configure &lt;em&gt;Webpack&lt;/em&gt; for production&lt;/li&gt;
&lt;li&gt;How to keep these two configuration files DRY&lt;/li&gt;
&lt;li&gt;What IIFEs are and why we use them&lt;/li&gt;
&lt;li&gt;Writing client-side JavaScript modules and the correct way to import them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This time, we're finally going to work on generating HTML files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with HTML
&lt;/h2&gt;

&lt;p&gt;We're on the home straight now. We've done images, JavaScript and CSS. Just HTML to go. Soon, you'll be able to write your &lt;em&gt;own&lt;/em&gt; code and discard me like an old sock.&lt;/p&gt;

&lt;p&gt;HTML is less of a focus for a lot of Single Page Applications (called "SPA" by its fam). This is because most of the work of rendering snippets of HTML is taken up by JavaScript - the HTML is really just a wrapper into which JavaScript plonks markup.&lt;/p&gt;

&lt;p&gt;But you might not want to do that. Wouldn't it be nice to split HTML into modules, so that repeated parts (such as the header and the footer) could be found in one place and then pulled into the page?&lt;/p&gt;

&lt;p&gt;To achieve this goal, we'll need to import four different modules. Spoilers follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/posthtml" rel="noopener noreferrer"&gt;PostHTML&lt;/a&gt; gives JavaScript the power to mess around with HTML&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/posthtml-cli" rel="noopener noreferrer"&gt;posthtml-cli&lt;/a&gt; allows us to control &lt;em&gt;PostHTML&lt;/em&gt; using &lt;em&gt;Node&lt;/em&gt;, so we can string all this together&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/posthtml-modules" rel="noopener noreferrer"&gt;posthtml-modules&lt;/a&gt; lets us cut up our page into tiny wee bits (also known as modules)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/htmlnano" rel="noopener noreferrer"&gt;htmlnano&lt;/a&gt; takes a HTML page and removes all the repeated whitespace, just like we've already done with our JavaScript and CSS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These packages aren't as popular as the ones we've been using up until now. That's for a couple of reasons:&lt;/p&gt;

&lt;h3&gt;
  
  
  MVC
&lt;/h3&gt;

&lt;p&gt;Single-page applications don't need to generate multiple HTML pages. They build them on-the-fly.&lt;/p&gt;

&lt;p&gt;MVC stands for "Model View Controller". This is one way to split up an application's code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Model" is basically the same thing as the data. It's what's feeding into the application and needs to be displayed&lt;/li&gt;
&lt;li&gt;"View" is closest to a template. It's the structure which sits around the data and gives it shape and allows the user to visually group together similar parts of the page&lt;/li&gt;
&lt;li&gt;"Controller" is all the business logic which decides what happens when the user interacts with the user interface in some way, such as clicking a "like" button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way of splitting up the code is another example of &lt;a href="https://en.wikipedia.org/wiki/Separation_of_concerns" rel="noopener noreferrer"&gt;separation of concerns&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The MVC approach is dominating the application market at the time of writing. The three main front-end frameworks which do this are &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt;, &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue&lt;/a&gt; and &lt;a href="https://angular.dev/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; but there are many, many more.&lt;/p&gt;

&lt;p&gt;Front-end MVC frameworks run &lt;em&gt;client-side&lt;/em&gt;. This means all the JavaScript is parsed by the web browser. Our approach with &lt;em&gt;PostHTML&lt;/em&gt; will be rendered &lt;em&gt;server&lt;/em&gt; side (also called SSR)&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. Because MVC frameworks handle everything which PostHTML and its pals do, PostHTML is not as popular as React.&lt;/p&gt;

&lt;h3&gt;
  
  
  JAMSTACK
&lt;/h3&gt;

&lt;p&gt;The second reason that PostHTML isn't a very popular package is JAMSTACK. &lt;a href="https://jamstack.org/" rel="noopener noreferrer"&gt;JAMSTACK&lt;/a&gt; sits on top of an MVC application and allows it to build out individual pages to create a multi-paged site. These pages exist as actual HTML pages for users and search engines to visit but once a modern browser lands on any single page, it kind of fakes a reload when the user requests to move to a different page.&lt;/p&gt;

&lt;p&gt;The data for just that next page is pulled into the MVC application and the current page is rebuilt with the new content. Just in case the user looks at the address bar, that is rewritten with the new URL (even though that flat HTML page wasn't loaded).&lt;/p&gt;

&lt;p&gt;This approach has a few advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The smallest possible parcel of data is sent across the internet&lt;/li&gt;
&lt;li&gt;The web browser usually doesn't have to re-render the whole page, just a small part of it&lt;/li&gt;
&lt;li&gt;The user doesn't have to suffer the existential doubt with comes when they look at an empty screen for a fraction of a second&lt;/li&gt;
&lt;li&gt;Because the pages all exist on the web server, the site can easily be spidered by search engines and users can enter the site on "deep" pages&lt;/li&gt;
&lt;li&gt;Some code files split the JavaScript into what is running on the server and what is running in the browser. This means we can manipulate sensitive data (such as API keys) with JavaScript without the risk of exposing this to a public URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JAMSTACK frameworks do both server side rendering &lt;em&gt;and&lt;/em&gt; client-side rendering. Twice the work!&lt;/p&gt;

&lt;p&gt;But all of this is a course in it's own right and won't be covered here. Sorry! Let's get back to the more traditional, static approach for now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing &lt;em&gt;PostHTML&lt;/em&gt;, &lt;em&gt;posthtml-cli&lt;/em&gt;, &lt;em&gt;posthtml-modules&lt;/em&gt; and &lt;em&gt;htmlnano&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Let's install all four of the packages we want at once. In a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; posthtml posthtml-cli posthtml-modules htmlnano
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! You have unlocked a new achievement! "Downloaded your first security vulnerability". Note that your terminal says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;5 moderate severity vulnerabilities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What can you do about this? Glad you asked - usually, &lt;em&gt;bugger-all&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;
  Side quest: Node security woes
  &lt;br&gt;
&lt;a&gt;&lt;/a&gt;So we can run &lt;code&gt;npm audit&lt;/code&gt; from a terminal to see a list of these issues. It tells us:&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;got  &amp;lt;11.8.5
Severity: moderate
Got allows a redirect to a UNIX socket - https://github.com/advisories/GHSA-pfrx-2q88-qq97
fix available via &lt;span class="sb"&gt;`&lt;/span&gt;npm audit fix &lt;span class="nt"&gt;--force&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
Will &lt;span class="nb"&gt;install &lt;/span&gt;posthtml-cli@0.7.7, which is a breaking change
node_modules/got
  package-json  &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt;6.5.0
  Depends on vulnerable versions of got
  node_modules/package-json
    latest-version  0.2.0 - 5.1.0
    Depends on vulnerable versions of package-json
    node_modules/latest-version
      update-notifier  0.2.0 - 5.1.0
      Depends on vulnerable versions of latest-version
      node_modules/update-notifier
        posthtml-cli  &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;0.8.0
        Depends on vulnerable versions of update-notifier
        node_modules/posthtml-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Wait, so all versions of &lt;em&gt;posthtml-cli&lt;/em&gt; &lt;em&gt;greater&lt;/em&gt; than 0.7.7 depend upon a vulnerable version of &lt;em&gt;got&lt;/em&gt;? And the fix is to install an &lt;em&gt;older&lt;/em&gt; version of &lt;em&gt;posthtml-cli&lt;/em&gt;? How does this make sense? Also to downgrade to the "safe" version of &lt;em&gt;posthtml-cli&lt;/em&gt; is a breaking change. &lt;em&gt;How&lt;/em&gt; broken exactly? No idea what to do with that information.&lt;/p&gt;
&lt;h2&gt;
  
  
  Should I even care?
&lt;/h2&gt;

&lt;p&gt;The vulnerability is to &lt;em&gt;posthtml_cli&lt;/em&gt; which helps us interact with &lt;em&gt;PostHTML&lt;/em&gt; using the terminal. &lt;em&gt;posthtml_cli&lt;/em&gt; sits within the &lt;code&gt;devDependencies&lt;/code&gt; node of &lt;code&gt;package.json&lt;/code&gt;. Which means it never makes it to the live site. It wouldn't make any &lt;em&gt;sense&lt;/em&gt; on the live site. So this situation is only risky if a hacker had direct access to your computer, ran the site locally, then used the compromised version of &lt;em&gt;got&lt;/em&gt; to ... do something?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pro-tip&lt;/em&gt;: if a hacker has control over your computer, the gig is already up. They will be too busy stealing your data to bother running a dev environment.&lt;/p&gt;

&lt;p&gt;Instead of forcing a breaking change, let's try the gentle method and see how far it gets us. In a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm audit fix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did that fix the issue? No. Did it break anything else? Also no. Did it make us feel better? Little bit. Let's move on.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;




&lt;h3&gt;
  
  
  Configuring &lt;em&gt;PostHTML&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Remember when we installed &lt;em&gt;Webpack&lt;/em&gt; and it came with it's own configuration file which needed to sit in the root of our project? &lt;em&gt;PostHTML&lt;/em&gt;'s got one of those too. It's called &lt;code&gt;posthtml.json&lt;/code&gt; and looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/views/**/*.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"posthtml-modules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/views"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"htmlnano"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So create that, if you could. It sits along site &lt;code&gt;package.json&lt;/code&gt; and all the rest in the root of the &lt;em&gt;Node&lt;/em&gt; application.&lt;/p&gt;

&lt;p&gt;You might have noticed that this is pointing at a directory which doesn't exist yet - a sub-folder of &lt;code&gt;src&lt;/code&gt; called &lt;code&gt;views&lt;/code&gt;. Why is this?&lt;/p&gt;

&lt;p&gt;We need somewhere to put all our HTML where it won't get mixed up with all the other file types. For example, if we pointed &lt;em&gt;PostHTML&lt;/em&gt; at the &lt;code&gt;src&lt;/code&gt; directory and then set up a watch task to look for changes, this task would trigger every time we did something inside the &lt;code&gt;scss&lt;/code&gt; or &lt;code&gt;js&lt;/code&gt; folders too.&lt;/p&gt;

&lt;p&gt;The reason it's called &lt;code&gt;views&lt;/code&gt; echoes what I said earlier about MVC - the &lt;code&gt;views&lt;/code&gt; part of MVC is the presentational HTML.&lt;/p&gt;




&lt;p&gt;
  Wildcards
  &lt;br&gt;
You might spot a bunch of asterisks hanging out on this line of &lt;code&gt;posthtml.json&lt;/code&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;:


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"input": "src/views/**/*.html",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this context, the asterisks are behaving as a "wildcard" (in normal JavaScript, a single asterisk would act as a multiplication symbol). Wildcard characters can stand for anything, like in that card game I've never played because it doesn't interest me. So the path here translates into English as "any files which end with a &lt;code&gt;html&lt;/code&gt; file extension inside the &lt;code&gt;src/views&lt;/code&gt; directory or any of the sub-directories (or no sub-directory)". So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Any sub-directory ↓↓ ↓ any file name
"input": "src/views/**/*.html",
or no sub-directory ↑↑
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might reasonably assume that this pattern works in the same way as the &lt;em&gt;sass&lt;/em&gt; package does - that it watches a directory and replicates anything it finds there into a different directory. Sadly, this isn't the case. But we'll fix that issue in a bit.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;Finally, the &lt;code&gt;plugins&lt;/code&gt; node calls two other packages we just installed, &lt;code&gt;posthtml-modules&lt;/code&gt; and &lt;code&gt;htmlnano&lt;/code&gt;. &lt;em&gt;posthtml-modules&lt;/em&gt; has &lt;a href="https://www.npmjs.com/package/posthtml-modules#options" rel="noopener noreferrer"&gt;a number of different options we can specify&lt;/a&gt;. We've simply pointed it at our &lt;code&gt;views&lt;/code&gt; directory and asked that when it first starts up, it should automatically run.&lt;/p&gt;

&lt;p&gt;The second plugin is &lt;em&gt;htmlnano&lt;/em&gt; the configuration of which is ... &lt;a href="https://htmlnano.netlify.app/config" rel="noopener noreferrer"&gt;not well documented&lt;/a&gt;. So we've passing it an empty configuration object, which is the same as telling it to run with the default configuration. If we don't pass it &lt;em&gt;something&lt;/em&gt;, it won't run at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the new commands
&lt;/h3&gt;

&lt;p&gt;Back in &lt;code&gt;package.json&lt;/code&gt;, let's set up our develop and build tasks for HTML files. They'll sit inside the &lt;code&gt;scripts&lt;/code&gt; node and look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"html-build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"posthtml -c posthtml.json"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"watch-html"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onchange &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/views&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/fragments&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -- npm run html-build"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've added them inside the &lt;code&gt;scripts&lt;/code&gt; node at the bottom.&lt;/p&gt;

&lt;p&gt;Hang on - there's &lt;em&gt;another&lt;/em&gt; new directory referenced here called &lt;code&gt;fragments&lt;/code&gt;. What's that all about?&lt;/p&gt;

&lt;h3&gt;
  
  
  More new directories
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;PostHTML&lt;/em&gt; will process any and all &lt;code&gt;html&lt;/code&gt; files inside our &lt;code&gt;views&lt;/code&gt; directory and we also want to use little fragments of HTML to include into these pages. By splitting these out into a new, &lt;em&gt;sibling&lt;/em&gt; directory, we can ensure that these wee bits never end up as &lt;code&gt;html&lt;/code&gt; files in their own right on the live site. So the directory structure of the &lt;code&gt;src&lt;/code&gt; folder should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
🗀 src
  🗀 fragments
  🗀 img
  🗀 js
  🗀 scss
  🗀 views
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both the &lt;code&gt;fragments&lt;/code&gt; and the &lt;code&gt;views&lt;/code&gt; directories need to be scrutinised by the watch task, so if we change a file inside either, the page will rebuild.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the new &lt;code&gt;package.json&lt;/code&gt; commands mean
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;html-build&lt;/code&gt; task invokes &lt;code&gt;posthtml&lt;/code&gt; with a &lt;code&gt;c&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"html-build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"posthtml -c posthtml.json"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can't find this in the documentation but my guess is that it stands for "configuration". The next argument passed is the path to the configuration JSON, so this checks out.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;onchange&lt;/em&gt; watching two different directories
&lt;/h3&gt;

&lt;p&gt;The other new command we've just added to &lt;code&gt;package.json&lt;/code&gt; does something a little new:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"watch-html"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onchange &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/views&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/fragments&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -- npm run html-build"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package &lt;em&gt;onchange&lt;/em&gt; can watch two different directories at the same time, and then run the specified command if either changes. This means we can use the &lt;code&gt;watch-html&lt;/code&gt; command to look into both &lt;code&gt;src/views&lt;/code&gt; and &lt;code&gt;src/fragments&lt;/code&gt; and run the &lt;code&gt;html-build&lt;/code&gt; command if any files in either directory changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the new commands to the &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;prepare&lt;/code&gt; commands
&lt;/h3&gt;

&lt;p&gt;Your &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;prepare&lt;/code&gt; commands should include our new commands, so they now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run serve&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-dev&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run watch-images&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run watch-js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run watch-html&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-prod&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;node tools/image-compress.js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npx webpack --config webpack.prod.mjs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run html-build&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Moving and splitting the HTML
&lt;/h3&gt;

&lt;p&gt;Drag your &lt;code&gt;index.html&lt;/code&gt; from &lt;code&gt;dist&lt;/code&gt; to &lt;code&gt;src/views&lt;/code&gt;. Let's break it up!&lt;/p&gt;

&lt;p&gt;Create a new file inside your &lt;code&gt;fragments&lt;/code&gt; directory called &lt;code&gt;head.html&lt;/code&gt;. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/main.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now edit the &lt;code&gt;head&lt;/code&gt; tag of your &lt;code&gt;index.html&lt;/code&gt; so it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello Worm&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;module&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"src/fragments/head.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've moved all the tags which are common to all pages on the site and put them inside one fragment. That path doesn't make a lot of sense, does it? The way we usually navigate between directories is to use &lt;code&gt;../&lt;/code&gt; to move into the parent directory and to just add the sub-directory name in the same way as &lt;code&gt;src&lt;/code&gt; is used here. This implies that all of the &lt;code&gt;src&lt;/code&gt; directory lives under the &lt;code&gt;views&lt;/code&gt; directory, which we know isn't the case.&lt;/p&gt;

&lt;p&gt;This is happening because &lt;code&gt;posthtml-modules&lt;/code&gt; is the package which is interpreting this path and as far as it's concerned, it's running in the root of the &lt;em&gt;Node&lt;/em&gt; application. And the root of the &lt;em&gt;Node&lt;/em&gt; application is where the &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;dist&lt;/code&gt; directories live. But this certainly is confusing.&lt;/p&gt;

&lt;p&gt;Let's check if this all works.&lt;/p&gt;

&lt;p&gt;In a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm. Nothing there. Let's debug this. Is there anything in the &lt;code&gt;dist&lt;/code&gt; directory yet? No? Of course - &lt;em&gt;PostHTML&lt;/em&gt; only builds a new HTML file once there's been a change, which there hasn't been since we started the web server.&lt;/p&gt;

&lt;p&gt;Add a space, then delete it, in &lt;code&gt;src/views/index.html&lt;/code&gt;. Wait for the terminal to finish, then refresh your web browser.&lt;/p&gt;

&lt;p&gt;Hopefully the web page should appear. Now change the &lt;code&gt;Hello Worm&lt;/code&gt; text in &lt;code&gt;index.html&lt;/code&gt; however you like, to make sure it updates automatically once you save.&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;head.html&lt;/code&gt;, break the path to the CSS by changing the line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/main.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/main2.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you save the file, the text should change to &lt;em&gt;Times New Roman&lt;/em&gt;, the most boring of typefaces.  Change it back and the correct typeface should reappear!&lt;/p&gt;

&lt;p&gt;Now open up the &lt;code&gt;index.html&lt;/code&gt; file inside the &lt;code&gt;dist&lt;/code&gt; directory. It should look like this, more or less:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"no-js"&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-GB"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello Worm&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/main.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello Worm&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/img/example-01.webp"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"An animal, yesterday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/js/main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah, barely room to swing a cat in there. Just the way we like it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;What we've got so far does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows us to create a page with server-side includes (that's another way of describing what &lt;code&gt;posthtml-modules&lt;/code&gt; does)&lt;/li&gt;
&lt;li&gt;Converts our SCSS to CSS then minifys it&lt;/li&gt;
&lt;li&gt;Concatenates (joins together) and minifies our JavaScript files&lt;/li&gt;
&lt;li&gt;Converts our images to next-gen versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a great place to start! But I'd like to handle those images in a slightly better way which uses both &lt;em&gt;posthtml-modules&lt;/em&gt; and all those next-generation image files we generated with &lt;em&gt;sharp&lt;/em&gt;. Let's do that in the next part.&lt;/p&gt;

&lt;p&gt;Let's review what we learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Currently, MVC is the dominant paradigm for application development on the web&lt;/li&gt;
&lt;li&gt;JAMSTACK is MVC but with an added static website&lt;/li&gt;
&lt;li&gt;How to handle &lt;em&gt;Node&lt;/em&gt; security concerns&lt;/li&gt;
&lt;li&gt;How to configure &lt;em&gt;PostHTML&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Splitting HTML into modules can save us effort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/Ross-Angus/node-js-for-developers/tree/chapter-6" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;View Chapter 6 code snapshot on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Quiz
&lt;/h1&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/emYGzGj?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Technically, we'll be creating the static HTML pages &lt;em&gt;before&lt;/em&gt; the code even reaches the server. We're doing it on our local machine. Perhaps there's a word for this, but I couldn't find one in a cursory search. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Perhaps you're wondering what swear word you're supposed to type there instead. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>node</category>
      <category>npm</category>
      <category>posthtml</category>
      <category>posthtmlmodules</category>
    </item>
    <item>
      <title>Chapter 5: JavaScript</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Mon, 10 Mar 2025 15:06:28 +0000</pubDate>
      <link>https://forem.com/rossangus/nodejs-for-developers-course-chapter-5-javascript-4526</link>
      <guid>https://forem.com/rossangus/nodejs-for-developers-course-chapter-5-javascript-4526</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://www.tiktok.com/@audyofcourse" rel="noopener noreferrer"&gt;Audy of Course&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Last time, we finished optimising our images:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Used the &lt;code&gt;fs&lt;/code&gt; module from &lt;em&gt;Node&lt;/em&gt; to elegantly handle nested directories inside &lt;code&gt;src/img&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Learned about short-circuit evaluation in JavaScript&lt;/li&gt;
&lt;li&gt;Stole a utility function from &lt;a href="https://www.learnwithparam.com/" rel="noopener noreferrer"&gt;Param Harrison&lt;/a&gt; to get a list of files and folders in &lt;em&gt;Node&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;We looked at how we might split up a long, complicated script into modules&lt;/li&gt;
&lt;li&gt;We learned the shorthand which can be used in JavaScript if the name and property are the same&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we're going to write some more JavaScript to process our JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript
&lt;/h2&gt;

&lt;p&gt;We want to do a couple of things with our JavaScript:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We want to write bleeding-edge JavaScript, including this season's must-have methods&lt;/li&gt;
&lt;li&gt;We want it to run fast on a decent proportion of the web&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We're going to be writing our JavaScript using ES6 modules which allows us to split them into many different units. And while &lt;a href="https://caniuse.com/?search=es6" rel="noopener noreferrer"&gt;support for ES6 is excellent&lt;/a&gt;, downloading all of those files individually isn't great for a couple of reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If your host doesn't support HTTP2 then each file requires a new connection, all of which adds overhead&lt;/li&gt;
&lt;li&gt;The end user needs to download all of your comments and whitespace too, which they probably aren't interested in&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Guess what? There's A Package For That™!&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing Webpack
&lt;/h3&gt;

&lt;p&gt;Webpack was invented for one reason and one reason only: to take a bunch of JavaScript modules and squash them into one package. Which is exactly what we're going to use it for.&lt;/p&gt;




&lt;p&gt;
  Historical aside
  &lt;p&gt;Remember when I talked about &lt;a href="https://dev.to/rossangus/nodejs-for-developers-course-chapter-0-installing-and-updating-node-2kim#node-focus"&gt;how Ryan Dahl made Node.js and intended it to have one purpose and that other people would build on top of it&lt;/a&gt;? What this originally led to was what are called "task runners" - early examples included &lt;a href="https://gruntjs.com/" rel="noopener noreferrer"&gt;Grunt&lt;/a&gt; (which wrote files to disk) and &lt;a href="https://gulpjs.com/" rel="noopener noreferrer"&gt;Gulp&lt;/a&gt; (which ran files from memory). &lt;a href="https://www.npmjs.com/package/gulp" rel="noopener noreferrer"&gt;Gulp's still pretty popular&lt;/a&gt;! (but nowhere as popular as &lt;a href="https://www.npmjs.com/package/webpack" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Task runners are complicated to configure and as we've learned, we can mostly get by without them these days.&lt;/p&gt;

&lt;p&gt;However, when &lt;em&gt;Webpack&lt;/em&gt; came along, people started using it as a task runner. And it works. So everything we've done so far could probably be achieved using &lt;em&gt;Webpack&lt;/em&gt;. But I don't think we need to do that &lt;a href="https://edspencer.me.uk/posts/2019-06-20-should-you-use-webpack-for-building-scss/" rel="noopener noreferrer"&gt;and I'm not alone&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But don't take my word for it. By all means, check out the &lt;a href="https://webpack.js.org/loaders/sass-loader/" rel="noopener noreferrer"&gt;&lt;em&gt;sass-loader&lt;/em&gt; for Webpack&lt;/a&gt;. It won't be covered in this guide. If anyone has done this successfully and come back here to tell me why I'm wrong, here's your medal:&lt;/p&gt;

&lt;p&gt;🎖&lt;/p&gt;



&lt;/p&gt;




&lt;h3&gt;
  
  
  Installing &lt;em&gt;Webpack&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Let's use the street-slang &lt;em&gt;NPM&lt;/em&gt; version. In a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; webpack webpack-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how we're installing both &lt;em&gt;Webpack&lt;/em&gt; &lt;em&gt;and&lt;/em&gt; &lt;em&gt;webpack-cli&lt;/em&gt; on the same line. Nice little trick there.&lt;/p&gt;

&lt;p&gt;What's this &lt;code&gt;webpack-cli&lt;/code&gt; business? Don't worry about it. It's just &lt;em&gt;Webpack&lt;/em&gt;'s wee pal. Won't go anywhere without it.&lt;/p&gt;

&lt;p&gt;This should plonk a reference to &lt;em&gt;Webpack&lt;/em&gt; and &lt;em&gt;webpack-cli&lt;/em&gt; in our &lt;code&gt;package.json&lt;/code&gt;. You know the score by now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring &lt;em&gt;Webpack&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Remember how we had to create &lt;code&gt;.gitignore&lt;/code&gt;? Sometimes packages are &lt;em&gt;so&lt;/em&gt; special that they don't want to add their configuration into &lt;code&gt;package.json&lt;/code&gt; and demand their own special configuration files. &lt;em&gt;Webpack&lt;/em&gt;'s like that&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bespoke configuration file
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Webpack&lt;/em&gt;'s configuration file is called &lt;code&gt;webpack.config.mjs&lt;/code&gt; and it should sit in the root of your Node application - as a sibling to &lt;code&gt;package.json&lt;/code&gt; and the rest of the gang.&lt;/p&gt;

&lt;p&gt;Here's what it should look like:&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="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fileURLToPath&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;node:url&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;__filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fileURLToPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/js/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;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;main.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist/js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;
  What's an MJS file?
  &lt;p&gt;This configuration file example is pretty far from &lt;a href="https://webpack.js.org/guides/getting-started/" rel="noopener noreferrer"&gt;the &lt;em&gt;Webpack&lt;/em&gt; documentation&lt;/a&gt;. Even the file extension is different. Why is this?&lt;/p&gt;

&lt;p&gt;Remember when we &lt;a href="https://dev.to/rossangus/nodejs-for-developers-course-chapter-3-images-part-one-383e#module-mode"&gt;enabled JavaScript modules in Node&lt;/a&gt;? This led to &lt;em&gt;Webpack&lt;/em&gt; expecting &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules" rel="noopener noreferrer"&gt;ES Module syntax&lt;/a&gt;. This is not what the documentation for &lt;em&gt;Webpack&lt;/em&gt; is expecting.&lt;/p&gt;

&lt;p&gt;The file extension &lt;code&gt;mjs&lt;/code&gt; is short for "Modular JavaScript" and reminds Node how they should be treated.&lt;/p&gt;



&lt;/p&gt;




&lt;p&gt;The first line of &lt;code&gt;webpack.config.mjs&lt;/code&gt; imports the &lt;code&gt;path&lt;/code&gt; function from &lt;em&gt;Node&lt;/em&gt; and renames it "path". &lt;em&gt;Imaginative&lt;/em&gt;. The &lt;code&gt;path&lt;/code&gt; function lets us view and manipulate the paths of the files we want to change. Earlier, &lt;a href="https://dev.to/rossangus/chapter-4-images-part-two-f26#get-files"&gt;we used the &lt;code&gt;fs&lt;/code&gt; module in &lt;em&gt;Node&lt;/em&gt; to change files on the hard drive&lt;/a&gt;, when we wanted a list of images. But this is just a configuration file so it's not directly changing files.&lt;/p&gt;

&lt;p&gt;We import another in-build Node function &lt;code&gt;fileURLToPath&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;
  Different kinds of imports in ES6
  &lt;p&gt;When a JavaScript module exports just a single function or one function is marked as the default, we can call it like this:&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="nx"&gt;myFunction&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;./tools/my-function.js&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;But sometimes you want to have a few different functions in the same file. Let's say a file looks like this:&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;export&lt;/span&gt; &lt;span class="nx"&gt;returnRandomNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;returnRandomWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//  Hello ↓&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;returnRandomEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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;We could import these functions into a different module like this:&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="nx"&gt;returnRandomEmoji&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;returnRandomNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;returnRandomWord&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;./tools/randoms.js&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;Because &lt;code&gt;returnRandomEmoji&lt;/code&gt; is the &lt;code&gt;default&lt;/code&gt; export from &lt;code&gt;randoms.js&lt;/code&gt;, it gets to escape from the curly braces. The same cannot be said for &lt;code&gt;returnRandomNumber&lt;/code&gt; and &lt;code&gt;returnRandomWord&lt;/code&gt;, who are still exported, but just not as the default.&lt;/p&gt;



&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;When we're writing JavaScript in this way, each file is assumed to be a module. This means the JavaScript engine makes certain assumptions about what it exports - whether it does so or not. The &lt;em&gt;Webpack&lt;/em&gt; configuration file does nothing except exports some JSON which represents the configuration information. That's what the &lt;code&gt;module.exports&lt;/code&gt; object means. Why isn't it just a JSON file? Probably because it needs to call on various &lt;em&gt;Node&lt;/em&gt; functions to return path information.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;entry&lt;/code&gt; node points to a new file in a new directory within our application. We'll create something in there in a moment.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;output&lt;/code&gt; node points to another file - this will be generated by &lt;em&gt;Webpack&lt;/em&gt; and it's what we should point our HTML file at. We've set it to create the file &lt;code&gt;main.js&lt;/code&gt; inside the &lt;code&gt;dist/js&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;The second node inside the &lt;code&gt;output&lt;/code&gt; node - &lt;code&gt;path&lt;/code&gt; - uses the &lt;code&gt;path&lt;/code&gt; component from &lt;em&gt;Node&lt;/em&gt;. &lt;a href="https://nodejs.org/api/path.html#pathresolvepaths" rel="noopener noreferrer"&gt;The &lt;code&gt;resolve&lt;/code&gt; function&lt;/a&gt; just squashes together two strings. The second of which is our good old &lt;code&gt;dist&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;If we were using &lt;em&gt;Webpack&lt;/em&gt; in the conventional way (that is, using ES5 syntax), we could access an environmental variable called &lt;code&gt;__dirname&lt;/code&gt;. This returns the full path to the root of our application. However, in ES6, we need to derive this variable using the lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;__filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fileURLToPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it sounds like I don't fully understand this, that's because I don't fully understand this. Basically, by trying to force &lt;em&gt;Webpack&lt;/em&gt; to use ES6 we've left the documentation behind and are in (mostly) uncharted territory. Perhaps there's a reason why most people don't do it this way.&lt;/p&gt;

&lt;p&gt;With all this in mind, think of this file as just some fancy JSON.&lt;/p&gt;

&lt;h4&gt;
  
  
  Wait, if it's fancy JSON, how come each node ends with a comma?
&lt;/h4&gt;

&lt;p&gt;Remember when I told you how strict JSON was about ending every node with a comma, &lt;em&gt;apart from the last one&lt;/em&gt;? This doesn't follow that rule because it's a JavaScript object which we're &lt;em&gt;treating&lt;/em&gt; like JSON, rather than &lt;em&gt;real&lt;/em&gt; JSON.&lt;/p&gt;

&lt;p&gt;If it's any consolation, I'm sorry for the confusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up our directories
&lt;/h3&gt;

&lt;p&gt;We need to set up directories for our JavaScript. Inside your &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;dist&lt;/code&gt; directories, add a new &lt;code&gt;js&lt;/code&gt; directory to each. Have it as a sibling of &lt;code&gt;img&lt;/code&gt;, so they look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🗀 dist
  🗀 css
  🗀 img
  🗀 js
🗀 src
  🗀 img
  🗀 js
  🗀 scss
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add your entry point
&lt;/h3&gt;

&lt;p&gt;Create a new JavaScript file inside &lt;code&gt;src/js&lt;/code&gt; called &lt;code&gt;index.js&lt;/code&gt;. Let's keep it simple for now:&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello worm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the file which the &lt;em&gt;Webpack&lt;/em&gt; configuration file is pointed at as an input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading the configuration into &lt;em&gt;Webpack&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Just creating the file isn't enough. We need to point &lt;em&gt;Webpack&lt;/em&gt; at it, much like you might bring a horse to water.&lt;/p&gt;

&lt;p&gt;Let's run &lt;em&gt;Webpack&lt;/em&gt; from the terminal. Type (or copy-&amp;amp;-paste) the following into your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx webpack &lt;span class="nt"&gt;--config&lt;/span&gt; webpack.config.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hang on, what's &lt;code&gt;npx&lt;/code&gt; when it's at its gran's?&lt;/p&gt;

&lt;p&gt;First, a quick reminder of what our old pal &lt;em&gt;NPM&lt;/em&gt; does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installs packages dragged from the internet&lt;/li&gt;
&lt;li&gt;Runs specific commands we've added to &lt;code&gt;package.json&lt;/code&gt; such as &lt;code&gt;start&lt;/code&gt; or &lt;code&gt;prepare&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What if we wanted to pull out a specific bit of functionality we know is hidden inside &lt;code&gt;node_modules&lt;/code&gt; but didn't need to write a command for it? This is one of the two things &lt;em&gt;npx&lt;/em&gt; does&lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;The line we've just typed into the terminal asks &lt;code&gt;npx&lt;/code&gt; to find our &lt;code&gt;webpack&lt;/code&gt; application, then passes it the &lt;code&gt;--config&lt;/code&gt; flag and also the location of our &lt;code&gt;webpack-config.mjs&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;After you've run this command, you should see a new file appear in &lt;code&gt;dist/js/&lt;/code&gt; called &lt;code&gt;main.js&lt;/code&gt;. Have a look!&lt;/p&gt;

&lt;p&gt;Our single line of JavaScript has somehow turned into 49 lines where the developer was paid by the underscore. Don't worry - we won't be writing code like this. If you read some of the comments, you'll notice Comrade Webpack telling us that we're seeing this mess because we triggered a development build of the JavaScript, rather than a production build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding in a production build
&lt;/h3&gt;

&lt;p&gt;We've got two choices here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a duplicate of &lt;code&gt;webpack.config.mjs&lt;/code&gt; and tweak it a bit for production&lt;/li&gt;
&lt;li&gt;Install another plugin&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I know option 2 doesn't sound appealing but we all took a sacred oath to uphold the principles of &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;DRY&lt;/a&gt; and we cannot break that pact.&lt;/p&gt;

&lt;p&gt;(More serious justification: because the production and development configurations share a lot of data, we want to share that common data between them, so that if we update it in one place, it impacts both environments. If we didn't do this, we might run the risk of an issue appearing on the live site which doesn't exist on the development site, which would take ages to debug.)&lt;/p&gt;




&lt;p&gt;
  Development sites?
  &lt;p&gt;The vast majority of websites you interact with have shadowy clones of themselves which you're not allowed to see. These are used to preview and test changes to that site to various stakeholders before the general public clicks all over them. They have a bunch of different names which reflect their purposes. Here's some of my favourites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Development (or dev)&lt;/li&gt;
&lt;li&gt;Quality-assurance (or qa)&lt;/li&gt;
&lt;li&gt;Staging (or stag)&lt;/li&gt;
&lt;li&gt;Pre-production (or pre-prod)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, finished code is published to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production (or prod)&lt;/li&gt;
&lt;li&gt;Live (same as prod)&lt;/li&gt;
&lt;/ul&gt;



&lt;/p&gt;




&lt;h3&gt;
  
  
  Installing &lt;em&gt;webpack-merge&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;webpack-merge&lt;/em&gt; takes two different webpack configuration files and squashes them together. This lets our development and production configuration inherit from a third configuration we're going to call &lt;code&gt;webpack.common.mjs&lt;/code&gt;. First, let's install the package. In a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; webpack-merge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Splitting up our configuration
&lt;/h3&gt;

&lt;p&gt;Rename &lt;code&gt;webpack.configuration.mjs&lt;/code&gt; to &lt;code&gt;webpack.common.mjs&lt;/code&gt;. Then take a copy of it and call it &lt;code&gt;webpack.dev.mjs&lt;/code&gt;. All these files should sit in the root of the &lt;em&gt;Node&lt;/em&gt; application (they should be siblings to &lt;code&gt;package.json&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;webpack.common.mjs&lt;/code&gt; just needs a single line removed. The line which sets the &lt;code&gt;mode&lt;/code&gt; to &lt;code&gt;development&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ← Remove this line&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new &lt;code&gt;webpack.dev.mjs&lt;/code&gt; file should look like this:&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;merge&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;webpack-merge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;common&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;./webpack.common.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;devtool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inline-source-map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the start of our new configuration file, we import the &lt;code&gt;merge&lt;/code&gt; function from &lt;code&gt;webpack-merge&lt;/code&gt;. Then we pull in our other file, which we're calling &lt;code&gt;common&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then we take the imported &lt;code&gt;common&lt;/code&gt; config and squash it together with some new JSON, using the &lt;code&gt;merge()&lt;/code&gt; function. This returns some JSON which is exported as if nothing happened.&lt;/p&gt;

&lt;p&gt;The new JSON object sets the &lt;code&gt;mode&lt;/code&gt; to &lt;code&gt;development&lt;/code&gt; and uses the &lt;code&gt;inline-source-map&lt;/code&gt; &lt;code&gt;devtool&lt;/code&gt;. That's all we need to know for now!&lt;/p&gt;

&lt;h3&gt;
  
  
  Remind me: why do we need a development and production mode configuration?
&lt;/h3&gt;

&lt;p&gt;Just like with &lt;em&gt;SASS&lt;/em&gt;, while we're writing JavaScript we need to know which specific file any errors might be coming from. If we let &lt;em&gt;Webpack&lt;/em&gt; compile all our code into a single file just like on live, we couldn't trace it back to a specific source file. The browser would always report the error on "line 1" of the JavaScript file (because &lt;em&gt;Webpack&lt;/em&gt; will squash all our code into a single line).&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;mode&lt;/code&gt; is set to &lt;code&gt;development&lt;/code&gt; the browser will magically know the specific source file the error has originated from. So much easier.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;production&lt;/code&gt; configuration strips all of that out, giving us just the leanest possible JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing it out
&lt;/h3&gt;

&lt;p&gt;To test out our new configuration, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx webpack &lt;span class="nt"&gt;--config&lt;/span&gt; webpack.dev.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your &lt;code&gt;dist/js/main.js&lt;/code&gt; file and it should look the same as it did before. All this work and nothing to show for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a production configuration
&lt;/h3&gt;

&lt;p&gt;Let's create a new configuration for production. Take a copy of &lt;code&gt;webpack.dev.mjs&lt;/code&gt; and call it &lt;code&gt;webpack.prod.mjs&lt;/code&gt;. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;import &lt;span class="o"&gt;{&lt;/span&gt; merge &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'webpack-merge'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
import common from &lt;span class="s1"&gt;'./webpack.common.mjs'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;default merge&lt;span class="o"&gt;(&lt;/span&gt;common, &lt;span class="o"&gt;{&lt;/span&gt;
  mode: &lt;span class="s1"&gt;'production'&lt;/span&gt;,
&lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test &lt;em&gt;that&lt;/em&gt; configuration out at the terminal by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx webpack &lt;span class="nt"&gt;--config&lt;/span&gt; webpack.prod.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Now&lt;/em&gt; have another look at &lt;code&gt;dist/js/main.js&lt;/code&gt;. It should look like this:&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="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello worm&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;Well, at least it's small now. What's with the syntax? Was the developer paid by the parentheses?&lt;/p&gt;

&lt;h4&gt;
  
  
  Immediately Invoked Function Expressions
&lt;/h4&gt;

&lt;p&gt;You know how everyone's frightened of (in ascending order of threat) spiders, nuclear war and polluting the global scope? &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/IIFE" rel="noopener noreferrer"&gt;Immediately Invoked Function Expressions&lt;/a&gt; (or IIFEs, to their friends) were our best method of encapsulating variables in JavaScript before ES5 and the like.&lt;/p&gt;

&lt;p&gt;Because variables which are declared &lt;em&gt;inside&lt;/em&gt; of functions are only in scope &lt;em&gt;within&lt;/em&gt; that function, IIFEs are a method of declaring an anonymous function which immediately runs (or is &lt;em&gt;invoked&lt;/em&gt;). &lt;em&gt;Webpack&lt;/em&gt; uses a modern arrow function variation but you can use older methods too. Essentially, the first set of parenthesis encapsulates the code and the second set runs it.&lt;/p&gt;

&lt;h4&gt;
  
  
  "use strict"
&lt;/h4&gt;

&lt;p&gt;This invokes a slightly more strict version of JavaScript, at least compared to how things used to be in The Bad Old Days. Because &lt;em&gt;Webpack&lt;/em&gt; is generating backward looking JavaScript, this tells the JavaScript parsing engines in browsers to step up their game.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding in our new commands to &lt;code&gt;package.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Remember our old pal &lt;code&gt;package.json&lt;/code&gt;? Let's see how they are doing. We currently have a load of commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sass-dev&lt;/code&gt; - converts SASS files to CSS during development&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sass-prod&lt;/code&gt; - converts SASS files to CSS for production (not currently used)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;serve&lt;/code&gt; - starts up a web server and points it at the &lt;code&gt;dist&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;start&lt;/code&gt; - runs three different of the commands above at the same time - &lt;code&gt;serve&lt;/code&gt;, &lt;code&gt;sass-dev&lt;/code&gt; and &lt;code&gt;watch-images&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prepare&lt;/code&gt; - populates the &lt;code&gt;dist&lt;/code&gt; directory with production-ready files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;watch-images&lt;/code&gt; - watches the &lt;code&gt;img&lt;/code&gt; directory for changes, then invokes our &lt;code&gt;image-compress&lt;/code&gt; script&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Take a moment to appreciate how much you've learned.&lt;/p&gt;




&lt;p&gt;OK, the moment has passed.&lt;/p&gt;

&lt;p&gt;Let's add our production &lt;em&gt;webpack&lt;/em&gt; command to our &lt;code&gt;prepare&lt;/code&gt; task. It should now read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-prod&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;node tools/compress-all-images.js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npx webpack --config webpack.prod.mjs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;prepare&lt;/code&gt; command should be used to create production-ready files.&lt;/p&gt;

&lt;p&gt;While we're at it, let's add a new watch task to pick up on any JavaScript changes. After the &lt;code&gt;watch-images&lt;/code&gt; command, add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"watch-js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onchange &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -- npx webpack --config webpack.dev.mjs"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;watch-js&lt;/code&gt; command will produce files which help us with development. Now let's call &lt;code&gt;watch-js&lt;/code&gt; as part of the &lt;code&gt;start&lt;/code&gt; command, so it reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run serve&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-dev&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run watch-images&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run watch-js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing out the production JavaScript
&lt;/h3&gt;

&lt;p&gt;Let's test this all out! Delete the contents of the &lt;code&gt;dist/js&lt;/code&gt;, &lt;code&gt;dist/css&lt;/code&gt; and &lt;code&gt;dist/img&lt;/code&gt; directories (leave &lt;code&gt;dist/index.html&lt;/code&gt;!), then in the terminal type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(remember, this is the same as running &lt;code&gt;npm run prepare&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Now check those same directories again. You should see the production-ready CSS, JavaScript and images.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;scripts&lt;/code&gt; node of your &lt;code&gt;package.json&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sass-dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --watch --update --style=expanded src/scss:dist/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sass-prod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --no-source-map --style=compressed src/scss:dist/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"browser-sync start --server &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --files &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --watch &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run serve&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-dev&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run watch-images&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run watch-js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-prod&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;node tools/compress-all-images.js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npx webpack --config webpack.prod.mjs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"watch-images"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onchange &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/img&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -- node tools/image-compress.js {{file}} {{event}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"watch-js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onchange &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -- npx webpack --config webpack.dev.mjs"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing out the development JavaScript
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;dist/index.html&lt;/code&gt;, include a &lt;code&gt;script&lt;/code&gt; tag to link to your JavaScript file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/js/main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should appear inside your &lt;code&gt;body&lt;/code&gt; tag but after all the rest of the HTML. This is so the page renders first and then the JavaScript has access to everything in the DOM.&lt;/p&gt;

&lt;p&gt;Start up your local server with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should (eventually) see your page. Right-click and &lt;code&gt;inspect&lt;/code&gt; an element to open the developer tools. Switch to the &lt;em&gt;Console&lt;/em&gt; tab at the top of the developer tools and you should see the text &lt;code&gt;Hello worm&lt;/code&gt; appear.&lt;/p&gt;

&lt;p&gt;Does it change, when we update the JavaScript? Edit &lt;code&gt;src/js/index.js&lt;/code&gt; so that it reads:&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello world&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;Check the console in your browser. It should now say "Hello world" instead of "Hello worm".&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript Modules
&lt;/h2&gt;

&lt;p&gt;A quick reminder of how our &lt;code&gt;scss&lt;/code&gt; directory looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🗀 src
  ...
  🗀 scss
    _fonts.scss
    main.scss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How this works is that &lt;em&gt;SASS&lt;/em&gt; ignores any files which start with an underscore. So we use &lt;code&gt;main.scss&lt;/code&gt; to import all of the &lt;code&gt;scss&lt;/code&gt; files in any directory structure we want. That way, we can keep our sass modular and still export just one file to the production environment.&lt;/p&gt;

&lt;p&gt;We want to take a similar approach with JavaScript. Remember when we split &lt;code&gt;image-compress.js&lt;/code&gt; into different functions, then imported and exported them? We want to do this with our production JavaScript too (reminder: all the JavaScript we've currently written except &lt;code&gt;console.log("Hello world");&lt;/code&gt; won't end up on the live site).&lt;/p&gt;

&lt;p&gt;Rather than using underscores, we don't need to do anything to exclude individual JavaScript files from our bundle. &lt;em&gt;Webpack&lt;/em&gt; is opt-in rather than opt-out. In fact the only JavaScript file &lt;em&gt;Webpack&lt;/em&gt; will look at is &lt;code&gt;src/js/index.js&lt;/code&gt;, because that's what we defined in &lt;code&gt;webpack.common.mjs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All we need to produce modular JavaScript is to write a module and import it into &lt;code&gt;index.js&lt;/code&gt;. So let's do that now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Directory structure
&lt;/h3&gt;

&lt;p&gt;Inside your &lt;code&gt;src/js&lt;/code&gt; directory, create a new folder called &lt;code&gt;modules&lt;/code&gt;. Inside that, create &lt;em&gt;another&lt;/em&gt; directory called &lt;code&gt;add-emoji&lt;/code&gt;. Inside &lt;em&gt;that&lt;/em&gt;, add a new file called &lt;code&gt;add-emoji.js&lt;/code&gt;. Your directory structure should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🗀 src
  ...
  🗀 js
    🗀 modules
      🗀 add-emoji
        add-emoji.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(VS Code might try and help you out by flattening this out visually)&lt;/p&gt;

&lt;p&gt;Personally, I'd advice &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case" rel="noopener noreferrer"&gt;kebab case&lt;/a&gt; for directories and file names, and &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Camel_case" rel="noopener noreferrer"&gt;camel case&lt;/a&gt; for function names.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Emoji
&lt;/h3&gt;

&lt;p&gt;This is our first client-side (i.e. this runs on the "client" or web browser) module:&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;addEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;😊&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afterbegin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;big&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/big&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a function called &lt;code&gt;addEmoji&lt;/code&gt; which takes two parameters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An emoji as a string (or just, you know, a string of text)&lt;/li&gt;
&lt;li&gt;A CSS selector of a tag where you want to insert the emoji just after the opening tag (this is also a string)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the function is called without any arguments, it falls back to the defaults of rendering a 😊 emoji just after the start of the body tag. The emoji itself is rendered inside a &lt;code&gt;big&lt;/code&gt; tag because no-one has used this tag for over five years now and I'm worried its internet licence might expire.&lt;/p&gt;

&lt;p&gt;Note that I'm using short-circuit evaluation on the second line inside the function - this will check if the &lt;code&gt;element&lt;/code&gt; exists or not. So even if the function is called with an invalid CSS selector, no error will be triggered.&lt;/p&gt;

&lt;p&gt;But of course, a module will do nothing until it's called.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calling the module
&lt;/h3&gt;

&lt;p&gt;Open up &lt;code&gt;src/js/index.js&lt;/code&gt;. It should look like this:&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello world&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;Get rid of that line and instead, import our new module, then call our new function three times:&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="nx"&gt;addEmoji&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;./modules/add-emoji/add-emoji.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Will add `😊` after the opening `body` tag&lt;/span&gt;
&lt;span class="nf"&gt;addEmoji&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Will add `🪱` inside the first `p` tag on the page&lt;/span&gt;
&lt;span class="nf"&gt;addEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🪱&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Will not find a `section` tag on the page and silently fail&lt;/span&gt;
&lt;span class="nf"&gt;addEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;😈&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;section&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;h4&gt;
  
  
  To add the file extension or not
&lt;/h4&gt;

&lt;p&gt;It's possible that &lt;em&gt;VS Code&lt;/em&gt; tried to finish that import statement above for you, like an eager fan in the front row of a concert (you are the singer in this analogy). And in doing so, it dropped the &lt;code&gt;.js&lt;/code&gt;. &lt;em&gt;This is an error&lt;/em&gt;. Because we're importing a local file, we need to specify this file extension, no matter what &lt;em&gt;VS Code&lt;/em&gt; thinks.&lt;/p&gt;

&lt;p&gt;You might encounter module imports elsewhere which don't need to specify a file extension. These rely on specific configurations to handle fallback cases such as this to work. I want to keep the &lt;em&gt;Webpack&lt;/em&gt; configuration as small as possible, so I've not added them&lt;sup id="fnref3"&gt;3&lt;/sup&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing this out
&lt;/h3&gt;

&lt;p&gt;Your page should have refreshed after each save. You should see a new smiley face above &lt;code&gt;Hello Worm&lt;/code&gt;, a new worm emoji inside that paragraph and &lt;em&gt;no&lt;/em&gt; devil face on the page (unless you added a &lt;code&gt;section&lt;/code&gt; tag, of course).&lt;/p&gt;

&lt;p&gt;Most importantly, you shouldn't see anything in the console of the web browser (apart from complaining that you're missing a &lt;a href="https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs" rel="noopener noreferrer"&gt;favicon&lt;/a&gt;) - no errors, no &lt;code&gt;Hello world&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Congratulations! You're writing client-side code in a modern way!&lt;/p&gt;

&lt;p&gt;Let's review what we learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Webpack&lt;/em&gt; does for JavaScript what SASS does for CSS (and SASS)&lt;/li&gt;
&lt;li&gt;How to configure &lt;em&gt;Webpack&lt;/em&gt; for development&lt;/li&gt;
&lt;li&gt;Two different types of module imports in JavaScript&lt;/li&gt;
&lt;li&gt;Why we might use &lt;em&gt;NPX&lt;/em&gt; as well as &lt;em&gt;NPM&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;How to configure &lt;em&gt;Webpack&lt;/em&gt; for production&lt;/li&gt;
&lt;li&gt;How to keep these two configuration files DRY&lt;/li&gt;
&lt;li&gt;What IIFEs are and why we use them&lt;/li&gt;
&lt;li&gt;Writing client-side JavaScript modules and the correct way to import them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/Ross-Angus/node-js-for-developers/tree/chapter-5" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;View Chapter 5 code snapshot on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Quiz
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/WbNELVV?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Strictly speaking, &lt;em&gt;Webpack&lt;/em&gt; wants to work with zero configuration but it's rare that the out-of-the-box experience is exactly what we want. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;The other is to run a bit of functionality, but instead of it being pulled from your hard drive &lt;em&gt;the internet acts as your hard drive&lt;/em&gt;. Woah. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Well, I &lt;em&gt;did&lt;/em&gt; add the configuration but it didn't work first time and I got bored ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>node</category>
      <category>npm</category>
      <category>javascript</category>
      <category>webpack</category>
    </item>
    <item>
      <title>Chapter 4: images part two</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Sat, 08 Mar 2025 17:33:12 +0000</pubDate>
      <link>https://forem.com/rossangus/chapter-4-images-part-two-f26</link>
      <guid>https://forem.com/rossangus/chapter-4-images-part-two-f26</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://www.instagram.com/yuraforrat/" rel="noopener noreferrer"&gt;Yura Forrat&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Last time, we started to look at optimising images:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We used the &lt;em&gt;onchange&lt;/em&gt; package to pass events from the file system to our JavaScript&lt;/li&gt;
&lt;li&gt;We ran arbitrary JavaScript files directly in &lt;em&gt;Node&lt;/em&gt; - no web browser required&lt;/li&gt;
&lt;li&gt;We saw that &lt;em&gt;Node&lt;/em&gt; gave us access to different methods of accessing path and filename information&lt;/li&gt;
&lt;li&gt;We learned that &lt;em&gt;Sharp&lt;/em&gt; is this season's must-have image optimisation package&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's still a lot to do before we meet our goals set out at the start of the previous lesson. Here's the features we wanted to build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;Drop an image into the &lt;code&gt;src/img&lt;/code&gt; directory and it automatically gets converted into a next-gen format in the &lt;code&gt;dist/img&lt;/code&gt; directory&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;Run a task which converts &lt;em&gt;all&lt;/em&gt; images in the &lt;code&gt;src/img&lt;/code&gt; directory into their optimised versions in &lt;code&gt;dist/img&lt;/code&gt; - for example when you've added some images but the server wasn't running at the time&lt;/li&gt;
&lt;li&gt;&lt;del&gt;Overwrite an existing image in &lt;code&gt;src/img&lt;/code&gt; with a new one with the same name, and have the corresponding images in &lt;code&gt;dist/img&lt;/code&gt; update automatically&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;Use as many different sub-directories in &lt;code&gt;src/img&lt;/code&gt; and have those be replicated on &lt;code&gt;dist/img&lt;/code&gt; - for example &lt;code&gt;src/img/gallery/open-day&lt;/code&gt; which lives inside &lt;code&gt;dist&lt;/code&gt; as &lt;code&gt;dist/img/gallery/open-day&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's continue the build!&lt;/p&gt;

&lt;h3&gt;
  
  
  Nested directories
&lt;/h3&gt;

&lt;p&gt;Our script works perfect, but only if the image in question is inside &lt;code&gt;src/img&lt;/code&gt;. What about if we wanted to create sub-directories inside &lt;code&gt;src/img&lt;/code&gt;, such as &lt;code&gt;src/img/gallery&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Let's see what happens. In a terminal, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run watch-images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Create a new folder inside &lt;code&gt;src/img&lt;/code&gt; and call it &lt;code&gt;gallery&lt;/code&gt;. Copy one of the images and paste it into there.&lt;/p&gt;

&lt;p&gt;I got an error in the terminal which read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error: ./dist/img/gallery/example-01.webp: unable to open &lt;span class="k"&gt;for &lt;/span&gt;write
windows error: The system cannot find the file specified.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;sharp&lt;/em&gt; tried to write the optimised image into a new folder called &lt;code&gt;gallery&lt;/code&gt; which lives inside &lt;code&gt;dist/img&lt;/code&gt; - exactly as we'd hoped. But it couldn't find the directory. Let's give the script the power to create directories.&lt;/p&gt;

&lt;p&gt;For this, we'll need to lean on another part of &lt;em&gt;Node&lt;/em&gt;, &lt;a href="https://nodejs.org/api/fs.html" rel="noopener noreferrer"&gt;the &lt;code&gt;fs&lt;/code&gt; module&lt;/a&gt;. &lt;code&gt;fs&lt;/code&gt; stands for "file system" and not a rude exclamation you might use when your code doesn't work.&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="nx"&gt;fs&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;node:fs&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;Just like before, we're going to use two functions built into &lt;code&gt;fs&lt;/code&gt;. The first, &lt;a href="https://nodejs.org/api/fs.html#fsexistssyncpath" rel="noopener noreferrer"&gt;&lt;code&gt;existsSync()&lt;/code&gt;&lt;/a&gt;, takes a parameter which is a path. It returns &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; depending if the path exists or not.&lt;/p&gt;

&lt;p&gt;The second function is called &lt;a href="https://nodejs.org/api/fs.html#fsmkdirsyncpath-options" rel="noopener noreferrer"&gt;&lt;code&gt;mkdirSync&lt;/code&gt;&lt;/a&gt; and allows us to make one or more directories in the path we supply it.&lt;/p&gt;

&lt;p&gt;We can use these two functions inside your &lt;code&gt;if&lt;/code&gt; statement, between where the variables are initialised and the calls to &lt;em&gt;sharp&lt;/em&gt;, like this:&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Guess what: there's a shortened form of &lt;code&gt;if&lt;/code&gt; too, as long as the JavaScript fits onto one line. It uses the double ampersand and the cool name for it is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND#short-circuit_evaluation" rel="noopener noreferrer"&gt;short-circuit evaluation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It looks like this:&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="kc"&gt;true&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Truth detected!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would cause a JavaScript alert with the string &lt;code&gt;Truth detected!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However this would do nothing:&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="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Can you hear this?&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;How this works is the JavaScript parsing engine works from left to right. It will continue until it finds something false and then skip the rest of the statement and work further down the file. So we could shorten our &lt;code&gt;if&lt;/code&gt; statement to:&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't go hog-wild with this technique. Remember that JavaScript needs to be readable as well as using the most up-to-date slang.&lt;/p&gt;

&lt;p&gt;OK, but what does this statement do? First, we check if the &lt;code&gt;distPath&lt;/code&gt; we want to use exists or not. If it &lt;em&gt;doesn't&lt;/em&gt; exist, we call &lt;code&gt;mkdirSync&lt;/code&gt; on it, while passing a &lt;code&gt;recursive&lt;/code&gt; boolean in the option object. This will recursively call &lt;code&gt;mkdirSync&lt;/code&gt; over and over again, until all the directories are created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing out nested directories
&lt;/h3&gt;

&lt;p&gt;Let's try this out. Cancel the &lt;code&gt;watch-images&lt;/code&gt; task and create some nested folders inside &lt;code&gt;src/img&lt;/code&gt;, for example &lt;code&gt;src/gallery/1/2/3&lt;/code&gt;. Restart the &lt;code&gt;watch-images&lt;/code&gt; task and drop an image inside the &lt;code&gt;3&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Now check what's inside the &lt;code&gt;dist/img&lt;/code&gt; directory. You should see a mirror of the &lt;code&gt;src&lt;/code&gt; directory structure, except now your example file is a &lt;em&gt;webp&lt;/em&gt; rather than a &lt;em&gt;png&lt;/em&gt;. Great! We can cross off this requirement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;Use as many different sub-directories in &lt;code&gt;src/img&lt;/code&gt; and have those be replicated on &lt;code&gt;dist/img&lt;/code&gt; - for example &lt;code&gt;src/img/gallery/open-day&lt;/code&gt; which lives inside &lt;code&gt;dist&lt;/code&gt; as &lt;code&gt;dist/img/gallery/open-day&lt;/code&gt;&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Adding &lt;code&gt;watch-images&lt;/code&gt; to our &lt;code&gt;start&lt;/code&gt; task
&lt;/h3&gt;

&lt;p&gt;Cancel the &lt;code&gt;watch-images&lt;/code&gt; task using ctrl + c. Let's edit our old pal &lt;code&gt;package.json&lt;/code&gt; and introduce &lt;code&gt;watch-images&lt;/code&gt; to the &lt;code&gt;start&lt;/code&gt; krew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run serve&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-dev&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run watch-images&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lovely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check if the image changes
&lt;/h3&gt;

&lt;p&gt;From a terminal, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your example web page should open and accuse you of being a worm. Let's add an image.&lt;/p&gt;

&lt;p&gt;Open the file &lt;code&gt;dist/index.html&lt;/code&gt;. Add a paragraph before the closing &lt;code&gt;body&lt;/code&gt; tag so that the whole file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"no-js"&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-GB"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello Worm&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/main.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello Worm&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/img/example-01.webp"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"An animal, yesterday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&amp;gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will hopefully have a few different images inside &lt;code&gt;dist/img&lt;/code&gt; by this point, none of which might be called &lt;code&gt;example-01.webp&lt;/code&gt; so update the &lt;code&gt;img&lt;/code&gt; tag so it points to an image which actually exists. Save the file.&lt;/p&gt;

&lt;p&gt;Let's update the image! Visit &lt;a href="https://imgur.com/a/example-images-BygxeTs" rel="noopener noreferrer"&gt;the example image page&lt;/a&gt; and download a &lt;em&gt;different&lt;/em&gt; image from the one which is currently displayed on your page. Use this image to overwrite &lt;code&gt;example-01.png&lt;/code&gt; (or whatever) in your &lt;code&gt;src/img&lt;/code&gt; directory. You should see your &lt;code&gt;image-compress.js&lt;/code&gt; script kick into action. And once it's finished, you should see the browser refresh and the displayed image should update.&lt;/p&gt;

&lt;p&gt;This happens "automagically" because we've already configured &lt;code&gt;browser-sync&lt;/code&gt; to watch the &lt;code&gt;dist&lt;/code&gt; directory and to refresh when something happens in there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing task
&lt;/h3&gt;

&lt;p&gt;Just like with the CSS, our hypothetical second developer will inherit this codebase and an empty &lt;code&gt;dist/img&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;We need to write a task which will go through all the images in &lt;code&gt;src/img&lt;/code&gt; and optimise them. As the site grows in complexity, this isn't something we'd want to do every time we start the local server, because that would take &lt;em&gt;ages&lt;/em&gt;. But it should run at least once, when a new developer downloads the project.&lt;/p&gt;

&lt;p&gt;Let's start by grabbing a list of all the &lt;code&gt;src&lt;/code&gt; images.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get all files&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;We need to write a function which pulls a list of file names from an arbitrary directory. Inside the &lt;code&gt;tools&lt;/code&gt; folder, create a new file called &lt;code&gt;get-files.js&lt;/code&gt;. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import the "file system" function, which is built into Node.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&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;// Get an array of all files and directories in the passed directory using fs.readdirSync&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Create the full path of the file/directory by concatenating the passed directory and file/directory name&lt;/span&gt;
  &lt;span class="k"&gt;for &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;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&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;name&lt;/span&gt; &lt;span class="o"&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;dir&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;file&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="c1"&gt;// Check if the current file/directory is a directory using fs.statSync&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isDirectory&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// If it is a directory, recursively call the getFiles function with the directory path and the files array&lt;/span&gt;
      &lt;span class="nf"&gt;getFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;files&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;// If it is a file, push the full path to the files array&lt;/span&gt;
      &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;files&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;I stole this from &lt;a href="https://www.learnwithparam.com/blog/get-all-files-in-a-folder-using-nodejs" rel="noopener noreferrer"&gt;Learn with Param&lt;/a&gt;. Does this make me a &lt;a href="https://www.goodreads.com/quotes/629531-good-artists-copy-great-artists-steal" rel="noopener noreferrer"&gt;great artist&lt;/a&gt;? That's not for me to say.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;getFiles()&lt;/code&gt; to pull in a list of the images
&lt;/h3&gt;

&lt;p&gt;Create a third file in our new &lt;code&gt;tools&lt;/code&gt; directory called &lt;code&gt;compress-all-images.js&lt;/code&gt;. It should look like this:&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="nx"&gt;getFiles&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;./get-files.js&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does exactly two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Imports the function &lt;code&gt;get-files.js&lt;/code&gt; from the same directory&lt;/li&gt;
&lt;li&gt;Console-logs the output of that function, if it's pointed at &lt;code&gt;./src/img&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Save any missing images from &lt;a href="https://imgur.com/a/q5VgZku" rel="noopener noreferrer"&gt;the example page&lt;/a&gt; into &lt;code&gt;src/img&lt;/code&gt;, just so we have a good supply.&lt;/p&gt;

&lt;p&gt;In the terminal type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node tools/compress-all-images.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see an output something like this inside your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'./src/img/example-01.png'&lt;/span&gt;,
  &lt;span class="s1"&gt;'./src/img/example-02.png'&lt;/span&gt;,
  &lt;span class="s1"&gt;'./src/img/example-03.png'&lt;/span&gt;,
  &lt;span class="s1"&gt;'./src/img/example-04.png'&lt;/span&gt;,
  &lt;span class="s1"&gt;'./src/img/example-05.png'&lt;/span&gt;,
  &lt;span class="s1"&gt;'./src/img/gallery/1/2/3/example-01.png'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those square brackets mean it's an Array. A nice way to work with Arrays is to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map" rel="noopener noreferrer"&gt;call the &lt;code&gt;map&lt;/code&gt; method&lt;/a&gt; on them. This means that map goes through each item in an Array and runs the same function on it (a function we're just about to define). Let's store that data in an variable, then iterate over it with &lt;code&gt;map()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getFiles&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;./get-files.js&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;allImagePaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;allImagePaths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our new function is passed as a parameter to &lt;code&gt;map&lt;/code&gt; and we're writing it inline, as an arrow function. The function receives an automatic parameter which I've chosen to call &lt;code&gt;path&lt;/code&gt;. In the example above, the first value of &lt;code&gt;path&lt;/code&gt; will be &lt;code&gt;./src/img/example-01.png&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Call the JavaScript file from the terminal a second time, using the up arrow, if you're in a hurry.&lt;/p&gt;

&lt;p&gt;As expected, this &lt;code&gt;console.log()&lt;/code&gt;s the same data, but as individual lines. Now we just need to replicate a bunch of code from &lt;code&gt;image-compress.js&lt;/code&gt; in this file, right?&lt;/p&gt;

&lt;p&gt;Let's see if we can split this reused code off into different modules, so we don't write the same code twice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making &lt;code&gt;image-compress.js&lt;/code&gt; more modular
&lt;/h3&gt;

&lt;p&gt;A lot of &lt;code&gt;image-compress.js&lt;/code&gt; is concerned with getting the right &lt;code&gt;dist&lt;/code&gt; path and extracting the filename from a path. Let's pull all of that code into a new file inside &lt;code&gt;tools&lt;/code&gt; called &lt;code&gt;get-dist-path.js&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;It should look like this:&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="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getDistPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&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;trimPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thisPath&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;thisPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// The path from the root of the Node application to the filename of the image&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dirName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// The image name, plus file extension&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// The image file extension&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// The path to the source image, minus the `src` bit&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trimPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dirName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// The name of the image, without the file extension&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;extName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`./dist&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;subPath&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;fileName&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;
  Shorthand property and method names in JavaScript
  &lt;p&gt;You might have notices some weird &lt;code&gt;return&lt;/code&gt; syntax there:&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fileName&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;a href="https://ui.dev/shorthand-properties" rel="noopener noreferrer"&gt;a shorthand form of an object in JavaScript&lt;/a&gt;. It's the same as this:&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;distPath&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fileName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the &lt;em&gt;name&lt;/em&gt; and the &lt;em&gt;value&lt;/em&gt; are the same (even if one is a variable), you can skip the value part in JavaScript (not in JSON!)&lt;/p&gt;



&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;The &lt;code&gt;getDistPath&lt;/code&gt; function is passed a &lt;code&gt;src&lt;/code&gt; path and uses this to work out what the equivalent &lt;code&gt;dist&lt;/code&gt; path should be.&lt;/p&gt;

&lt;p&gt;But there's still code in &lt;code&gt;image-compress.js&lt;/code&gt; which just calls &lt;em&gt;Sharp&lt;/em&gt;, which would be common to &lt;code&gt;compress-all-images.js&lt;/code&gt;. So let's split &lt;em&gt;that&lt;/em&gt; out into a new file too. Inside the &lt;code&gt;tools&lt;/code&gt; directory, create a new file called &lt;code&gt;write-images.js&lt;/code&gt;. It should look like this:&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="nx"&gt;fs&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;node:fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sharp&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;sharp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getDistPath&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;./get-dist-path.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;writeImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDistPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&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;distPath&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;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.webp`&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;This means we can import the function &lt;code&gt;writeImages&lt;/code&gt; into &lt;code&gt;image-compress.js&lt;/code&gt; and then get rid of a &lt;em&gt;lot&lt;/em&gt; of code:&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;argv&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;node:process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;writeImages&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;./write-images.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Destructuring the Array from Node which includes data we need&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// White-list of events which should cause Sharp to generate images&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;triggerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// If the wrong kind of event triggers this script, do nothing&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;triggerEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;writeImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&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;Because the only thing which differs between &lt;code&gt;compress-all-images.js&lt;/code&gt; and &lt;code&gt;image-compress.js&lt;/code&gt; is how they are called and if the web browser is running or not.&lt;/p&gt;




&lt;p&gt;
  Breaking down code
  &lt;p&gt;It might feel a bit as if we've been shuffling code about between a whole lot of files in a pretty arbitrary way here. Indeed other programmers might choose to slice this code up differently. Splitting a long piece of code into different modules is an example of &lt;a href="https://en.wikipedia.org/wiki/Separation_of_concerns" rel="noopener noreferrer"&gt;separation of concerns&lt;/a&gt; and is quite a challenging topic to teach!&lt;/p&gt;

&lt;p&gt;The approach we've stumbled into here had us writing one big script which did everything, then realising we'd need to write a second script which did 90% the same thing.&lt;/p&gt;

&lt;p&gt;We could, of course, just duplicate the first script and make some changes to it. But breaking it into modules will help us in all kinds of ways in the future.&lt;/p&gt;

&lt;p&gt;One way to work out how to split up a long script up is to add comments before each line explaining what it does. Then see if groups of those comments use the same words, suggesting different "concerns".&lt;/p&gt;

&lt;p&gt;Another way is to look at inputs and outputs. In our example, we had a lot of code which just edited strings to normalise them and translate them from the &lt;code&gt;src&lt;/code&gt; to the &lt;code&gt;dist&lt;/code&gt; directories. Not only does this code belong together, it's easy to see how useful it might be in the future for other purposes.&lt;/p&gt;

&lt;p&gt;The approach we've taken here where we write the code first, then &lt;a href="https://medium.com/@london.lingo.01/the-art-of-code-refactoring-in-javascript-techniques-for-improving-code-quality-edbfd119584a" rel="noopener noreferrer"&gt;refactor&lt;/a&gt; it later is normal. Don't expect everything to be perfect right away. We can always improve things later.&lt;/p&gt;



&lt;/p&gt;




&lt;p&gt;Finally, let's import &lt;code&gt;writeImages.js&lt;/code&gt; into &lt;code&gt;compress-all-images.js&lt;/code&gt;. We need to slightly trim the paths, to remove the &lt;code&gt;./&lt;/code&gt; at the start:&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="nx"&gt;getFiles&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;./get-files.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;writeImages&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;./write-images.js&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;allImagePaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;allImagePaths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trimPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;writeImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trimPath&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;h3&gt;
  
  
  Generating out other image formats
&lt;/h3&gt;

&lt;p&gt;Now that &lt;code&gt;write-images.js&lt;/code&gt; is common to both &lt;code&gt;image-compress.js&lt;/code&gt; and &lt;code&gt;compress-all-images.js&lt;/code&gt;, let's make it generate three times as many images! We won't use these other formats yet (that's coming soon) but there's no harm in outputting them ahead of time.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;write-images.js&lt;/code&gt; and add more &lt;em&gt;Sharp&lt;/em&gt; calls like this:&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="nx"&gt;fs&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;node:fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sharp&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;sharp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getDistPath&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;./get-dist-path.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;writeImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDistPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;avif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&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;distPath&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;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.avif`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&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;distPath&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;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.webp`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jpeg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&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;distPath&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;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jpg`&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;Now we're generating an &lt;a href="https://web.dev/learn/images/avif" rel="noopener noreferrer"&gt;avif&lt;/a&gt;, a webp and a boring old jpg file for each image dropped into &lt;code&gt;src/img&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a new task to compress all images
&lt;/h3&gt;

&lt;p&gt;Finally, let's add another task to our &lt;code&gt;prepare&lt;/code&gt; command, so that all the images are generated when our second developer (we really ought to give this imaginary person a name) runs &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open up &lt;code&gt;package.json&lt;/code&gt; and edit your &lt;code&gt;prepare&lt;/code&gt; task, so it runs &lt;code&gt;compress-all-images.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-prod&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;node tools/compress-all-images.js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing out optimising all images
&lt;/h3&gt;

&lt;p&gt;Delete &lt;code&gt;dist/img&lt;/code&gt; then run &lt;code&gt;npm install&lt;/code&gt; from the terminal. Once it's finished, you should see your &lt;code&gt;img&lt;/code&gt; directory reappear as if by magic and be populated by each image in your &lt;code&gt;src/img&lt;/code&gt; directory, in a mouth-watering choice of three flavours.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;Run a task which converts &lt;em&gt;all&lt;/em&gt; images in the &lt;code&gt;src/img&lt;/code&gt; directory into their optimised versions in &lt;code&gt;dist/img&lt;/code&gt; - for example when you've added some images but the server wasn't running at the time&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's review what we learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Used the &lt;code&gt;fs&lt;/code&gt; module from &lt;em&gt;Node&lt;/em&gt; to elegantly handle nested directories inside &lt;code&gt;src/img&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Learned about short-circuit evaluation in JavaScript&lt;/li&gt;
&lt;li&gt;Stole a utility function from &lt;a href="https://www.learnwithparam.com/" rel="noopener noreferrer"&gt;Param Harrison&lt;/a&gt; to get a list of files and folders in &lt;em&gt;Node&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;How we might split up a long, complicated script into modules&lt;/li&gt;
&lt;li&gt;The shorthand which can be used in JavaScript if the name and property are the same&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/Ross-Angus/node-js-for-developers/tree/chapter-4" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;View Chapter 4 code snapshot on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Quiz
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/QwWMWGa?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>node</category>
      <category>npm</category>
      <category>sharp</category>
      <category>webp</category>
    </item>
    <item>
      <title>Chapter 3: images part one</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Fri, 07 Mar 2025 14:27:01 +0000</pubDate>
      <link>https://forem.com/rossangus/nodejs-for-developers-course-chapter-3-images-part-one-383e</link>
      <guid>https://forem.com/rossangus/nodejs-for-developers-course-chapter-3-images-part-one-383e</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://mmahbubaalahi.wordpress.com/" rel="noopener noreferrer"&gt;M Mahbub A Alahi&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Last time, we configured a local server and previewed our work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We installed &lt;em&gt;browser-sync&lt;/em&gt; so we can see the contents of &lt;code&gt;dist&lt;/code&gt; in a web browser&lt;/li&gt;
&lt;li&gt;Learned about escape characters&lt;/li&gt;
&lt;li&gt;Ran more than one command at the same time&lt;/li&gt;
&lt;li&gt;Learned about the special &lt;code&gt;start&lt;/code&gt; command name&lt;/li&gt;
&lt;li&gt;Installed, then removed a package because we changed our mind&lt;/li&gt;
&lt;li&gt;Learned that if something goes wrong with &lt;code&gt;node_modules&lt;/code&gt; we can always nuke it and start again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This lesson is all about optimising images. There's a lot to get through here, but I'm going to take it slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fun break
&lt;/h2&gt;

&lt;p&gt;Perhaps right now, you're feeling a bit of imposter syndrome. We are standing on the shoulders of the giants who made all this technology, and yet we are not fit to shine their shoes.&lt;/p&gt;

&lt;p&gt;Don't worry: I have a cure for that.&lt;/p&gt;

&lt;p&gt;Let's visit a site made by some of the best engineers the world has ever seen. How about &lt;a href="https://www.apple.com/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;? I need you to open the home page in &lt;em&gt;Chrome&lt;/em&gt; (sorry, &lt;em&gt;Firefox&lt;/em&gt; - I'm cheating on you this one time). Don't dismiss the cookies or click on anything else and open the developer tools. You can do this by either hitting the F12 key or right-clicking on an empty patch of page and selecting &lt;code&gt;Inspect&lt;/code&gt; from the pop-up menu.&lt;/p&gt;

&lt;p&gt;There's a lot going on in the developer tools but I want you to pay attention to the tabs across the top. They list off the different parts and start &lt;em&gt;Elements Console Sources Network ...&lt;/em&gt; I'd like you to select &lt;em&gt;Lighthouse&lt;/em&gt;. It might be hidden behind one of these wee chaps: ».&lt;/p&gt;

&lt;p&gt;Once the Lighthouse panel has displayed, click the &lt;code&gt;Analyze page load&lt;/code&gt; button and wait for the results. Is everything green?&lt;/p&gt;

&lt;p&gt;You can scroll down the report and under the &lt;em&gt;Diagnostics&lt;/em&gt; heading, you should see a red warning triangle next to a sub-heading which reads &lt;em&gt;Serve images in next-gen formats&lt;/em&gt;. We're going to address this problem in the next lesson, but let me ramble on about &lt;em&gt;Lighthouse&lt;/em&gt; for a bit first.&lt;/p&gt;

&lt;p&gt;Running a Lighthouse audit is mostly a little disheartening. Getting green scores across the board is basically impossible, if you have a marketing department which is determined to drop a 500MB auto-playing video into a popup window. And even if you don't have this problem, improving the score requires many different departments working together.&lt;/p&gt;

&lt;p&gt;In the next lesson, we're going to do what Apple failed to do: serve all our images in "next gen" formats.&lt;/p&gt;

&lt;p&gt;(and if you're still feeling like a bad engineer, try running &lt;a href="https://www.microsoft.com/" rel="noopener noreferrer"&gt;Microsoft&lt;/a&gt; through Lighthouse)&lt;/p&gt;

&lt;h2&gt;
  
  
  Compressing images
&lt;/h2&gt;

&lt;p&gt;When it comes to &lt;em&gt;Node&lt;/em&gt; packages which can convert images to cutting-edge modern formats, the new hotness is &lt;a href="https://www.npmjs.com/package/sharp" rel="noopener noreferrer"&gt;Sharp&lt;/a&gt;. It's so bleeding-edge that we need to write some of our own tools to run it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sharp&lt;/em&gt; is designed to be called from within a &lt;em&gt;Node&lt;/em&gt; application and can do all kinds of stuff like adding blurs, adding text, rotating or converting images to different formats. It's just the last bit we want to use.&lt;/p&gt;

&lt;p&gt;However, it doesn't just get installed and then called, like the other plugins we've been using. We're going to have to write a few functions to get it to do what we want.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we want to achieve
&lt;/h3&gt;

&lt;p&gt;An ideal developer user experience would be the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You start to run your local server&lt;/li&gt;
&lt;li&gt;You dump a bunch of images into the &lt;code&gt;src/img&lt;/code&gt; directory (this doesn't exist yet)&lt;/li&gt;
&lt;li&gt;Those images get automagically converted into next generation files in the &lt;code&gt;dist/img&lt;/code&gt; directory (this doesn't exist either, yet)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You might also want to do any of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run a task which converts &lt;em&gt;all&lt;/em&gt; images in the &lt;code&gt;src/img&lt;/code&gt; directory into their optimised versions in &lt;code&gt;dist/img&lt;/code&gt; - for example when you've added some images but the server wasn't running at the time&lt;/li&gt;
&lt;li&gt;Overwrite an existing image in &lt;code&gt;src/img&lt;/code&gt; with a new one with the same name, and have the corresponding images in &lt;code&gt;dist/img&lt;/code&gt; update automatically&lt;/li&gt;
&lt;li&gt;Use as many different sub-directories in &lt;code&gt;src/img&lt;/code&gt; and have those be replicated on &lt;code&gt;dist/img&lt;/code&gt; - for example &lt;code&gt;src/img/gallery/open-day&lt;/code&gt; which lives inside &lt;code&gt;dist&lt;/code&gt; as &lt;code&gt;dist/img/gallery/open-day&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a lot of features! It's easy to feel overwhelmed when seeing requirements like this. You might mentally try and solve all of them at once. Resist this temptation, if you can! Remember that computers are very, very stupid and need to be told very simple instructions. Most of our job is to take complicated tasks and break them down into very simple steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating new directories
&lt;/h3&gt;

&lt;p&gt;Let's create some new directories for these images to sit inside. Add some &lt;code&gt;img&lt;/code&gt; directories, so your Node project folder looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🗀 dist
  🗀 css
  🗀 img
🗀 src
  🗀 img
  🗀 scss
.gitattributes
.gitignore
package.json
package-lock.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now we just need to write all the code which converts the image from one format to another. So how do we get started?&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing another package
&lt;/h3&gt;

&lt;p&gt;If you guessed "installing another package from NPM", you have earned a new achievement: "Web Developer Cynicism Level 1". Spend those experience points wisely! The package we need to install is called &lt;a href="https://www.npmjs.com/package/onchange" rel="noopener noreferrer"&gt;&lt;em&gt;onchange&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing onchange
&lt;/h3&gt;

&lt;p&gt;Inside a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; onchange
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to use &lt;em&gt;onchange&lt;/em&gt;, we need to edit &lt;code&gt;package.json&lt;/code&gt;. Currently, the scripts node looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sass-dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --watch --update --style=expanded src/scss:dist/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sass-prod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --no-source-map --style=compressed src/scss:dist/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"browser-sync start --server &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --files &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --watch &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run serve&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-dev&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-prod&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to change it to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sass-dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --watch --update --style=expanded src/scss:dist/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sass-prod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --no-source-map --style=compressed src/scss:dist/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"browser-sync start --server &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --files &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --watch &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run serve&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-dev&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-prod&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"log-images"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onchange &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/img&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -- echo '{{event}} to {{file}}'"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down that new command (the second half of which I took directly from &lt;a href="https://www.npmjs.com/package/onchange#usage" rel="noopener noreferrer"&gt;the &lt;em&gt;onchange&lt;/em&gt; documentation&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;log-images&lt;/code&gt; - this is the name we're calling our new command. It could be anything, as long as it doesn't include spaces&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onchange&lt;/code&gt; - when we run our task, the first thing it does is invoke the &lt;code&gt;onchange&lt;/code&gt; method which we've just added to &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\"src/img\"&lt;/code&gt; - this is the (escaped) path to the directory which &lt;code&gt;onchange&lt;/code&gt; will be watching.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--&lt;/code&gt; - I'm indebted to the talented and funny &lt;a href="https://dev.to/moopet"&gt;Ben Sinclair&lt;/a&gt; for clarification on what this means. This is shorthand for &lt;q&gt;everything after this point isn't a flag&lt;/q&gt;. This means that any commands which happen to include a hyphen for the rest of the line won't be accidentally treated like a flag.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;echo&lt;/code&gt; - this allows us to add text to the terminal. It's the same as &lt;code&gt;console.log()&lt;/code&gt; in JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'{{event}} to {{file}}'&lt;/code&gt; - this is a string which is built up from two tokens &lt;code&gt;{{event}}&lt;/code&gt; and &lt;code&gt;{{file}}&lt;/code&gt;. This string will be &lt;code&gt;echo&lt;/code&gt;ed to the terminal (this just means that the text will appear in the terminal). These tokens are passed by &lt;code&gt;onchange&lt;/code&gt;. What do they mean? Let's find out!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now from a terminal, run our new command by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run log-images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;onchange&lt;/em&gt; runs constantly - it won't stop until we put the terminal in focus and hold down ctrl and c to cancel out of it. This means that as we update the directories, you can watch the terminal and see it update in real time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://imgur.com/a/example-images-BygxeTs" rel="noopener noreferrer"&gt;Here's some example images to download&lt;/a&gt;. Save the first one to &lt;code&gt;src/img&lt;/code&gt; and watch what happens in the terminal. You should see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"'add"&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt; &lt;span class="s2"&gt;"src&lt;/span&gt;&lt;span class="se"&gt;\i&lt;/span&gt;&lt;span class="s2"&gt;mg&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;&lt;span class="s2"&gt;7zTxla.png'"&lt;/span&gt;
&lt;span class="s2"&gt;"'change"&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt; &lt;span class="s2"&gt;"src&lt;/span&gt;&lt;span class="se"&gt;\i&lt;/span&gt;&lt;span class="s2"&gt;mg&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;&lt;span class="s2"&gt;7zTxla.png'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now save the second one, but overwrite the first. You should see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"'change"&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt; &lt;span class="s2"&gt;"src&lt;/span&gt;&lt;span class="se"&gt;\i&lt;/span&gt;&lt;span class="s2"&gt;mg&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;&lt;span class="s2"&gt;7zTxla.png'"&lt;/span&gt;
&lt;span class="s2"&gt;"'change"&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt; &lt;span class="s2"&gt;"src&lt;/span&gt;&lt;span class="se"&gt;\i&lt;/span&gt;&lt;span class="s2"&gt;mg&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;&lt;span class="s2"&gt;7zTxla.png'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now try renaming the file to &lt;code&gt;example-01&lt;/code&gt;. You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"'add"&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt; &lt;span class="s2"&gt;"src&lt;/span&gt;&lt;span class="se"&gt;\i&lt;/span&gt;&lt;span class="s2"&gt;mg&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;xample-01.png'"&lt;/span&gt;
&lt;span class="s2"&gt;"'unlink"&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt; &lt;span class="s2"&gt;"src&lt;/span&gt;&lt;span class="se"&gt;\i&lt;/span&gt;&lt;span class="s2"&gt;mg&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;&lt;span class="s2"&gt;7zTxla.png'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, so the two &lt;code&gt;{{event}}&lt;/code&gt;s we're interested in are &lt;code&gt;add&lt;/code&gt; and &lt;code&gt;change&lt;/code&gt; - when these occur, we need to ... do something. And that something has to do with the value of the second argument &lt;code&gt;{{file}}&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  JS Module mode&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;First thing we need to do is tell &lt;em&gt;Node&lt;/em&gt; that we want to use JS modules. Modules are the most modern way to use JavaScript, where every function lives in its own wee file and has a single purpose (at least that's the idea).&lt;/p&gt;

&lt;p&gt;To enable this, we need to add a new node to &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This needs to sit in the "root" of &lt;code&gt;package.json&lt;/code&gt;, as a sibling of other nodes you might recognise, such as &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;version&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A course on Node.js for front end developers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Passing arguments to a script
&lt;/h3&gt;

&lt;p&gt;We're going to write a script (don't worry - it's a &lt;em&gt;javascript&lt;/em&gt;) which will run and gather together all this information. But it's not going to sit inside &lt;code&gt;src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a new directory called &lt;code&gt;tools&lt;/code&gt; and put it in the root of the &lt;em&gt;Node&lt;/em&gt; application. This means it's a sibling to &lt;code&gt;dist&lt;/code&gt; and &lt;code&gt;src&lt;/code&gt;. The word "root" can get pretty confusing. Here's three different roots we might encounter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The root of the &lt;em&gt;Node&lt;/em&gt; application (where &lt;code&gt;package.json&lt;/code&gt; lives)&lt;/li&gt;
&lt;li&gt;The root of the local web server (that's the &lt;code&gt;dist&lt;/code&gt; directory)&lt;/li&gt;
&lt;li&gt;What &lt;em&gt;Microsoft™ Windows™&lt;/em&gt; regards as the root of the file system (this is probably the C: drive on your computer and we'll try and avoid this root as much as possible)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The directory structure of your project should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🗀 dist
  🗀 css
  🗀 img
🗀 src
  🗀 img
  🗀 scss
🗀 tools
.gitattributes
.gitignore
package.json
package-lock.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new file should be called &lt;code&gt;image-compress.js&lt;/code&gt;. It looks like this:&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;argv&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;node:process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;argv&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm. There's a lot going on there. Let's break it down.&lt;/p&gt;

&lt;p&gt;First thing we do is import a function called &lt;a href="https://nodejs.org/api/process.html#processargv" rel="noopener noreferrer"&gt;&lt;code&gt;argv()&lt;/code&gt;&lt;/a&gt; from &lt;code&gt;node:process&lt;/code&gt;. This is built into &lt;em&gt;Node&lt;/em&gt; and allows us to examine arguments, but not in the philosophical sense. We're going to pass &lt;code&gt;image-compress.js&lt;/code&gt; an argument in a moment.&lt;/p&gt;

&lt;p&gt;Next, we do what is called &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment" rel="noopener noreferrer"&gt;destructuring&lt;/a&gt;, which is a very cool, modern way to assign a bunch of variable at the same time. Because we know that &lt;code&gt;argv()&lt;/code&gt; is going to send us data inside an Array in a particular order, we can get ahead of the game and give them nicknames.&lt;/p&gt;

&lt;p&gt;Finally, we &lt;code&gt;console.log()&lt;/code&gt; two of our favourite arguments to the console. (&lt;code&gt;console.log()&lt;/code&gt; is like &lt;code&gt;echo&lt;/code&gt;, but in JavaScript)&lt;/p&gt;

&lt;h3&gt;
  
  
  Couple's therapy (passing arguments on)
&lt;/h3&gt;

&lt;p&gt;Are you still running &lt;code&gt;log-images&lt;/code&gt;? Stop that! Type ctrl and c in the terminal to kill that command.&lt;/p&gt;

&lt;p&gt;Now in &lt;code&gt;package.json&lt;/code&gt; we're going to update that command so it does something more useful. Change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"log-images"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onchange &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/img&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -- echo '{{event}} to {{file}}'"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"watch-images"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onchange &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;src/img&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -- node tools/image-compress.js {{file}} {{event}}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works exactly the same as before, but instead of &lt;code&gt;echo&lt;/code&gt;ing to the terminal, we're passing the &lt;code&gt;{{file}}&lt;/code&gt; and &lt;code&gt;{{event}}&lt;/code&gt; arguments to our script. Let's see if it works.&lt;/p&gt;

&lt;p&gt;In the terminal, type&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run watch-images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now return to &lt;a href="https://imgur.com/a/example-portal-images-q5VgZku" rel="noopener noreferrer"&gt;my example image page&lt;/a&gt; and download the second image again. Save it to &lt;code&gt;src/img&lt;/code&gt; and just leave the name how it is.&lt;/p&gt;

&lt;p&gt;You should see this in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src&lt;span class="se"&gt;\i&lt;/span&gt;mg&lt;span class="se"&gt;\r&lt;/span&gt;BZtOtY.png add
src&lt;span class="se"&gt;\i&lt;/span&gt;mg&lt;span class="se"&gt;\r&lt;/span&gt;BZtOtY.png change
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might not seem like much progress, but we're using the power of &lt;em&gt;Node&lt;/em&gt; to see a live update of what's happening on your hard drive. But this time we can manipulate it with JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hang on, what's doing the image optimisation exactly?
&lt;/h3&gt;

&lt;p&gt;The package we're going to install to do the optimisation is called &lt;a href="https://www.npmjs.com/package/sharp" rel="noopener noreferrer"&gt;&lt;em&gt;sharp&lt;/em&gt;&lt;/a&gt;. It's quite big and complicated, so it might take a little while to finish. Let's install it! Cancel out the &lt;code&gt;watch-images&lt;/code&gt; command which is currently running and in the terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; sharp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should update the &lt;code&gt;devDependencies&lt;/code&gt; node in &lt;code&gt;package.json&lt;/code&gt; with a reference to &lt;em&gt;sharp&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Import &lt;em&gt;sharp&lt;/em&gt; into &lt;code&gt;image-compress.js&lt;/code&gt; and see if it works. At the top of the file, add this line:&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="nx"&gt;sharp&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;sharp&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;Now let's call &lt;em&gt;sharp&lt;/em&gt; and point it as the &lt;code&gt;filePath&lt;/code&gt; variable. Your whole file should look like this:&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;argv&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;node:process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sharp&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;sharp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;argv&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./dist/img/filename.webp`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;
  Chaining
  &lt;p&gt;&lt;a href="https://medium.com/@hayavuk/method-chaining-explained-d3ac63fee91c" rel="noopener noreferrer"&gt;Some functions can be "chained" together&lt;/a&gt;. This means that the output from one is passed to the next. So the &lt;em&gt;Sharp&lt;/em&gt; call above could also be laid out like this:&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="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./dist/img/filename.webp`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a little harder to read so usually we indent the chained functions by one tab stop, like the original example.&lt;/p&gt;



&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;Let's run the task again. In the terminal, type&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run watch-images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now try taking any &lt;a href="https://imgur.com/a/example-portal-images-q5VgZku" rel="noopener noreferrer"&gt;example image&lt;/a&gt; and saving it to &lt;code&gt;src/img&lt;/code&gt;. Give it a new name which doesn't currently exist inside the directory.&lt;/p&gt;

&lt;p&gt;If everything's worked, you should see a new file inside your &lt;code&gt;dist/img&lt;/code&gt; folder called &lt;code&gt;filename.webp&lt;/code&gt;. Your first "next generation" image! You should be pleased.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sensible naming conventions
&lt;/h3&gt;

&lt;p&gt;But we can't call &lt;em&gt;all&lt;/em&gt; of our images &lt;code&gt;filename&lt;/code&gt;, that would be silly. Luckily, &lt;em&gt;Node&lt;/em&gt; comes with a whole suite of tools called &lt;em&gt;path&lt;/em&gt; to help us manipulate filepaths and filenames. Let's pull it into the top of &lt;code&gt;image-compress.js&lt;/code&gt; like we did with &lt;em&gt;sharp&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:path&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;Now let's see what we're working with. Change &lt;code&gt;image-compress.js&lt;/code&gt; so it looks like this:&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;argv&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;node:process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sharp&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;sharp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The path from the root of the Node application to the filename of the image&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dirName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// The image name, plus file extension&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// The image file extension&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Arguments:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Paths: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dirName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./dist/img/filename.webp`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quick reminder of where we are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;onchange&lt;/code&gt; is passing us the path to a file which has changed, plus an event, which we're not using yet&lt;/li&gt;
&lt;li&gt;We're loading the path into our script file&lt;/li&gt;
&lt;li&gt;We're using &lt;em&gt;Node&lt;/em&gt; to split this path into different parts&lt;/li&gt;
&lt;li&gt;Those parts are being &lt;code&gt;console.log()&lt;/code&gt;ed to the terminal, along with the original path&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing the logs
&lt;/h3&gt;

&lt;p&gt;Cancel the &lt;code&gt;watch-images&lt;/code&gt; task, then immediately restart it (just in case &lt;em&gt;Node&lt;/em&gt; is cacheing an old version).&lt;/p&gt;




&lt;p&gt;
  Terminal history
  &lt;p&gt;Your terminal has a sort of history. If you want to re-use a command you've typed in the past, you can press the up arrow key (↑) on your keyboard to cycle through all the old commands you've entered. Press up and down until you find &lt;code&gt;npm run watch-images&lt;/code&gt; again, then hit return.&lt;/p&gt;



&lt;/p&gt;




&lt;p&gt;Rename an existing image inside &lt;code&gt;src/img&lt;/code&gt; to a new name (doesn't matter what). You should see an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error: Input file is missing: src&lt;span class="se"&gt;\i&lt;/span&gt;mg&lt;span class="se"&gt;\r&lt;/span&gt;BZtOtY.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's going on here?&lt;/p&gt;

&lt;p&gt;Right now, this code is running every time &lt;em&gt;onchange&lt;/em&gt; detects a change inside &lt;code&gt;src/img&lt;/code&gt;. This error is triggered because &lt;em&gt;sharp&lt;/em&gt; can't find an image we're telling it to optimise.&lt;/p&gt;

&lt;p&gt;Comment-out &lt;em&gt;sharp&lt;/em&gt;, so we can see what's going on. Commenting-out can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sharp(filePath)&lt;/span&gt;
&lt;span class="c1"&gt;//   .webp()&lt;/span&gt;
&lt;span class="c1"&gt;//   .toFile(`./dist/img/filename.webp`);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try renaming the file again and look in the terminal. This is what I saw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Arguments: src&lt;span class="se"&gt;\i&lt;/span&gt;mg&lt;span class="se"&gt;\c&lt;/span&gt;hanged.png add
Paths:  src&lt;span class="se"&gt;\i&lt;/span&gt;mg changed.png .png
Arguments: src&lt;span class="se"&gt;\i&lt;/span&gt;mg&lt;span class="se"&gt;\t&lt;/span&gt;emp.png &lt;span class="nb"&gt;unlink
&lt;/span&gt;Paths:  src&lt;span class="se"&gt;\i&lt;/span&gt;mg temp.png .png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, we've got two different &lt;code&gt;console.log()&lt;/code&gt;s happening, but they seem to be happening twice. Why is this?&lt;/p&gt;

&lt;p&gt;The first set of logs happen during an &lt;code&gt;add&lt;/code&gt; event. The second happen during an &lt;code&gt;unlink&lt;/code&gt; event. It looks like &lt;code&gt;unlink&lt;/code&gt; is the same as deleting a file. So when I renamed a file, what really happened was that the old file was copied and added into the directory under a new name, then the old file was deleted.&lt;/p&gt;

&lt;p&gt;We need to only run our code when the right kind of event happens inside &lt;code&gt;src/img&lt;/code&gt;. Save another image from &lt;a href="https://imgur.com/a/example-portal-images-q5VgZku" rel="noopener noreferrer"&gt;the example image page&lt;/a&gt; but this time, save it over an existing image &lt;em&gt;which isn't the same image&lt;/em&gt;. You should see logs which look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Arguments: src&lt;span class="se"&gt;\i&lt;/span&gt;mg&lt;span class="se"&gt;\c&lt;/span&gt;hanged.png change
Paths:  src&lt;span class="se"&gt;\i&lt;/span&gt;mg changed.png .png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we did that, we got a &lt;code&gt;change&lt;/code&gt; event. So the two events we need to watch out for are &lt;code&gt;change&lt;/code&gt; and &lt;code&gt;add&lt;/code&gt;. Let's store them in an Array, for safe keeping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// White-list of events which should cause Sharp to generate images&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;triggerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&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;Put this variable &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;import&lt;/code&gt; statements. Now we need to corden off our call to &lt;em&gt;sharp&lt;/em&gt; behind an &lt;code&gt;if&lt;/code&gt; statement. This should only run if the &lt;code&gt;fileEvent&lt;/code&gt; is present in our new &lt;code&gt;triggerEvents&lt;/code&gt; Array. So the &lt;code&gt;if&lt;/code&gt; statement should look like this:&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;triggerEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./dist/img/filename.webp`&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;(it's safe to comment in &lt;em&gt;sharp&lt;/em&gt; inside the &lt;code&gt;if&lt;/code&gt; statement)&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows™ woes
&lt;/h3&gt;

&lt;p&gt;I have terrible news for you and I, members of The™ Windows™ Community™: those kids who use Linux, MacOS and so forth giggle at us behind our backs. You see, Windows uses a backslash (&lt;code&gt;\&lt;/code&gt;) to represent nested directories on the filesystem. The cool kids use a forward slash (&lt;code&gt;/&lt;/code&gt;) instead. If our code is going to run elsewhere, we need to get on board.&lt;/p&gt;

&lt;p&gt;Change your &lt;code&gt;dirName&lt;/code&gt; variable, so it reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The path from the root of the Node application to the filename of the image&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dirName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks like it replaces &lt;code&gt;\\&lt;/code&gt; with &lt;code&gt;/&lt;/code&gt;, but it doesn't. Remember escape characters? This is another example. The first backslash tells us to ignore the fact that the parser would usually ignore a backslash and to &lt;em&gt;not&lt;/em&gt; ignore it. It's a double negative. If you're confused right now, take strength from the thought that this is a normal reaction.&lt;/p&gt;

&lt;p&gt;What have we achieved? Let's trigger the script again and find out! Rename an existing file to something new. Your console logs should now look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Arguments: src&lt;span class="se"&gt;\i&lt;/span&gt;mg&lt;span class="se"&gt;\e&lt;/span&gt;xample-02.png add
Paths:  src/img example-02.png .png
Arguments: src&lt;span class="se"&gt;\i&lt;/span&gt;mg&lt;span class="se"&gt;\h&lt;/span&gt;7zTxla.png &lt;span class="nb"&gt;unlink
&lt;/span&gt;Paths:  src/img h7zTxla.png .png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how the &lt;code&gt;dirName&lt;/code&gt; variable now reads &lt;code&gt;src/img&lt;/code&gt;. Before, it was &lt;code&gt;src\img&lt;/code&gt;. What's nice about this change is that if the same script is run on a &lt;em&gt;Linux&lt;/em&gt; or &lt;em&gt;MacOS&lt;/em&gt; environment, it won't break because there won't be any back slashes to replace.&lt;/p&gt;

&lt;p&gt;Let's get rid of the &lt;code&gt;Arguments&lt;/code&gt; &lt;code&gt;console.log()&lt;/code&gt;. We've learned all we can from it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building up a path to our distribution image
&lt;/h3&gt;

&lt;p&gt;We need to change the path of a changed image - for example &lt;code&gt;src/img/example.png&lt;/code&gt; into the path of a converted image - for example &lt;code&gt;dist/img/example.webp&lt;/code&gt;. To do this, we need to find the image directory path (&lt;code&gt;img&lt;/code&gt;) and the file name (&lt;code&gt;example&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To get the image directory path, let's write a new function!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trimPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thisPath&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thisPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;newPath&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;This takes a path (for example &lt;code&gt;src/img&lt;/code&gt;) and then uses the variable we just created - &lt;code&gt;srcDirectoryName&lt;/code&gt; - and finds the first instance of that in the path, before replacing it with ... nothing. So it removes it.&lt;/p&gt;

&lt;p&gt;In arrow functions, if we can squeeze the code onto one line &lt;em&gt;and&lt;/em&gt; the function returns something, we can omit both the &lt;code&gt;{}&lt;/code&gt; and the &lt;code&gt;return&lt;/code&gt; statement and the function can automatically return the product. And if our arrow function has exactly one argument, we can dispense with the parenthesis around it. So our &lt;code&gt;trimPath&lt;/code&gt; function can also be expressed as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trimPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thisPath&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;thisPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might prefer the longer version for clarity and I respect that decision.&lt;/p&gt;

&lt;p&gt;Add your favourite version of this function (only one, mind!) to &lt;code&gt;image-compress.js&lt;/code&gt;. Let's use this function to create a new variable. Put it after the file extension variable, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The image file extension&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// The path to the source image, minus the `src` bit&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trimPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dirName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the image filename without the file extension, do a similar trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The path to the source image, minus the `src` bit&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trimPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dirName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// The name of the image, without the file extension&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;extName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This takes the filename (&lt;code&gt;example.png&lt;/code&gt;) and replaces the file extension (&lt;code&gt;.png&lt;/code&gt;) with nothing, leaving us just the name (&lt;code&gt;example&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Finally, we need to stitch together a couple of strings which we'll use more than once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`./dist&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;subPath&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give us &lt;code&gt;dist/img&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;OK, we've got enough scraps of string to knot together into a proper path now. Change your call to &lt;em&gt;sharp&lt;/em&gt; so it looks like this:&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="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&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;distPath&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;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.webp`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;
  Template literals
  &lt;p&gt;Using the backtick character to build up string like this more flexible than the legacy method, which looks like this:&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="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.webp&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;Another advantage of template literals is that they can include whitespace, so you can use whole chunks of JSON data spread out down your code, if you need to. You can switch between strings and simple JavaScript expressions by encapsulating JavaScript inside &lt;code&gt;${}&lt;/code&gt;.&lt;/p&gt;



&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;Get rid of your &lt;code&gt;console.log()&lt;/code&gt; in &lt;code&gt;image-compress.js&lt;/code&gt;. In terms of what order variables, imports and functions should appear in your file, the imports should always appear first. You shouldn't call on a variable before you've declared it. Some developers say you should declare all variables at the top of your file, but this isn't always practical (what about variables which occur inside of functions?).&lt;/p&gt;

&lt;p&gt;I personally think that if your code includes an &lt;code&gt;if&lt;/code&gt; statement, then moving what variables you can to inside the statement helps lower the memory footprint (as they're never put into memory, if the code isn't parsed).&lt;/p&gt;

&lt;p&gt;This would leave us with the following code:&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;argv&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;node:process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sharp&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;sharp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Destructuring the Array from Node which includes data we need&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// White-list of events which should cause Sharp to generate images&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;triggerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// If the wrong kind of event triggers this script, do nothing&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;triggerEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileEvent&lt;/span&gt;&lt;span class="p"&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;trimPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thisPath&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;thisPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// The path from the root of the Node application to the filename of the image&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dirName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// The image name, plus file extension&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// The image file extension&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// The path to the source image, minus the `src` bit&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trimPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dirName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// The name of the image, without the file extension&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;extName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`./dist&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;subPath&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="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFile&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;distPath&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;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.webp`&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;We've still plenty more to do, but let's recap what we've learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can use the &lt;em&gt;onchange&lt;/em&gt; package to pass events from the file system to our JavaScript&lt;/li&gt;
&lt;li&gt;We can run arbitrary JavaScript files directly in &lt;em&gt;Node&lt;/em&gt; - no web browser required&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Node&lt;/em&gt; gives us access to different methods of accessing path and filename information&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Sharp&lt;/em&gt; is this season's must-have image optimisation package (although perhaps that's changed now, if you are reading this in the future)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/Ross-Angus/node-js-for-developers/tree/chapter-3" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;View Chapter 3 code snapshot on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Quiz
&lt;/h1&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/QwWgmax?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>node</category>
      <category>npm</category>
      <category>sharp</category>
      <category>webp</category>
    </item>
    <item>
      <title>Chapter 2: Previewing our site in a browser</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Wed, 05 Mar 2025 17:01:06 +0000</pubDate>
      <link>https://forem.com/rossangus/chapter-2-previewing-our-site-in-a-browser-2dco</link>
      <guid>https://forem.com/rossangus/chapter-2-previewing-our-site-in-a-browser-2dco</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://www.instagram.com/igorstarkoff/" rel="noopener noreferrer"&gt;Igor Starkov&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Last time, we got through quite a bit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;created a new GitHub repo&lt;/li&gt;
&lt;li&gt;initiated the repo&lt;/li&gt;
&lt;li&gt;been introduced to JSON&lt;/li&gt;
&lt;li&gt;learned how and why to split up CSS&lt;/li&gt;
&lt;li&gt;added some directories and a landing page&lt;/li&gt;
&lt;li&gt;installed our first package: &lt;em&gt;SASS&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;hidden files from version control&lt;/li&gt;
&lt;li&gt;written some SCSS files&lt;/li&gt;
&lt;li&gt;written two &lt;code&gt;package.json&lt;/code&gt; commands&lt;/li&gt;
&lt;li&gt;generated our final CSS&lt;/li&gt;
&lt;li&gt;checked our changes into &lt;em&gt;GitHub&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This lesson will be a little more modest in scope. We're going to try and walk and chew gum at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Previewing our site in a web browser
&lt;/h2&gt;

&lt;p&gt;Wouldn't it be nice to see what our site looks like as we work? And to have the browser refresh automatically whenever we made a change to our code? Guess what - There's A Package For That™!&lt;/p&gt;

&lt;p&gt;Type this into the terminal in &lt;em&gt;VS Code&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; browser-sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know we've covered this before, but let's go over some of the new, hip slang:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm&lt;/code&gt; - switch to Node Package Manager mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i&lt;/code&gt; - short for "install", so run the &lt;code&gt;install&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-D&lt;/code&gt; - this is the shorted form of the &lt;code&gt;--save-dev&lt;/code&gt; flag (which ensures that none of the code for this package reaches the live site)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser-sync&lt;/code&gt; - this is &lt;a href="https://www.npmjs.com/package/browser-sync" rel="noopener noreferrer"&gt;the package&lt;/a&gt; we're installing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What does &lt;em&gt;browser-sync&lt;/em&gt; do?
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Node&lt;/em&gt; comes with a web server built in. &lt;em&gt;browser-sync&lt;/em&gt; allows us to spin up a server, open a web browser window and point at it and automatically refresh it, when we've make changes to the source files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring &lt;em&gt;browser-sync&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;package.json&lt;/code&gt;, add a new command to the &lt;code&gt;scripts&lt;/code&gt; node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"serve"&lt;/span&gt;: &lt;span class="s2"&gt;"browser-sync start --server &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --files &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --watch &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;serve&lt;/code&gt; - this is the name for our new command. We can call it by running &lt;code&gt;npm run serve&lt;/code&gt; inside a terminal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser-sync&lt;/code&gt; - call the new &lt;em&gt;browser-sync&lt;/em&gt; package&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;start&lt;/code&gt; - this calls the &lt;code&gt;start&lt;/code&gt; method within &lt;em&gt;browser-sync&lt;/em&gt; which starts the &lt;em&gt;browser-sync&lt;/em&gt; server. This might seem redundant (why call &lt;em&gt;browser-sync&lt;/em&gt; at all, if you don't want to start it?) but the &lt;em&gt;browser-sync&lt;/em&gt; function does other stuff too, such as reloading or setting up an init file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--server&lt;/code&gt; - this flag specifies that the particular service we want from &lt;em&gt;browser-sync&lt;/em&gt; is for it to be a fake server.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\"dist\"&lt;/code&gt; - this points to the "root" of this particular server. Which is &lt;code&gt;dist&lt;/code&gt;. And because we needed to use nested quotation marks, we had to "escape" them using &lt;code&gt;\&lt;/code&gt; (see escape characters, below)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--files \"dist\"&lt;/code&gt; - this specifies a location to watch for files other than HTML - for example images.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--watch \"dist\"&lt;/code&gt; - this tells &lt;em&gt;browser-sync&lt;/em&gt; to watch a particular directory for changes. If those files update, then &lt;em&gt;browser-sync&lt;/em&gt; should automatically refresh the web browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember that because we've added a new node to &lt;code&gt;package.json&lt;/code&gt;, we'll need to make sure that the preceding node has a comma in the right place. &lt;em&gt;VS Code&lt;/em&gt; should show you some angry red wiggles, if you've done it wrong. Usually on the following node from where the comma should be.&lt;/p&gt;




&lt;p&gt;
  Escape characters
  &lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An escape character is not the same as Harry Houdini. Instead, it's shorthand for "ignore the next thing I type". So let's say you wanted to use a double quotation mark in JSON, you couldn't just do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"actor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dwayne "&lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Rock&lt;/span&gt;&lt;span class="s2"&gt;" Johnson"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"character"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harry "&lt;/span&gt;&lt;span class="err"&gt;King&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Handcuffs&lt;/span&gt;&lt;span class="s2"&gt;" Houdini"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... because if you did so, the strings &lt;code&gt;The Rock&lt;/code&gt; and &lt;code&gt;King of Handcuffs&lt;/code&gt; will escaped their quotation marks and roam free inside the JSON file.&lt;/p&gt;

&lt;p&gt;Instead, you'd need to type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"actor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dwayne &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;The Rock&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; Johnson"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"character"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harry &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;King of Handcuffs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; Houdini"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON parser would read the &lt;code&gt;actor&lt;/code&gt; value as: &lt;em&gt;quote&lt;/em&gt; Dwayne &lt;em&gt;ignore the next character, I'm not finished&lt;/em&gt; "The Rock &lt;em&gt;still not finished&lt;/em&gt;" Johnson &lt;em&gt;OK, I'm done now, end quote&lt;/em&gt;.&lt;/p&gt;



&lt;br&gt;
&lt;/p&gt;




&lt;h3&gt;
  
  
  Testing &lt;em&gt;browser-sync&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Inside a terminal, call your new command using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might take a few seconds but eventually a new tab should open in your web browser pointing to your example page. And the text &lt;code&gt;Hello Worm&lt;/code&gt; should appear. It should &lt;em&gt;not&lt;/em&gt; be in the Times New Roman font. Note how &lt;code&gt;serve&lt;/code&gt; is still running in the terminal.&lt;/p&gt;




&lt;p&gt;
  Windows firewall warning
  &lt;p&gt;You might also see a warning about Windows Firewall blocking some features of this app. Click on &lt;em&gt;Allow access&lt;/em&gt;. &lt;em&gt;Browser-sync's&lt;/em&gt; cool.&lt;/p&gt;



&lt;/p&gt;




&lt;p&gt;Look at how we linked the CSS file in the HTML file (&lt;code&gt;dist/index.html&lt;/code&gt;) using the path &lt;code&gt;/css/main.css&lt;/code&gt;. That initial forward slash means "start from the root of the site". So if we owned the domain "node.com", the CSS file could be found at this URL: &lt;code&gt;https://node.com/css/main.css&lt;/code&gt;. However, if we dragged the HTML file into a browser, that path &lt;em&gt;wouldn't&lt;/em&gt; work because Windows boxes think of the &lt;em&gt;root&lt;/em&gt; on their file system differently.&lt;/p&gt;

&lt;p&gt;This means that &lt;em&gt;browser-sync&lt;/em&gt; is opening the local server correctly!&lt;/p&gt;

&lt;h3&gt;
  
  
  Watching the SCSS
&lt;/h3&gt;

&lt;p&gt;Remember the &lt;code&gt;watch-scss&lt;/code&gt; command we wrote? That isn't currently running. Under Windows, we can't just run two commands at the same time. &lt;em&gt;Until we install another package, that is!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's stop &lt;code&gt;serve&lt;/code&gt; from running. Put the terminal in focus and type CTRL + C and agree to stop the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing &lt;em&gt;npm-run-all&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Let's install &lt;em&gt;npm-run-all&lt;/em&gt;. Into a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; npm-run-all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring &lt;em&gt;npm-run-all&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;This is the new command we need to add to the &lt;code&gt;scripts&lt;/code&gt; node of &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"start"&lt;/span&gt;: &lt;span class="s2"&gt;"run-p serve sass-dev"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(quick angry red wiggles check: is your &lt;code&gt;package.json&lt;/code&gt; valid json?)&lt;/p&gt;

&lt;p&gt;We're defining a new command but hang on - it calls &lt;code&gt;run-p&lt;/code&gt; not &lt;code&gt;npm-run-all&lt;/code&gt;. What gives?&lt;/p&gt;

&lt;p&gt;Buried deep inside &lt;a href="https://github.com/mysticatea/npm-run-all/blob/bf91f94ce597aa61da37d2e4208ce8c48bc86673/docs/run-p.md" rel="noopener noreferrer"&gt;the source of npm-run-all is the &lt;code&gt;run-p&lt;/code&gt; command&lt;/a&gt;. &lt;code&gt;run-p&lt;/code&gt; is short for "run in parallel". This means it will run our &lt;code&gt;serve&lt;/code&gt; command and &lt;em&gt;also&lt;/em&gt; our &lt;code&gt;sass-dev&lt;/code&gt; command at the same time.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's in a name?
&lt;/h3&gt;

&lt;p&gt;There's a reason &lt;a href="https://docs.npmjs.com/cli/v11/commands/npm-start" rel="noopener noreferrer"&gt;we called this command &lt;code&gt;start&lt;/code&gt;&lt;/a&gt;. It's a special name which is assumed to run whatever the default action of our codebase is.&lt;/p&gt;

&lt;p&gt;Normally, when we run a command from &lt;code&gt;package.json&lt;/code&gt;, we'd need to type the following into the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run my-command-name-goes-here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, with &lt;code&gt;start&lt;/code&gt; we just need to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try it out! Type the above command into a terminal and a browser window should open and display the text &lt;code&gt;Hello Worm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, open &lt;code&gt;src/scss/_fonts/scss&lt;/code&gt; and add a new typeface to the front of the SCSS variable. Use &lt;code&gt;"Comic Sans MS"&lt;/code&gt; (also add the quotation marks).&lt;/p&gt;

&lt;p&gt;It should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$font-system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Comic Sans MS"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apple-system&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Segoe UI"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Oxygen-Sans&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ubuntu&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Cantarell&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Helvetica Neue"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$font-system&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;Your page should update with this "classic" meme font as soon as you hit &lt;em&gt;save&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change of plans
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;npm-run-all&lt;/em&gt; is nice, but I've changed my mind. Have a look at &lt;a href="https://www.npmjs.com/package/npm-run-all" rel="noopener noreferrer"&gt;the NPM page for npm-run-all&lt;/a&gt;. What do you notice? I'll give you a hint: it was last published (at the time of writing) six years ago.&lt;/p&gt;

&lt;p&gt;That is roughly the Victorian period of web development. I've forced you to bolt a steam engine to a spaceship. What can we do about this?&lt;/p&gt;

&lt;h3&gt;
  
  
  Out with the old
&lt;/h3&gt;

&lt;p&gt;Working with Node is confusing at the best of times so I want you to know we can always reverse a mistake. Let's wrench out the steam engine and watch as it drifts away in space, leaving behind lumps of coal, glittering like asteroids. To uninstall a package from our application, we can type &lt;code&gt;npm uninstall&lt;/code&gt; and then the name of the package. First cancel the &lt;code&gt;start&lt;/code&gt; command with &lt;em&gt;control&lt;/em&gt; + &lt;em&gt;c&lt;/em&gt; and a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm uninstall npm-run-all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll leave the &lt;code&gt;package.json&lt;/code&gt; unchanged at the moment.&lt;/p&gt;

&lt;p&gt;Perhaps, like me, you're suspicious of this. So in Windows Explorer, navigate to your application directory inside GitHub and peek inside the &lt;code&gt;node_modules&lt;/code&gt; folder. There should &lt;em&gt;not&lt;/em&gt; be a folder inside there called &lt;code&gt;npm-run-all&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(try not to think about how much code is already inside &lt;code&gt;node_modules&lt;/code&gt;. It's a black box, remember?)&lt;/p&gt;

&lt;h4&gt;
  
  
  The nuclear option
&lt;/h4&gt;

&lt;p&gt;Sometimes, your &lt;code&gt;node_modules&lt;/code&gt; folder gets a bit corrupt. I haven't experienced it in a while. Perhaps it was an issue which effected older versions of &lt;em&gt;Node&lt;/em&gt; more. But here's the nuclear option: simply delete the whole directory.&lt;/p&gt;

&lt;p&gt;Remember that &lt;code&gt;node_modules&lt;/code&gt; won't ever live in your &lt;em&gt;Git&lt;/em&gt; repo. So in order to build it up from scratch, in a terminal type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will look through your &lt;code&gt;package.json&lt;/code&gt; file and treat it like a shopping list, tootling round the aisles of &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;NPM&lt;/a&gt; and grabbing all the packages we need for a fresh install.&lt;/p&gt;

&lt;h3&gt;
  
  
  In with the new
&lt;/h3&gt;

&lt;p&gt;OK, so what's the new hotness which replaced &lt;em&gt;npm-run-all&lt;/em&gt;? It's called &lt;a href="https://www.npmjs.com/package/concurrently" rel="noopener noreferrer"&gt;&lt;em&gt;concurrently&lt;/em&gt;&lt;/a&gt;. We can tell it's the new hotness for a couple of reasons. At the time of writing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It has multiple &lt;em&gt;million&lt;/em&gt; weekly downloads&lt;/li&gt;
&lt;li&gt;The repo was updated within the last month&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Install &lt;em&gt;concurrently&lt;/em&gt; by going to your terminal and typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; concurrently
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Configuring &lt;em&gt;concurrently&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;The syntax is similar to &lt;em&gt;npm-run-all&lt;/em&gt;, but each different command call should be wrapped in quotes. So the command in &lt;code&gt;package.json&lt;/code&gt; which currently reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p serve watch-scss"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... should now read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run serve&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-dev&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(There are those escape characters again. They still look weird but you should get used to them eventually)&lt;/p&gt;

&lt;p&gt;Let's test that out! In a terminal, run &lt;code&gt;npm start&lt;/code&gt;. Your "Hello worm" page should load. It should still be in MS Comic Sans. Edit your &lt;code&gt;_fonts.scss&lt;/code&gt; file so that the &lt;code&gt;$font-system&lt;/code&gt; variable is back to normal. The variable should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$font-system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apple-system&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Segoe UI"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Oxygen-Sans&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ubuntu&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Cantarell&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Helvetica Neue"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hit save and your browser window should automagically update with whatever counts as a normal system font in your world. Yay! Your current setup can walk and chew gum at the same time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Collaborating
&lt;/h2&gt;

&lt;p&gt;Remember when we hid the directories &lt;code&gt;dist&lt;/code&gt; and &lt;code&gt;node_modules&lt;/code&gt; from &lt;em&gt;Git&lt;/em&gt;? We did that because I claimed that a new developer could download our code and derive all the &lt;code&gt;dist&lt;/code&gt; files using just the &lt;code&gt;src&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;The command we've just added will do this, but only when a developer runs the &lt;code&gt;start&lt;/code&gt; command and happens to edit a SASS file. What about when a new developer joins the project and runs the site for the first time? There won't be any &lt;code&gt;dist&lt;/code&gt; directory and all the files inside.&lt;/p&gt;

&lt;p&gt;As we add more functionality to this toolkit, we're going to need to increase what needs to be done when someone first runs the site. We'll also need to add a command which you can run when you want to build all of the files for the production website. Hey! These are the same thing!&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a new &lt;code&gt;prepare&lt;/code&gt; command
&lt;/h3&gt;

&lt;p&gt;Let's add a new command to &lt;code&gt;package.json&lt;/code&gt;. Call it &lt;code&gt;prepare&lt;/code&gt;. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sass-dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --watch --update --style=expanded src/scss:dist/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sass-prod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --no-source-map --style=compressed src/scss:dist/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"browser-sync start --server &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --files &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --watch &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run serve&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-dev&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm run sass-prod&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;prepare&lt;/code&gt; is &lt;a href="https://docs.npmjs.com/cli/v10/using-npm/scripts" rel="noopener noreferrer"&gt;a special keyword&lt;/a&gt; for &lt;em&gt;NPM&lt;/em&gt; because this command runs automatically after a developer runs &lt;code&gt;npm install&lt;/code&gt; in their terminal.&lt;/p&gt;

&lt;p&gt;This means that our hypothetical second developer will download the repo, then run &lt;code&gt;npm install&lt;/code&gt; and will automatically generate out all the CSS into the &lt;code&gt;dist&lt;/code&gt; directory without realising they're doing it. And that CSS will be based on the latest version of the SCSS.&lt;/p&gt;

&lt;p&gt;Perhaps you're wondering why we're using &lt;code&gt;concurrently&lt;/code&gt; - we're running exactly one task. The answer is that you are at the start of a wonderful journey of discovery. During this epic quest, we'll be adding many more commands to &lt;code&gt;package.json&lt;/code&gt; which take files from &lt;code&gt;src&lt;/code&gt; to &lt;code&gt;dist&lt;/code&gt; and compress them in different ways. And we'll be adding these commands onto our new &lt;code&gt;prepare&lt;/code&gt; command as we go. It's going to be really cool, I promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check-in time
&lt;/h2&gt;

&lt;p&gt;Open up &lt;em&gt;GitHub Desktop&lt;/em&gt; and check in your work. Remember to push it to GitHub too!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Here's what we've achieved in this lesson:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We installed &lt;em&gt;browser-sync&lt;/em&gt; so we can see the contents of &lt;code&gt;dist&lt;/code&gt; in a web browser&lt;/li&gt;
&lt;li&gt;Learned about escape characters&lt;/li&gt;
&lt;li&gt;Ran more than one command at the same time&lt;/li&gt;
&lt;li&gt;Learned about the special &lt;code&gt;start&lt;/code&gt; command name&lt;/li&gt;
&lt;li&gt;Installed, then removed a package because we changed our mind&lt;/li&gt;
&lt;li&gt;Learned that if something goes wrong with &lt;code&gt;node_modules&lt;/code&gt; we can always nuke it and start again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/Ross-Angus/node-js-for-developers/tree/chapter-2a" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;View Chapter 2 code snapshot on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Quiz
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/qEBmxMz?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>node</category>
      <category>npm</category>
      <category>browsersync</category>
    </item>
    <item>
      <title>Chapter 1: setup, CSS, version control and SASS</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Wed, 05 Mar 2025 16:25:33 +0000</pubDate>
      <link>https://forem.com/rossangus/nodejs-for-developers-course-chapter-1-setup-css-version-control-and-sass-206a</link>
      <guid>https://forem.com/rossangus/nodejs-for-developers-course-chapter-1-setup-css-version-control-and-sass-206a</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://www.instagram.com/rquirosfoto/" rel="noopener noreferrer"&gt;Rodolfo Quirós&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Last time, we introduced you to a whole bunch of different software you'll need to get started with &lt;em&gt;Node&lt;/em&gt;. This gang:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; (and &lt;a href="https://github.com/apps/desktop" rel="noopener noreferrer"&gt;GitHub Desktop&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/windows/package-manager/winget/" rel="noopener noreferrer"&gt;Winget&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Schniz/fnm" rel="noopener noreferrer"&gt;Fast Node Manager&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine these are your new work colleagues. Some of them we'll be spending a lot of time with. Perhaps eating lunch together, maybe going out for drinks after work. Others we just needed briefly and can casually discard like yesterday's socks. Some will make awkward eye-contact with us, but we'll ignore them completely.&lt;/p&gt;

&lt;p&gt;Today, let's get to know GitHub a little better and get our new project set up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create new GitHub repo
&lt;/h2&gt;

&lt;p&gt;(these instructions assume you have both a GitHub account and have downloaded and installed &lt;a href="https://github.com/apps/desktop" rel="noopener noreferrer"&gt;Github Desktop&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://github.com/apps/desktop" rel="noopener noreferrer"&gt;Github Desktop&lt;/a&gt; and from the &lt;code&gt;File&lt;/code&gt; menu, select &lt;code&gt;New repository&lt;/code&gt;. The &lt;em&gt;Name&lt;/em&gt; field will be the name of the folder you'll create, so although it's a little counter-intuitive, you should create the repository in the &lt;em&gt;root&lt;/em&gt; of your &lt;code&gt;GitHub&lt;/code&gt; folder. For example, mine is here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\R&lt;/span&gt;oss-Angus&lt;span class="se"&gt;\D&lt;/span&gt;ocuments&lt;span class="se"&gt;\G&lt;/span&gt;itHub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think up a name for your test project. Personally, I'd keep your name lower-case and without spaces (use hyphens instead, if you like). But perhaps this isn't relevant any more and I've just got into the habit. While it's possible to change the name later, it's the sort of thing where the old name might keep cropping up and annoy you, so choose well.&lt;/p&gt;

&lt;p&gt;I've called my version &lt;code&gt;node-js-for-developers&lt;/code&gt;. At the end of each chapter, I'll include a link to a snapshot of my code so you can compare it to your own, in case you run into problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open your new folder
&lt;/h2&gt;

&lt;p&gt;From your IDE, browse to your new folder and open it. The only file inside should be a &lt;code&gt;.gitattributes&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;This is a configuration file for &lt;em&gt;GIT&lt;/em&gt;. We won't be editing this. Don't open it. I forbid it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Allow &lt;em&gt;Node&lt;/em&gt; to move in
&lt;/h2&gt;

&lt;p&gt;Open a terminal in &lt;em&gt;VS Code&lt;/em&gt; (from the &lt;em&gt;View&lt;/em&gt; menu, select &lt;em&gt;Terminal&lt;/em&gt;) and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The terminal will ask you a series of questions. If you feel inclined, pretend you're in the film &lt;em&gt;Wargames&lt;/em&gt;. If you press &lt;em&gt;enter&lt;/em&gt;, rather than answering the questions, the default answer will be chosen. Most of the defaults are sensible and don't worry - we can always change them later.&lt;/p&gt;

&lt;p&gt;Let's break down this &lt;code&gt;npm init&lt;/code&gt; command. We've already talked about package managers like &lt;em&gt;winget&lt;/em&gt; but &lt;em&gt;npm&lt;/em&gt; is a package manager we're going to use more than once. &lt;em&gt;npm&lt;/em&gt; is also used to run commands in &lt;em&gt;Node&lt;/em&gt;. Which is what we're doing now.&lt;/p&gt;

&lt;p&gt;By typing &lt;code&gt;npm&lt;/code&gt;, we're switching the terminal to &lt;code&gt;npm&lt;/code&gt; mode, where it has access to other methods.&lt;/p&gt;

&lt;p&gt;Calling &lt;code&gt;init&lt;/code&gt; is one of these methods. &lt;code&gt;init&lt;/code&gt; is short for "initiation". It sets up the current directory as the root of a new &lt;em&gt;Node&lt;/em&gt; application. That boils down to creating a new file called &lt;code&gt;package.json&lt;/code&gt;. We're going to spend a &lt;em&gt;lot&lt;/em&gt; of time editing &lt;code&gt;package.json&lt;/code&gt;. In many ways, it's the heart of the application.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;package.json&lt;/code&gt; in &lt;em&gt;VS Code&lt;/em&gt;. This is the sort of boilerplate for the repo. Or perhaps like a library card for a book, if you remember the last century. Note how your default answers to the quiz &lt;em&gt;Node&lt;/em&gt; just made you take appear inside &lt;code&gt;package.json&lt;/code&gt;. This is where you can change them.&lt;/p&gt;

&lt;p&gt;This is what mine looks like currently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node-js-for-developers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A course on Node.js for front end developers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Error: no test specified&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; exit 1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ross Angus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go through each of these, although &lt;a href="https://docs.npmjs.com/cli/v11/configuring-npm/package-json" rel="noopener noreferrer"&gt;&lt;em&gt;NPM&lt;/em&gt; has a more detailed article&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; - this is the name of the directory for your project. Think of it as the short, computer-friendly version of your project name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt; - we'll talk about version &lt;em&gt;control&lt;/em&gt; at the end of this lesson but this allows you to give an arbitrary number to the version. Is it bad to admit I tend to ignore this field?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt; - this text appears when users search for your package on &lt;em&gt;npm&lt;/em&gt;. Think of it like a summary of your project.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; - this is the entry point of our application. During this course, we're going to install a lot of packages which will help us to perform certain tasks. These packages will live in a folder called &lt;code&gt;node_modules&lt;/code&gt;. Each package is a project in its own right and has its own dependences (yes, sometimes there's &lt;em&gt;another&lt;/em&gt; &lt;code&gt;node_modules&lt;/code&gt; directory inside the package's folder inside &lt;code&gt;node_modules&lt;/code&gt;. How deep does the rabbit hole go? No-one knows). When &lt;em&gt;Node&lt;/em&gt; is deciding how to load these packages, it needs an entry point to the package. That's what this node is for. Hey - guess what &lt;code&gt;main&lt;/code&gt; defaults to, if you don't declare it? That's right - &lt;code&gt;index.js&lt;/code&gt;. So this is useless and can be deleted. Thanks for coming to my TED talk.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scripts&lt;/code&gt; - we're going to spend a lot of time inside this particular node of &lt;code&gt;package.json&lt;/code&gt;. &lt;code&gt;scripts&lt;/code&gt; lets us give nicknames to long, complicated commands we'd usually run within the terminal. We can add as many as we want (and I'm going to add plenty). More on this later.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;author&lt;/code&gt; - your name should be here. Next chance you get, change your profession on your passport to "Author" and refuse to elaborate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;license&lt;/code&gt; - this is the software licence which you're assigning to your code. It defaults to &lt;a href="https://docs.npmjs.com/cli/v11/configuring-npm/package-json" rel="noopener noreferrer"&gt;ISC&lt;/a&gt; which I'm going to paraphrase as "you can use this but if you die doing so, it's not my fault".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  JSON files
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; is written in &lt;em&gt;json&lt;/em&gt; format, or JavaScript Object Notation to it's mum. It's a data format which should be familiar if you've worked with JavaScript objects. However, where it differs from JavaScript objects is it's a little more strict. In JavaScript, you could create an object like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Joseph&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;surName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Merrick&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;born&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1862-08-05&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;died&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1890-04-11&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;catchphrase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I am not an object, I am a human being!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;human&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we expressed this data as JSON, we would need to place all the name properties in quotes like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Joseph"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"surName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Merrick"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"born"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1862-08-05"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"died"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1890-04-11"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"catchphrase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I am not an object, I am a human being!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"human"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also couldn't have a comma on the last name/value pair, so instead of &lt;code&gt;"human": true,&lt;/code&gt; we have &lt;code&gt;"human": true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's a more complicated example, with more data-types. Look at what happens to the final node within nested objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text-example"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This is a text example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"array-example"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"This value is an Array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"object-example"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nested-text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This example contains json inside your json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"boolean-example"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nested-integer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nested-array"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"last-node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"As this is the last node inside the nested object, it has no comma to the right -&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"final-node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No comma to the right-&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our old pal &lt;em&gt;VS Code&lt;/em&gt; will probably throw up some wiggly red lines if we do it wrong, so look out for them. If you're struggling to see why it doesn't work, try an &lt;a href="https://jsonlint.com/" rel="noopener noreferrer"&gt;online JSON Validator&lt;/a&gt; and see if it pushes you in the right direction.&lt;/p&gt;

&lt;p&gt;With that done, let's start working on the CSS of our site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling CSS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Legacy CSS
&lt;/h3&gt;

&lt;p&gt;In some legacy sites you might work on during your career, you might encounter CSS files which are thousands of lines long. You probably won't have time to refactor this code (most likely you're just adding a quick fix) but when projects reach this level of complexity, the best thing we can do is to nuke them from orbit and rebuild from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can we do this better from the start?
&lt;/h3&gt;

&lt;p&gt;If we split out CSS into different files where each one is concerned with a different part of our application then in theory, when we remove a component, we can remove its CSS at the same time and not impact any other part of the site.&lt;/p&gt;

&lt;p&gt;Of course, we could do this with existing technology by simply importing many different CSS files into each page, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-GB"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Lots of CSS files&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/fonts.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/grid.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/header.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/footer.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/typefaces.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/heading.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/bodytext.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/call-to-action.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would work! But here's the downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unless the site is served using HTTP2, each of those CSS files adds a little bit of overhead to the browser, as it needs to handshake a new connection each time to the server&lt;/li&gt;
&lt;li&gt;Each of those files has lots of whitespace which helps us, the developers, see what's going on but is irrelevant to the browser. Why force our users to download whitespace?&lt;/li&gt;
&lt;li&gt;Each CSS file requires a new &lt;code&gt;link&lt;/code&gt; tag to be put into the relevant pages. If we &lt;em&gt;do&lt;/em&gt; remove a CSS file, all those pages need to be updated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to this, we might want to use some of the power of &lt;a href="https://sass-lang.com/" rel="noopener noreferrer"&gt;SASS&lt;/a&gt; on our site.&lt;/p&gt;




&lt;p&gt;
  What's SASS?
  &lt;p&gt;&lt;a href="https://sass-lang.com/" rel="noopener noreferrer"&gt;SASS&lt;/a&gt; is an extension of CSS which allows us to do stuff which wasn't originally build into CSS. It's been so influential that CSS has copied some of its homework.&lt;/p&gt;

&lt;p&gt;One of its many features is that it lets us nest rules inside each other which has some advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It lets us specify and class at the top level and the nested rule automatically inherits that classname&lt;/li&gt;
&lt;li&gt;If we need to change a class name, this can be done at the root just once&lt;/li&gt;
&lt;li&gt;It can be used to reflect the structure of the HTML, meaning there's a closer relationship between the HTML and CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, it's also possible to accidentally generate a &lt;em&gt;lot&lt;/em&gt; of CSS with relatively simple SCSS rules, so it should be used with care.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;So a good solution to this problem looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can create as many CSS (or SCSS) files as we like, in a nested directory structure if we want&lt;/li&gt;
&lt;li&gt;When we remove an SCSS file, the final CSS file will reflect this&lt;/li&gt;
&lt;li&gt;We can use comments in these files which won't reach the live site (this only works in SCSS, when we use comments which start with &lt;code&gt;//&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;All this SCSS is converted into a single CSS file&lt;/li&gt;
&lt;li&gt;The CSS file has the minimum amount of whitespace in it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Splitting up our CSS
&lt;/h3&gt;

&lt;p&gt;The first problem we want to solve is to have our CSS split into different parts which do different jobs. There's lots of different theories about the best way to do this but I'm fond of a mixture of &lt;a href="https://bradfrost.com/blog/post/atomic-web-design/" rel="noopener noreferrer"&gt;Atomic Design&lt;/a&gt; for global styles and &lt;a href="https://getbem.com/" rel="noopener noreferrer"&gt;BEM&lt;/a&gt; for styles which only exist on one component.&lt;/p&gt;

&lt;p&gt;That's quite a lot of homework I've just given you! What's more important than learning a methodology and enforcing it rigidly is adapting to how things are done in your current workplace.&lt;/p&gt;

&lt;p&gt;For example, if we order our CSS rules any old way, it's difficult to spot when a declaration has been repeated, especially when the rule gets long:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.logo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Fun quiz: find the repeated declaration in the above rule. Now do this with any legacy CSS you encounter for the rest of your career)&lt;/p&gt;

&lt;p&gt;I got used to ordering my declarations alphabetically, to ensure this repetition didn't happen. But then I worked on a project which enforced the &lt;a href="https://github.com/airbnb/css" rel="noopener noreferrer"&gt;Airbnb CSS / Sass Styleguide&lt;/a&gt;, which had a different take. And I couldn't even check code in which didn't match their method.&lt;/p&gt;

&lt;p&gt;Try and keep an open mind about these things, because they're liable to change. And if you're in a position to change things, do so with compassion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make some sub-folders
&lt;/h2&gt;

&lt;p&gt;With all this in mind, let's set up some directories for the files we're going to be creating. Our application will have two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The source folder&lt;/li&gt;
&lt;li&gt;The distribution folder&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We're going to do all our edits on files within the source folder, then packages we're going to install in a bit will automatically copy these over to the distribution folder in a format which is web-ready. For example, an SCSS rule such as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... will end up as the following CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kind of file is really hard to read, but downloads slightly faster.&lt;/p&gt;

&lt;p&gt;However, we're not going to use the words "source" and "distribution", we're going to use their &lt;em&gt;street&lt;/em&gt; names: &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;dist&lt;/code&gt;. Your application directory should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🗀 dist
  🗀 css
🗀 src
  🗀 scss
.gitattributes
package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just empty folders are fine for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a landing page
&lt;/h2&gt;

&lt;p&gt;A good place to start is &lt;a href="https://github.com/h5bp/html5-boilerplate/blob/main/dist/index.html" rel="noopener noreferrer"&gt;the HTML5 boilerplate project&lt;/a&gt;. We just need a simple HTML page for now. Like this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"no-js"&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-GB"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello Worm&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello Worm&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new file inside the &lt;code&gt;dist&lt;/code&gt; folder by right-clicking on it and selecting &lt;em&gt;New File&lt;/em&gt; from the pop-up menu. Call the file &lt;code&gt;index.html&lt;/code&gt; and then open it in &lt;em&gt;VS Code&lt;/em&gt;. Paste the HTML markup above into it and save it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install our first package: Sass
&lt;/h2&gt;

&lt;p&gt;Right, we've had a peek at SCSS. Let's install it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing
&lt;/h3&gt;

&lt;p&gt;In the terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; sass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down what each part of this command does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm&lt;/code&gt; - this invokes &lt;em&gt;Node Package Manager&lt;/em&gt;. Think of this as switching the terminal to &lt;em&gt;NPM&lt;/em&gt; mode (but just for the length of time it takes the command to execute).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;install&lt;/code&gt; - this calls the &lt;code&gt;install&lt;/code&gt; method, which is built in to &lt;em&gt;NPM&lt;/em&gt;. It's a bit like calling a function, but this function lives within the &lt;em&gt;NPM&lt;/em&gt; world.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--save-dev&lt;/code&gt; - this is a "flag" (flags start with a hyphen). NPM gives us access to many JavaScript libraries, some of which are useful just while we're developing, and some of which are useful on our actual live site. This particular package (&lt;em&gt;sass&lt;/em&gt;) is only useful while we're developing. None of the code which helps us convert scss to css should end up on the live site. So this flag ensures that remains the case. We can see this the effect of this on &lt;code&gt;package.json&lt;/code&gt; in a moment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sass&lt;/code&gt; - this is the name of the package on &lt;a href="https://www.npmjs.com/package/sass" rel="noopener noreferrer"&gt;the &lt;em&gt;NPM&lt;/em&gt; database&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What's changed?
&lt;/h3&gt;

&lt;p&gt;Let's look at &lt;code&gt;package.json&lt;/code&gt; once more - the new node looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.83.4"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we've used the &lt;code&gt;--save-dev&lt;/code&gt; flag, the package is inside a node called &lt;code&gt;devDependencies&lt;/code&gt;. This means that the code which &lt;em&gt;Sass&lt;/em&gt; uses won't end up on the live site.&lt;/p&gt;

&lt;p&gt;That number (1.83.4, in my example) represents what version number of the package is installed. Note that packages age like milk - they can and will go out of date, develop security holes, fall out of development, get replaced by different packages. It's basically a nightmare.&lt;/p&gt;

&lt;p&gt;The caret (&lt;code&gt;^&lt;/code&gt;) symbol (yes, I had to look that word up) means "this version of sass is supported, along with all future versions".&lt;/p&gt;

&lt;p&gt;What we've done with the install is to dump a bunch of code into a new folder called &lt;code&gt;node_modules&lt;/code&gt;. You can think of this folder as "here be dragons". You should not edit any code inside here or check this folder into your repo. All of this code is pulled down remotely from NPM and when another developer pulls down &lt;em&gt;your&lt;/em&gt; code, they will pull down the contents of &lt;code&gt;node_modules&lt;/code&gt; afresh from NPM, rather than getting it from your repo.&lt;/p&gt;

&lt;p&gt;This means your repo remains relatively small, but &lt;code&gt;node_modules&lt;/code&gt; can get big. Like, &lt;em&gt;really&lt;/em&gt; big. It's scary how big it gets. Try not to think about it too much, or you'll start to cry (speaking from experience).&lt;/p&gt;

&lt;h3&gt;
  
  
  Hide &lt;code&gt;node_modules&lt;/code&gt; and &lt;code&gt;dist&lt;/code&gt; from GIT
&lt;/h3&gt;

&lt;p&gt;Because of what I've just said, we &lt;em&gt;really&lt;/em&gt; don't want to check &lt;code&gt;node_modules&lt;/code&gt; into GIT. And by "really" I mean "I forgot about this bit, the first time I tried this".&lt;/p&gt;

&lt;p&gt;Here's what &lt;em&gt;you&lt;/em&gt; need to do: create a new file in the root of your project called &lt;code&gt;.gitignore&lt;/code&gt;. Here's what should be inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node_modules
dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.gitignore&lt;/code&gt; is another configuration file, like &lt;code&gt;package.json&lt;/code&gt; or (don't look at it!) &lt;code&gt;gitattributes&lt;/code&gt;. It (and stop me if I'm going too fast here) contains a list of files and directories which &lt;em&gt;GIT&lt;/em&gt; should ignore.&lt;/p&gt;

&lt;p&gt;That's it. GIT will now ignore any files inside the &lt;code&gt;node_modules&lt;/code&gt; or &lt;code&gt;dist&lt;/code&gt; directories. You can add other files and paths in this file in the future, should you need to. You might notice that your new &lt;code&gt;dist&lt;/code&gt; folder has turned dark grey in &lt;em&gt;VS Code&lt;/em&gt;. This indicates that you've done everything right and the contents of &lt;code&gt;dist&lt;/code&gt; are being ignored.&lt;/p&gt;

&lt;h4&gt;
  
  
  Wait, why are we ignoring &lt;code&gt;dist&lt;/code&gt;?
&lt;/h4&gt;

&lt;p&gt;The important files in the repo are the source files. All files in the &lt;code&gt;dist&lt;/code&gt; directory will (eventually) be derived purely from the source.&lt;/p&gt;

&lt;p&gt;If a different developer downloaded this repo, they'd need to install it (basically download their own version of &lt;code&gt;node_modules&lt;/code&gt;), then run it, in order to generate the contents of the &lt;code&gt;dist&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Don't worry: we'll cover all this again. Unless I forget.&lt;/p&gt;

&lt;h3&gt;
  
  
  File setup
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;SASS&lt;/em&gt; takes &lt;code&gt;scss&lt;/code&gt; files and transforms them into CSS files. Specifically, one CSS file.&lt;/p&gt;

&lt;p&gt;We're going to use it to split our CSS up into lots of different files, then squash them all together into an unreadable mess (just like I showed earlier), but just for the live site. Let's do some setup!&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;src/scss&lt;/code&gt; folder, create two files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main.scss&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_fonts.scss&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;_fonts.scss&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$font-system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apple-system&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Segoe UI"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Oxygen-Sans&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ubuntu&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Cantarell&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Helvetica Neue"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$font-system&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;This is a very simple Sass file which assigns a long string (the list of local typefaces) to a variable (&lt;code&gt;$font-system&lt;/code&gt;) and them applies it to the &lt;code&gt;body&lt;/code&gt; tag. Variables are native to CSS now, but let's pretend that isn't true.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main.scss&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'_fonts'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This imports the whole of the &lt;code&gt;_fonts.scss&lt;/code&gt; file into the &lt;code&gt;main.scss&lt;/code&gt; file. Note that any &lt;code&gt;@use&lt;/code&gt; rules you add to your Sass files should appear before any actual CSS code. But that won't be an issue with us, as we probably won't be mixing the two in the same file.&lt;/p&gt;

&lt;p&gt;Note the underscore as well. We'll come back to that later. (My editor informs me that foreshadowing helps to develop narrative tension. Is this working? Are you feeling tense?)&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring
&lt;/h3&gt;

&lt;p&gt;Using our new sass package in &lt;code&gt;node_modules&lt;/code&gt;, we can convert scss files into css files, but our site doesn't know how to yet. Let's configure &lt;code&gt;package.json&lt;/code&gt; so it knows how to do this.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;scripts&lt;/code&gt; part of &lt;code&gt;package.json&lt;/code&gt; currently looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Error: no test specified&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; exit 1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to change it, so it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sass-dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --watch --update --style=expanded src/scss:dist/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sass-prod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass --no-source-map --style=compressed src/scss:dist/css"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've set a little trap for you here - your &lt;code&gt;scripts&lt;/code&gt; node in &lt;code&gt;package.json&lt;/code&gt; won't be the final set of nodes in your file. That means that the closing brace (&lt;code&gt;}&lt;/code&gt;) should have a comma after it. Hopefully the angry red wiggles inside &lt;em&gt;VS Code&lt;/em&gt; should indicate where the error is.&lt;/p&gt;

&lt;h4&gt;
  
  
  What does this mean?
&lt;/h4&gt;

&lt;p&gt;Each new node in the &lt;code&gt;scripts&lt;/code&gt; object represents a command we can run from the command line. The name part (for example &lt;code&gt;sass-dev&lt;/code&gt; or &lt;code&gt;sass-prod&lt;/code&gt;) is a little like a variable in JavaScript - it's a shorthand we can use to refer to the value part. The value part (for example &lt;code&gt;sass --watch --update --style=expanded src/scss:dist/css&lt;/code&gt;) is a whole command line which runs when we type out the first bit.&lt;/p&gt;

&lt;p&gt;Let's break down exactly what the &lt;code&gt;sass-dev&lt;/code&gt; command does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sass&lt;/code&gt; - calls the sass executable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--watch&lt;/code&gt; - this is the "watch" flag. This means that the sass executable will watch a bunch of directories and files for any changes, then do something. This kind of behaviour is possible in &lt;em&gt;Node&lt;/em&gt;, but not in native JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--update&lt;/code&gt; - this checks the change date of the SCSS file against the CSS file it's going to create. If the SCSS is younger, then it overwrites the CSS file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--style=expanded&lt;/code&gt; - this is a flag (&lt;code&gt;style&lt;/code&gt;) with an argument (&lt;code&gt;expanded&lt;/code&gt;) passed to it. This defines what kind of CSS will be generated. An expanded style will use what is called a &lt;a href="https://web.dev/articles/source-maps" rel="noopener noreferrer"&gt;source map&lt;/a&gt; which means even though the browser is parsing CSS, when we inspect an element, we'll see the source SCSS instead! This will be really helpful for debugging.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/scss:dist/css&lt;/code&gt; - these are two paths, separated by a colon. Translated into English, this means "look inside the &lt;code&gt;src/scss&lt;/code&gt; directory for your source files, then output them to the &lt;code&gt;dist/css&lt;/code&gt; directory".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The underscore
&lt;/h3&gt;

&lt;p&gt;Remember how the scss source had two files, &lt;code&gt;main.scss&lt;/code&gt; and &lt;code&gt;_fonts.scss&lt;/code&gt;? Here's the clever bit: &lt;em&gt;Sass&lt;/em&gt; will ignore all scss files which start with an underscore. It will take the remaining files and replicate them in the target directory (in our case, &lt;code&gt;dist/css&lt;/code&gt;). But because we've pulled the &lt;code&gt;_fonts.scss&lt;/code&gt; file into &lt;code&gt;main.scss&lt;/code&gt; using &lt;code&gt;@use&lt;/code&gt;, it gets added in anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing it out
&lt;/h3&gt;

&lt;p&gt;We've defined two custom commands. Let's try the &lt;code&gt;sass-prod&lt;/code&gt; one out. In a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run sass-prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quick reminder: this switches the terminal to &lt;em&gt;Node Package Manager&lt;/em&gt; mode, calls the &lt;code&gt;run&lt;/code&gt; function, then executes the &lt;code&gt;sass-prod&lt;/code&gt;. &lt;em&gt;npm&lt;/em&gt; will look inside &lt;code&gt;package.json&lt;/code&gt; for a matching name inside the &lt;code&gt;scripts&lt;/code&gt; node and attempt to run it.&lt;/p&gt;

&lt;p&gt;This is invaluable when you're downloading a new project and you don't know how to make it start up. Check inside &lt;code&gt;package.json&lt;/code&gt; to see what it does (I mean, the documentation should tell you, but it might not).&lt;/p&gt;

&lt;p&gt;Now open up the new file which has been created inside the &lt;code&gt;dist/css&lt;/code&gt; directory. It should be our CSS, but all squashed up. This is the command we need to run when we've finished development and we want to generate the production-ready CSS which lives on the live site. It won't be generated with source maps and all unnecessary white space will be removed.&lt;/p&gt;




&lt;p&gt;
  Source maps
  &lt;p&gt;Source maps are a way of telling the web browser exactly where a particular bit of code is coming from. Let's say we have a style sheet called &lt;code&gt;call-to-action.scss&lt;/code&gt; which is being compiled into a CSS file called &lt;code&gt;main.css&lt;/code&gt;. Under normal circumstances, when we inspected an element using the developer tools, it would tell us the CSS was coming from &lt;code&gt;main.css&lt;/code&gt;. And if we're removing all of the white space from our CSS, it will also helpfully report that the CSS appears on line one (line one is 20,000 characters long).&lt;/p&gt;

&lt;p&gt;We can use source maps during development (and &lt;em&gt;only&lt;/em&gt; during development) to allow the browser to point us in the right direction. They're really cool and you'll quickly take them for granted.&lt;/p&gt;



&lt;/p&gt;




&lt;p&gt;Let's link our new CSS file into our example page. Inside the &lt;code&gt;head&lt;/code&gt; tag, add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/main.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Saving and versioning our work
&lt;/h2&gt;

&lt;p&gt;Hopefully you've been saving your work as you go along (most stuff simply won't work if you don't). But now we've made a bit of progress, it's time to &lt;em&gt;version&lt;/em&gt; our work. This takes a kind of snapshot of what we've done, so if we need to, we can return to it later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointing &lt;em&gt;GitHub Desktop&lt;/em&gt; at your files
&lt;/h3&gt;

&lt;p&gt;We do this in &lt;em&gt;GitHub Desktop&lt;/em&gt;. Open it and from the &lt;em&gt;File&lt;/em&gt; menu, select the option &lt;em&gt;Add local repository...&lt;/em&gt; Browse your computer for the right directory (it's probably in &lt;code&gt;Documents&lt;/code&gt; &amp;gt; &lt;code&gt;GitHub&lt;/code&gt; and whatever name you gave it) click the button &lt;code&gt;Select Folder&lt;/code&gt; then on the next window, the &lt;code&gt;Add repository&lt;/code&gt; button.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;GitHub Desktop&lt;/em&gt; anatomy
&lt;/h3&gt;

&lt;p&gt;The GitHub Desktop window has three parts to it. On the left is a list of all the changed files. These could be new files you've added or old files which have changed since you last checked them in. If you select any of these files, you can see the code changes on the right hand side.&lt;/p&gt;

&lt;p&gt;This right hand side panel has two parts. If the file is already in the repo (none of our files are like this) then the left hand side will show what the file looked like before you changed it. Anything you've changed or deleted will be in scary red. On the right hand side is the same file with the new parts highlighted in green. All of our files are new, so they will all consist just of the green parts.&lt;/p&gt;

&lt;p&gt;Finally, at the bottom right is a window for adding notes to your check-in. This has your avatar against it, to remind you that everyone will know what you typed and will judge you for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commit messages, a political history
&lt;/h3&gt;

&lt;p&gt;Developers take writing a good commit message &lt;em&gt;very seriously&lt;/em&gt; and have Opinions™ about it. I wouldn't be surprised if at least one land war in Asia was triggered by a bad commit message.&lt;/p&gt;

&lt;p&gt;Perhaps the take-away here is that a commit message isn't necessarily a summary of &lt;em&gt;what&lt;/em&gt; you've changed - because developers can work that out from the files themselves. But it's perhaps better to say &lt;em&gt;why&lt;/em&gt; you changed it.&lt;/p&gt;

&lt;p&gt;For example, let's say you've been assigned a support ticket for an existing website. This ticket will have an ID and this is crucial to note in the message, as it will give anyone following you a breakdown of the whole job. But just in case the ticket is inaccessible, try and summarise why the change was made in a short message after the ID.&lt;/p&gt;

&lt;p&gt;But before you get too attached to this methodology, remember that different work places will have different ways of doing this. One attempt to standardise the practice is &lt;a href="https://www.conventionalcommits.org/" rel="noopener noreferrer"&gt;Conventional Commits&lt;/a&gt;. Try and stay flexible and follow the best practice where you work.&lt;/p&gt;

&lt;p&gt;Once you've completed all of these fields to your satisfaction, click on the &lt;em&gt;Commit to &lt;strong&gt;main&lt;/strong&gt;&lt;/em&gt; button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hang on - what's &lt;em&gt;main&lt;/em&gt;?
&lt;/h3&gt;

&lt;p&gt;A full understanding of version control, branching and merging is a little outside the scope of this course but one of the most powerful aspects of &lt;em&gt;Git&lt;/em&gt; is that you can take a copy of the codebase off on a little detour, while the rest of your colleagues work on it in isolation. Then, once you've finished your work and someone has checked it over, it can be merged into the &lt;em&gt;main&lt;/em&gt; trunk of the repository.&lt;/p&gt;

&lt;p&gt;What we're doing here is considered beyond reckless - working on and checking into &lt;em&gt;main&lt;/em&gt; directly. In the so-called real world, you'll be working on a branch of a branch of a branch. Someone much more important than a developer will merge your work into the &lt;em&gt;main&lt;/em&gt; branch.&lt;/p&gt;

&lt;p&gt;But you are (and you should feel a warm glow of satisfaction any moment now) the most important person in this project. You work on the &lt;em&gt;main&lt;/em&gt; branch and live your best life.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local and remote
&lt;/h3&gt;

&lt;p&gt;Just one more small point to make about &lt;em&gt;Git&lt;/em&gt;: you've successfully checked in your code now, but it still just lives on your local machine. This is fine, but not so good if you want to collaborate with other developers. In order for that to work you need to &lt;em&gt;push&lt;/em&gt; your code to &lt;em&gt;GitHub&lt;/em&gt; itself.&lt;/p&gt;

&lt;p&gt;If you do that (and you should - aside from everything else, it's a useful backup), you can choose to make it public or private. So you shouldn't just save and commit your work, you also need to push it to GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We've done a lot in this lesson! We've:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;created a new GitHub repo&lt;/li&gt;
&lt;li&gt;initiated the repo&lt;/li&gt;
&lt;li&gt;been introduced to JSON&lt;/li&gt;
&lt;li&gt;learned how and why to split up CSS&lt;/li&gt;
&lt;li&gt;added some directories and a landing page&lt;/li&gt;
&lt;li&gt;installed our first package: &lt;em&gt;SASS&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;hidden files from version control&lt;/li&gt;
&lt;li&gt;written some SCSS files&lt;/li&gt;
&lt;li&gt;written two &lt;code&gt;package.json&lt;/code&gt; commands&lt;/li&gt;
&lt;li&gt;generated our final CSS&lt;/li&gt;
&lt;li&gt;checked our changes into &lt;em&gt;GitHub&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a lot. While we've only seen the output CSS inside &lt;em&gt;VS Code&lt;/em&gt;, in the next chapter, we'll install another package which will let us see the results in the browser, just as it would appear on a server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Ross-Angus/node-js-for-developers/tree/chapter-1a" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;View Chapter 1 code snapshot on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Quiz
&lt;/h1&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/zxYwpWd?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>node</category>
      <category>sass</category>
      <category>github</category>
      <category>npm</category>
    </item>
    <item>
      <title>Chapter 0: installing and updating Node</title>
      <dc:creator>Ross Angus</dc:creator>
      <pubDate>Wed, 05 Mar 2025 14:33:44 +0000</pubDate>
      <link>https://forem.com/rossangus/nodejs-for-developers-course-chapter-0-installing-and-updating-node-2kim</link>
      <guid>https://forem.com/rossangus/nodejs-for-developers-course-chapter-0-installing-and-updating-node-2kim</guid>
      <description>&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://www.instagram.com/tima_miroshnichenko/" rel="noopener noreferrer"&gt;Tima Miroshnichenko&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is a course to introduce a developer to Node.js, as it's used in the world of front-end development. It's split into chapters and will take you through the process of building a static HTML site in a modern way using the latest tools (at least at the time of writing). There'll be jokes. There'll be quizzes. There'll be a lot of code. But we're going to take it slow, OK? OK.&lt;/p&gt;

&lt;p&gt;This is the course I wish I could have taken as a younger developer. Hopefully it's useful to someone else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ground rules
&lt;/h2&gt;

&lt;p&gt;I think there's two kinds of developers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Developers who are in love with technology&lt;/li&gt;
&lt;li&gt;Developers who want to build things&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We need both! The first kind go on to become principle developers or work in DevOps. I'm the second kind.&lt;/p&gt;

&lt;p&gt;This means when I encounter a new technology, I'm not automatically excited by it because it means I need to take days out of my life to learn it, when I could be building stuff instead.&lt;/p&gt;

&lt;p&gt;So my first question when someone tells me about some new tech is "yeah, but &lt;em&gt;why&lt;/em&gt;?"&lt;/p&gt;

&lt;p&gt;I'm going to take this approach in this guide. Because we're going to encounter a &lt;em&gt;lot&lt;/em&gt; of technology in this guide, all stacked on top of each other like the level in &lt;a href="https://www.youtube.com/watch?v=cb8yNvIrn8Q" rel="noopener noreferrer"&gt;Getting Over it With Bennett Foddy&lt;/a&gt;. And at certain points, you're probably going to feel frustrated and think that web development requires an insane footprint just to render &lt;code&gt;Hello world&lt;/code&gt; to a web page (this is a normal reaction).&lt;/p&gt;

&lt;p&gt;Here's the thing: this guide assumes you already know how to do a bit of programming in JavaScript. But this guide isn't about &lt;em&gt;programming&lt;/em&gt;, it's about &lt;em&gt;engineering&lt;/em&gt;. We're going to be wearing hard-hats, hi-vis vests and boots with regulation toe-caps. We'll be building an application which others can contribute to easily and safely. This, for better or worse, is more or less what web development looks like in the real world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  You, the reader has:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;learned a bit of CSS, HTML and JavaScript&lt;/li&gt;
&lt;li&gt;done some tutorials and have got the hang of things&lt;/li&gt;
&lt;li&gt;want to build something a little more complicated (perhaps you've already given it a go, and it got unwieldy really fast)&lt;/li&gt;
&lt;li&gt;keep encountering code which doesn't make any sense and references to technology which you haven't learned about yet&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Version control
&lt;/h3&gt;

&lt;p&gt;This guide expects you to have an account with &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and to have the vague idea that version control is like saving stuff "with knobs on". Please go set one up! It's free and you'll likely use it professionally, so don't choose a silly username.&lt;/p&gt;

&lt;p&gt;I also make use of &lt;a href="https://desktop.github.com/download/" rel="noopener noreferrer"&gt;GitHub Desktop&lt;/a&gt; right at the start. It's true that you can do everything in GitHub Desktop using the command line, but give me a break. I want to build user interfaces. Don't judge me for wanting to &lt;em&gt;use&lt;/em&gt; them too.&lt;/p&gt;

&lt;h3&gt;
  
  
  IDE (Integrated Development Environment)
&lt;/h3&gt;

&lt;p&gt;Developers these days generally edit their code using an IDE. This does nice stuff like syntactic highlighting and shows all your other files on the left. I use &lt;a href="https://code.visualstudio.com/download" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; (it's friends call it "VS Code", but I'm more of a work colleague). It's fine!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;VS Code&lt;/em&gt; lets you open a terminal (Windows sometimes calls this a "command line") and type commands right next to your code. This means your IDE mostly consists of three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your code&lt;/li&gt;
&lt;li&gt;The other files and folders which make up your application&lt;/li&gt;
&lt;li&gt;The terminal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can open the terminal in VS Code by going to the &lt;code&gt;View&lt;/code&gt; menu and selecting &lt;code&gt;Terminal&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  OS
&lt;/h3&gt;

&lt;p&gt;This guide is written from the assumption that you're using a fairly recent version of Microsoft™ Windows™. Sorry. It'll mostly work in other operating systems but I've not tested it. Windows™ can sometimes be the most difficult to get this tech stack working&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;I'm going to call "folders" "directories" in this guide because that's what the other operating systems call them. If you get into this habit, other developers will think you are 10% more cool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get started with &lt;em&gt;Node.js&lt;/em&gt;!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hold up - what even &lt;em&gt;is&lt;/em&gt; &lt;em&gt;Node&lt;/em&gt;?
&lt;/h3&gt;

&lt;p&gt;Like me, I'm sure you've looked at something in the real world and thought "hmmm. It sure would be useful to manipulate that object's methods, properties or events using JavaScript". Objects such as your pet or spouse, for example.&lt;/p&gt;

&lt;p&gt;A similar thought occurred to Ryan "Not Roald" Dahl in 2009. He wanted to expand what JavaScript could do.&lt;/p&gt;

&lt;p&gt;JavaScript is what's called "sandboxed". Because JavaScript was originally designed to be downloaded from a distant website and executed in the browser, there wasn't any way to ensure if code was safe or not. So browsers ran the code inside sandboxed environments, where it was restricted from browsing your hard drive and looking for passwords. It was a bit like putting it in jail.&lt;/p&gt;

&lt;p&gt;Ryan wanted a version of JavaScript which ran on the server and &lt;em&gt;did&lt;/em&gt; have access to the hard drive. This means it could do useful stuff like squashing together a bunch of different CSS files into one big CSS file which is completely unreadable becausemostofthewhitespacehasbeenremoved or taking your boring old JPEG images and converting them into the new image formats such as (in ascending order of coolness) &lt;em&gt;JPEG 2000&lt;/em&gt;, &lt;em&gt;WebP&lt;/em&gt; or &lt;em&gt;AVIF&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;But Ryan was also wise enough to know what &lt;em&gt;Node&lt;/em&gt; &lt;em&gt;shouldn't&lt;/em&gt; do. He knew that as long as he focused on the core of the technology, all the other stuff would be written by other people and they could be equally focused in making their stuff do one thing really well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is &lt;em&gt;Node&lt;/em&gt; installed?
&lt;/h3&gt;

&lt;p&gt;All of the following technology is reliant on &lt;em&gt;Node&lt;/em&gt;, so first, let's check if that's installed or not.&lt;/p&gt;

&lt;p&gt;From the terminal in your IDE (probably &lt;em&gt;VS Code&lt;/em&gt;), type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or if you're short of time, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press enter, obvs.&lt;/p&gt;

&lt;p&gt;What we're doing here is checking if &lt;em&gt;Node&lt;/em&gt; exists within your development environment or not. Right from the off, I want you to feel comfortable with the terminal throwing up weird messages at you. It's likely that some angry, red, bold text is shouting at you right now, claiming that &lt;em&gt;Node&lt;/em&gt; isn't recognised or that you are bad at spelling. Try not to let the error messages upset you. Think of them as being shouted by a toddler who is cross because they need something to eat.&lt;/p&gt;

&lt;p&gt;The two commands above mean the same thing, but the second one is the sort of street-slang version. We'll see this sort of thing a lot in the following lessons.&lt;/p&gt;

&lt;p&gt;If &lt;em&gt;Node&lt;/em&gt; &lt;em&gt;is&lt;/em&gt; installed, then the second part of the command (&lt;code&gt;--version&lt;/code&gt; or &lt;code&gt;-v&lt;/code&gt;) will be parsed. This part is called a "flag" and works a little like an argument being passed to a function. Yes, we've called on &lt;em&gt;Node&lt;/em&gt;, but what do we want it to do? (we want it to tell us what version it is)&lt;/p&gt;

&lt;p&gt;Why are there two ways of saying the same thing? I guess sometimes you're in a hurry and value expediency over readability.&lt;/p&gt;

&lt;h2&gt;
  
  
  No &lt;em&gt;Node&lt;/em&gt;? What now?
&lt;/h2&gt;

&lt;p&gt;You can &lt;a href="https://nodejs.org/en/download/prebuilt-installer" rel="noopener noreferrer"&gt;download &lt;em&gt;Node&lt;/em&gt; as a normal installer&lt;/a&gt;, but where's the fun in that? &lt;em&gt;Real&lt;/em&gt; developers use Package Managers instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait, package managers? Like the post office?
&lt;/h3&gt;

&lt;p&gt;Package managers are a type of service (usually driven through the terminal) which allow you to add or remove packages to your application. They work a bit like plugins in your web browser. We'll need to install a lot of them as we go through this course.&lt;/p&gt;

&lt;p&gt;We're going to use one package manager (&lt;em&gt;winget&lt;/em&gt;) to install a second package manager (&lt;em&gt;Node Package Manager&lt;/em&gt; or &lt;em&gt;npm&lt;/em&gt;). If this seems daft to you, strap in, because there's a lot more where that came from.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;winget&lt;/em&gt; comes built-in to &lt;em&gt;Windows&lt;/em&gt;. &lt;em&gt;Node&lt;/em&gt; does not. &lt;em&gt;winget&lt;/em&gt; is only available on &lt;em&gt;Windows&lt;/em&gt; so won't work for other operating systems. This means we need a second package manager because people who somehow manage to exist without &lt;em&gt;Windows&lt;/em&gt; don't use &lt;em&gt;winget&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So think of using &lt;em&gt;winget&lt;/em&gt; to download &lt;em&gt;npm&lt;/em&gt; as opening up &lt;em&gt;Microsoft Edge&lt;/em&gt; in order to &lt;a href="https://www.mozilla.org/firefox/" rel="noopener noreferrer"&gt;download the superior &lt;em&gt;Firefox&lt;/em&gt;&lt;/a&gt;. &lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The instructions on &lt;a href="https://nodejs.org/en/download/package-manager" rel="noopener noreferrer"&gt;the &lt;em&gt;Node&lt;/em&gt; site&lt;/a&gt; at the time of writing look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download and install fnm:&lt;/span&gt;
winget &lt;span class="nb"&gt;install &lt;/span&gt;Schniz.fnm

&lt;span class="c"&gt;# Download and install Node.js:&lt;/span&gt;
fnm &lt;span class="nb"&gt;install &lt;/span&gt;22

&lt;span class="c"&gt;# Verify the Node.js version:&lt;/span&gt;
node &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="c"&gt;# Should print "v23.7.0".&lt;/span&gt;

&lt;span class="c"&gt;# Verify npm version:&lt;/span&gt;
npm &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="c"&gt;# Should print "10.9.2".&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to work through this one line at a time. First, type (or copy-and-paste) this into your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;winget &lt;span class="nb"&gt;install &lt;/span&gt;Schniz.fnm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next (and this doesn't seem to be covered in the instructions) you need to shut down &lt;em&gt;VS Code&lt;/em&gt; and restart it. Not sure why this is required. Perhaps &lt;em&gt;VS Code&lt;/em&gt; needs to sleep on it?&lt;/p&gt;

&lt;p&gt;Let's go through this command item by item:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;winget&lt;/code&gt; - this invokes the &lt;em&gt;winget&lt;/em&gt; software, which is built-in to &lt;em&gt;Windows&lt;/em&gt;. Think of this as switching the terminal to &lt;em&gt;winget&lt;/em&gt; mode, for the duration of the command.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;install&lt;/code&gt; - this is like a function call to the install part of &lt;em&gt;winget&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Schniz.fnm&lt;/code&gt; - this requests that we install the &lt;a href="https://winget.run/pkg/Schniz/fnm" rel="noopener noreferrer"&gt;Fast Node Manager package from the winget package library&lt;/a&gt;. &lt;code&gt;Schniz&lt;/code&gt; is the name of the user who wrote this package. &lt;code&gt;fnm&lt;/code&gt; is the name of the package. Once we get to &lt;em&gt;Node Package Manager&lt;/em&gt;, it works in a slightly simpler way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fast Node Manager (&lt;em&gt;fnm&lt;/em&gt;)
&lt;/h3&gt;

&lt;p&gt;The first thing we did - before installing Node itself even - was install something called &lt;em&gt;fnm&lt;/em&gt; or &lt;em&gt;Fast Node Manager&lt;/em&gt;. This is a much bigger deal that you might think.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fast Node Manager&lt;/em&gt; lets us do a whole different bunch of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download old versions of &lt;em&gt;Node&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Download a newer version of &lt;em&gt;Node&lt;/em&gt; which has just come out&lt;/li&gt;
&lt;li&gt;Switch between different versions of &lt;em&gt;Node&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Automatically use a particular version of &lt;em&gt;Node&lt;/em&gt; for one particular project&lt;/li&gt;
&lt;li&gt;Give these different versions of &lt;em&gt;Node&lt;/em&gt; nicknames which only we understand (just go with it, OK?)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Why is this necessary?
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Node&lt;/em&gt; is under active development. It's moved up three versions since I started writing this course and I expect it to release a new version by the time you've finished reading this sentence. Sometimes, these new versions break something in the older versions. Or we might use a package which relies on a technique which has changed in later versions of &lt;em&gt;Node&lt;/em&gt;. This means we might need to run different versions of &lt;em&gt;Node&lt;/em&gt; for different projects.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fast Node Manager&lt;/em&gt; lets us switch between them quickly. It's written in &lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; which is a very cool language I know nothing about other than it runs really fast and is used for mission-critical systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's continue installing &lt;em&gt;Node&lt;/em&gt;!
&lt;/h3&gt;

&lt;p&gt;We can use &lt;em&gt;Fast Node Manager&lt;/em&gt; to install the current version of &lt;em&gt;Node&lt;/em&gt;. But the "current version" comes in two flavours:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;LTS ("Long Term Support" - not short for "latest", as I originally thought)&lt;/li&gt;
&lt;li&gt;Current (under active development)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Long Term Support edition (which is one version number behind current) is the "stable" release. It's been heavily tested and is only changed in order to address security holes or bugs. This is the version &lt;em&gt;Node&lt;/em&gt; suggests you use on production environments (that's what web developers call the live web server).&lt;/p&gt;

&lt;p&gt;The current edition has all the new features and is a little more experimental. It's useful for authors of packages (which we'll come to eventually) to check if they still work when the new version comes out.&lt;/p&gt;

&lt;p&gt;Let's be conservative and install the &lt;em&gt;LTS&lt;/em&gt; version of &lt;em&gt;Node&lt;/em&gt;, which, at the time of writing, is 22. But just in case I forget to update this every six months for the rest of my life, &lt;a href="https://nodejs.org/en/download" rel="noopener noreferrer"&gt;have a quick check on the Node download page&lt;/a&gt;. In the terminal, type (or paste):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fnm use &lt;span class="nt"&gt;--install-if-missing&lt;/span&gt; 22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down this line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fnm&lt;/code&gt; - calls &lt;em&gt;Fast Node Manager&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;use&lt;/code&gt; - invokes the &lt;code&gt;use&lt;/code&gt; function, which is part of &lt;code&gt;fnm&lt;/code&gt;. This tells &lt;code&gt;fnm&lt;/code&gt; that we want to change the current version of &lt;em&gt;Node&lt;/em&gt; to a different one.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--install-if-missing&lt;/code&gt; - this tells &lt;em&gt;fnm&lt;/em&gt; that if it &lt;em&gt;doesn't&lt;/em&gt; find version 22 already installed, it should download and install it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;22&lt;/code&gt; - specifies that version 22 of &lt;em&gt;Node&lt;/em&gt; should be installed. &lt;em&gt;fnm&lt;/em&gt; won't install the version number 22.0.0. Instead, it will look for the very latest version of release 22 which has come out. Reminder: if you're reading this in the future (the only possible outcome, due to the linear nature of time), the number on the &lt;em&gt;Node&lt;/em&gt; site might be bigger now. I think this is due to inflation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we get to check which version of &lt;em&gt;Node&lt;/em&gt; we have installed, which is where we got into this whole sorry mess in the first place. We do this with the commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... for &lt;em&gt;Node&lt;/em&gt; and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... for &lt;em&gt;Node Package Manager&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Great work, team!&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the environmental variables
&lt;/h2&gt;

&lt;p&gt;For reasons the authors of &lt;em&gt;Fast Node Manager&lt;/em&gt; are keeping a closely guarded secret, we now need to configure some environmental variables. This used to be part of the install process of &lt;em&gt;Node&lt;/em&gt; but with the latest versions, they've removed it. Perhaps someone decided the learning curve was looking a little shallow.&lt;/p&gt;

&lt;p&gt;You'll encounter environmental variables during your career but in this context, they're basically the settings for &lt;em&gt;Node&lt;/em&gt;. But before I get into how to change them, I'll need to introduce you to &lt;a href="https://learn.microsoft.com/en-us/powershell/scripting/overview" rel="noopener noreferrer"&gt;PowerShell&lt;/a&gt;. We've talked about the terminal and how that's what the cool people call the command prompt. But Windows these days comes with a sexed-up version of the command prompt called &lt;em&gt;PowerShell&lt;/em&gt;. It's what's running inside VS Code when you open the terminal.&lt;/p&gt;

&lt;p&gt;We can create a profile for &lt;em&gt;PowerShell&lt;/em&gt; by typing this into the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-not&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Test-Path &lt;span class="nv"&gt;$profile&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; New-Item &lt;span class="nv"&gt;$profile&lt;/span&gt; &lt;span class="nt"&gt;-Force&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an &lt;code&gt;if&lt;/code&gt; statement which tests if &lt;em&gt;PowerShell&lt;/em&gt; has a profile or not, and creates one if it doesn't. Whichever version of reality you live in, after you've run this, a profile will exist for &lt;em&gt;PowerShell&lt;/em&gt;. You can open this profile by typing this into the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Invoke-Item &lt;span class="nv"&gt;$profile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like the idea of &lt;code&gt;Invoke-Item&lt;/code&gt;. Makes me feel like a wizard. An empty file will probably appear. Not in &lt;em&gt;VS Code&lt;/em&gt;, but in something horrible like &lt;em&gt;Nodepad&lt;/em&gt;. Add the following line to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fnm &lt;span class="nb"&gt;env&lt;/span&gt; &lt;span class="nt"&gt;--use-on-cd&lt;/span&gt; &lt;span class="nt"&gt;--shell&lt;/span&gt; powershell | Out-String | Invoke-Expression
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file and close it. Close, then re-open &lt;em&gt;VS Code&lt;/em&gt; for good measure. I'm not 100% sure this is required, but we need &lt;em&gt;PowerShell&lt;/em&gt; to load its environmental variables from scratch and it seems sensible. Sometimes, web development is indistinguishable from superstition.&lt;/p&gt;

&lt;p&gt;Here's what I think is going on with the line we added to the &lt;em&gt;PowerShell&lt;/em&gt; profile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fnm&lt;/code&gt; - now we've installed Fast Node Manager, we're calling it for the first time, which is a little like switching the terminal to &lt;em&gt;fnm&lt;/em&gt; mode.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env&lt;/code&gt; - this sets a new environmental variable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--use-on-cd&lt;/code&gt; - hey, I remember this! This is a flag! This particular flag allows &lt;em&gt;fnm&lt;/em&gt; to switch &lt;em&gt;Node&lt;/em&gt; versions on the fly. For example, let's say you need to edit a &lt;em&gt;Node&lt;/em&gt; project which requires an antique version of &lt;em&gt;Node&lt;/em&gt;. &lt;em&gt;fnm&lt;/em&gt; will find a configuration file in the root of the project and then use an older matching version of &lt;em&gt;Node&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--shell&lt;/code&gt; - a "shell" is another word for "terminal". Or command prompt. This flag allows us to choose a shell to assign this environmental variable to, even though it appears inside the profile file for &lt;em&gt;PowerShell&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;poweshell&lt;/code&gt; - this confirms that the shell we want to assign this environmental variable to is &lt;em&gt;PowerShell&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;| Out-String | Invoke-Expression&lt;/code&gt; - Full disclosure: I can't find either of these commands in &lt;a href="https://github.com/Schniz/fnm/blob/master/docs/configuration.md" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;. Let's spin this as a learning experience. Sometimes, we have to use commands we don't fully understand and just have faith in how they work. (Did that sound convincing?)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Updating Node
&lt;/h2&gt;

&lt;p&gt;Wait, we've just installed &lt;em&gt;Node&lt;/em&gt; and we already need to know how to update it? Yup. The world of &lt;em&gt;Node&lt;/em&gt; moves very quickly but by default, &lt;em&gt;Node&lt;/em&gt; (or more accurately, &lt;em&gt;fnm&lt;/em&gt;) keeps us locked into the version we first installed. This is to ensure that any breaking changes introduced with future versions won't break old code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;fnm&lt;/em&gt; lets us juggle multiple version of &lt;em&gt;Node&lt;/em&gt; which we can swap between on a whim. Just for fun (YMMV), let's get &lt;em&gt;fnm&lt;/em&gt; to list all the versions of &lt;em&gt;Node&lt;/em&gt; it knows about. In the terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fnm ls-remote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ls&lt;/code&gt; is short for &lt;code&gt;list&lt;/code&gt;. So this asks &lt;em&gt;fnm&lt;/em&gt; for a list of all the different &lt;em&gt;Node&lt;/em&gt; versions it's possible to use. Let's install whatever the stable version is this week. In the terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fnm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-lts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might recognise the &lt;code&gt;-lts&lt;/code&gt; flag from earlier - it stands for "long term support". If &lt;em&gt;Node&lt;/em&gt; has moved up a level or two since you last installed it, this will automatically pull down the new version. However, we won't automatically use it. Let's see what versions we have locally installed. In a terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fnm &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My console shows this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt; v22.13.1 lts-latest
&lt;span class="k"&gt;*&lt;/span&gt; v23.7.0 default
&lt;span class="k"&gt;*&lt;/span&gt; system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The line highlighted in blue in your terminal will represent the version of &lt;em&gt;Node&lt;/em&gt; which is currently in use. In order to switch to a different one, you can either use the version number like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fnm use 22.13.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... or the alias (I called it a "nickname" earlier), like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fnm use lts-latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List off your &lt;em&gt;Node&lt;/em&gt; versions again and the blue highlight should have moved to &lt;code&gt;lts-latest&lt;/code&gt;. You can double-check this by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Uninstalling old versions of &lt;em&gt;Node&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;If you have a version of &lt;em&gt;Node&lt;/em&gt; you're no longer using and which is just cluttering up your hard drive, you can remove it using the &lt;code&gt;uninstall&lt;/code&gt; command. It works like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fnm uninstall 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Updating Fast Node Manager itself
&lt;/h2&gt;

&lt;p&gt;According to &lt;a href="https://github.com/Schniz/fnm/blob/master/README.md" rel="noopener noreferrer"&gt;the GitHub page&lt;/a&gt;, updating &lt;em&gt;fnm&lt;/em&gt; should be as easy as typing this into the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://fnm.vercel.app/install | bash &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--skip-shell&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I couldn't get that to work but there's an easier way: you can &lt;a href="https://github.com/Schniz/fnm/releases" rel="noopener noreferrer"&gt;download the code directly from the releases page&lt;/a&gt; and install it like a normal Windows application (the one you want is probably &lt;em&gt;fnm-windows.zip&lt;/em&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;That was quite a lot to take in, but we're still laying the (metaphorical) foundations for the beautiful website we're going to build. Where this metaphor breaks down is that we tend to think of foundations as being stable. And &lt;em&gt;Node&lt;/em&gt; is under active development which means things change all the time.&lt;/p&gt;

&lt;p&gt;Most of the time, this won't impact us. We can update to the latest version of &lt;em&gt;Node&lt;/em&gt; and our code will sit on top quite happily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do things change so much?
&lt;/h3&gt;

&lt;p&gt;New exploits and vulnerabilities are constantly being found in web servers and Windows itself. It's why your computer keeps nagging you to restart every so often. But the good news is this: nothing we write in &lt;em&gt;Node&lt;/em&gt; will end up on a web server. All of this code is in service of generating nice, comfortable, flat HTML pages (at least on this course). So even if we do encounter terrible security holes (and we will), it won't harm the site you build. But we'll cover that as we go.&lt;/p&gt;

&lt;h1&gt;
  
  
  Quiz
&lt;/h1&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ross-angus/embed/QwWvWEV?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Where as in other operating systems, you can nest quotation marks like this: "Here is the outer quote 'here is the inner quote' and we are back outside again", in Windows we can't use single quotes and we need to "escape" the inner quotes. We'll be covering this in more detail later. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;It hurts to make this joke because I prefer Edge to Chrome. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>node</category>
      <category>windows</category>
      <category>install</category>
      <category>fnm</category>
    </item>
  </channel>
</rss>
