<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>just-enough on Computer Freud</title>
    <link>https://computerfreud.com/tags/just-enough/</link>
    <description>Recent content in just-enough on Computer Freud</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 27 Feb 2026 10:18:24 -0700</lastBuildDate><atom:link href="https://computerfreud.com/tags/just-enough/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Just Enough Python Packaging</title>
      <link>https://computerfreud.com/how-to/python-packaging/</link>
      <pubDate>Fri, 27 Feb 2026 10:18:24 -0700</pubDate>
      
      <guid>https://computerfreud.com/how-to/python-packaging/</guid>
      <description>&lt;h1 id=&#34;just-enough-python-packaging&#34;&gt;Just Enough Python Packaging &lt;a href=&#34;#just-enough-python-packaging&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;Skip poetry. Skip pip. Do not pass go, jump directly to uv. It is without question the best way to manage python packages in 2026.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.astral.sh/uv/&#34;&gt;uv&lt;/a&gt; is an extremely fast Python package and project manager written in Rust. It replaces &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;pip-tools&lt;/code&gt;, &lt;code&gt;venv&lt;/code&gt;, &lt;code&gt;poetry&lt;/code&gt;, &lt;code&gt;pyenv&lt;/code&gt;, and more. If you are still manually managing &lt;code&gt;.venv&lt;/code&gt; folders or fighting with &lt;code&gt;poetry lock&lt;/code&gt; times, it&amp;rsquo;s time to take some precious time back.&lt;/p&gt;
&lt;ol start=&#34;0&#34;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;You need uv.&lt;/strong&gt;&lt;br&gt;
Follow the &lt;a href=&#34;https://docs.astral.sh/uv/getting-started/installation/&#34;&gt;official installation guide&lt;/a&gt;. You shouldn&amp;rsquo;t be installing Python any other way than through &lt;code&gt;uv&lt;/code&gt; at this point.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Init the project.&lt;/strong&gt;&lt;br&gt;
Use the &lt;code&gt;--package&lt;/code&gt; flag. This sets up a standard &lt;code&gt;src&lt;/code&gt; layout which is the industry standard.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uv init my-cool-project --package
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd my-cool-project
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;Project Layout&lt;/strong&gt;&lt;br&gt;
Your project should look like this. This is the opinionated guidance that &lt;code&gt;uv init&lt;/code&gt; gives you, and you should keep it!&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;my-cool-project/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── pyproject.toml   # The brain of the project
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── uv.lock          # The source of truth (don&amp;#39;t touch)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── .python-version  # Managed for you
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── src/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── my_package/  # Your actual code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       └── __init__.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── tests/           # Where the tests live
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;Dependency Management&lt;/strong&gt;&lt;br&gt;
Add what you need through command line.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uv add requests
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also add this in the pyproject.toml.  Make sure you &lt;code&gt;uv sync&lt;/code&gt;!&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;&lt;strong&gt;Dev tools belong in dev groups.&lt;/strong&gt;&lt;br&gt;
Don&amp;rsquo;t pollute your production dependencies with &lt;code&gt;pytest&lt;/code&gt; or &lt;code&gt;ruff&lt;/code&gt;. Use the &lt;code&gt;--dev&lt;/code&gt; flag.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uv add --dev pytest ruff
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;&lt;strong&gt;Extras for the heavy stuff.&lt;/strong&gt;&lt;br&gt;
If your package has &amp;ldquo;optional&amp;rdquo; features (like Excel support or a heavy UI), use extras.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uv add pandas --optional excel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This keeps the base install lightweight. It allows users the freedom to choose what parts of your package to install.&lt;/p&gt;
&lt;ol start=&#34;6&#34;&gt;
&lt;li&gt;&lt;strong&gt;Run your code.&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;uv run&lt;/code&gt;. It ensures you&amp;rsquo;re using the exact versions defined in your lockfile.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uv run python src/my_package/main.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;7&#34;&gt;
&lt;li&gt;&lt;strong&gt;uvx is magic.&lt;/strong&gt;&lt;br&gt;
This is the equivalent of &lt;code&gt;npx&lt;/code&gt; for Node developers. It&amp;rsquo;s perfect for tools like &lt;a href=&#34;https://docs.astral.sh/ruff/&#34;&gt;Ruff&lt;/a&gt;. Ruff is so fast it makes other linters look like they&amp;rsquo;re from another century. It replaces Flake8, Black, and isort in one go.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uvx ruff check .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;todos&#34;&gt;TODOs: &lt;a href=&#34;#todos&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&#34;research-these-yourself&#34;&gt;(Research these yourself!) &lt;a href=&#34;#research-these-yourself&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.astral.sh/uv/concepts/workspaces/&#34;&gt;uv Workspaces&lt;/a&gt; for when your project gets too big.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.astral.sh/uv/guides/publish/&#34;&gt;Building and Publishing&lt;/a&gt; - &lt;code&gt;uv build&lt;/code&gt; is all you need.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.astral.sh/uv/guides/integration/docker/&#34;&gt;Docker integration&lt;/a&gt; - How to stop building 2GB Python images.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.astral.sh/ruff/configuration/&#34;&gt;Ruff Configuration&lt;/a&gt; - Automate your formatting so you never have to think about it again.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Just Enough Hugo</title>
      <link>https://computerfreud.com/how-to/hugo/</link>
      <pubDate>Thu, 10 Jul 2025 17:18:24 -0700</pubDate>
      
      <guid>https://computerfreud.com/how-to/hugo/</guid>
      <description>&lt;h1 id=&#34;just-enough-hugo&#34;&gt;Just Enough Hugo &lt;a href=&#34;#just-enough-hugo&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;&lt;a href=&#34;https://gohugo.io/getting-started/quick-start/&#34;&gt;Follow the quickstart&lt;/a&gt;.  Can&amp;rsquo;t get more straightforward than that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do not follow the advice regarding submodules.&lt;/strong&gt; Submodules are horrible and you should not even think about them.  We will discuss that point again later.&lt;/p&gt;
&lt;p&gt;Stop before you get there, even if you don&amp;rsquo;t have a working site yet.&lt;/p&gt;
&lt;h2 id=&#34;themes&#34;&gt;Themes &lt;a href=&#34;#themes&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As indicated by quickstart, you need a theme.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://themes.gohugo.io/&#34;&gt;https://themes.gohugo.io/&lt;/a&gt; is a good place to look, as is github.  (many of these will point you to the github repo for the theme anyway).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sites will indicate to you to do one of two different things; either submodule the theme or clone it to your repo&lt;/strong&gt;.  Both are terrible choices and there is one that&amp;rsquo;s not discussed upfront.&lt;/p&gt;
&lt;h3 id=&#34;hugo-modules&#34;&gt;Hugo Modules &lt;a href=&#34;#hugo-modules&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Newer versions of Hugo do what could only be done with Golang before.  There is better documentation for this now.&lt;br&gt;
&lt;a href=&#34;https://gohugo.io/hugo-modules/use-modules/&#34;&gt;https://gohugo.io/hugo-modules/use-modules/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re ready for a theme, follow the instructions above.  Once you do it this way, you never go back.  This is a much more lightweight and malleable approach than Git submodules.&lt;/p&gt;
&lt;p&gt;You could technically manage the module with golang directly.  It&amp;rsquo;s probably equivalent at this point.&lt;/p&gt;
&lt;h2 id=&#34;config&#34;&gt;Config &lt;a href=&#34;#config&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://gohugo.io/getting-started/configuration/&#34;&gt;https://gohugo.io/getting-started/configuration/&lt;/a&gt;
Choose Toml,Yaml,or Json according to your preference.&lt;/p&gt;
&lt;p&gt;A good early practice is to start separating config into default, dev, and production configs.
You only need to add &lt;em&gt;specific values&lt;/em&gt; that you want to override in non-default configs.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;config
├── _default
│   └── config.yaml
├── develop
│   └── config.yaml
└── main
    └── config.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You may also need to tie this in to CI in deployments, e.g. based on above:
&lt;code&gt;hugo --environment $GIT_BRANCH&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It should be pointed out this was written by someone more familiar with the &lt;code&gt;config.&lt;/code&gt; convention, which Hugo has apparently changed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With v0.109.0 and earlier the basename of the site configuration file was &lt;code&gt;config&lt;/code&gt; instead of &lt;code&gt;hugo&lt;/code&gt;. You can use either, but should transition to the new naming convention when practical.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;development-loop&#34;&gt;Development Loop &lt;a href=&#34;#development-loop&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Read about &lt;em&gt;archetypes&lt;/em&gt;, set them up and add new content.&lt;br&gt;
&lt;code&gt;hugo new my-cool-archetype/something.md&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;hugo server&lt;/code&gt; to see your stuff locally.&lt;/p&gt;
&lt;h2 id=&#34;deploying-to-production&#34;&gt;Deploying to Production &lt;a href=&#34;#deploying-to-production&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Look into Vercel, Cloudflare, Netlify, Azure Static Sites or other hosting providers with built-in support.&lt;/p&gt;
&lt;p&gt;Connect your repository to it directly.  A common pattern is previewing from PR and deploying production from &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s no reason to roll-your-own with AWS or another static bucket website unless you&amp;rsquo;re really insistent to do that.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Just Enough Node.js</title>
      <link>https://computerfreud.com/how-to/node.js/</link>
      <pubDate>Tue, 22 Oct 2024 20:18:24 -0700</pubDate>
      
      <guid>https://computerfreud.com/how-to/node.js/</guid>
      <description>&lt;h1 id=&#34;just-enough-nodejs&#34;&gt;Just Enough Node.js &lt;a href=&#34;#just-enough-nodejs&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h1&gt;&lt;ol start=&#34;0&#34;&gt;
&lt;li&gt;Create a project folder.  Using the command line, &lt;code&gt;cd&lt;/code&gt; into it.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;you-need-to-get-npm-you-want-nvm-to-get-node-to-get-npm&#34;&gt;You need to get NPM. You want NVM, to get Node, to get NPM. &lt;a href=&#34;#you-need-to-get-npm-you-want-nvm-to-get-node-to-get-npm&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;Start with having, or setting up &lt;a href=&#34;https://github.com/nvm-sh/nvm#installing-and-updating&#34;&gt;NVM&lt;/a&gt;.  You shouldn’t install Node any other way than this by now.&lt;/li&gt;
&lt;li&gt;Once NVM is set up, install a modern-ish version of Node.  You might want to catch up on the &lt;a href=&#34;https://github.com/nodejs/Release&#34;&gt;Node version schedule&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;nvm install 18
nvm use 18
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;As a good practice, create a &lt;code&gt;.nvmrc&lt;/code&gt; file in the project root, containing this version number (like 18 or more specific as needed).
A more precise approach might be:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;node -v &amp;gt; .nvmrc
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Init the project with npm:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;npm init
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;You now have a package.json file.  Using NPM, you can add a package to it as a dependency, like &lt;code&gt;axios&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;npm add axios
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;6&#34;&gt;
&lt;li&gt;You should create a package entrypoint.  If you used defaults for &lt;code&gt;npm init&lt;/code&gt; this should be &lt;code&gt;index.js&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;touch index.js
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;7&#34;&gt;
&lt;li&gt;In &lt;code&gt;index.js&lt;/code&gt;, we need to start using Axios.  We are going to use this to download a file.  We&amp;rsquo;re going to skip ahead a few steps by using the function below.
This also uses the &lt;code&gt;fs&lt;/code&gt; module which is a node built-in.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;axios&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;axios&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;path&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://i.imgflip.com/71soed.jpg&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;download&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;directory&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;filename&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;resolve&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;directory&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;filename&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;writer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;createWriteStream&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;newPath&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;axios&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;responseType&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;stream&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pipe&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;writer&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Promise((&lt;span style=&#34;color:#a6e22e&#34;&gt;resolve&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;reject&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;writer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;finish&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;resolve&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;writer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;error&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;reject&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;download&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image.jpg&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;8&#34;&gt;
&lt;li&gt;This technically achieves making a request, but to add flavor to our CLI workflow we might want to create a download script.  In package.json, add a &amp;ldquo;scripts&amp;rdquo; key if not present at the top-level, and add a &amp;ldquo;download&amp;rdquo; key to that, referencing index.js.  After you do this you could use &lt;code&gt;npm run download&lt;/code&gt; to run your command.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;scripts&amp;#34;: {
    &amp;#34;download&amp;#34;:&amp;#34;node index.js&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;9&#34;&gt;
&lt;li&gt;We have an MVP, but before shipping this we should really consider adding tests.
We can start by adding &lt;code&gt;jest&lt;/code&gt; as a &lt;strong&gt;Dev&lt;/strong&gt; dependency (using &lt;code&gt;-D&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;npm add -D jest
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;10&#34;&gt;
&lt;li&gt;Add &lt;code&gt;jest&lt;/code&gt; to your package.json &lt;code&gt;scripts&lt;/code&gt; key, so it now looks like:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;download&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node ./index.js&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;jest&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;11&#34;&gt;
&lt;li&gt;Fill in a test case in &lt;code&gt;test/index.test.js&lt;/code&gt;.  We&amp;rsquo;ll skip ahead some more, and add the following contents to it:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;index&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;../index.js&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;test&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a file is downloaded&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://i.imgflip.com/71soed.jpg&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;index&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;download&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image.jpg&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fileExists&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;existsSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;./image.jpg&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;expect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fileExists&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;toBe&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;12&#34;&gt;
&lt;li&gt;
&lt;p&gt;Run your new test with &lt;code&gt;npm test&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ooops!  Something failed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It seems to be that we forgot to export the function from our module, let&amp;rsquo;s fix that in &lt;code&gt;index.js&lt;/code&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;download&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;15&#34;&gt;
&lt;li&gt;Try again, and relish that you are now a Node.js dev!&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;npm&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;todos&#34;&gt;TODOs: &lt;a href=&#34;#todos&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&#34;research-these-yourself&#34;&gt;(Research these yourself!) &lt;a href=&#34;#research-these-yourself&#34; class=&#34;anchor&#34;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Some more things to think about from here are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Better abstracting our functions&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Making functions and tests run async&lt;/li&gt;
&lt;li&gt;Setup/teardown of tests&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
